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
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);
|
|
}
|
|
}
|