Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

929 lines
23 KiB

/*++
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;
}