Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1824 lines
73 KiB

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