|
|
/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
scrnsave.c
Abstract:
This source file implements the seven required functions for a Windows NT 5.0 migration DLL. The DLL demonstrates how the interface works by performing the Windows 9x screen saver upgrade.
The source here is a subset of the actual screen saver DLL that ships with Windows NT Setup.
This sample demonstrates:
- How to detect installation of your application
- A typical implementation of QueryVersion, Initialize9x, MigrateUser9x, MigrateSystem9x, InitializeNT, MigrateUserNT and MigraetSystemNT
- How to provide language-dependent incompatibility messages to the user
- How to remove Setup's incompatibility messages via [Handled] section
- Saving settings to a temporarly file in the working directory
- Mix use of ANSI and UNICODE
- Use of the SetupLogError API
- Deleting files
- Handling the move from system to system32
Author:
Jim Schmidt 11-Apr-1997
Revision History:
--*/
#include "pch.h"
BOOL pLoadFileNames ( VOID );
//
// Constants
//
#define CP_USASCII 1252
#define CP_JAPANESE 932
#define CP_CHT 950
#define CP_CHS 936
#define CP_KOREAN 949
#define END_OF_CODEPAGES -1
//
// Code page array
//
INT g_CodePageArray[] = { CP_USASCII, CP_JAPANESE, CP_CHT, CP_CHS, CP_KOREAN, END_OF_CODEPAGES };
//
// Multi-sz (i.e., double-nul terminated) list of files to find
//
CHAR g_ExeNamesBuf[1024]; CHAR g_MyProductId[MAX_PATH]; CHAR g_DefaultUser[MAX_PATH];
//
// Copies of the working directory and source directory
//
LPSTR g_WorkingDirectory = NULL; LPSTR g_SourceDirectories = NULL; // multi-sz
LPSTR g_SettingsFile = NULL; LPSTR g_MigrateDotInf = NULL;
//
// Registry locations and INI file sections
//
#define REGKEY_DESKTOP "Control Panel\\Desktop"
#define FULL_REGKEY_DESKTOP "HKR\\Control Panel\\Desktop"
#define FULL_REGKEY_PWD_PROVIDER "HKLM\\System\\CurrentControlSet\\Control\\PwdProvider\\SCRSAVE"
#define REGVAL_SCREENSAVEACTIVE "ScreenSaveActive"
#define REGVAL_SCREENSAVELOWPOWERACTIVE "ScreenSaveLowPowerActive"
#define REGVAL_SCREENSAVELOWPOWERTIMEOUT "ScreenSaveLowPowerTimeout"
#define REGVAL_SCREENSAVEPOWEROFFACTIVE "ScreenSavePowerOffActive"
#define REGVAL_SCREENSAVEPOWEROFFTIMEOUT "ScreenSavePowerOffTimeout"
#define REGVAL_SCREENSAVETIMEOUT "ScreenSaveTimeOut"
#define REGVAL_SCREENSAVEUSEPASSWORD "ScreenSaveUsePassword"
#define REGVAL_LOWPOWERACTIVE "LowPowerActive"
#define REGVAL_LOWPOWERTIMEOUT "LowPowerTimeout"
#define REGVAL_POWEROFFACTIVE "PowerOffActive"
#define REGVAL_POWEROFFTIMEOUT "PowerOffTimeout"
#define REGVAL_SCREENSAVERISSECURE "ScreenSaverIsSecure"
//
// State variables
//
BOOL g_FoundPassword = FALSE; LPCSTR g_User; CHAR g_UserNameBuf[MAX_PATH]; HANDLE g_hHeap; HINSTANCE g_hInst;
BOOL WINAPI DllMain ( IN HANDLE DllInstance, IN ULONG ReasonForCall, IN LPVOID Reserved ) { PCSTR Message;
switch (ReasonForCall) {
case DLL_PROCESS_ATTACH: //
// We don't need DLL_THREAD_ATTACH or DLL_THREAD_DETACH messages
//
DisableThreadLibraryCalls (DllInstance);
//
// Global init
//
g_hInst = DllInstance; g_hHeap = GetProcessHeap();
if (!MigInf_Initialize()) { return FALSE; }
Message = ParseMessage (MSG_PRODUCT_ID); if (Message) { _mbscpy (g_MyProductId, Message); FreeMessage (Message); }
Message = ParseMessage (MSG_DEFAULT_USER); if (Message) { _mbscpy (g_DefaultUser, Message); FreeMessage (Message); }
// Open log; FALSE means do not delete existing log
SetupOpenLog (FALSE); break;
case DLL_PROCESS_DETACH: MigInf_CleanUp();
// Clean up strings
if (g_WorkingDirectory) { HeapFree (g_hHeap, 0, g_WorkingDirectory); } if (g_SourceDirectories) { HeapFree (g_hHeap, 0, g_SourceDirectories); } if (g_SettingsFile) { HeapFree (g_hHeap, 0, g_SettingsFile); } if (g_MigrateDotInf) { HeapFree (g_hHeap, 0, g_MigrateDotInf); }
SetupCloseLog();
break; }
return TRUE; }
LONG CALLBACK QueryVersion ( OUT LPCSTR *ProductID, OUT LPUINT DllVersion, OUT LPINT *CodePageArray, OPTIONAL OUT LPCSTR *ExeNamesBuf, OPTIONAL LPVOID Reserved ) { //
// Complete load of string resources, act like not installed
// on resource error (unexpected condition).
//
if (!g_MyProductId[0] || !g_DefaultUser[0]) { return ERROR_NOT_INSTALLED; }
if (!pLoadFileNames()) { return ERROR_NOT_INSTALLED; }
//
// We do some preliminary investigation to see if
// our components are installed.
//
if (!GetScrnSaveExe()) { //
// We didn't detect any components, so we return
// ERROR_NOT_INSTALLED and the DLL will stop being called.
// Use this method as much as possible, because user enumeration
// for MigrateUser9x is relatively slow. However, don't spend too
// much time here because QueryVersion is expected to run quickly.
//
return ERROR_NOT_INSTALLED; }
//
// Screen saver is enabled, so tell Setup who we are. ProductID is used
// for display, so it must be localized. The ProductID string is
// converted to UNICODE for use on Windows NT via the MultiByteToWideChar
// Win32 API. It uses the same code page as FormatMessage to do
// its conversion.
//
*ProductID = g_MyProductId;
//
// Report our version. Zero is reserved for use by DLLs that
// ship with Windows NT.
//
*DllVersion = 1;
//
// We return an array that has all ANSI code pages that we have
// text for.
//
// Tip: If it makes more sense for your DLL to use locales,
// return ERROR_NOT_INSTALLED if the DLL detects that an appropriate
// locale is not installed on the machine.
//
*CodePageArray = g_CodePageArray;
//
// ExeNamesBuf - we pass a list of file names (the long versions)
// and let Setup find them for us. Keep this list short because
// every instance of the file on every hard drive will be reported
// in migrate.inf.
//
// Most applications don't need this behavior, because the registry
// usually contains full paths to installed components. We need it,
// though, because there are no registry settings that give us the
// paths of the screen saver DLLs.
//
*ExeNamesBuf = g_ExeNamesBuf;
return ERROR_SUCCESS; }
LONG CALLBACK Initialize9x ( IN LPCSTR WorkingDirectory, IN LPCSTR SourceDirectories, LPVOID Reserved ) { INT Len; //
// Because we returned ERROR_SUCCESS in QueryVersion, we are being
// called for initialization. Therefore, we know screen savers are
// enabled on the machine at this point.
//
//
// Make global copies of WorkingDirectory and SourceDirectories --
// we will not get this information again, and we shouldn't
// count on Setup keeping the pointer valid for the life of our
// DLL.
//
Len = CountStringBytes (WorkingDirectory); g_WorkingDirectory = HeapAlloc (g_hHeap, 0, Len);
if (!g_WorkingDirectory) { return GetLastError(); }
CopyMemory (g_WorkingDirectory, WorkingDirectory, Len);
Len = CountMultiStringBytes (SourceDirectories); g_SourceDirectories = HeapAlloc (g_hHeap, 0, Len);
if (!g_SourceDirectories) { return GetLastError(); }
CopyMemory (g_SourceDirectories, SourceDirectories, Len);
//
// Now create our private 'settings file' path
//
GenerateFilePaths();
//
// Return success to have MigrateUser9x called
//
// Tip: A DLL can save system settings during Initialize9x as
// well as MigrateSystem9x.
//
//
return ERROR_SUCCESS; }
LONG CALLBACK MigrateUser9x ( IN HWND ParentWnd, IN LPCSTR UnattendFile, IN HKEY UserRegKey, IN LPCSTR UserName, LPVOID Reserved ) { HKEY RegKey; LPCSTR ScrnSaveExe; DWORD rc = ERROR_SUCCESS; LPSTR SectionNameBuf, p; DWORD SectionNameSize; DWORD Len;
//
// This DLL does not require input from the user to upgrade
// their settings, so ParentWnd is not used. Avoid displaying
// any user interface when possible.
//
// We don't need to use UnattendFile settings because we are not
// a service (such as a network redirector). Therefore, we do not
// use the UnattendFile parameter.
//
// We don't have any files that need to be generated or expanded on
// the NT side of Setup, so we do not write to the
// [NT Disk Space Requirements] section of migrate.inf.
//
//
// We must collect a few registry keys:
//
// HKCU\Control Panel\Desktop
// ScreenSaveActive
// ScreenSaveLowPowerActive
// ScreenSaveLowPowerTimeout
// ScreenSavePowerOffActive
// ScreenSavePowerOffTimeout
// ScreenSaveTimeOut
// ScreenSaveUsePassword
//
// If ScreenSave_Data exists, we tell the user that their
// password is not supported by writing an incompatiility
// message.
//
//
// Save the user name in a global so our utils write to the
// correct section.
//
if (UserName) { g_User = UserName; } else { g_User = g_DefaultUser; }
// OpenRegKey is our utility (in utils.c)
RegKey = OpenRegKey (UserRegKey, REGKEY_DESKTOP); if (!RegKey) { //
// User's registry is invalid, so skip the user
//
return ERROR_NOT_INSTALLED; }
//
// Note: NO changes allowed on Win9x side, we can only read our
// settings and save them in a file.
//
if (!CopyRegValueToDatFile (RegKey, REGVAL_SCREENSAVEACTIVE) || !CopyRegValueToDatFile (RegKey, REGVAL_SCREENSAVELOWPOWERACTIVE) || !CopyRegValueToDatFile (RegKey, REGVAL_SCREENSAVELOWPOWERTIMEOUT) || !CopyRegValueToDatFile (RegKey, REGVAL_SCREENSAVEPOWEROFFACTIVE) || !CopyRegValueToDatFile (RegKey, REGVAL_SCREENSAVEPOWEROFFTIMEOUT) || !CopyRegValueToDatFile (RegKey, REGVAL_SCREENSAVETIMEOUT) || !CopyRegValueToDatFile (RegKey, REGVAL_SCREENSAVEUSEPASSWORD) ) { rc = GetLastError(); }
if (atoi (GetRegValueString (RegKey, REGVAL_SCREENSAVEUSEPASSWORD))) { // Queue change so there is only one message
g_FoundPassword = TRUE; }
//
// Save EXE location in our dat file
//
ScrnSaveExe = GetScrnSaveExe();
if (ScrnSaveExe) { if (!SaveDatFileKeyAndVal (S_SCRNSAVE_EXE, ScrnSaveExe)) { rc = GetLastError(); } }
//
// Copy control.ini sections to our dat file
//
SectionNameSize = 32768; SectionNameBuf = (LPSTR) HeapAlloc (g_hHeap, 0, SectionNameSize); if (!SectionNameBuf) { return GetLastError(); }
GetPrivateProfileString ( NULL, NULL, S_DOUBLE_EMPTY, SectionNameBuf, SectionNameSize, S_CONTROL_INI );
Len = _mbslen (S_SCRNSAVE_DOT); for (p = SectionNameBuf ; *p ; p = _mbschr (p, 0) + 1) { //
// Determine if section name has "Screen Saver." at the beginning
//
if (!_mbsnicmp (p, S_SCRNSAVE_DOT, Len)) { //
// It does, so save it to our private file
//
SaveControlIniSection (p, p + Len); } }
CloseRegKey (RegKey);
if (rc != ERROR_SUCCESS) { LOG ((LOG_ERROR, MSG_PROCESSING_ERROR, g_User, rc)); } else { //
// Write handled for every setting we are processing. Because this
// DLL supports only some of the values in the Desktop key, we must
// be very specific as to which values are actually handled. If
// your DLL handles all registry values AND subkeys of a registry
// key, you can specify NULL in the second parameter of
// MigInf_AddHandledRegistry.
//
MigInf_AddHandledRegistry (FULL_REGKEY_DESKTOP, REGVAL_SCREENSAVEACTIVE); MigInf_AddHandledRegistry (FULL_REGKEY_DESKTOP, REGVAL_SCREENSAVELOWPOWERACTIVE); MigInf_AddHandledRegistry (FULL_REGKEY_DESKTOP, REGVAL_SCREENSAVELOWPOWERTIMEOUT); MigInf_AddHandledRegistry (FULL_REGKEY_DESKTOP, REGVAL_SCREENSAVEPOWEROFFACTIVE); MigInf_AddHandledRegistry (FULL_REGKEY_DESKTOP, REGVAL_SCREENSAVEPOWEROFFTIMEOUT); MigInf_AddHandledRegistry (FULL_REGKEY_DESKTOP, REGVAL_SCREENSAVETIMEOUT); //
// We do not say that we handle REGVAL_SCREENSAVEUSEPASSWORD when we write
// an incompatibility message for it. If we did, we would be suppressing
// our own message!
//
if (!g_FoundPassword) { MigInf_AddHandledRegistry (FULL_REGKEY_DESKTOP, REGVAL_SCREENSAVEUSEPASSWORD); } }
return rc; }
LONG CALLBACK MigrateSystem9x ( IN HWND ParentWnd, IN LPCSTR UnattendFile, LPVOID Reserved ) { HINF MigrateInf; INFCONTEXT ic; CHAR FileName[MAX_PATH*2]; PCSTR Message;
//
// We handle the password provider incompatibility
//
MigInf_AddHandledRegistry (FULL_REGKEY_PWD_PROVIDER, NULL);
//
// Write incompatibility message if necessary (detected in MigrateUser9x)
//
if (g_FoundPassword) { Message = ParseMessage (MSG_PASSWORD_ALERT); MigInf_AddMessage (g_MyProductId, Message); FreeMessage (Message);
MigInf_AddMessageRegistry ( g_MyProductId, FULL_REGKEY_DESKTOP, REGVAL_SCREENSAVEUSEPASSWORD ); }
//
// Use Setup APIs to scan migration paths section
//
MigrateInf = SetupOpenInfFile ( g_MigrateDotInf, NULL, INF_STYLE_WIN4, NULL );
if (MigrateInf != INVALID_HANDLE_VALUE) { if (SetupFindFirstLine (MigrateInf, S_MIGRATION_PATHS, NULL, &ic)) { do { if (SetupGetStringField (&ic, 0, FileName, MAX_PATH, NULL)) { //
// We will be deleting the file, so we must notify Setup
// by writing an entry to [Moved] that has an empty right
// side.
//
MigInf_AddMovedFile (FileName, ""); } } while (SetupFindNextLine (&ic, &ic)); } SetupCloseInfFile (MigrateInf); } else { return GetLastError(); }
//
// Write memory version of migrate.inf to disk
//
if (!MigInf_WriteInfToDisk()) { return GetLastError(); }
return ERROR_SUCCESS; }
LONG CALLBACK InitializeNT ( IN LPCWSTR WorkingDirectory, IN LPCWSTR SourceDirectories, LPVOID Reserved ) { INT Length; LPCWSTR p;
//
// Save our working directory and source directory. We
// convert UNICODE to ANSI, and we use the system code page.
//
//GetSystemCodePage
//
// Compute length of source directories
//
p = SourceDirectories; while (*p) { p = wcschr (p, 0) + 1; } p++; Length = (p - SourceDirectories) / sizeof (WCHAR);
//
// Convert the directories from UNICODE to DBCS. This DLL is
// compiled in ANSI.
//
g_WorkingDirectory = (LPSTR) HeapAlloc (g_hHeap, 0, MAX_PATH); if (!g_WorkingDirectory) { return GetLastError(); }
WideCharToMultiByte ( CP_ACP, 0, WorkingDirectory, -1, g_WorkingDirectory, MAX_PATH, NULL, NULL );
g_SourceDirectories = (LPSTR) HeapAlloc (g_hHeap, 0, Length * sizeof(WCHAR)); if (!g_SourceDirectories) { return GetLastError(); }
WideCharToMultiByte ( CP_ACP, 0, SourceDirectories, Length, g_SourceDirectories, Length * sizeof (WCHAR), NULL, NULL );
//
// Now generate the derived file names
//
GenerateFilePaths();
//
// Note: We have no use for g_SourceDirectories for the screen saver
// upgrade. The g_SourceDirectories string points to the Windows
// NT media (i.e. e:\i386) and optional directories specified on
// the WINNT32 command line.
//
return ERROR_SUCCESS; }
LONG CALLBACK MigrateUserNT ( IN HINF UnattendInfHandle, IN HKEY UserRegKey, IN LPCWSTR UserName, LPVOID Reserved ) { HKEY DesktopRegKey; DWORD rc = ERROR_SUCCESS; BOOL b = TRUE;
//
// Setup gives us the UnattendInfHandle instead of the file name,
// so we don't have to open the inf file repeatitively. Since
// Setup opened the handle, let Setup close it.
//
//
// Convert UserName to ANSI
//
if (UserName) { WideCharToMultiByte ( CP_ACP, 0, UserName, -1, g_UserNameBuf, MAX_PATH, NULL, NULL );
g_User = g_UserNameBuf; } else { g_User = g_DefaultUser; }
//
// Setup copies all of the Win9x registry, EXCEPT for the registry
// keys that are suppressed in usermig.inf or wkstamig.inf.
//
// We need the HKCU\Control Panel\Desktop key, and because this is
// an OS key, the settings have been altered. Most applications
// store their settings in HKCU\Software, HKLM\Software or
// HKCC\Software, and all three of these keys are copied in their
// entirety (except the operating system settings in
// Software\Microsoft\Windows).
//
// When the non-OS software settings are copied from Win9x to NT, Setup
// sometimes alters their value. For example, all registry values
// that point to a file that was moved from SYSTEM to SYSTEM32
// are modified to point to the right place.
//
//
// Note: we use CreateRegKey here, but actually the key always exists
// because the NT defaults have been copied into the user's registry
// already. This approach reduces the possibility of failure.
//
DesktopRegKey = CreateRegKey (UserRegKey, REGKEY_DESKTOP); if (!DesktopRegKey) { rc = GetLastError(); LOG ((LOG_ERROR, MSG_REGISTRY_ERROR, g_User, rc));
return rc; }
// The variable b is used to fall through when we fail unexpectedly
b = TranslateGeneralSetting ( DesktopRegKey, REGVAL_SCREENSAVEACTIVE, NULL );
if (b) { b = TranslateGeneralSetting ( DesktopRegKey, REGVAL_SCREENSAVELOWPOWERACTIVE, REGVAL_LOWPOWERACTIVE ); } if (b) { b = TranslateGeneralSetting ( DesktopRegKey, REGVAL_SCREENSAVELOWPOWERTIMEOUT, REGVAL_LOWPOWERTIMEOUT ); } if (b) { b = TranslateGeneralSetting ( DesktopRegKey, REGVAL_SCREENSAVEPOWEROFFACTIVE, REGVAL_POWEROFFACTIVE ); } if (b) { b = TranslateGeneralSetting ( DesktopRegKey, REGVAL_SCREENSAVEPOWEROFFTIMEOUT, REGVAL_POWEROFFTIMEOUT ); } if (b) { b = TranslateGeneralSetting ( DesktopRegKey, REGVAL_SCREENSAVETIMEOUT, NULL ); } if (b) { b = TranslateGeneralSetting ( DesktopRegKey, REGVAL_SCREENSAVEUSEPASSWORD, REGVAL_SCREENSAVERISSECURE ); }
if (b) { b = SaveScrName (DesktopRegKey, S_SCRNSAVE_EXE); }
if (b) { //
// For screen savers work differently on Win9x and NT, perform
// translation.
//
TranslateScreenSavers (UserRegKey); //
// The other settings just need to be copied from control.ini
// to the registry.
//
CopyUntranslatedSettings (UserRegKey); }
CloseRegKey (DesktopRegKey);
//
// Always return success, because if an error occurred for one user,
// we don't have a reason not to process the next user. If your DLL
// runs into a fatal problem, such as a disk space shortage, you
// should return the error.
//
return ERROR_SUCCESS; }
LONG CALLBACK MigrateSystemNT ( IN HINF UnattendInfHandle, LPVOID Reserved ) { CHAR FileName[MAX_PATH]; HINF MigrateInf; INFCONTEXT ic;
//
// We now delete the Win9x screen savers that were replaced
// by Windows NT.
//
MigrateInf = SetupOpenInfFile ( g_MigrateDotInf, NULL, INF_STYLE_WIN4, NULL );
if (MigrateInf != INVALID_HANDLE_VALUE) {
//
// Use Setup APIs to scan migration paths section
//
if (SetupFindFirstLine (MigrateInf, S_MIGRATION_PATHS, NULL, &ic)) { do { if (SetupGetStringField (&ic, 0, FileName, MAX_PATH, NULL)) { //
// All 32-bit binaries located in the Win9x system directory
// were moved to system32. However, since we listed the
// screen savers in [Migration Paths], the screen savers were
// not moved.
//
// Now delete the file. Ignore errors because user may have
// lost power, and we may be going through this a second time.
//
if (!DeleteFile (FileName)) { if (GetLastError() != ERROR_FILE_NOT_FOUND) { LOG ((LOG_ERROR, MSG_DELETEFILE_ERROR)); } } else { LOG ((LOG_INFORMATION, MSG_DELETEFILE_SUCCESS, FileName)); } } } while (SetupFindNextLine (&ic, &ic)); } SetupCloseInfFile (MigrateInf); }
return ERROR_SUCCESS; }
BOOL pLoadFileNames ( VOID ) { PSTR p; PCSTR Message;
Message = ParseMessage (MSG_FILENAMES); _mbscpy (g_ExeNamesBuf, Message); FreeMessage (Message);
if (!g_ExeNamesBuf[0]) { return FALSE; }
p = g_ExeNamesBuf;
while (*p) { if (_mbsnextc (p) == '|') { *p = 0; }
p = _mbsinc (p); }
return TRUE; }
|