// SCRNSAVE.C -- skeleton for screen saver application // 4/5/94 francish merged NT and Win4 saver code, folded in SCRNSAVE.SCR // - 3/14/96: marcfo Pulled this file in from shell\control\scrnsave\common. // All changes marked with GL_SCRNSAVE. #define GL_SCRNSAVE 1 #define WIN31 #include #include #include "scrnsave.h" #include #include #ifdef GL_SCRNSAVE #include "glscrnsv.h" #endif #define DBG_MSGS 0 const TCHAR szScreenSaverKey[] = REGSTR_PATH_SCREENSAVE; TCHAR szPasswordActiveValue[] = REGSTR_VALUE_USESCRPASSWORD; const TCHAR szPasswordValue[] = REGSTR_VALUE_SCRPASSWORD; TCHAR szPwdDLL[] = TEXT("PASSWORD.CPL"); CHAR szFnName[] = "VerifyScreenSavePwd"; // Proc name, must be ANSI TCHAR szImmDLL[] = TEXT("IMM32.DLL"); CHAR szImmFnc[] = "ImmAssociateContext"; // Proc name, must be ANSI #if 0 TCHAR szCoolSaverHacks[] = REGSTR_PATH_SETUP TEXT("\\Screen Savers"); TCHAR szMouseThreshold[] = TEXT("Mouse Threshold"); TCHAR szPasswordDelay[] = TEXT("Password Delay"); #endif typedef BOOL(FAR PASCAL* VERIFYPWDPROC) (HWND); typedef HIMC(FAR PASCAL* IMMASSOCPROC) (HWND, HIMC); // variables declared in SCRNSAVE.H HINSTANCE hMainInstance = 0; HWND hMainWindow = 0; BOOL fChildPreview = FALSE; // other globals POINT ptMouse; BOOL fClosing = FALSE; BOOL fCheckingPassword = FALSE; HINSTANCE hInstPwdDLL = NULL; VERIFYPWDPROC VerifyPassword = NULL; static BOOL preview_like_fullscreen = FALSE; static UINT uShellAutoPlayQueryMessage = 0; HINSTANCE hInstImm = NULL; IMMASSOCPROC ImmFnc = NULL; HIMC hPrevImc = (HIMC)0L; static BOOL fOnWin95 = FALSE; //TRUE if on Chicago, FALSE if on Cairo // random junk DWORD dwWakeThreshold = 4; //default to slight movement DWORD dwPasswordDelay = 0; DWORD dwBlankTime = 0; #define MAX_PASSWORD_DELAY_IN_SECONDS (60) // forward declarations of internal fns #ifndef GL_SCRNSAVE // These are hooked out to glscrnsv.cxx static INT_PTR DoScreenSave(HWND hParent); static INT_PTR DoConfigBox(HWND hParent); #endif static INT_PTR DoSaverPreview(LPCTSTR szUINTHandle); static INT_PTR DoChangePw(LPCTSTR szUINTHandle); static BOOL DoPasswordCheck(HWND hParent); VOID LoadPwdDLL(VOID); VOID UnloadPwdDLL(VOID); // helper for time static DWORD GetElapsedTime(DWORD from, DWORD to) { return (to >= from) ? (to - from) : (1 + to + (((DWORD)-1) - from)); } // helper to convert text to unsigned int static UINT_PTR atoui(LPCTSTR szUINT) { UINT_PTR uValue = 0; while ((*szUINT >= TEXT('0')) && (*szUINT <= TEXT('9'))) uValue = ((uValue * 10) + (*szUINT++ - TEXT('0'))); return uValue; } // Local reboot and hotkey control (on Win95) static void HogMachine(BOOL value) { BOOL dummy; // NT is always secure, therefore we don't need to call this on Cairo/NT if (fOnWin95) { SystemParametersInfo(SPI_SCREENSAVERRUNNING, value, &dummy, 0); } } // entry point (duh) INT_PTR PASCAL WinMainN(HINSTANCE hInst, HINSTANCE hPrev, LPTSTR szCmdLine, int nCmdShow) { LPCTSTR pch = szCmdLine; HWND hParent = 0; OSVERSIONINFO osvi; hMainInstance = hInst; osvi.dwOSVersionInfoSize = sizeof(osvi); fOnWin95 = (GetVersionEx(&osvi) && osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS); #ifdef GL_SCRNSAVE // the shell sends this message to the foreground window before running an // AutoPlay app. we return 1 to cancel autoplay if we are password protected if (fOnWin95) { uShellAutoPlayQueryMessage = RegisterWindowMessage(TEXT("QueryCancelAutoPlay")); } else { uShellAutoPlayQueryMessage = 0; } #endif _try { for (;;) switch (*pch) { case TEXT('S'): case TEXT('s'): return DoScreenSave(NULL); #ifdef GL_SCRNSAVE case TEXT('W'): case TEXT('w'): do pch++; while (*pch == TEXT(' ')); // size parameters return DoWindowedScreenSave(pch); #endif case TEXT('L'): case TEXT('l'): // special switch for tests such as WinBench // this is NOT a hack to make bechmarks look good // it's a hack to allow you to benchmark a screen saver // many bechmarking apps require the whole screen in foreground // which makes it hard to measure how a screensaver adds CPU load // you must provide a parent window (just like preview mode) preview_like_fullscreen = TRUE; case TEXT('P'): case TEXT('p'): do pch++; while (*pch == TEXT(' ')); // skip to the good stuff return DoSaverPreview(pch); case TEXT('A'): case TEXT('a'): if (!fOnWin95) return -1; do pch++; while (*pch == TEXT(' ')); // skip to the good stuff return DoChangePw(pch); case TEXT('C'): case TEXT('c'): { HWND hwndParent = NULL ; // Look for optional parent window after the "C", // syntax is "C:hwnd_value" if (*(++pch) == TEXT(':')) { hwndParent = (HWND)atoui(++pch); } if (hwndParent == NULL || !IsWindow(hwndParent)) hwndParent = GetForegroundWindow(); return DoConfigBox(hwndParent); } case TEXT('\0'): return DoConfigBox(NULL); case TEXT(' '): case TEXT('-'): case TEXT('/'): pch++; // skip spaces and common switch prefixes break; default: return -1; } } _except(UnhandledExceptionFilter(GetExceptionInformation())) { // don't leave local reboot and hotkeys disabled on Win95 HogMachine(FALSE); } } // default screen-saver proc, declared in SCRNSAVE.H // intended to be called by the consumer's ScreenSaverProc where // DefWindowProc would normally be called LRESULT WINAPI DefScreenSaverProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { #if DBG_MSGS TCHAR szBuff[80]; wsprintf(szBuff, TEXT("** DefSSP received:\t0x%04lx 0x%08lx 0x%08lx\n"), uMsg, wParam, lParam); OutputDebugString(szBuff); #endif if (!fChildPreview && !fClosing) { switch (uMsg) { case WM_CLOSE: // Only do password check if on Windows 95. WinNT (Cairo) has // the password check built into the security desktop for // C2 compliance. if (fOnWin95) { if (!DoPasswordCheck(hWnd)) { GetCursorPos(&ptMouse); // re-establish return FALSE; } } #ifdef GL_SCRNSAVE // We need to know when we're being terminated, so we can do // various clean-up stuff SendMessage(hWnd, SS_WM_CLOSING, 0, 0); #endif break; case SCRM_VERIFYPW: if (fOnWin95) return (VerifyPassword ? (LRESULT)VerifyPassword(hWnd) : 1L); break; default: { POINT ptMove, ptCheck; if (fCheckingPassword) break; switch (uMsg) { case WM_SHOWWINDOW: if ((BOOL)wParam) SetCursor(NULL); break; case WM_SETCURSOR: SetCursor(NULL); return TRUE; case WM_MOUSEMOVE: GetCursorPos(&ptCheck); if ((ptMove.x = ptCheck.x - ptMouse.x) && (ptMove.x < 0)) ptMove.x *= -1; if ((ptMove.y = ptCheck.y - ptMouse.y) && (ptMove.y < 0)) ptMove.y *= -1; if (((DWORD)ptMove.x + (DWORD)ptMove.y) > dwWakeThreshold) { PostMessage(hWnd, WM_CLOSE, 0, 0l); ptMouse = ptCheck; } break; case WM_POWERBROADCAST: switch (wParam) { case PBT_APMRESUMECRITICAL: case PBT_APMRESUMESUSPEND: case PBT_APMRESUMESTANDBY: case PBT_APMRESUMEAUTOMATIC: // If the system is resuming from a real suspend // (as opposed to a failed suspend) deactivate the screensaver. if ((lParam & PBTF_APMRESUMEFROMFAILURE) == 0) goto PostClose; break; default: // The standard screensaver code shuts down on // all power broadcast messages. This doesn't // make much sense, but match the behavior so // that all screensavers operate the same way. goto PostClose; } break; case WM_POWER: // a critical resume does not generate a WM_POWERBROADCAST // to windows for some reason, but it does generate an old WM_POWER message. if (wParam == PWR_CRITICALRESUME) goto PostClose; break; case WM_ACTIVATEAPP: if (wParam) break; case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: case WM_KEYDOWN: case WM_SYSKEYDOWN: PostClose: PostMessage(hWnd, WM_CLOSE, 0, 0l); break; } } } } // the shell sends this message to the foreground window before running an // AutoPlay app. On Win95, we return 1 to cancel autoplay if we are password protected // On WinNT, secure screen savers run on a secure separate desktop, and will never see // this message, therefore, this code will never get executed. // BUGBUG - // On NT we don't want to take down the screen saver unless it is running // on the same desktop as the autoplay shell. There is code in the // NT autoplay shell that looks for this and does not run the app if // that is the case; however, I not positive that the uShellAutoPlayQueryMessage // will not go between desktops. (BradG assures me that it will not, but you // never know.) If secure screensavers on NT randomly close when you put // an autoplay cd in the drive, then this code should be examined closely. if ((uMsg == uShellAutoPlayQueryMessage) && uMsg) { PostMessage(hWnd, WM_CLOSE, 0, 0L); return (VerifyPassword != NULL); } return DefWindowProc(hWnd, uMsg, wParam, lParam); } // This window procedure takes care of important stuff before calling the // consumer's ScreenSaverProc. This helps to prevent us from getting hosed by wacky consumer code. LRESULT WINAPI RealScreenSaverProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CREATE: // screen saver does not need the IME if ((hInstImm = GetModuleHandle(szImmDLL)) && (ImmFnc = (IMMASSOCPROC)GetProcAddress(hInstImm, szImmFnc))) hPrevImc = ImmFnc(hWnd, (HIMC)0); GetCursorPos(&ptMouse);// establish the mouse position if (!fChildPreview) SetCursor(NULL); break; case WM_DESTROY: // screen saver does not need the IME if (hInstImm && ImmFnc && hPrevImc) ImmFnc(hWnd, hPrevImc); PostQuitMessage(0); break; case WM_SETTEXT: // don't let some fool change our title // we need to be able to use FindWindow() to find running instances // of full-screen windows screen savers // NOTE: USER slams our title in during WM_NCCREATE by calling the // defproc for WM_SETTEXT directly, so the initial title will get // there. If this ever changes, we can simply set a bypass flag // during WM_NCCREATE processing. return FALSE; case WM_SYSCOMMAND: if (!fChildPreview) { switch (wParam) { case SC_NEXTWINDOW: // no Alt-tabs case SC_PREVWINDOW: // no shift-alt-tabs case SC_SCREENSAVE: // no more screensavers return FALSE; } } break; case WM_HELP: case WM_CONTEXTMENU: if (fChildPreview) { // if we're in preview mode, pump the help stuff to our owner HWND hParent = GetParent(hWnd); if (hParent && IsWindow(hParent)) PostMessage(hParent, uMsg, (WPARAM)hParent, lParam); return TRUE; } break; case WM_TIMER: if (fClosing) return FALSE; Sleep(0); break; case WM_IME_NOTIFY: // Eat IMN_OPENSTATUSWINDOW so that the status window // isn't displayed. if (wParam == IMN_OPENSTATUSWINDOW) { return 0; } break; case WM_MOUSEMOVE: case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: case WM_KEYDOWN: case WM_SYSKEYDOWN: if (fClosing) return DefWindowProc(hWnd, uMsg, wParam, lParam); break; case WM_PAINT: if (fClosing) return DefWindowProc(hWnd, uMsg, wParam, lParam); if (!fChildPreview) SetCursor(NULL); break; } return ScreenSaverProc(hWnd, uMsg, wParam, lParam); } #ifdef GL_SCRNSAVE void #else static void #endif InitRealScreenSave() { #if 0 HKEY hkey; if (RegOpenKey(HKEY_CURRENT_USER, szCoolSaverHacks, &hkey) == ERROR_SUCCESS) { DWORD data, len, type; len = sizeof(data); if ((RegQueryValueEx(hkey, szMouseThreshold, NULL, &type, (LPBYTE)&data, &len) == ERROR_SUCCESS) && (type == REG_DWORD)) { dwWakeThreshold = max(dwWakeThreshold, data); } len = sizeof(data); if ((RegQueryValueEx(hkey, szPasswordDelay, NULL, &type, (LPBYTE)&data, &len) == ERROR_SUCCESS) && (type == REG_DWORD) && data) { data = min(MAX_PASSWORD_DELAY_IN_SECONDS, data); dwPasswordDelay = data * 1000; dwBlankTime = GetTickCount(); } } #endif LoadPwdDLL(); } #ifndef GL_SCRNSAVE static INT_PTR DoScreenSave(HWND hParent) { LPCTSTR pszWindowClass = TEXT("WindowsScreenSaverClass"); LPCTSTR pszWindowTitle; WNDCLASS cls; MSG msg; UINT uStyle; UINT uExStyle; int ncx, ncy; int nx, ny; cls.hCursor = NULL; cls.hIcon = LoadIcon(hMainInstance, MAKEINTATOM(ID_APP)); cls.lpszMenuName = NULL; cls.lpszClassName = pszWindowClass; cls.hbrBackground = GetStockObject(BLACK_BRUSH); cls.hInstance = hMainInstance; cls.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS | CS_OWNDC; cls.lpfnWndProc = RealScreenSaverProc; cls.cbWndExtra = 0; cls.cbClsExtra = 0; if (hParent) { RECT rcParent; GetClientRect(hParent, &rcParent); ncx = rcParent.right; ncy = rcParent.bottom; nx = 0; ny = 0; uStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN; uExStyle = 0; fChildPreview = TRUE; pszWindowTitle = TEXT("Preview"); // MUST differ from full screen } else { HWND hOther; #ifdef SM_CXVIRTUALSCREEN nx = GetSystemMetrics(SM_XVIRTUALSCREEN); ny = GetSystemMetrics(SM_YVIRTUALSCREEN); ncx = GetSystemMetrics(SM_CXVIRTUALSCREEN); ncy = GetSystemMetrics(SM_CYVIRTUALSCREEN); if (ncx == 0 || ncy == 0) #endif { RECT rc; HDC hdc = GetDC(NULL); GetClipBox(hdc, &rc); ReleaseDC(NULL, hdc); nx = rc.left; ny = rc.top; ncx = rc.right - rc.left; ncy = rc.bottom - rc.top; } uStyle = WS_POPUP | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; uExStyle = WS_EX_TOPMOST; pszWindowTitle = TEXT("Screen Saver"); // MUST differ from preview // if there is another NORMAL screen save instance, switch to it hOther = FindWindow(pszWindowClass, pszWindowTitle); if (hOther && IsWindow(hOther)) { SetForegroundWindow(hOther); return 0; } InitRealScreenSave(); } // the shell sends this message to the foreground window before running an // AutoPlay app. we return 1 to cancel autoplay if we are password protected if (fOnWin95) { uShellAutoPlayQueryMessage = RegisterWindowMessage(TEXT("QueryCancelAutoPlay")); } else { uShellAutoPlayQueryMessage = 0; } if (RegisterClass(&cls)) { hMainWindow = CreateWindowEx(uExStyle, pszWindowClass, pszWindowTitle, uStyle, nx, ny, ncx, ncy, hParent, (HMENU)NULL, hMainInstance, (LPVOID)NULL); } if (hMainWindow) { if (!fChildPreview) SetForegroundWindow(hMainWindow); while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } } UnloadPwdDLL();// free password-handling DLL if loaded return msg.wParam; } #endif static INT_PTR DoSaverPreview(LPCTSTR szUINTHandle) { HWND hParent = (HWND)atoui(szUINTHandle);// get parent handle from string // only preview on a valid parent window (NOT full screen) return ((hParent && IsWindow(hParent)) ? DoScreenSave(hParent) : -1); } #ifndef GL_SCRNSAVE static INT_PTR DoConfigBox(HWND hParent) { // let the consumer register any special controls for the dialog if (!RegisterDialogClasses(hMainInstance)) return FALSE; return DialogBox(hMainInstance, MAKEINTRESOURCE(DLG_SCRNSAVECONFIGURE), hParent, (DLGPROC)ScreenSaverConfigureDialog); } #endif static INT_PTR DoChangePw(LPCTSTR szUINTHandle) { // get parent handle from string HWND hParent = (HWND)atoui(szUINTHandle); if (!hParent || !IsWindow(hParent)) hParent = GetForegroundWindow(); ScreenSaverChangePassword(hParent);// allow the library to be hooked return 0; } static const TCHAR szMprDll[] = TEXT("MPR.DLL"); // not to be localized static const TCHAR szProviderName[] = TEXT("SCRSAVE"); // not to be localized #ifdef UNICODE static const CHAR szPwdChangePW[] = "PwdChangePasswordW"; // not to be localized #else static const CHAR szPwdChangePW[] = "PwdChangePasswordA"; // not to be localized #endif // bogus prototype typedef DWORD(FAR PASCAL* PWCHGPROC)(LPCTSTR, HWND, DWORD, LPVOID); void WINAPI ScreenSaverChangePassword(HWND hParent) { HINSTANCE mpr = LoadLibrary(szMprDll); if (mpr) { // netland hasn't cracked MNRENTRY yet PWCHGPROC pwd = (PWCHGPROC)GetProcAddress(mpr, szPwdChangePW); if (pwd) pwd(szProviderName, hParent, 0, NULL); FreeLibrary(mpr); } } static BOOL DoPasswordCheck(HWND hParent) { // don't reenter and don't check when we've already decided if (fCheckingPassword || fClosing) return FALSE; if (VerifyPassword) { static DWORD lastcheck = (DWORD)-1; DWORD curtime = GetTickCount(); MSG msg; if (dwPasswordDelay && (GetElapsedTime(dwBlankTime, curtime) < dwPasswordDelay)) { fClosing = TRUE; goto _didcheck; } // no rapid checking... if ((lastcheck != (DWORD)-1) && (GetElapsedTime(lastcheck, curtime) < 200)) { goto _didcheck; } fCheckingPassword = TRUE;// do the check #ifdef GL_SCRNSAVE // Put ss in idle mode during password dialog processing SendMessage(hParent, SS_WM_IDLE, SS_IDLE_ON, 0L); #endif // flush WM_TIMER messages before putting up the dialog PeekMessage(&msg, hParent, WM_TIMER, WM_TIMER, PM_REMOVE | PM_NOYIELD); PeekMessage(&msg, hParent, WM_TIMER, WM_TIMER, PM_REMOVE | PM_NOYIELD); // call the password verify proc fClosing = (BOOL)SendMessage(hParent, SCRM_VERIFYPW, 0, 0L); fCheckingPassword = FALSE; #ifdef GL_SCRNSAVE SendMessage(hParent, SS_WM_IDLE, SS_IDLE_OFF, 0L);// Restore normal display mode #endif if (!fClosing) SetCursor(NULL); lastcheck = GetTickCount();// curtime may be outdated by now } else { // passwords disabled or unable to load handler DLL, always allow exit fClosing = TRUE; } _didcheck: return fClosing; } // stolen from the CRT, used to shirink our code int _stdcall DummyEntry(void) { int i; STARTUPINFO si; LPTSTR pszCmdLine = GetCommandLine(); if (*pszCmdLine == TEXT('\"')) { /* * Scan, and skip over, subsequent characters until another double-quote or a null is encountered. */ while (*(pszCmdLine = CharNext(pszCmdLine)) && (*pszCmdLine != TEXT('\"'))); /* * If we stopped on a double-quote (usual case), skip over it. */ if (*pszCmdLine == TEXT('\"')) pszCmdLine++; } else { while ((UINT)*pszCmdLine > (UINT)TEXT(' ')) pszCmdLine = CharNext(pszCmdLine); } /* * Skip past any white space preceeding the second token. */ while (*pszCmdLine && ((UINT)*pszCmdLine <= (UINT)TEXT(' '))) { pszCmdLine = CharNext(pszCmdLine); } si.dwFlags = 0; GetStartupInfo(&si); i = (int)WinMainN(GetModuleHandle(NULL), NULL, pszCmdLine, si.dwFlags & STARTF_USESHOWWINDOW ? si.wShowWindow : SW_SHOWDEFAULT); ExitProcess(i); return i;// We never comes here. } // main() entry point to satisfy old NT screen savers void _cdecl main(int argc, char* argv[]) { DummyEntry(); } // WinMain() entry point to satisfy old NT screen savers int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int nCmdShow) { DummyEntry(); return 0; // reference unreferenced parameters (void)hInst; (void)hPrev; (void)szCmdLine; (void)nCmdShow; } VOID LoadPwdDLL(VOID) { HKEY hKey; if (!fOnWin95) return; if (hInstPwdDLL) UnloadPwdDLL(); // look in registry to see if password turned on, otherwise don't // bother to load password handler DLL if (RegOpenKey(HKEY_CURRENT_USER, szScreenSaverKey, &hKey) == ERROR_SUCCESS) { DWORD dwVal, dwSize = sizeof(dwVal); if ((RegQueryValueEx(hKey, szPasswordActiveValue, NULL, NULL, (BYTE*)&dwVal, &dwSize) == ERROR_SUCCESS) && dwVal) { // try to load the DLL that contains password proc. hInstPwdDLL = LoadLibrary(szPwdDLL); if (hInstPwdDLL) { VerifyPassword = (VERIFYPWDPROC)GetProcAddress(hInstPwdDLL, szFnName); if (VerifyPassword) HogMachine(TRUE); else UnloadPwdDLL(); } } RegCloseKey(hKey); } } VOID UnloadPwdDLL(VOID) { if (!fOnWin95) return; if (hInstPwdDLL) { FreeLibrary(hInstPwdDLL); hInstPwdDLL = NULL; if (VerifyPassword) { VerifyPassword = NULL; HogMachine(FALSE); } } } // compatbility stuff (to make porting easier) TCHAR szAppName[APPNAMEBUFFERLEN]; TCHAR szName[TITLEBARNAMELEN]; TCHAR szIniFile[MAXFILELEN]; TCHAR szScreenSaver[22]; TCHAR szHelpFile[MAXFILELEN]; TCHAR szNoHelpMemory[BUFFLEN]; // Quick fix for old screen savers that don't know about context sensitive help UINT MyHelpMessage = WM_HELP;