|
|
/************************************************************\
FILE: iernonce.cpp
CREATED: October 16, 1996
HISTORY: Initial version from BryanSt. Modified extensively for UI, root dependencies, bug fixes, etc. by PritObla.
COPYRIGHT: Copyright (c) 1996 Microsoft Coporation.
DEFINITIONS: SECTION - The old RunOnce API is in a flat section. The RunOnceEx API groups entries into sections for managability. If the Status dialog is displayed, it will display each section name while it's being processed. Example sections for Internet Explorer would be: ActiveX, Java, Internet Explorer. ENTRY - Entries are listed in a section. These are commands that need to be carried out. The commands can be a string to execute, a DLL to register (regsvr32), a DLL to unregister (regsvr32 -u), or to call a function in a DLL that has WinMain parameters (rundll32).
DESCRIPTION: 1.0 Goal Currently the "Run" and "RunOnce" feature in Explorer.exe will shell execute commands when the shell starts up. Several features are needed for this to be more robust for Internet Explorer 4.0.
The replacements are RunEx for Run and RunOnceEx for RunOnce. The main features are: � Status - A dialog will be displayed while the items are being processed. Internet Explorer 4.0 adds at least 30 items which take more than a minute on 486 machines. The entries to be processed will be grouped into sections and the dialog will highlight the current section being processed. The status dialog feature can be turned off by using a flag. � Performance - The majority of the Run and RunOnce commands are calls to regsvr32.exe and runonce32.exe. These create separate processes which is very inefficient. This API will not create separate processes for DLLs that need to be called in the same way that regsvr32.exe or rundll32.exe calls DLLs. This API also supports a dependency list of DLLs that should remain loaded while either all the sections or some of the sections are being processed. � Error Handling - If an exception occurs while calling a function in a DLL, the exception will be caught and error dialog will be displayed to the user. This error dialog can be suppressed using a flag in the API, but this is recommended only for cases where a debug is installed because it will crash explorer.exe. If a flag is set, an error and/or logging file can be generated in %WinDir% for debugging purposes. � Deterministic - Currently the order that the entries are processed is not deterministic. This new API will sort the entries and sections alphabetically to force a deterministic order. The current API will not wait until one entry is finished before starting the next entry, except for RunOnce items in Local_Machine. In order to be deterministic, this API will process command synchronously unless a flag is set to turned off this functionality. � Export Functionality - This API will be added to shell32.dll and called from explorer.exe. API functions will be exported so other applications can use this functionality if needed.
2.0 Registry Structure 2.1 RegistryFormat Text in italics are strings generated by the user.
[Local_Machine] or [Current_User] SOFTWARE\Microsoft\Windows\CurrentVersion\RunEx (same as RunOnceEx below) SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceEx Flags = [DWORD] 0x0000000 Title = [STR] "Status Dialog Title" (Example, "Installing Internet Explorer 4.0") Depend\ <PlaceHolder1> = [STR] "<DLL/OCX Filename>" (filename with or without the path) <PlaceHolder...> = [STR] "<DLL/OCX Filename>" (filename with or without the path) <SectionPlaceHolder1>\ @="SectionName1" <Entry1> = [STR] "<EntryFormat>" (See Entry Format description below) <Entry...> = [STR] "<EntryFormat >" Depend\ <PlaceHolder1> = [STR] "<DLL/OCX Filename>" <PlaceHolder...> = [STR] "<DLL/OCX Filename>" < SectionPlaceHolder2>\
2.1.1 Flags // Old RunOnce Flags
#define RRA_DEFAULT 0x0000
#define RRA_DELETE 0x0001
#define RRA_WAIT 0x0002
#define RRA_SHELLSERVICEOBJECTS 0x0004
// New RunOnceEx Flags
#define RRAEX_NO_ERROR_DIALOGS 0x0008
#define RRAEX_ERRORFILE 0x0010
#define RRAEX_LOG_FILE 0x0020
#define RRAEX_NO_EXCEPTION_TRAPPING 0x0040
#define RRAEX_NO_STATUS_DIALOG 0x0080
#define RRAEX_IGNORE_REG_FLAGS 0x0100
RRA_DEFAULT - All features are off but can be turned on by the flags in the Flags registry key. RRA_DELETE - Delete the registry entries after processing them. Normally used for RunOnceEx and not used for RunEx. RRA_WAIT - If the entry is a command to be executed, this flag causes the items to be processed synchronously. RRA_SHELLSERVICEOBJECTS - If this flag is set, we load inproc dlls from the registry key and QI them for IOleCommandTarget. CGID_ShellServiceObject notifications are sent to these objects letting them know about shell status. RRAEX_NO_STATUS_DIALOG - Use this flag to cause the Status dialog to not be displayed while processing the entries. RRAEX_ NO_ERROR_DIALOGS - This flag will turn off error dialogs from being displayed. RRAEX_ERRORFILE - This flag will cause the file "C:\Windows\RunOnceEx.err" to be created if errors occur. RRAEX_LOG_FILE - This flag will cause the file "C:\Windows\RunOnceEx.log" to be created giving the status of commands being executed. (For debugging) RRAEX_ NO_EXCEPTION_TRAPPING - If this flag is set, exceptions will not be caught when registering DLLs. This should only be used when a debug is setup or buggy DLLs will cause explorer.exe to crash. RRAEX_IGNORE_REG_FLAGS - If this flag is set, then the Flags registry entry will be ignored.
The Flags registry key will be deleted if the RRAEX_DELETE flag was set.
2.1.2 Title The Status Dialog�s title will be set to Title registry key if it exists. This key will be deleted if the RRAEX_DELETE flag was set. 2.1.3 Top Level Depend Entries Each entry under the Depend branch will be read. These registry entries need to be type STR and the data needs to be a valid filename with or without a path. The registry value will be ignored. LoadLibrary() will be called on each entry and they will remain loaded until all the sections have been processed. This key will be deleted if the RRA_DELETE flag was set.
Example: SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceEx\Depend "Should stay loaded" = [STR] "shell32.dll" 2.1.4 Sections Each non-"Depend" keys under RunEx and RunOnceEx is considered a section. The names of the Sections are read into memory, sorted alphabetically. The key name is displayed in the Status Dialog if the key doesn�t have data to specify the Display Name. The sections will be processed in alphabetic order. The Section�s branch in the registry will be deleted after being processed if the RRAEX_DELETE flag was set.
Each Section will contain "entries" which are string registry keys. The registry entry value is only used to sort the entries. The registry entry data will be in the format specified in 2.2 Entry Format.
Example: SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceEx\IE4 03 Java @="Java" "IE4 01 Java" = [STR] "||my.exe -quiet -url http://www.microsft.com/" "IE4 02 Main Java File" = [STR] "msjava.dll|DllRegisterServer"
If the Section has a Display Name, then the key name should contain an abbreviation of the product followed by a number that specifies the order that this section should be processed in. Because the section names are sorted alphabetically, this will make sure that all entries for one product are processed together and that the number makes the order obvious.
An Example: SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceEx\IE4 03 Java @="Java" SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceEx\IE4 04 Scripting @="ActiveX Scripting"
2.1.4.1 Section Dependencies The list off DLLs in the section�s Depend key are loaded before a section�s entries are executed and then unloaded after they have been executed. This key will be deleted if the RRA_DELETE flag was set.
Example: SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceEx\Java "IE4 01 Java" = [STR] "||my.exe -quiet -url http://www.microsft.com/" "IE4 02 Java" = [STR] "shdocvw.dll|DllRegisterServer" SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceEx\Java\Depend "Keep Loaded" = [STR] "urlmon.dll"
2.2 Entry Format "<DllFileName>|<FunctionName>|<CmdLineArgs>"
The Entry Format will fall into one of three categories, 1) Shell Execute (same as old RunOnce method), 2) Call DllRegisterServer or DllUnregisterServer in the specified DLL (same as regsvr32.exe), and 3) Call a function in the specified DLL with WinMain parameters (same as rundll32.exe) and the <CmdLineArgs> will be passed in the CmdLine parameter. � Register DLL - The <DllFileName> is required to be non-empty. The <FunctionName> sections must equal "DllRegisterServer" or "DllUnregisterServer". The <CmdLineArgs> must be empty and there must not be a semicolon after <FunctionName>. Example: "shdocvw.dll|DllRegisterServer". � Calling a Function - The <DllFileName> and <FunctionName> sections are required to be non-empty. The <CmdLineArgs> can be empty and there must be a semicolon after <FunctionName>. Example: "C:\winnt\system32\my.dll|MyWinMain|-Start". � Shell Execute - The <DllFileName> and <FunctionName> sections are required to be empty. The <DllFileName> string will be executed in same way the old RunOnce items where executed. Example: "||iexplore.exe http://www.microsoft.com/".
3.0 Function Calls void ProcessRunOnceEx(DWORD dwFlags); This function will call RunRegAppsAndObjectsEx() with the RunEx and RunOnceEx registry keys in both Local_Machine and Current_User sections of the registry with the Flags passed in. RRA_WAIT will be passed because it�s on be default. RRA_DELETE will be passed for RunOnceEx.
void RunRegAppsAndObjectsEx(HKEY hkeyParent, LPCTSTR szSubkey, DWORD dwFlags); This function will process the entries in this section of the registry. The szSubKey cannot point to a section of the registry that has the old Run or RunOnce format.
\************************************************************/
/*Includes---------------------------------------------------------*/
#include "iernonce.h"
#include "resource.h"
#ifdef WX86
#include <wx86ofl.h>
#endif // WX86
//////////////////////////////////////////////////////////////////
// Constants:
//////////////////////////////////////////////////////////////////
#define MAX_REG_PATH 256 // Registry paths should not get much longer than this or there is a major perf hit
#define MAX_REG_VALUE 80 // Registry values should not get much longer than this or there is a major perf hit
#define ARRAY_GROW_RATE 32 // This should be large enough to cover most applications
#define SEPERATOR_CHAR TEXT('|')
//////////////////////////////////////////////////////////////////
// TYPES:
//////////////////////////////////////////////////////////////////
typedef enum { eHDL_Load, eHDL_Unload } HDL_Type;
typedef int (*WINMAIN_PARAMS)(HINSTANCE, HINSTANCE, LPSTR, int); typedef LONG (*DLLREGISTERSERVER_PARAMS)(); typedef HRESULT (*DLLINSTALL_PARAMS)(BOOL bInstall, LPCWSTR pszCmdLine);
//////////////////////////////////////////////////////////////////
// GLOBALS:
//////////////////////////////////////////////////////////////////
const TCHAR * c_szDependencyName = TEXT("Depend"); const TCHAR * c_szFlagsRegValue = TEXT("Flags"); const TCHAR * g_c_szTitleRegValue = TEXT("Title"); const TCHAR * g_c_szSystemDat = TEXT("system.dat"); const TCHAR * g_c_szSystem1st = TEXT("system.1st"); const TCHAR * g_c_szSetupKey = TEXT("Software\\Microsoft\\IE Setup\\Setup"); const TCHAR * g_c_szRegBackupPath = TEXT("RegistryBackup"); const TCHAR * g_c_szServicesRegValue = TEXT("Services");
HINSTANCE g_hinst = NULL; HANDLE g_hHeap = NULL;
RUNONCEEXPROCESSCALLBACK g_pCallbackProc = NULL; BOOL g_bQuiet = FALSE; int g_nTotal = 0; int g_nCurrent = 0;
BOOL g_bRunningOnNT = FALSE; BOOL g_bBackupSystemDat = FALSE; #if 0
BOOL g_bDeleteSystemIE4 = FALSE; #endif
int g_iNDisplaySections = 0; TCHAR g_szTitleString[256] = TEXT("");
// related to logging
HANDLE g_hLogFile = INVALID_HANDLE_VALUE;
#ifdef WX86
// If Wx86 has to be loaded we use this flag to make sure it is unloaded
// at the end of RunOnceExProcess.
BOOL g_fWx86Loaded = FALSE; #endif
//////////////////////////////////////////////////////////////////
// Internal Functions
//////////////////////////////////////////////////////////////////
void WINAPI RunOnceExProcess(HWND hWnd, HINSTANCE hInstance, LPSTR lpCmdLine, int nCmdShow); void RunOnceExProcessReg(HKEY hkeyParent, LPCTSTR pszSubkey, DWORD dwFlags); HDPA GetSections(HKEY hkeyParent, LPCTSTR szSubkey, DWORD dwFlags, int * pNumberOfSections); HDPA GetEntries(HKEY hRootKey, LPCTSTR szSectionName, DWORD dwFlags, int * pNumberOfEntries); void HandleDependencyDLLs(HKEY hkeyParent, LPCTSTR szRegPath, LPCTSTR szRegSubPath, DWORD dwFlags, HDL_Type hdlDirection); DWORD GetFlagsInRegistry(HKEY hkeyParent, LPCTSTR szSubkey); void ShellExecuteRegApp(LPTSTR pszCmdLine, DWORD dwFlags); BOOL HaveDependServices(SC_HANDLE hService); void CheckServices(DWORD dwFlags);
BOOL WINAPI DllMain(HINSTANCE hDLLInst, DWORD fdwReason, LPVOID lpvReserved) { if (fdwReason == DLL_PROCESS_ATTACH) { OSVERSIONINFO osvi;
DisableThreadLibraryCalls(hDLLInst);
// The DLL is being loaded for the first time by a given process.
// Perform per-process initialization here. If the initialization
// is successful, return TRUE; if unsuccessful, return FALSE.
// Initialize the global variable holding the hinstance:
g_hinst = hDLLInst; g_hHeap = GetProcessHeap();
osvi.dwOSVersionInfoSize = sizeof(osvi); GetVersionEx(&osvi); if (VER_PLATFORM_WIN32_NT == osvi.dwPlatformId) g_bRunningOnNT = TRUE; } return TRUE; }
#ifdef WX86
/****************************************************\
FUNCTION: RunOnceLoadLibrary
PARAMETERS: LPCTSTR lpszFileName - DLL to load
BOOL* pfWx86DLL - Ptr to a BOOL that we set to TRUE if the DLL is x86.
DESCRIPTION: This function will is a wrapper for LoadLibraryEx() that will also load x86 DLLs on RISC. \***************************************************/
HINSTANCE RunOnceLoadLibrary( LPCTSTR lpszFileName, BOOL* pfWx86DLL ) { HINSTANCE hInstance = LoadLibraryEx(lpszFileName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); if ( (hInstance == NULL) && (GetLastError() == ERROR_BAD_EXE_FORMAT) ) { WCHAR achDllToLoad[ MAX_PATH ]; #ifdef UNICODE
lstrcpy(achDllToLoad, lpszFileName); #else // UNICODE
MultiByteToWideChar(CP_ACP, 0, lpszFileName, -1, achDllToLoad, ARRAYSIZE(achDllToLoad)); #endif // UNICODE
if ( !g_fWx86Loaded ) { g_fWx86Loaded = Wx86Load(); }
if ( g_fWx86Loaded ) { hInstance = Wx86LoadX86Dll(achDllToLoad, LOAD_WITH_ALTERED_SEARCH_PATH); if ( hInstance != NULL ) { *pfWx86DLL = TRUE; } } } return hInstance; }
#else // WX86
// If we're on x86 then a simple macro works...
#define RunOnceLoadLibrary(_arg1, _arg2) LoadLibraryEx(_arg1, NULL, LOAD_WITH_ALTERED_SEARCH_PATH)
#endif // WX86
void BackupRegistry() { HKEY hkSetup = NULL; DWORD unused; TCHAR szSystemDatPath[MAX_PATH] = TEXT(""); TCHAR szSystemIE4Path[MAX_PATH] = TEXT("");
// flush the registry and copy system.dat to system.ie4;
// this is for the PSS folks; if the registry gets corrupt after IE4 has been installed, user can copy system.ie4 to system.dat
// quite an expensive operation!
RegFlushKey(HKEY_CLASSES_ROOT); RegFlushKey(HKEY_CURRENT_USER); RegFlushKey(HKEY_LOCAL_MACHINE); RegFlushKey(HKEY_USERS);
GetWindowsDirectory(szSystemDatPath, ARRAYSIZE(szSystemDatPath)); if(RegCreateKeyEx(HKEY_LOCAL_MACHINE, g_c_szSetupKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &hkSetup, &unused) == ERROR_SUCCESS) { unused = sizeof(szSystemIE4Path); RegQueryValueEx(hkSetup, g_c_szRegBackupPath, 0, NULL, (BYTE *)szSystemIE4Path, &unused); } // if we don't have a path yet default to root of windows drive
if(szSystemIE4Path[0] == TEXT('\0')) { lstrcpy(szSystemIE4Path, szSystemDatPath); GetParentDir(szSystemIE4Path); AddPath(szSystemIE4Path, g_c_szSystem1st); }
AddPath(szSystemDatPath, g_c_szSystemDat);
SetFileAttributes(szSystemDatPath, FILE_ATTRIBUTE_NORMAL); SetFileAttributes(szSystemIE4Path, FILE_ATTRIBUTE_NORMAL);
if(CopyFile(szSystemDatPath, szSystemIE4Path, FALSE)) { // Record where we put it in the registry
if(hkSetup) RegSetValueEx(hkSetup, g_c_szRegBackupPath, 0, REG_SZ, (BYTE *)szSystemIE4Path, sizeof(TCHAR) * (lstrlen(szSystemIE4Path) + 1));
// Write something to the active setup log
}
if(hkSetup) RegCloseKey(hkSetup);
SetFileAttributes(szSystemDatPath, FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM); SetFileAttributes(szSystemIE4Path, FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM); }
VOID InitCallback(RUNONCEEXPROCESSCALLBACK pCallbackProc, BOOL bQuiet) { g_pCallbackProc = pCallbackProc; g_bQuiet = bQuiet; }
/****************************************************\
FUNCTION: RunOnceExProcess
PARAMETERS: DWORD dwFlags - Caller can specify behavior with flags.
DESCRIPTION: This function will run the RunEx API. It will process RunOnceEx and RunEx in both Current_User and Local_Machine \***************************************************/ void WINAPI RunOnceExProcess(HWND hWnd, HINSTANCE hInstance, LPSTR lpCmdLine, int nCmdShow) { const TCHAR * szRunOnceExPath = REGSTR_PATH_RUNONCEEX; //const TCHAR * szRunExPath = TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunEx");
DWORD dwFlags = RRA_DEFAULT;
if (lpCmdLine != NULL) dwFlags |= AtoL(lpCmdLine);
if (g_bQuiet) dwFlags |= RRAEX_NO_STATUS_DIALOG | RRAEX_NO_ERROR_DIALOGS;
RunOnceExProcessReg(HKEY_LOCAL_MACHINE, szRunOnceExPath, dwFlags | RRA_DELETE | RRA_WAIT); RunOnceExProcessReg(HKEY_CURRENT_USER, szRunOnceExPath, dwFlags | RRA_DELETE | RRA_WAIT);
#ifdef WX86
if ( g_fWx86Loaded ) { Wx86Unload(); } #endif
//RunOnceExProcessReg(HKEY_LOCAL_MACHINE, szRunExPath, dwFlags);
//RunOnceExProcessReg(HKEY_CURRENT_USER, szRunExPath, dwFlags);
if (!g_bRunningOnNT && g_bBackupSystemDat) { BackupRegistry(); } if (g_bRunningOnNT) { CheckServices(dwFlags); } }
/****************************************************\
FUNCTION: RunOnceExProcessReg
PARAMETERS: HKEY hkeyParent - Registry Branch to process. LPCTSTR pszSubkey - Registry Path to process. DWORD dwFlags - Registry Entry to modify.
DESCRIPTION: This function will run the API on one part of the registry. \***************************************************/ void RunOnceExProcessReg(HKEY hkeyParent, LPCTSTR pszSubkey, DWORD dwFlags) { HDPA hdpaSections = NULL; HKEY hRunExKey = NULL; RunOnceExSection * pCurrentSection = NULL; int iNumberOfSections = 0; int iIndex; ARGSINFO aiArgs; static DWORD dwCreationFlags = CREATE_ALWAYS; // first time the log file is opened, the contents should be truncated
static TCHAR szLogFileName[MAX_PATH] = TEXT("");
if (!(RRAEX_IGNORE_REG_FLAGS & dwFlags)) dwFlags |= GetFlagsInRegistry(hkeyParent, pszSubkey);
*g_szTitleString = TEXT('\0'); if (RegOpenKeyEx(hkeyParent, pszSubkey, NULL, KEY_READ, &hRunExKey) == ERROR_SUCCESS) { DWORD dwTitleSize = sizeof(g_szTitleString);
// if a value for Title is specified, read it in
RegQueryValueEx(hRunExKey, g_c_szTitleRegValue, NULL, NULL, (LPBYTE) g_szTitleString, &dwTitleSize); RegCloseKey(hRunExKey); }
// don't process if there are entries in wininit.ini or PendingFileRenameOperations
if ((dwFlags & RRAEX_QUIT_IF_REBOOT_NEEDED) && NeedReboot(0)) return;
if (g_bRunningOnNT && (dwFlags & RRAEX_CHECK_NT_ADMIN) && !IsNTAdmin(0, NULL)) { // user has no NT Admin privileges; we cannot process the RunOnceEx items
// make sure that the msg box is displayed
ReportError((dwFlags & ~RRAEX_NO_ERROR_DIALOGS), IDS_RUNONCEEX_NOT_NT_ADMIN); LogOff(FALSE);
// won't reach here
return; }
if (dwFlags & RRAEX_BACKUP_SYSTEM_DAT) g_bBackupSystemDat = TRUE;
if (dwFlags & RRAEX_LOG_FILE) { if (*szLogFileName == TEXT('\0')) GetLogFileName(TEXT("RunOnceExLogFile"), szLogFileName, ARRAYSIZE(szLogFileName));
if (*szLogFileName) { if (hkeyParent == HKEY_LOCAL_MACHINE) { TCHAR szBuf[MAX_PATH + MAX_REG_PATH + 16];
// dump the registry entries before we process them so that if some error occurs,
// it is easy to cut and paste the entries to a reg file and import it.
wsprintf(szBuf, TEXT("regedit /e \"%s\" HKEY_LOCAL_MACHINE\\%s"), szLogFileName, pszSubkey); ShellExecuteRegApp(szBuf, RRA_WAIT); dwCreationFlags = OPEN_ALWAYS; // from now on, the log file should be opened in append mode
}
StartLogging(szLogFileName, dwCreationFlags); dwCreationFlags = OPEN_ALWAYS; // from now on, the log file should be opened in append mode
} }
// Log that we are starting to process these commands.
WriteToLog(TEXT("[%1:%2]\r\n"), (HKEY_LOCAL_MACHINE == hkeyParent) ? TEXT("HKLM"):TEXT("HKCU"), pszSubkey); LogFlags(dwFlags);
g_iNDisplaySections = 0; hdpaSections = GetSections(hkeyParent, pszSubkey, dwFlags, &iNumberOfSections);
// Load the dependency DLLs that are common to all sections
HandleDependencyDLLs(hkeyParent, pszSubkey, "", dwFlags, eHDL_Load);
if (iNumberOfSections) // call ProcessSections only if there is atleast one section to process
{ // show UI if the NO_STATUS_DIALOG flag is NOT specified, and
// there is atleast one section that has display text
if (0 == (RRAEX_NO_STATUS_DIALOG & dwFlags) && g_iNDisplaySections) { // pack the args of ProcessSections into aiArgs
aiArgs.hkeyParent = hkeyParent; aiArgs.pszSubkey = pszSubkey; aiArgs.dwFlags = dwFlags; aiArgs.hdpaSections = hdpaSections; aiArgs.iNumberOfSections = iNumberOfSections;
DialogBoxParam(g_hinst, MAKEINTRESOURCE(IDD_RUNONCE), NULL, DlgProcRunOnceEx, (LPARAM) &aiArgs); } else ProcessSections(hkeyParent, pszSubkey, dwFlags, hdpaSections, iNumberOfSections, NULL); }
// Unload the dependency DLLs that are common to all sections
HandleDependencyDLLs(hkeyParent, pszSubkey, "", dwFlags, eHDL_Unload);
// We iterate twice. Once to process all the entries, and the second time to delete
// the entries. We do this because some code (like Dialog Status drawing) might want
// to access previously processed entries.
for (iIndex = 0; iIndex < iNumberOfSections; iIndex++) { pCurrentSection = (RunOnceExSection *) DPA_GetPtr(hdpaSections, iIndex); if (pCurrentSection) { delete pCurrentSection; } }
if (hdpaSections) { DPA_Destroy(hdpaSections); hdpaSections = NULL; }
// Delete the Flags and Title registry entry if the Delete flag is set.
if (RRA_DELETE & dwFlags) { if (ERROR_SUCCESS == RegOpenKeyEx(hkeyParent, pszSubkey, NULL, KEY_READ | KEY_WRITE, &hRunExKey)) { // Delete this key if the Delete key is set anywhere.
RegDeleteValue(hRunExKey, c_szFlagsRegValue); RegDeleteValue(hRunExKey, g_c_szTitleRegValue); RegCloseKey(hRunExKey); } }
WriteToLog(TEXT("\r\n"));
if (dwFlags & RRAEX_LOG_FILE) StopLogging(); }
/****************************************************\
FUNCTION: ProcessSections
PARAMETERS: HKEY hkeyParent - Registry Branch to process. LPCTSTR pszSubkey - Registry Path to process. DWORD dwFlags - Registry Entry to modify. HDPA hdpaSections - Array of Sections int iNumberOfSections - Number of Sections in the Array
DESCRIPTION: This function will call to have each Section processed. \***************************************************/ void ProcessSections(HKEY hkeyParent, LPCTSTR pszSubkey, DWORD dwFlags, HDPA hdpaSections, int iNumberOfSections, HWND hWnd) { RunOnceExSection * pCurrentSection = NULL; int iIndex, iDisplayIndex; BOOL fOleInitialized = TRUE;
// Got to initialize it here; if it's done in RunOnceExProcess, i.e., process level initialization,
// registration of asctrls.ocx fails -- looks like OleInitialize needs to be done per thread
if (FAILED(OleInitialize(NULL))) fOleInitialized = FALSE;
iDisplayIndex = -1;
// If there's a callback, count total things to do...
if (g_pCallbackProc) { g_nTotal = 0; g_nCurrent = 0; for (iIndex = 0; iIndex < iNumberOfSections; iIndex++) { if ((pCurrentSection = (RunOnceExSection *) DPA_GetPtr(hdpaSections, iIndex)) == NULL) continue; g_nTotal += pCurrentSection->m_NumberOfEntries; } }
for (iIndex = 0; iIndex < iNumberOfSections; iIndex++) { if ((pCurrentSection = (RunOnceExSection *) DPA_GetPtr(hdpaSections, iIndex)) == NULL) continue;
if (hWnd != NULL) { if (*pCurrentSection->m_szDisplayName != TEXT('\0')) SendMessage(hWnd, LB_SETCURSEL, ++iDisplayIndex, 0); else if (iDisplayIndex == -1) SendMessage(hWnd, LB_SETCURSEL, 0, 0); }
pCurrentSection->Process(hkeyParent, pszSubkey, dwFlags); }
if (fOleInitialized) OleUninitialize(); }
/****************************************************\
FUNCTION: GetFlagsInRegistry
PARAMETERS: HKEY hkeyParent - What branch of the registry to look. LPCTSTR szSubkey - What path in the registry to look. DWORD return - Flags that were found in the registry
DESCRIPTION: Return with the Flags found in the registry or RRA_DEFAULT if none where found. \***************************************************/ DWORD GetFlagsInRegistry(HKEY hkeyParent, LPCTSTR szSubkey) { DWORD dwFlagsInRegistry = RRA_DEFAULT; HKEY hFlagsKey = NULL; DWORD dwKeySize = sizeof(dwFlagsInRegistry);
if (ERROR_SUCCESS == RegOpenKeyEx(hkeyParent, szSubkey, NULL, KEY_QUERY_VALUE, &hFlagsKey)) { RegQueryValueEx(hFlagsKey, c_szFlagsRegValue, NULL, NULL, (LPBYTE) &dwFlagsInRegistry, &dwKeySize); RegCloseKey(hFlagsKey); }
return(dwFlagsInRegistry); }
/****************************************************\
FUNCTION: HandleDependencyDLLs
PARAMETERS: HKEY hkeyParent - Registry Branch to process. LPCTSTR szRegPath - Registry Path to process. LPCTSTR szRegSubPath - Registry Path to process. DWORD dwFlags - Flags. Do we want to delete after Unload? HDL_Type hdlDirection - Load or Unload?
DESCRIPTION: This function will be passed a "Depend" section of the registry that will contain registry keys. These STR registry keys have filenames and data. These filenames need to be loaded (LoadLibrary()) or freed (FreeLibrary()) depending on the HDL_Type parameter to this function. \***************************************************/ void HandleDependencyDLLs(HKEY hkeyParent, LPCTSTR szRegPath, LPCTSTR szRegSubPath, DWORD dwFlags, HDL_Type hdlDirection) { TCHAR szBasePath[MAX_REG_PATH]; TCHAR szValueName[MAX_REG_VALUE]; TCHAR szDLLFileName[MAX_PATH]; HKEY hBaseKey = NULL; HKEY hDependKey = NULL; DWORD dwCurrDLL = 0; DWORD dwKeyType = 0; DWORD dwValueSize = ARRAYSIZE(szValueName); DWORD dwDLLNameSize = sizeof(szDLLFileName); long lEnumError;
if ((NULL != szRegSubPath) && (TEXT('\0') != *szRegSubPath)) // If the szRegSubPath isn't empty, we concatonate the two strings.
wsprintf(szBasePath, TEXT("%s\\%s"), szRegPath, szRegSubPath); else lstrcpy(szBasePath, szRegPath);
// Open the key for Read only if we are doing a load or an unload where the registry does not need to be deleted.
// If we are unloading and we need to remove the registry entry, this function will fail if the user doesn't have
// permission.
if (ERROR_SUCCESS == RegOpenKeyEx(hkeyParent, szBasePath, NULL, (KEY_READ | (((eHDL_Unload == hdlDirection) && (dwFlags & RRA_DELETE)) ? KEY_WRITE:0)), &hBaseKey)) { if (ERROR_SUCCESS == RegOpenKeyEx(hBaseKey, c_szDependencyName, NULL, (KEY_READ | (((eHDL_Unload == hdlDirection) && (dwFlags & RRA_DELETE)) ? KEY_WRITE:0)), &hDependKey)) { // Iterate through each value
for (dwCurrDLL = 0; ERROR_NO_MORE_ITEMS != (lEnumError = RegEnumValue(hDependKey, dwCurrDLL, szValueName, &dwValueSize, NULL, &dwKeyType, (LPBYTE) szDLLFileName, &dwDLLNameSize)); dwCurrDLL++) { if ((REG_SZ == dwKeyType) && (ERROR_SUCCESS == lEnumError)) { if (eHDL_Load == hdlDirection) { // We need to load this library to keep it in memory.
BOOL fWx86DLL; if (NULL == RunOnceLoadLibrary( szDLLFileName, &fWx86DLL )) ReportError(dwFlags, IDS_RUNONCEEX_LOAD_DEPEND_FAILED, szDLLFileName); else WriteToLog(TEXT("Dependency DLL loaded: %1\r\n"), szDLLFileName); } else // Else Unload
{ // We need to unload this library
HINSTANCE hInst = GetModuleHandle(szDLLFileName); if (NULL != hInst) { if (FreeLibrary(hInst)) WriteToLog(TEXT("Dependency DLL unloaded: %1\r\n"), szDLLFileName); } } }
dwValueSize = ARRAYSIZE(szValueName); dwDLLNameSize = sizeof(szDLLFileName); }
RegCloseKey(hDependKey);
if ((eHDL_Unload == hdlDirection) && (dwFlags & RRA_DELETE)) { // We need to remove the registry key so we don't process this again next time.
RegDeleteKey(hBaseKey, c_szDependencyName); } }
RegCloseKey(hBaseKey); } }
/////////////////////////////////////////////////////////////////////
// CLASS: RunOnceExEntry
/////////////////////////////////////////////////////////////////////
/****************************************************\
FUNCTION: RunOnceExEntry
PARAMETERS: LPTSTR lpszNewEntryName - Name of Entry LPTSTR lpszNewCmd - Entry Command DWORD dwFlags - Flags
DESCRIPTION: This function will create a RunOnceEx Entry and set it's Name, Cmd, and section. \***************************************************/ RunOnceExEntry::RunOnceExEntry(LPTSTR lpszNewEntryName, LPTSTR lpszNewCmd, DWORD dwFlags) { const TCHAR * szRegisterFunctionName = TEXT("DllRegisterServer"); const TCHAR * szUnregisterFunctionName = TEXT("DllUnregisterServer"); const TCHAR * szInstallFunctionName = TEXT("DllInstall"); LPTSTR lpszFileName = lpszNewCmd; LPTSTR lpszFunctionName = NULL; LPTSTR lpszCmdLineArgs = NULL; LPTSTR lpszCurrentChar = lpszNewCmd;
lstrcpy(m_szRunOnceExEntryName, lpszNewEntryName); m_ROAction = eRO_Unknown; *m_szFileName = TEXT('\0'); *m_szFunctionName = TEXT('\0'); *m_szCmdLineArgs = TEXT('\0');
// These entries come in this format "<FileName>|<FunctionName>|<CmdLineArgs>".
// We first need to find the end of the <FileName>.
// Find the end of the filename.
if (NULL == (lpszCurrentChar = LocalStrChr(lpszCurrentChar, SEPERATOR_CHAR))) { // It must be an EXE (or something that is executable) because it doesn't have a function name.
lstrcpy(m_szCmdLineArgs, lpszNewCmd); m_ROAction = eRO_Exe; // Remember that we will need to ShellExec this later.
} else { // We found a '|' so we have the <FileName>.
*lpszCurrentChar = TEXT('\0'); // Terminate the filename.
lstrcpy(m_szFileName, lpszNewCmd); *lpszCurrentChar = SEPERATOR_CHAR; // Remove the temporary termination.
lpszCurrentChar = CharNext(lpszCurrentChar);
// Now lets work on Getting the <FunctionName>.
lpszFunctionName = lpszCurrentChar; // Remember the beginning of the FunctionName before iterating.
if (NULL == (lpszCurrentChar = LocalStrChr(lpszCurrentChar, SEPERATOR_CHAR))) { // If we have found the end of the string without the second '|', then
// this needs to be a DllRegisterServer or DllUnregisterServer.
lstrcpy(m_szFunctionName, lpszFunctionName); if (2 == CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, lpszFunctionName, -1, szRegisterFunctionName, -1)) { m_ROAction = eRO_Register; } else { if (2 == CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, lpszFunctionName, -1, szUnregisterFunctionName, -1)) { m_ROAction = eRO_Unregister; } else { m_ROAction = eRO_Unknown; // An error has occured. The user didn't specify a valid FunctionName.
ReportError(dwFlags, IDS_RUNONCEEX_BAD_FUNCTIONNAME, m_szFunctionName); } } } else { // We have enountered the second '|' which delemites the <CmdLineArgs> section. By this point, the function we
// are calling needs WinMain() parameters.or it's a DllInstall function
*lpszCurrentChar = TEXT('\0'); lstrcpy(m_szFunctionName, lpszFunctionName); *lpszCurrentChar = SEPERATOR_CHAR; // Remove the temporary termination.
lpszCurrentChar = CharNext(lpszCurrentChar); lstrcpy(m_szCmdLineArgs, lpszCurrentChar);
if (TEXT('\0') == *m_szFunctionName) { if (TEXT('\0') == *m_szFileName) { m_ROAction = eRO_Exe; } else { // This command is invalid because commands to be shell execed need to start
// Need to have the <FileName> and <FunctionName> parameters empty.
m_ROAction = eRO_Unknown; ReportError(dwFlags, IDS_RUNONCEEX_BAD_SHELLEXEC_CMD, lpszNewCmd); } } else { // check if it's DllInstall
if (2 == CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, m_szFunctionName, -1, szInstallFunctionName, -1)) { m_ROAction = eRO_Install; } else { m_ROAction = eRO_WinMainFunction; } } } } }
/****************************************************\
FUNCTION: ~RunOnceExEntry
PARAMETERS: none
DESCRIPTION: This destructor will free memory that it used. \***************************************************/ RunOnceExEntry::~RunOnceExEntry() { }
/****************************************************\
FUNCTION: RunOnceExEntry::Process
PARAMETERS: HKEY hkeyParent - Section of the registry being processed. LPCTSTR szSubkey - Path to the registry entries being processed. LPCTSTR szSectonName - Only used for logging purposes. DWORD dwFlags - Flags to determine if we delete command after executing it.
DESCRIPTION: This function will Processes the entry. This means it will execute the command or call a function in a DLL. After it finishes, it will delete the registry entry if appropriate. \***************************************************/ void RunOnceExEntry::Process(HKEY hkeyParent, LPCTSTR szSubkey, LPCTSTR szSectionName, DWORD dwFlags) { HINSTANCE hInstance = NULL; WINMAIN_PARAMS pfWinMainFunction = NULL; DLLREGISTERSERVER_PARAMS pfDllRegisterServer = NULL; DLLINSTALL_PARAMS pfDllInstall = NULL;
TCHAR szRegKeyPath[MAX_REG_PATH] = TEXT(""); HKEY hCurrentSectionKey = NULL;
BOOL fWx86DLL = FALSE;
// Delete Registry Entry
if ((NULL != szSectionName) && (RRA_DELETE & dwFlags)) { wsprintf(szRegKeyPath, TEXT("%s\\%s"), szSubkey, szSectionName); LocalSHDeleteValue(hkeyParent, szRegKeyPath, m_szRunOnceExEntryName); }
WriteToLog(TEXT("File:%1; Function:%2; Args:%3; Action:"), m_szFileName, m_szFunctionName, m_szCmdLineArgs);
switch(m_ROAction) { case eRO_Register: case eRO_Unregister: WriteToLog(m_ROAction == eRO_Register ? TEXT("DllRegisterServer()\r\n") : TEXT("DllUnRegisterServer()\r\n")); hInstance = RunOnceLoadLibrary( m_szFileName, &fWx86DLL ); if (NULL != hInstance) { HRESULT hResult = S_OK; char pcstrDLLToLoad[MAX_PATH];
#ifdef UNICODE
WideCharToMultiByte(CP_ACP, 0, m_szFunctionName, -1, pcstrDLLToLoad, ARRAYSIZE(pcstrDLLToLoad), NULL, NULL); #else // UNICODE
lstrcpy(pcstrDLLToLoad, m_szFunctionName); #endif // UNICODE
if (NULL != (pfDllRegisterServer = (DLLREGISTERSERVER_PARAMS) GetProcAddress(hInstance, pcstrDLLToLoad))) { if (RRAEX_NO_EXCEPTION_TRAPPING & dwFlags) // Don't trap exceptions if that's what the user wants.
{ // This flag is off be default because you better have a debugger installed if you don't catch
// these exceptions
#ifdef WX86
if ( fWx86DLL ) { hResult = Wx86EmulateX86(pfDllRegisterServer, 0, NULL ); } else #endif
hResult = (*pfDllRegisterServer)(); } else { _try { #ifdef WX86
if ( fWx86DLL ) { hResult = Wx86EmulateX86(pfDllRegisterServer, 0, NULL ); } else #endif
hResult = (*pfDllRegisterServer)(); } _except(EXCEPTION_EXECUTE_HANDLER) // Catch all exceptions.
{ ReportError(dwFlags, IDS_RUNONCEEX_EXCEPTION, m_szFunctionName, m_szFileName); } } if (FAILED(hResult)) { WriteToLog(TEXT("An error occurred calling ""%1"" in ""%2"". (HRESULT = %3!lx!)\r\n"), m_szFunctionName, m_szFileName, hResult); ReportError(dwFlags, IDS_RUNONCEEX_REGISTER_ERROR, m_szFileName); } } else { ReportError(dwFlags, IDS_RUNONCEEX_FIND_FUNC_FAILED, m_szFunctionName, m_szFileName); } FreeLibrary(hInstance); } else { ReportError(dwFlags, IDS_RUNONCEEX_CANNOT_LOAD_DLL, m_szFileName); }
break;
case eRO_Install: WriteToLog(TEXT("DllInstall()\r\n")); hInstance = RunOnceLoadLibrary( m_szFileName, &fWx86DLL ); if (NULL != hInstance) { char pcstrDLLToLoad[MAX_PATH]; WCHAR pwstrDLLCmdLineArgs[MAX_PATH]; HRESULT hResult = S_OK;
#ifdef UNICODE
WideCharToMultiByte(CP_ACP, 0, m_szFunctionName, -1, pcstrDLLToLoad, ARRAYSIZE(pcstrDLLToLoad), NULL, NULL); lstrcpy(pwstrDLLCmdLineArgs, m_szCmdLineArgs); #else // UNICODE
lstrcpy(pcstrDLLToLoad, m_szFunctionName); MultiByteToWideChar(CP_ACP, 0, m_szCmdLineArgs, -1, pwstrDLLCmdLineArgs, ARRAYSIZE(pwstrDLLCmdLineArgs)); #endif // UNICODE
if (NULL != (pfDllInstall = (DLLINSTALL_PARAMS) GetProcAddress(hInstance, pcstrDLLToLoad))) { BOOL bInstall; WCHAR *pwszArgs;
// parse the command line
for (pwszArgs = pwstrDLLCmdLineArgs; *pwszArgs != L',' && *pwszArgs != L'\0'; pwszArgs++) ; if (*pwszArgs == L',') *pwszArgs++ = L'\0';
// args to DllInstall is (BOOL, LPWSTR)
bInstall = (*pwstrDLLCmdLineArgs != L'u') && (*pwstrDLLCmdLineArgs != L'U');
if (RRAEX_NO_EXCEPTION_TRAPPING & dwFlags) // Don't trap exceptions if that's what the caller wants.
{ // This flag is off by default because you better have a debugger installed if you don't catch
// these exceptions
#ifdef WX86
if ( fWx86DLL ) { DWORD dwArgs[ 2 ]; dwArgs[ 0 ] = (DWORD) bInstall; dwArgs[ 1 ] = (DWORD) pwszArgs; hResult = Wx86EmulateX86(pfDllInstall, ARRAYSIZE(dwArgs), dwArgs); } else #endif
hResult = (*pfDllInstall)(bInstall, pwszArgs); } else { _try { #ifdef WX86
if ( fWx86DLL ) { DWORD dwArgs[ 2 ]; dwArgs[ 0 ] = (DWORD) bInstall; dwArgs[ 1 ] = (DWORD) pwszArgs; hResult = Wx86EmulateX86(pfDllInstall, ARRAYSIZE(dwArgs), dwArgs); } else #endif
hResult = (*pfDllInstall)(bInstall, pwszArgs); } _except(EXCEPTION_EXECUTE_HANDLER) // Catch all exceptions.
{ ReportError(dwFlags, IDS_RUNONCEEX_EXCEPTION, m_szFunctionName, m_szFileName); } } if (FAILED(hResult)) { WriteToLog(TEXT("An error occurred calling ""%1"" in ""%2"". (HRESULT = %3!lx!)\r\n"), m_szFunctionName, m_szFileName, hResult); ReportError(dwFlags, IDS_RUNONCEEX_REGISTER_ERROR, m_szFileName); } } else { ReportError(dwFlags, IDS_RUNONCEEX_CANNOT_FIND_FUNCTION, m_szFunctionName, m_szFileName); } FreeLibrary(hInstance); } else { ReportError(dwFlags, IDS_RUNONCEEX_CANNOT_LOAD_DLL, m_szFileName); } break;
case eRO_WinMainFunction: WriteToLog(TEXT("WinMain type function\r\n")); hInstance = RunOnceLoadLibrary( m_szFileName, &fWx86DLL ); if (NULL != hInstance) { char pcstrDLLToLoad[MAX_PATH]; char pcstrDLLCmdLineArgs[MAX_PATH];
#ifdef UNICODE
WideCharToMultiByte(CP_ACP, 0, m_szFunctionName, -1, pcstrDLLToLoad, ARRAYSIZE(pcstrDLLToLoad), NULL, NULL); WideCharToMultiByte(CP_ACP, 0, m_szCmdLineArgs, -1, pcstrDLLCmdLineArgs, ARRAYSIZE(pcstrDLLCmdLineArgs), NULL, NULL); #else // UNICODE
lstrcpy(pcstrDLLToLoad, m_szFunctionName); lstrcpy(pcstrDLLCmdLineArgs, m_szCmdLineArgs); #endif // UNICODE
if (NULL != (pfWinMainFunction = (WINMAIN_PARAMS) GetProcAddress(hInstance, pcstrDLLToLoad))) { if (RRAEX_NO_EXCEPTION_TRAPPING & dwFlags) // Don't trap exceptions if that's what the caller wants.
{ // This flag is off be default because you better have a debugger installed if you don't catch
// these exceptions
#ifdef WX86
if ( fWx86DLL ) { DWORD dwArgs[ 4 ]; dwArgs[ 0 ] = (DWORD) NULL; dwArgs[ 1 ] = (DWORD) NULL; dwArgs[ 2 ] = (DWORD) pcstrDLLCmdLineArgs; dwArgs[ 3 ] = (DWORD) 0; Wx86EmulateX86(pfWinMainFunction, ARRAYSIZE(dwArgs), dwArgs); } else #endif
(*pfWinMainFunction)(NULL, NULL, pcstrDLLCmdLineArgs, 0); } else { _try { #ifdef WX86
if ( fWx86DLL ) { DWORD dwArgs[ 4 ]; dwArgs[ 0 ] = (DWORD) NULL; dwArgs[ 1 ] = (DWORD) NULL; dwArgs[ 2 ] = (DWORD) pcstrDLLCmdLineArgs; dwArgs[ 3 ] = (DWORD) 0; Wx86EmulateX86(pfWinMainFunction, ARRAYSIZE(dwArgs), dwArgs); } else #endif
(*pfWinMainFunction)(NULL, NULL, pcstrDLLCmdLineArgs, 0); } _except(EXCEPTION_EXECUTE_HANDLER) // Catch all exceptions.
{ ReportError(dwFlags, IDS_RUNONCEEX_EXCEPTION, m_szFunctionName, m_szFileName); } } } else { ReportError(dwFlags, IDS_RUNONCEEX_CANNOT_FIND_FUNCTION, m_szFunctionName, m_szFileName); } FreeLibrary(hInstance); } else { ReportError(dwFlags, IDS_RUNONCEEX_CANNOT_LOAD_DLL, m_szFileName); } break;
case eRO_Exe: WriteToLog(TEXT("ShellExec Command\r\n")); { if (RRAEX_NO_EXCEPTION_TRAPPING & dwFlags) // Don't trap exceptions if that's what the caller wants.
{ // This flag is off be default because you better have a debugger installed if you don't catch
// these exceptions
ShellExecuteRegApp(m_szCmdLineArgs, dwFlags); } else { _try { ShellExecuteRegApp(m_szCmdLineArgs, dwFlags); } _except(EXCEPTION_EXECUTE_HANDLER) // Catch all exceptions.
{ ReportError(dwFlags, IDS_RUNONCEEX_EXE_EXCEPTION, m_szCmdLineArgs); } } } break;
case eRO_Unknown: default: WriteToLog(TEXT("Unknown\r\n")); break; } }
/////////////////////////////////////////////////////////////////////
// CLASS: RunOnceExSection
/////////////////////////////////////////////////////////////////////
/****************************************************\
FUNCTION: RunOnceExSection
PARAMETERS: LPTSTR lpszNewSectionName - The name of the new section LPTSTR lpszNewDisplayName - The display name of the new section
DESCRIPTION: This constructor will set the name of the newly created section. \***************************************************/ RunOnceExSection::RunOnceExSection(LPTSTR lpszNewSectionName, LPTSTR lpszNewDisplayName) { lstrcpy(m_szRunOnceExSectionName, lpszNewSectionName);
if ((NULL != lpszNewDisplayName) && (TEXT('\0') != *lpszNewDisplayName)) { // Set the DisplayName as long as it was valid.
lstrcpy(m_szDisplayName, lpszNewDisplayName); } else { // It was invalid, so we use the SectionName for the DisplayName.
//lstrcpy(m_szDisplayName, lpszNewSectionName);
*m_szDisplayName = TEXT('\0'); // just don't display anything
} m_hEntryArray = NULL; m_NumberOfEntries = 0; }
/****************************************************\
FUNCTION: ~RunOnceExSection
PARAMETERS: none
DESCRIPTION: This destructor will free memory that it used. \***************************************************/ RunOnceExSection::~RunOnceExSection() { if (NULL != m_hEntryArray) { RunOnceExEntry * pFirstSection;
for (int iIndex = 0; iIndex < m_NumberOfEntries; iIndex++) { pFirstSection = (RunOnceExEntry *) DPA_GetPtr(m_hEntryArray, iIndex); delete pFirstSection; }
DPA_Destroy(m_hEntryArray); } }
/****************************************************\
FUNCTION: RunOnceExSection::Process
PARAMETERS: HKEY hkeyParent - Section of the registry being processed. LPCTSTR szSubkey - Path to the registry entries being processed. DWORD dwFlags - Flags to determine if we delete command after executing it.
DESCRIPTION: This function will process each entry in it, delete the registry key it used, and then repeat the process with the next section. \***************************************************/ void RunOnceExSection::Process(HKEY hkeyParent, LPCTSTR szSubkey, DWORD dwFlags) { RunOnceExEntry * pCurrentEntry = NULL; HKEY hSectionKey = NULL; int iEntryIndex;
WriteToLog(TEXT("\r\n")); LogDateAndTime(); WriteToLog(TEXT("Section:%1\r\n"), m_szRunOnceExSectionName);
// Load the dependency DLLs for this sections
HandleDependencyDLLs(hkeyParent, szSubkey, m_szRunOnceExSectionName, dwFlags, eHDL_Load);
if (NULL != m_hEntryArray) { for (iEntryIndex = 0; iEntryIndex < m_NumberOfEntries; iEntryIndex++) { pCurrentEntry = (RunOnceExEntry *) DPA_GetPtr(m_hEntryArray, iEntryIndex); if (pCurrentEntry) pCurrentEntry->Process(hkeyParent, szSubkey, m_szRunOnceExSectionName, dwFlags);
// If there is a call back, send back information
if (g_pCallbackProc) { g_nCurrent++; g_pCallbackProc(g_nCurrent, g_nTotal, NULL); } } }
// Unload the dependency DLLs for this sections
HandleDependencyDLLs(hkeyParent, szSubkey, m_szRunOnceExSectionName, dwFlags, eHDL_Unload);
LogDateAndTime();
if (RRA_DELETE & dwFlags) { TCHAR szKeyToDelete[MAX_REG_PATH]; wsprintf(szKeyToDelete, TEXT("%s\\%s"), szSubkey, m_szRunOnceExSectionName); LocalSHDeleteKey(hkeyParent, szKeyToDelete); }
}
/****************************************************\
FUNCTION: CompareSection
PARAMETERS: RunOnceExSection * pSection1 - The first Section to be compared RunOnceExSection * pSection2 - The second Section to be compared LPARAM lpNotUsed - Not used. int return - (-1) if the first is smaller.
DESCRIPTION: The following function will determine which comes first when sorted. \***************************************************/ INT CALLBACK CompareSection(RunOnceExSection * pSection1, RunOnceExSection * pSection2, LPARAM lpNotUsed) { if (1 == CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, pSection1->m_szRunOnceExSectionName, -1, pSection2->m_szRunOnceExSectionName, -1)) return(-1);
return(1); }
/****************************************************\
FUNCTION: CompareEntries
PARAMETERS: RunOnceExEntry * pEntry1 - The first Entry to be compared RunOnceExEntry * pEntry2 - The second Entry to be compared LPARAM lpNotUsed - Not used. int return - (-1) if the first is smaller.
DESCRIPTION: The following function will determine which comes first when sorted. \***************************************************/ INT CALLBACK CompareEntries(RunOnceExEntry * pEntry1, RunOnceExEntry * pEntry2, LPARAM lpNotUsed) { if (1 == CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, pEntry1->m_szRunOnceExEntryName, -1, pEntry2->m_szRunOnceExEntryName, -1)) return(-1);
return(1); }
/****************************************************\
FUNCTION: GetSections
PARAMETERS: HKEY hkeyParent - Section of the registry being processed. LPCTSTR szSubkey - Path to the registry entries being processed. DWORD dwFlags - Flags.
DESCRIPTION: This function will open the RunOnceEx registry key and read all the sections and entries. It will read these into memory and set *ppFirstSection to the first (root) section. \***************************************************/ HDPA GetSections(HKEY hkeyParent, LPCTSTR szSubkey, DWORD dwFlags, int * pNumberOfSections) { TCHAR szCurrentSectionName[MAX_ENTRYNAME] = TEXT(""); TCHAR szCurrSectionDisplayName[MAX_ENTRYNAME] = TEXT(""); HKEY hRootKey = NULL; DWORD dwCurrentSection = 0;
DWORD dwRegType = 0; long lDisplayNameSize = ARRAYSIZE(szCurrSectionDisplayName); long lEnumError; HDPA hDPA_Sections = DPA_Create(ARRAY_GROW_RATE); RunOnceExSection * pNewSection = NULL;
*pNumberOfSections = 0;
if (NULL != hDPA_Sections) { // Do we have any RegOpenKeyEx sections to process/read?
if (ERROR_SUCCESS == RegOpenKeyEx(hkeyParent, szSubkey, NULL, KEY_READ, &hRootKey)) { // IthkeyParenterate through each section
for (dwCurrentSection = 0; ERROR_NO_MORE_ITEMS != (lEnumError = RegEnumKey(hRootKey, dwCurrentSection, szCurrentSectionName, ARRAYSIZE(szCurrentSectionName))); dwCurrentSection++) { if (ERROR_SUCCESS == lEnumError) { lDisplayNameSize = sizeof(szCurrSectionDisplayName); if (RegQueryValue(hRootKey, szCurrentSectionName, szCurrSectionDisplayName, &lDisplayNameSize) != ERROR_SUCCESS) *szCurrSectionDisplayName = TEXT('\0');
// Only Process non-"Depend" entries.
if (2 != CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, szCurrentSectionName, -1, c_szDependencyName, -1)) { pNewSection = new RunOnceExSection(szCurrentSectionName, szCurrSectionDisplayName); if (NULL != pNewSection) { if (*szCurrSectionDisplayName) g_iNDisplaySections++; DPA_SetPtr(hDPA_Sections, *pNumberOfSections, (void *) pNewSection); pNewSection->m_hEntryArray = GetEntries(hRootKey, szCurrentSectionName, dwFlags, &(pNewSection->m_NumberOfEntries)); (*pNumberOfSections)++; } } } }
RegCloseKey(hRootKey); }
DPA_Sort(hDPA_Sections, (PFNDPACOMPARE) CompareSection, 0); }
return(hDPA_Sections); }
/****************************************************\
FUNCTION: GetEntries
PARAMETERS: HKEY hkeyParent - Section of the registry being processed. LPCTSTR szSubkey - Path to the registry entries being processed. DWORD dwFlags - Flags. int * pNumberOfEntries - OUT: Number of sections read.
DESCRIPTION: This function will open the RunOnceEx registry section key and read all the entries. \***************************************************/ HDPA GetEntries(HKEY hRootKey, LPCTSTR szSectionName, DWORD dwFlags, int * pNumberOfEntries) { HKEY hCurrentSectionKey = NULL;
TCHAR szCurrentEntryName[MAX_ENTRYNAME] = TEXT(""); TCHAR szCurrentEntryCmd[MAX_PATH] = TEXT(""); DWORD dwEntrySize = 0; DWORD dwEntryCmdSize = 0;
DWORD dwCurrentEntry = 0; DWORD dwRegType = 0; long lEnumError; HDPA hDPA_Entries = DPA_Create(ARRAY_GROW_RATE); RunOnceExEntry * pNewEntry = NULL;
*pNumberOfEntries = 0;
if (NULL != hDPA_Entries) { if (ERROR_SUCCESS == RegOpenKeyEx(hRootKey, szSectionName, NULL, KEY_READ, &hCurrentSectionKey)) { DWORD dwKeyType;
// Iterate through each value
dwEntrySize = ARRAYSIZE(szCurrentEntryName); dwEntryCmdSize = sizeof(szCurrentEntryCmd);
for (dwCurrentEntry = 0; ERROR_NO_MORE_ITEMS != (lEnumError = RegEnumValue(hCurrentSectionKey, dwCurrentEntry, szCurrentEntryName, &dwEntrySize, NULL, &dwKeyType, (LPBYTE) szCurrentEntryCmd, &dwEntryCmdSize)); dwCurrentEntry++) { if (ERROR_SUCCESS == lEnumError) { // An empty Entry Name is not acceptable because that is the Display Name for the section.
if ((REG_SZ == dwKeyType) && (TEXT('\0') != *szCurrentEntryName)) { pNewEntry = new RunOnceExEntry(szCurrentEntryName, szCurrentEntryCmd, dwFlags); if (NULL != pNewEntry) { if (eRO_Unknown != pNewEntry->m_ROAction) { DPA_SetPtr(hDPA_Entries, *pNumberOfEntries, (void *) pNewEntry); (*pNumberOfEntries)++; } else delete pNewEntry; } } }
dwEntrySize = ARRAYSIZE(szCurrentEntryName); dwEntryCmdSize = sizeof(szCurrentEntryCmd); }
RegCloseKey(hCurrentSectionKey); }
DPA_Sort(hDPA_Entries, (PFNDPACOMPARE) CompareEntries, 0); }
return(hDPA_Entries); }
// taken from \\trango\slmadd\src\shell\shell32\shellprv.h
#define FillExecInfo(_info, _hwnd, _verb, _file, _params, _dir, _show) \
(_info).hwnd = _hwnd; \ (_info).lpVerb = _verb; \ (_info).lpFile = _file; \ (_info).lpParameters = _params; \ (_info).lpDirectory = _dir; \ (_info).nShow = _show; \ (_info).fMask = 0; \ (_info).cbSize = sizeof(SHELLEXECUTEINFO);
//
// Path processing function
//
#define PPCF_ADDQUOTES 0x00000001 // return a quoted name if required
#define PPCF_ADDARGUMENTS 0x00000003 // appends arguments (and wraps in quotes if required)
#define PPCF_NODIRECTORIES 0x00000010 // don't match to directories
#define PPCF_NORELATIVEOBJECTQUALIFY 0x00000020 // don't return fully qualified relative objects
#define PPCF_FORCEQUALIFY 0x00000040 // qualify even non-relative names
typedef LONG WINSHELLAPI (WINAPI * LPPATHPROCESSCOMMAND)(LPCTSTR, LPTSTR, int, DWORD);
/****************************************************\
FUNCTION: ShellExecuteRegApp
PARAMETERS: LPCTSTR pszCmdLine - Cmd line to execute DWORD dwFlags - Flags to specify if we need to wait for command to finish.
DESCRIPTION: The following handles running an application and optionally waiting for it to terminate. \***************************************************/ void ShellExecuteRegApp(LPTSTR pszCmdLine, DWORD dwFlags) { TCHAR szBuf[MAX_PATH];
GetSystemDirectory(szBuf, ARRAYSIZE(szBuf));
if (RunningOnIE4()) { HINSTANCE hShell32DLL = NULL; LPPATHPROCESSCOMMAND pfnPathProcessCommand; TCHAR szQuotedCmdLine[MAX_PATH+2]; SHELLEXECUTEINFO ExecInfo; LPTSTR lpszArgs; BOOL fPPCSuccess = FALSE; // PathProcessCommand succeeded
AddPath(szBuf, TEXT("shell32.dll"));
if ((hShell32DLL = LoadLibraryEx(szBuf, NULL, LOAD_WITH_ALTERED_SEARCH_PATH)) != NULL) { if ((pfnPathProcessCommand = (LPPATHPROCESSCOMMAND) GetProcAddress(hShell32DLL, (LPCSTR) 653)) != NULL) { //
// We used to call CreateProcess( NULL, szCmdLine, ...) here,
// but thats not useful for people with apppaths stuff.
//
// Gross, but if the process command fails, copy the command line to let
// shell execute report the errors
if ((pfnPathProcessCommand)(pszCmdLine, szQuotedCmdLine, ARRAYSIZE(szQuotedCmdLine), PPCF_ADDARGUMENTS|PPCF_FORCEQUALIFY) != -1) fPPCSuccess = TRUE; }
FreeLibrary(hShell32DLL); }
if (!fPPCSuccess) lstrcpy(szQuotedCmdLine, pszCmdLine);
lpszArgs = LocalPathGetArgs(szQuotedCmdLine); if (*lpszArgs) *(lpszArgs-1) = TEXT('\0'); // Strip args
LocalPathUnquoteSpaces(szQuotedCmdLine);
FillExecInfo(ExecInfo, NULL, NULL, szQuotedCmdLine, lpszArgs, szBuf, SW_SHOWNORMAL); ExecInfo.fMask |= SEE_MASK_NOCLOSEPROCESS; if (dwFlags & RRAEX_NO_ERROR_DIALOGS) // Don't display Error dialog
ExecInfo.fMask |= SEE_MASK_FLAG_NO_UI;
if (ShellExecuteEx(&ExecInfo)) { if ((dwFlags & RRA_WAIT) && (ExecInfo.hProcess != NULL)) { MsgWaitForMultipleObjectsLoop(ExecInfo.hProcess, INFINITE); }
CloseHandle(ExecInfo.hProcess); } } else // old Win95 logic -- just call CreateProcess
{ STARTUPINFO startup; PROCESS_INFORMATION pi;
ZeroMemory(&startup, sizeof(startup)); startup.cb = sizeof(startup);
if (CreateProcess(NULL, pszCmdLine, NULL, NULL, FALSE, CREATE_NEW_PROCESS_GROUP, NULL, szBuf, &startup, &pi)) { if (dwFlags & RRA_WAIT) { MsgWaitForMultipleObjectsLoop(pi.hProcess, INFINITE); }
CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } } }
#define BUFFER_SIZE 1024
BOOL HaveDependServices(SC_HANDLE hService) { HRESULT hr = S_OK; LPBYTE lpBuffer = NULL; DWORD dwSize = BUFFER_SIZE; // Start with 1k
DWORD dwBytesNeeded; DWORD dwNumServices; BOOL bDependServices = FALSE;
lpBuffer = (LPBYTE) LocalAlloc(LPTR, dwSize); if (lpBuffer) { if (!EnumDependentServices(hService, SERVICE_STATE_ALL, (LPENUM_SERVICE_STATUS)lpBuffer, dwSize, &dwBytesNeeded, &dwNumServices)) { if (GetLastError() == ERROR_MORE_DATA) { dwSize = dwBytesNeeded + 32; LocalFree(lpBuffer); lpBuffer = (LPBYTE) LocalAlloc(LPTR, dwSize); if (lpBuffer) { if (!EnumDependentServices(hService, SERVICE_STATE_ALL, (LPENUM_SERVICE_STATUS)lpBuffer, dwSize, &dwBytesNeeded, &dwNumServices)) { hr = HRESULT_FROM_WIN32(GetLastError()); } } else hr = HRESULT_FROM_WIN32(GetLastError()); } else hr = HRESULT_FROM_WIN32(GetLastError());
} if (SUCCEEDED(hr)) { // If at least one service depends on this one?
bDependServices = (dwNumServices != 0); } if (lpBuffer) LocalFree(lpBuffer); } return bDependServices; }
void CheckServices(DWORD dwFlags) { SC_HANDLE hSCM = NULL; SC_HANDLE hService = NULL; BOOL bRebootNeeded = FALSE; HKEY hKey = NULL; LPSTR pServices = NULL; LPSTR pCheckService = NULL; LONG lRet = 0; DWORD dwSize;
pServices = (LPSTR)LocalAlloc(LPTR, BUFFER_SIZE); if (pServices) { // Get the services to check for from the registry.
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_RUNONCEEX, NULL, KEY_READ|KEY_WRITE, &hKey) == ERROR_SUCCESS) { dwSize = BUFFER_SIZE - 1; lRet = RegQueryValueEx(hKey, g_c_szServicesRegValue, NULL, NULL, (LPBYTE)pServices, &dwSize); if (lRet == ERROR_MORE_DATA) { dwSize += 32; LocalFree(pServices); pServices = (LPSTR)LocalAlloc(LPTR, dwSize);
if (pServices) { lRet = RegQueryValueEx(hKey, g_c_szServicesRegValue, NULL, NULL, (LPBYTE)pServices, &dwSize); } } if (lRet != ERROR_SUCCESS) { if (pServices) { LocalFree(pServices); pServices = NULL; } } RegDeleteValue(hKey, g_c_szServicesRegValue); RegCloseKey(hKey); }
if ((pServices) && (*pServices)) { hSCM = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS); if (hSCM) { pCheckService = pServices; // zero out all forward slashes to get the services terminated
while (*pCheckService) { if (*pCheckService == '/') { *pCheckService = '\0'; pCheckService++; } else pCheckService = CharNext(pCheckService); } // RegQueryValueExA is using the same buffer to convert the UNICODE
// string to a ANSI string. This will leave some UNICODE characters
// after the ANSI strings and the services are not realy double 0
// terminated anymore. This will ensure that the list is still
// double 0 terminated
pCheckService++; *pCheckService = '\0';
pCheckService = pServices; while (!bRebootNeeded && (*pCheckService)) { hService = OpenService(hSCM, (LPCSTR)pCheckService, STANDARD_RIGHTS_REQUIRED | SERVICE_ENUMERATE_DEPENDENTS); if (hService) { bRebootNeeded = HaveDependServices(hService); CloseServiceHandle(hService); }
pCheckService += lstrlen(pCheckService) + 1; } CloseServiceHandle(hSCM); } } if (pServices) LocalFree(pServices); } if (bRebootNeeded) { ReportError((dwFlags & ~RRAEX_NO_ERROR_DIALOGS), IDS_RUNONCEEX_SERVICE_REQUIRES_REBOOT); LogOff(TRUE); } }
|