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

  1. /************************************************************\
  2. FILE: iernonce.cpp
  3. CREATED: October 16, 1996
  4. HISTORY:
  5. Initial version from BryanSt.
  6. Modified extensively for UI, root dependencies, bug fixes, etc. by PritObla.
  7. COPYRIGHT:
  8. Copyright (c) 1996 Microsoft Coporation.
  9. DEFINITIONS:
  10. SECTION - The old RunOnce API is in a flat section. The RunOnceEx
  11. API groups entries into sections for managability. If the
  12. Status dialog is displayed, it will display each section
  13. name while it's being processed. Example sections for
  14. Internet Explorer would be: ActiveX, Java, Internet Explorer.
  15. ENTRY - Entries are listed in a section. These are commands
  16. that need to be carried out. The commands can be a string
  17. to execute, a DLL to register (regsvr32), a DLL to unregister (regsvr32 -u),
  18. or to call a function in a DLL that has WinMain parameters (rundll32).
  19. DESCRIPTION:
  20. 1.0 Goal
  21. Currently the "Run" and "RunOnce" feature in Explorer.exe will shell
  22. execute commands when the shell starts up. Several features are
  23. needed for this to be more robust for Internet Explorer 4.0.
  24. The replacements are RunEx for Run and RunOnceEx for RunOnce. The
  25. main features are:
  26. Status - A dialog will be displayed while the items are being
  27. processed. Internet Explorer 4.0 adds at least 30 items which
  28. take more than a minute on 486 machines. The entries to be
  29. processed will be grouped into sections and the dialog will
  30. highlight the current section being processed. The status dialog
  31. feature can be turned off by using a flag.
  32. Performance - The majority of the Run and RunOnce commands are
  33. calls to regsvr32.exe and runonce32.exe. These create separate
  34. processes which is very inefficient. This API will not create
  35. separate processes for DLLs that need to be called in the same way
  36. that regsvr32.exe or rundll32.exe calls DLLs. This API also supports
  37. a dependency list of DLLs that should remain loaded while either all
  38. the sections or some of the sections are being processed.
  39. Error Handling - If an exception occurs while calling a function in
  40. a DLL, the exception will be caught and error dialog will be
  41. displayed to the user. This error dialog can be suppressed using a
  42. flag in the API, but this is recommended only for cases where a
  43. debug is installed because it will crash explorer.exe. If a flag is
  44. set, an error and/or logging file can be generated in %WinDir% for
  45. debugging purposes.
  46. Deterministic - Currently the order that the entries are processed
  47. is not deterministic. This new API will sort the entries and
  48. sections alphabetically to force a deterministic order. The current
  49. API will not wait until one entry is finished before starting the
  50. next entry, except for RunOnce items in Local_Machine. In order to
  51. be deterministic, this API will process command synchronously unless
  52. a flag is set to turned off this functionality.
  53. Export Functionality - This API will be added to shell32.dll and
  54. called from explorer.exe. API functions will be exported so other
  55. applications can use this functionality if needed.
  56. 2.0 Registry Structure
  57. 2.1 RegistryFormat
  58. Text in italics are strings generated by the user.
  59. [Local_Machine] or [Current_User]
  60. SOFTWARE\Microsoft\Windows\CurrentVersion\RunEx (same as RunOnceEx below)
  61. SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceEx
  62. Flags = [DWORD] 0x0000000
  63. Title = [STR] "Status Dialog Title" (Example, "Installing Internet Explorer 4.0")
  64. Depend\
  65. <PlaceHolder1> = [STR] "<DLL/OCX Filename>" (filename with or without the path)
  66. <PlaceHolder...> = [STR] "<DLL/OCX Filename>" (filename with or without the path)
  67. <SectionPlaceHolder1>\
  68. @="SectionName1"
  69. <Entry1> = [STR] "<EntryFormat>" (See Entry Format description below)
  70. <Entry...> = [STR] "<EntryFormat >"
  71. Depend\
  72. <PlaceHolder1> = [STR] "<DLL/OCX Filename>"
  73. <PlaceHolder...> = [STR] "<DLL/OCX Filename>"
  74. < SectionPlaceHolder2>\
  75. 2.1.1 Flags
  76. // Old RunOnce Flags
  77. #define RRA_DEFAULT 0x0000
  78. #define RRA_DELETE 0x0001
  79. #define RRA_WAIT 0x0002
  80. #define RRA_SHELLSERVICEOBJECTS 0x0004
  81. // New RunOnceEx Flags
  82. #define RRAEX_NO_ERROR_DIALOGS 0x0008
  83. #define RRAEX_ERRORFILE 0x0010
  84. #define RRAEX_LOG_FILE 0x0020
  85. #define RRAEX_NO_EXCEPTION_TRAPPING 0x0040
  86. #define RRAEX_NO_STATUS_DIALOG 0x0080
  87. #define RRAEX_IGNORE_REG_FLAGS 0x0100
  88. RRA_DEFAULT - All features are off but can be turned on by the flags
  89. in the Flags registry key.
  90. RRA_DELETE - Delete the registry entries after processing them.
  91. Normally used for RunOnceEx and not used for RunEx.
  92. RRA_WAIT - If the entry is a command to be executed, this flag causes
  93. the items to be processed synchronously.
  94. RRA_SHELLSERVICEOBJECTS - If this flag is set, we load inproc dlls
  95. from the registry key and QI them for IOleCommandTarget.
  96. CGID_ShellServiceObject notifications are sent to these objects
  97. letting them know about shell status.
  98. RRAEX_NO_STATUS_DIALOG - Use this flag to cause the Status dialog to
  99. not be displayed while processing the entries.
  100. RRAEX_ NO_ERROR_DIALOGS - This flag will turn off error dialogs from
  101. being displayed.
  102. RRAEX_ERRORFILE - This flag will cause the file "C:\Windows\RunOnceEx.err"
  103. to be created if errors occur.
  104. RRAEX_LOG_FILE - This flag will cause the file "C:\Windows\RunOnceEx.log"
  105. to be created giving the status of commands being executed. (For debugging)
  106. RRAEX_ NO_EXCEPTION_TRAPPING - If this flag is set, exceptions will
  107. not be caught when registering DLLs. This should only be used when a
  108. debug is setup or buggy DLLs will cause explorer.exe to crash.
  109. RRAEX_IGNORE_REG_FLAGS - If this flag is set, then the Flags registry
  110. entry will be ignored.
  111. The Flags registry key will be deleted if the RRAEX_DELETE flag was
  112. set.
  113. 2.1.2 Title
  114. The Status Dialogs title will be set to Title registry key if it
  115. exists. This key will be deleted if the RRAEX_DELETE flag was set.
  116. 2.1.3 Top Level Depend Entries
  117. Each entry under the Depend branch will be read. These registry
  118. entries need to be type STR and the data needs to be a valid filename
  119. with or without a path. The registry value will be ignored.
  120. LoadLibrary() will be called on each entry and they will remain loaded
  121. until all the sections have been processed. This key will be deleted
  122. if the RRA_DELETE flag was set.
  123. Example:
  124. SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceEx\Depend
  125. "Should stay loaded" = [STR] "shell32.dll"
  126. 2.1.4 Sections
  127. Each non-"Depend" keys under RunEx and RunOnceEx is considered a
  128. section. The names of the Sections are read into memory, sorted
  129. alphabetically. The key name is displayed in the Status Dialog if
  130. the key doesnt have data to specify the Display Name. The sections
  131. will be processed in alphabetic order. The Sections branch in the
  132. registry will be deleted after being processed if the RRAEX_DELETE
  133. flag was set.
  134. Each Section will contain "entries" which are string registry keys.
  135. The registry entry value is only used to sort the entries. The
  136. registry entry data will be in the format specified in 2.2 Entry
  137. Format.
  138. Example:
  139. SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceEx\IE4 03 Java
  140. @="Java"
  141. "IE4 01 Java" = [STR] "||my.exe -quiet -url http://www.microsft.com/"
  142. "IE4 02 Main Java File" = [STR] "msjava.dll|DllRegisterServer"
  143. If the Section has a Display Name, then the key name should contain
  144. an abbreviation of the product followed by a number that specifies
  145. the order that this section should be processed in. Because the
  146. section names are sorted alphabetically, this will make sure that
  147. all entries for one product are processed together and that the number
  148. makes the order obvious.
  149. An Example:
  150. SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceEx\IE4 03 Java
  151. @="Java"
  152. SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceEx\IE4 04 Scripting
  153. @="ActiveX Scripting"
  154. 2.1.4.1 Section Dependencies
  155. The list off DLLs in the sections Depend key are loaded before a
  156. sections entries are executed and then unloaded after they have
  157. been executed. This key will be deleted if the RRA_DELETE flag was
  158. set.
  159. Example:
  160. SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceEx\Java
  161. "IE4 01 Java" = [STR] "||my.exe -quiet -url http://www.microsft.com/"
  162. "IE4 02 Java" = [STR] "shdocvw.dll|DllRegisterServer"
  163. SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceEx\Java\Depend
  164. "Keep Loaded" = [STR] "urlmon.dll"
  165. 2.2 Entry Format
  166. "<DllFileName>|<FunctionName>|<CmdLineArgs>"
  167. The Entry Format will fall into one of three categories,
  168. 1) Shell Execute (same as old RunOnce method),
  169. 2) Call DllRegisterServer or DllUnregisterServer in the
  170. specified DLL (same as regsvr32.exe), and 3) Call a function
  171. in the specified DLL with WinMain parameters (same as rundll32.exe)
  172. and the <CmdLineArgs> will be passed in the CmdLine parameter.
  173. Register DLL - The <DllFileName> is required to be non-empty. The
  174. <FunctionName> sections must equal "DllRegisterServer" or
  175. "DllUnregisterServer". The <CmdLineArgs> must be empty and there
  176. must not be a semicolon after <FunctionName>.
  177. Example: "shdocvw.dll|DllRegisterServer".
  178. Calling a Function - The <DllFileName> and <FunctionName> sections
  179. are required to be non-empty. The <CmdLineArgs> can be empty and there
  180. must be a semicolon after <FunctionName>.
  181. Example: "C:\winnt\system32\my.dll|MyWinMain|-Start".
  182. Shell Execute - The <DllFileName> and <FunctionName> sections are
  183. required to be empty. The <DllFileName> string will be executed in
  184. same way the old RunOnce items where executed.
  185. Example: "||iexplore.exe http://www.microsoft.com/".
  186. 3.0 Function Calls
  187. void ProcessRunOnceEx(DWORD dwFlags);
  188. This function will call RunRegAppsAndObjectsEx() with the RunEx and
  189. RunOnceEx registry keys in both Local_Machine and Current_User
  190. sections of the registry with the Flags passed in. RRA_WAIT will be
  191. passed because its on be default. RRA_DELETE will be passed for
  192. RunOnceEx.
  193. void RunRegAppsAndObjectsEx(HKEY hkeyParent, LPCTSTR szSubkey, DWORD dwFlags);
  194. This function will process the entries in this section of the registry.
  195. The szSubKey cannot point to a section of the registry that has the
  196. old Run or RunOnce format.
  197. \************************************************************/
  198. /*Includes---------------------------------------------------------*/
  199. #include "iernonce.h"
  200. #include "resource.h"
  201. #ifdef WX86
  202. #include <wx86ofl.h>
  203. #endif // WX86
  204. //////////////////////////////////////////////////////////////////
  205. // Constants:
  206. //////////////////////////////////////////////////////////////////
  207. #define MAX_REG_PATH 256 // Registry paths should not get much longer than this or there is a major perf hit
  208. #define MAX_REG_VALUE 80 // Registry values should not get much longer than this or there is a major perf hit
  209. #define ARRAY_GROW_RATE 32 // This should be large enough to cover most applications
  210. #define SEPERATOR_CHAR TEXT('|')
  211. //////////////////////////////////////////////////////////////////
  212. // TYPES:
  213. //////////////////////////////////////////////////////////////////
  214. typedef enum
  215. {
  216. eHDL_Load,
  217. eHDL_Unload
  218. } HDL_Type;
  219. typedef int (*WINMAIN_PARAMS)(HINSTANCE, HINSTANCE, LPSTR, int);
  220. typedef LONG (*DLLREGISTERSERVER_PARAMS)();
  221. typedef HRESULT (*DLLINSTALL_PARAMS)(BOOL bInstall, LPCWSTR pszCmdLine);
  222. //////////////////////////////////////////////////////////////////
  223. // GLOBALS:
  224. //////////////////////////////////////////////////////////////////
  225. const TCHAR * c_szDependencyName = TEXT("Depend");
  226. const TCHAR * c_szFlagsRegValue = TEXT("Flags");
  227. const TCHAR * g_c_szTitleRegValue = TEXT("Title");
  228. const TCHAR * g_c_szSystemDat = TEXT("system.dat");
  229. const TCHAR * g_c_szSystem1st = TEXT("system.1st");
  230. const TCHAR * g_c_szSetupKey = TEXT("Software\\Microsoft\\IE Setup\\Setup");
  231. const TCHAR * g_c_szRegBackupPath = TEXT("RegistryBackup");
  232. const TCHAR * g_c_szServicesRegValue = TEXT("Services");
  233. HINSTANCE g_hinst = NULL;
  234. HANDLE g_hHeap = NULL;
  235. RUNONCEEXPROCESSCALLBACK g_pCallbackProc = NULL;
  236. BOOL g_bQuiet = FALSE;
  237. int g_nTotal = 0;
  238. int g_nCurrent = 0;
  239. BOOL g_bRunningOnNT = FALSE;
  240. BOOL g_bBackupSystemDat = FALSE;
  241. #if 0
  242. BOOL g_bDeleteSystemIE4 = FALSE;
  243. #endif
  244. int g_iNDisplaySections = 0;
  245. TCHAR g_szTitleString[256] = TEXT("");
  246. // related to logging
  247. HANDLE g_hLogFile = INVALID_HANDLE_VALUE;
  248. #ifdef WX86
  249. // If Wx86 has to be loaded we use this flag to make sure it is unloaded
  250. // at the end of RunOnceExProcess.
  251. BOOL g_fWx86Loaded = FALSE;
  252. #endif
  253. //////////////////////////////////////////////////////////////////
  254. // Internal Functions
  255. //////////////////////////////////////////////////////////////////
  256. void WINAPI RunOnceExProcess(HWND hWnd, HINSTANCE hInstance, LPSTR lpCmdLine, int nCmdShow);
  257. void RunOnceExProcessReg(HKEY hkeyParent, LPCTSTR pszSubkey, DWORD dwFlags);
  258. HDPA GetSections(HKEY hkeyParent, LPCTSTR szSubkey, DWORD dwFlags, int * pNumberOfSections);
  259. HDPA GetEntries(HKEY hRootKey, LPCTSTR szSectionName, DWORD dwFlags, int * pNumberOfEntries);
  260. void HandleDependencyDLLs(HKEY hkeyParent, LPCTSTR szRegPath, LPCTSTR szRegSubPath, DWORD dwFlags, HDL_Type hdlDirection);
  261. DWORD GetFlagsInRegistry(HKEY hkeyParent, LPCTSTR szSubkey);
  262. void ShellExecuteRegApp(LPTSTR pszCmdLine, DWORD dwFlags);
  263. BOOL HaveDependServices(SC_HANDLE hService);
  264. void CheckServices(DWORD dwFlags);
  265. BOOL WINAPI DllMain(HINSTANCE hDLLInst, DWORD fdwReason, LPVOID lpvReserved)
  266. {
  267. if (fdwReason == DLL_PROCESS_ATTACH)
  268. {
  269. OSVERSIONINFO osvi;
  270. DisableThreadLibraryCalls(hDLLInst);
  271. // The DLL is being loaded for the first time by a given process.
  272. // Perform per-process initialization here. If the initialization
  273. // is successful, return TRUE; if unsuccessful, return FALSE.
  274. // Initialize the global variable holding the hinstance:
  275. g_hinst = hDLLInst;
  276. g_hHeap = GetProcessHeap();
  277. osvi.dwOSVersionInfoSize = sizeof(osvi);
  278. GetVersionEx(&osvi);
  279. if (VER_PLATFORM_WIN32_NT == osvi.dwPlatformId)
  280. g_bRunningOnNT = TRUE;
  281. }
  282. return TRUE;
  283. }
  284. #ifdef WX86
  285. /****************************************************\
  286. FUNCTION: RunOnceLoadLibrary
  287. PARAMETERS:
  288. LPCTSTR lpszFileName - DLL to load
  289. BOOL* pfWx86DLL - Ptr to a BOOL that we set to
  290. TRUE if the DLL is x86.
  291. DESCRIPTION:
  292. This function will is a wrapper for LoadLibraryEx()
  293. that will also load x86 DLLs on RISC.
  294. \***************************************************/
  295. HINSTANCE RunOnceLoadLibrary( LPCTSTR lpszFileName, BOOL* pfWx86DLL )
  296. {
  297. HINSTANCE hInstance = LoadLibraryEx(lpszFileName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
  298. if ( (hInstance == NULL) && (GetLastError() == ERROR_BAD_EXE_FORMAT) )
  299. {
  300. WCHAR achDllToLoad[ MAX_PATH ];
  301. #ifdef UNICODE
  302. lstrcpy(achDllToLoad, lpszFileName);
  303. #else // UNICODE
  304. MultiByteToWideChar(CP_ACP, 0, lpszFileName, -1,
  305. achDllToLoad, ARRAYSIZE(achDllToLoad));
  306. #endif // UNICODE
  307. if ( !g_fWx86Loaded )
  308. {
  309. g_fWx86Loaded = Wx86Load();
  310. }
  311. if ( g_fWx86Loaded )
  312. {
  313. hInstance = Wx86LoadX86Dll(achDllToLoad, LOAD_WITH_ALTERED_SEARCH_PATH);
  314. if ( hInstance != NULL )
  315. {
  316. *pfWx86DLL = TRUE;
  317. }
  318. }
  319. }
  320. return hInstance;
  321. }
  322. #else // WX86
  323. // If we're on x86 then a simple macro works...
  324. #define RunOnceLoadLibrary(_arg1, _arg2) LoadLibraryEx(_arg1, NULL, LOAD_WITH_ALTERED_SEARCH_PATH)
  325. #endif // WX86
  326. void BackupRegistry()
  327. {
  328. HKEY hkSetup = NULL;
  329. DWORD unused;
  330. TCHAR szSystemDatPath[MAX_PATH] = TEXT("");
  331. TCHAR szSystemIE4Path[MAX_PATH] = TEXT("");
  332. // flush the registry and copy system.dat to system.ie4;
  333. // this is for the PSS folks; if the registry gets corrupt after IE4 has been installed, user can copy system.ie4 to system.dat
  334. // quite an expensive operation!
  335. RegFlushKey(HKEY_CLASSES_ROOT);
  336. RegFlushKey(HKEY_CURRENT_USER);
  337. RegFlushKey(HKEY_LOCAL_MACHINE);
  338. RegFlushKey(HKEY_USERS);
  339. GetWindowsDirectory(szSystemDatPath, ARRAYSIZE(szSystemDatPath));
  340. if(RegCreateKeyEx(HKEY_LOCAL_MACHINE, g_c_szSetupKey, 0, NULL,
  341. REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE,
  342. NULL, &hkSetup, &unused) == ERROR_SUCCESS)
  343. {
  344. unused = sizeof(szSystemIE4Path);
  345. RegQueryValueEx(hkSetup, g_c_szRegBackupPath, 0, NULL, (BYTE *)szSystemIE4Path, &unused);
  346. }
  347. // if we don't have a path yet default to root of windows drive
  348. if(szSystemIE4Path[0] == TEXT('\0'))
  349. {
  350. lstrcpy(szSystemIE4Path, szSystemDatPath);
  351. GetParentDir(szSystemIE4Path);
  352. AddPath(szSystemIE4Path, g_c_szSystem1st);
  353. }
  354. AddPath(szSystemDatPath, g_c_szSystemDat);
  355. SetFileAttributes(szSystemDatPath, FILE_ATTRIBUTE_NORMAL);
  356. SetFileAttributes(szSystemIE4Path, FILE_ATTRIBUTE_NORMAL);
  357. if(CopyFile(szSystemDatPath, szSystemIE4Path, FALSE))
  358. {
  359. // Record where we put it in the registry
  360. if(hkSetup)
  361. RegSetValueEx(hkSetup, g_c_szRegBackupPath, 0, REG_SZ,
  362. (BYTE *)szSystemIE4Path, sizeof(TCHAR) * (lstrlen(szSystemIE4Path) + 1));
  363. // Write something to the active setup log
  364. }
  365. if(hkSetup)
  366. RegCloseKey(hkSetup);
  367. SetFileAttributes(szSystemDatPath, FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM);
  368. SetFileAttributes(szSystemIE4Path, FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM);
  369. }
  370. VOID InitCallback(RUNONCEEXPROCESSCALLBACK pCallbackProc, BOOL bQuiet)
  371. {
  372. g_pCallbackProc = pCallbackProc;
  373. g_bQuiet = bQuiet;
  374. }
  375. /****************************************************\
  376. FUNCTION: RunOnceExProcess
  377. PARAMETERS:
  378. DWORD dwFlags - Caller can specify behavior
  379. with flags.
  380. DESCRIPTION:
  381. This function will run the RunEx API. It will
  382. process RunOnceEx and RunEx in both Current_User
  383. and Local_Machine
  384. \***************************************************/
  385. void WINAPI RunOnceExProcess(HWND hWnd, HINSTANCE hInstance, LPSTR lpCmdLine, int nCmdShow)
  386. {
  387. const TCHAR * szRunOnceExPath = REGSTR_PATH_RUNONCEEX;
  388. //const TCHAR * szRunExPath = TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunEx");
  389. DWORD dwFlags = RRA_DEFAULT;
  390. if (lpCmdLine != NULL)
  391. dwFlags |= AtoL(lpCmdLine);
  392. if (g_bQuiet)
  393. dwFlags |= RRAEX_NO_STATUS_DIALOG | RRAEX_NO_ERROR_DIALOGS;
  394. RunOnceExProcessReg(HKEY_LOCAL_MACHINE, szRunOnceExPath, dwFlags | RRA_DELETE | RRA_WAIT);
  395. RunOnceExProcessReg(HKEY_CURRENT_USER, szRunOnceExPath, dwFlags | RRA_DELETE | RRA_WAIT);
  396. #ifdef WX86
  397. if ( g_fWx86Loaded )
  398. {
  399. Wx86Unload();
  400. }
  401. #endif
  402. //RunOnceExProcessReg(HKEY_LOCAL_MACHINE, szRunExPath, dwFlags);
  403. //RunOnceExProcessReg(HKEY_CURRENT_USER, szRunExPath, dwFlags);
  404. if (!g_bRunningOnNT && g_bBackupSystemDat)
  405. {
  406. BackupRegistry();
  407. }
  408. if (g_bRunningOnNT)
  409. {
  410. CheckServices(dwFlags);
  411. }
  412. }
  413. /****************************************************\
  414. FUNCTION: RunOnceExProcessReg
  415. PARAMETERS:
  416. HKEY hkeyParent - Registry Branch to process.
  417. LPCTSTR pszSubkey - Registry Path to process.
  418. DWORD dwFlags - Registry Entry to modify.
  419. DESCRIPTION:
  420. This function will run the API on one part of
  421. the registry.
  422. \***************************************************/
  423. void RunOnceExProcessReg(HKEY hkeyParent, LPCTSTR pszSubkey, DWORD dwFlags)
  424. {
  425. HDPA hdpaSections = NULL;
  426. HKEY hRunExKey = NULL;
  427. RunOnceExSection * pCurrentSection = NULL;
  428. int iNumberOfSections = 0;
  429. int iIndex;
  430. ARGSINFO aiArgs;
  431. static DWORD dwCreationFlags = CREATE_ALWAYS; // first time the log file is opened, the contents should be truncated
  432. static TCHAR szLogFileName[MAX_PATH] = TEXT("");
  433. if (!(RRAEX_IGNORE_REG_FLAGS & dwFlags))
  434. dwFlags |= GetFlagsInRegistry(hkeyParent, pszSubkey);
  435. *g_szTitleString = TEXT('\0');
  436. if (RegOpenKeyEx(hkeyParent, pszSubkey, NULL, KEY_READ, &hRunExKey) == ERROR_SUCCESS)
  437. {
  438. DWORD dwTitleSize = sizeof(g_szTitleString);
  439. // if a value for Title is specified, read it in
  440. RegQueryValueEx(hRunExKey, g_c_szTitleRegValue, NULL, NULL, (LPBYTE) g_szTitleString, &dwTitleSize);
  441. RegCloseKey(hRunExKey);
  442. }
  443. // don't process if there are entries in wininit.ini or PendingFileRenameOperations
  444. if ((dwFlags & RRAEX_QUIT_IF_REBOOT_NEEDED) && NeedReboot(0))
  445. return;
  446. if (g_bRunningOnNT && (dwFlags & RRAEX_CHECK_NT_ADMIN) && !IsNTAdmin(0, NULL))
  447. {
  448. // user has no NT Admin privileges; we cannot process the RunOnceEx items
  449. // make sure that the msg box is displayed
  450. ReportError((dwFlags & ~RRAEX_NO_ERROR_DIALOGS), IDS_RUNONCEEX_NOT_NT_ADMIN);
  451. LogOff(FALSE);
  452. // won't reach here
  453. return;
  454. }
  455. if (dwFlags & RRAEX_BACKUP_SYSTEM_DAT)
  456. g_bBackupSystemDat = TRUE;
  457. if (dwFlags & RRAEX_LOG_FILE)
  458. {
  459. if (*szLogFileName == TEXT('\0'))
  460. GetLogFileName(TEXT("RunOnceExLogFile"), szLogFileName, ARRAYSIZE(szLogFileName));
  461. if (*szLogFileName)
  462. {
  463. if (hkeyParent == HKEY_LOCAL_MACHINE)
  464. {
  465. TCHAR szBuf[MAX_PATH + MAX_REG_PATH + 16];
  466. // dump the registry entries before we process them so that if some error occurs,
  467. // it is easy to cut and paste the entries to a reg file and import it.
  468. wsprintf(szBuf, TEXT("regedit /e \"%s\" HKEY_LOCAL_MACHINE\\%s"), szLogFileName, pszSubkey);
  469. ShellExecuteRegApp(szBuf, RRA_WAIT);
  470. dwCreationFlags = OPEN_ALWAYS; // from now on, the log file should be opened in append mode
  471. }
  472. StartLogging(szLogFileName, dwCreationFlags);
  473. dwCreationFlags = OPEN_ALWAYS; // from now on, the log file should be opened in append mode
  474. }
  475. }
  476. // Log that we are starting to process these commands.
  477. WriteToLog(TEXT("[%1:%2]\r\n"), (HKEY_LOCAL_MACHINE == hkeyParent) ? TEXT("HKLM"):TEXT("HKCU"), pszSubkey);
  478. LogFlags(dwFlags);
  479. g_iNDisplaySections = 0;
  480. hdpaSections = GetSections(hkeyParent, pszSubkey, dwFlags, &iNumberOfSections);
  481. // Load the dependency DLLs that are common to all sections
  482. HandleDependencyDLLs(hkeyParent, pszSubkey, "", dwFlags, eHDL_Load);
  483. if (iNumberOfSections) // call ProcessSections only if there is atleast one section to process
  484. {
  485. // show UI if the NO_STATUS_DIALOG flag is NOT specified, and
  486. // there is atleast one section that has display text
  487. if (0 == (RRAEX_NO_STATUS_DIALOG & dwFlags) && g_iNDisplaySections)
  488. {
  489. // pack the args of ProcessSections into aiArgs
  490. aiArgs.hkeyParent = hkeyParent;
  491. aiArgs.pszSubkey = pszSubkey;
  492. aiArgs.dwFlags = dwFlags;
  493. aiArgs.hdpaSections = hdpaSections;
  494. aiArgs.iNumberOfSections = iNumberOfSections;
  495. DialogBoxParam(g_hinst, MAKEINTRESOURCE(IDD_RUNONCE), NULL, DlgProcRunOnceEx, (LPARAM) &aiArgs);
  496. }
  497. else
  498. ProcessSections(hkeyParent, pszSubkey, dwFlags, hdpaSections, iNumberOfSections, NULL);
  499. }
  500. // Unload the dependency DLLs that are common to all sections
  501. HandleDependencyDLLs(hkeyParent, pszSubkey, "", dwFlags, eHDL_Unload);
  502. // We iterate twice. Once to process all the entries, and the second time to delete
  503. // the entries. We do this because some code (like Dialog Status drawing) might want
  504. // to access previously processed entries.
  505. for (iIndex = 0; iIndex < iNumberOfSections; iIndex++)
  506. {
  507. pCurrentSection = (RunOnceExSection *) DPA_GetPtr(hdpaSections, iIndex);
  508. if (pCurrentSection)
  509. {
  510. delete pCurrentSection;
  511. }
  512. }
  513. if (hdpaSections)
  514. {
  515. DPA_Destroy(hdpaSections);
  516. hdpaSections = NULL;
  517. }
  518. // Delete the Flags and Title registry entry if the Delete flag is set.
  519. if (RRA_DELETE & dwFlags)
  520. {
  521. if (ERROR_SUCCESS == RegOpenKeyEx(hkeyParent, pszSubkey, NULL, KEY_READ | KEY_WRITE, &hRunExKey))
  522. {
  523. // Delete this key if the Delete key is set anywhere.
  524. RegDeleteValue(hRunExKey, c_szFlagsRegValue);
  525. RegDeleteValue(hRunExKey, g_c_szTitleRegValue);
  526. RegCloseKey(hRunExKey);
  527. }
  528. }
  529. WriteToLog(TEXT("\r\n"));
  530. if (dwFlags & RRAEX_LOG_FILE)
  531. StopLogging();
  532. }
  533. /****************************************************\
  534. FUNCTION: ProcessSections
  535. PARAMETERS:
  536. HKEY hkeyParent - Registry Branch to process.
  537. LPCTSTR pszSubkey - Registry Path to process.
  538. DWORD dwFlags - Registry Entry to modify.
  539. HDPA hdpaSections - Array of Sections
  540. int iNumberOfSections - Number of Sections in the Array
  541. DESCRIPTION:
  542. This function will call to have each Section
  543. processed.
  544. \***************************************************/
  545. void ProcessSections(HKEY hkeyParent, LPCTSTR pszSubkey, DWORD dwFlags, HDPA hdpaSections, int iNumberOfSections, HWND hWnd)
  546. {
  547. RunOnceExSection * pCurrentSection = NULL;
  548. int iIndex, iDisplayIndex;
  549. BOOL fOleInitialized = TRUE;
  550. // Got to initialize it here; if it's done in RunOnceExProcess, i.e., process level initialization,
  551. // registration of asctrls.ocx fails -- looks like OleInitialize needs to be done per thread
  552. if (FAILED(OleInitialize(NULL)))
  553. fOleInitialized = FALSE;
  554. iDisplayIndex = -1;
  555. // If there's a callback, count total things to do...
  556. if (g_pCallbackProc)
  557. {
  558. g_nTotal = 0;
  559. g_nCurrent = 0;
  560. for (iIndex = 0; iIndex < iNumberOfSections; iIndex++)
  561. {
  562. if ((pCurrentSection = (RunOnceExSection *) DPA_GetPtr(hdpaSections, iIndex)) == NULL)
  563. continue;
  564. g_nTotal += pCurrentSection->m_NumberOfEntries;
  565. }
  566. }
  567. for (iIndex = 0; iIndex < iNumberOfSections; iIndex++)
  568. {
  569. if ((pCurrentSection = (RunOnceExSection *) DPA_GetPtr(hdpaSections, iIndex)) == NULL)
  570. continue;
  571. if (hWnd != NULL)
  572. {
  573. if (*pCurrentSection->m_szDisplayName != TEXT('\0'))
  574. SendMessage(hWnd, LB_SETCURSEL, ++iDisplayIndex, 0);
  575. else if (iDisplayIndex == -1)
  576. SendMessage(hWnd, LB_SETCURSEL, 0, 0);
  577. }
  578. pCurrentSection->Process(hkeyParent, pszSubkey, dwFlags);
  579. }
  580. if (fOleInitialized)
  581. OleUninitialize();
  582. }
  583. /****************************************************\
  584. FUNCTION: GetFlagsInRegistry
  585. PARAMETERS:
  586. HKEY hkeyParent - What branch of the registry to look.
  587. LPCTSTR szSubkey - What path in the registry to look.
  588. DWORD return - Flags that were found in the registry
  589. DESCRIPTION:
  590. Return with the Flags found in the registry
  591. or RRA_DEFAULT if none where found.
  592. \***************************************************/
  593. DWORD GetFlagsInRegistry(HKEY hkeyParent, LPCTSTR szSubkey)
  594. {
  595. DWORD dwFlagsInRegistry = RRA_DEFAULT;
  596. HKEY hFlagsKey = NULL;
  597. DWORD dwKeySize = sizeof(dwFlagsInRegistry);
  598. if (ERROR_SUCCESS == RegOpenKeyEx(hkeyParent, szSubkey, NULL, KEY_QUERY_VALUE, &hFlagsKey))
  599. {
  600. RegQueryValueEx(hFlagsKey, c_szFlagsRegValue, NULL, NULL, (LPBYTE) &dwFlagsInRegistry, &dwKeySize);
  601. RegCloseKey(hFlagsKey);
  602. }
  603. return(dwFlagsInRegistry);
  604. }
  605. /****************************************************\
  606. FUNCTION: HandleDependencyDLLs
  607. PARAMETERS:
  608. HKEY hkeyParent - Registry Branch to process.
  609. LPCTSTR szRegPath - Registry Path to process.
  610. LPCTSTR szRegSubPath - Registry Path to process.
  611. DWORD dwFlags - Flags. Do we want to delete after Unload?
  612. HDL_Type hdlDirection - Load or Unload?
  613. DESCRIPTION:
  614. This function will be passed a "Depend" section
  615. of the registry that will contain registry keys.
  616. These STR registry keys have filenames and data.
  617. These filenames need to be loaded (LoadLibrary())
  618. or freed (FreeLibrary()) depending on the HDL_Type
  619. parameter to this function.
  620. \***************************************************/
  621. void HandleDependencyDLLs(HKEY hkeyParent, LPCTSTR szRegPath, LPCTSTR szRegSubPath, DWORD dwFlags, HDL_Type hdlDirection)
  622. {
  623. TCHAR szBasePath[MAX_REG_PATH];
  624. TCHAR szValueName[MAX_REG_VALUE];
  625. TCHAR szDLLFileName[MAX_PATH];
  626. HKEY hBaseKey = NULL;
  627. HKEY hDependKey = NULL;
  628. DWORD dwCurrDLL = 0;
  629. DWORD dwKeyType = 0;
  630. DWORD dwValueSize = ARRAYSIZE(szValueName);
  631. DWORD dwDLLNameSize = sizeof(szDLLFileName);
  632. long lEnumError;
  633. if ((NULL != szRegSubPath) && (TEXT('\0') != *szRegSubPath)) // If the szRegSubPath isn't empty, we concatonate the two strings.
  634. wsprintf(szBasePath, TEXT("%s\\%s"), szRegPath, szRegSubPath);
  635. else
  636. lstrcpy(szBasePath, szRegPath);
  637. // Open the key for Read only if we are doing a load or an unload where the registry does not need to be deleted.
  638. // If we are unloading and we need to remove the registry entry, this function will fail if the user doesn't have
  639. // permission.
  640. if (ERROR_SUCCESS == RegOpenKeyEx(hkeyParent, szBasePath, NULL,
  641. (KEY_READ | (((eHDL_Unload == hdlDirection) && (dwFlags & RRA_DELETE)) ? KEY_WRITE:0)), &hBaseKey))
  642. {
  643. if (ERROR_SUCCESS == RegOpenKeyEx(hBaseKey, c_szDependencyName, NULL,
  644. (KEY_READ | (((eHDL_Unload == hdlDirection) && (dwFlags & RRA_DELETE)) ? KEY_WRITE:0)), &hDependKey))
  645. {
  646. // Iterate through each value
  647. for (dwCurrDLL = 0;
  648. ERROR_NO_MORE_ITEMS != (lEnumError = RegEnumValue(hDependKey, dwCurrDLL, szValueName, &dwValueSize, NULL, &dwKeyType, (LPBYTE) szDLLFileName, &dwDLLNameSize));
  649. dwCurrDLL++)
  650. {
  651. if ((REG_SZ == dwKeyType) && (ERROR_SUCCESS == lEnumError))
  652. {
  653. if (eHDL_Load == hdlDirection)
  654. {
  655. // We need to load this library to keep it in memory.
  656. BOOL fWx86DLL;
  657. if (NULL == RunOnceLoadLibrary( szDLLFileName, &fWx86DLL ))
  658. ReportError(dwFlags, IDS_RUNONCEEX_LOAD_DEPEND_FAILED, szDLLFileName);
  659. else
  660. WriteToLog(TEXT("Dependency DLL loaded: %1\r\n"), szDLLFileName);
  661. }
  662. else // Else Unload
  663. {
  664. // We need to unload this library
  665. HINSTANCE hInst = GetModuleHandle(szDLLFileName);
  666. if (NULL != hInst)
  667. {
  668. if (FreeLibrary(hInst))
  669. WriteToLog(TEXT("Dependency DLL unloaded: %1\r\n"), szDLLFileName);
  670. }
  671. }
  672. }
  673. dwValueSize = ARRAYSIZE(szValueName);
  674. dwDLLNameSize = sizeof(szDLLFileName);
  675. }
  676. RegCloseKey(hDependKey);
  677. if ((eHDL_Unload == hdlDirection) && (dwFlags & RRA_DELETE))
  678. {
  679. // We need to remove the registry key so we don't process this again next time.
  680. RegDeleteKey(hBaseKey, c_szDependencyName);
  681. }
  682. }
  683. RegCloseKey(hBaseKey);
  684. }
  685. }
  686. /////////////////////////////////////////////////////////////////////
  687. // CLASS: RunOnceExEntry
  688. /////////////////////////////////////////////////////////////////////
  689. /****************************************************\
  690. FUNCTION: RunOnceExEntry
  691. PARAMETERS:
  692. LPTSTR lpszNewEntryName - Name of Entry
  693. LPTSTR lpszNewCmd - Entry Command
  694. DWORD dwFlags - Flags
  695. DESCRIPTION:
  696. This function will create a RunOnceEx Entry
  697. and set it's Name, Cmd, and section.
  698. \***************************************************/
  699. RunOnceExEntry::RunOnceExEntry(LPTSTR lpszNewEntryName, LPTSTR lpszNewCmd, DWORD dwFlags)
  700. {
  701. const TCHAR * szRegisterFunctionName = TEXT("DllRegisterServer");
  702. const TCHAR * szUnregisterFunctionName = TEXT("DllUnregisterServer");
  703. const TCHAR * szInstallFunctionName = TEXT("DllInstall");
  704. LPTSTR lpszFileName = lpszNewCmd;
  705. LPTSTR lpszFunctionName = NULL;
  706. LPTSTR lpszCmdLineArgs = NULL;
  707. LPTSTR lpszCurrentChar = lpszNewCmd;
  708. lstrcpy(m_szRunOnceExEntryName, lpszNewEntryName);
  709. m_ROAction = eRO_Unknown;
  710. *m_szFileName = TEXT('\0');
  711. *m_szFunctionName = TEXT('\0');
  712. *m_szCmdLineArgs = TEXT('\0');
  713. // These entries come in this format "<FileName>|<FunctionName>|<CmdLineArgs>".
  714. // We first need to find the end of the <FileName>.
  715. // Find the end of the filename.
  716. if (NULL == (lpszCurrentChar = LocalStrChr(lpszCurrentChar, SEPERATOR_CHAR)))
  717. {
  718. // It must be an EXE (or something that is executable) because it doesn't have a function name.
  719. lstrcpy(m_szCmdLineArgs, lpszNewCmd);
  720. m_ROAction = eRO_Exe; // Remember that we will need to ShellExec this later.
  721. }
  722. else
  723. {
  724. // We found a '|' so we have the <FileName>.
  725. *lpszCurrentChar = TEXT('\0'); // Terminate the filename.
  726. lstrcpy(m_szFileName, lpszNewCmd);
  727. *lpszCurrentChar = SEPERATOR_CHAR; // Remove the temporary termination.
  728. lpszCurrentChar = CharNext(lpszCurrentChar);
  729. // Now lets work on Getting the <FunctionName>.
  730. lpszFunctionName = lpszCurrentChar; // Remember the beginning of the FunctionName before iterating.
  731. if (NULL == (lpszCurrentChar = LocalStrChr(lpszCurrentChar, SEPERATOR_CHAR)))
  732. {
  733. // If we have found the end of the string without the second '|', then
  734. // this needs to be a DllRegisterServer or DllUnregisterServer.
  735. lstrcpy(m_szFunctionName, lpszFunctionName);
  736. if (2 == CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, lpszFunctionName, -1, szRegisterFunctionName, -1))
  737. {
  738. m_ROAction = eRO_Register;
  739. }
  740. else
  741. {
  742. if (2 == CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, lpszFunctionName, -1, szUnregisterFunctionName, -1))
  743. {
  744. m_ROAction = eRO_Unregister;
  745. }
  746. else
  747. {
  748. m_ROAction = eRO_Unknown;
  749. // An error has occured. The user didn't specify a valid FunctionName.
  750. ReportError(dwFlags, IDS_RUNONCEEX_BAD_FUNCTIONNAME, m_szFunctionName);
  751. }
  752. }
  753. }
  754. else
  755. {
  756. // We have enountered the second '|' which delemites the <CmdLineArgs> section. By this point, the function we
  757. // are calling needs WinMain() parameters.or it's a DllInstall function
  758. *lpszCurrentChar = TEXT('\0');
  759. lstrcpy(m_szFunctionName, lpszFunctionName);
  760. *lpszCurrentChar = SEPERATOR_CHAR; // Remove the temporary termination.
  761. lpszCurrentChar = CharNext(lpszCurrentChar);
  762. lstrcpy(m_szCmdLineArgs, lpszCurrentChar);
  763. if (TEXT('\0') == *m_szFunctionName)
  764. {
  765. if (TEXT('\0') == *m_szFileName)
  766. {
  767. m_ROAction = eRO_Exe;
  768. }
  769. else
  770. {
  771. // This command is invalid because commands to be shell execed need to start
  772. // Need to have the <FileName> and <FunctionName> parameters empty.
  773. m_ROAction = eRO_Unknown;
  774. ReportError(dwFlags, IDS_RUNONCEEX_BAD_SHELLEXEC_CMD, lpszNewCmd);
  775. }
  776. }
  777. else
  778. {
  779. // check if it's DllInstall
  780. if (2 == CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, m_szFunctionName, -1, szInstallFunctionName, -1))
  781. {
  782. m_ROAction = eRO_Install;
  783. }
  784. else
  785. {
  786. m_ROAction = eRO_WinMainFunction;
  787. }
  788. }
  789. }
  790. }
  791. }
  792. /****************************************************\
  793. FUNCTION: ~RunOnceExEntry
  794. PARAMETERS:
  795. none
  796. DESCRIPTION:
  797. This destructor will free memory that it used.
  798. \***************************************************/
  799. RunOnceExEntry::~RunOnceExEntry()
  800. {
  801. }
  802. /****************************************************\
  803. FUNCTION: RunOnceExEntry::Process
  804. PARAMETERS:
  805. HKEY hkeyParent - Section of the registry being processed.
  806. LPCTSTR szSubkey - Path to the registry entries being processed.
  807. LPCTSTR szSectonName - Only used for logging purposes.
  808. DWORD dwFlags - Flags to determine if we delete command after executing it.
  809. DESCRIPTION:
  810. This function will Processes the entry.
  811. This means it will execute the command or
  812. call a function in a DLL. After it finishes,
  813. it will delete the registry entry if appropriate.
  814. \***************************************************/
  815. void RunOnceExEntry::Process(HKEY hkeyParent, LPCTSTR szSubkey, LPCTSTR szSectionName, DWORD dwFlags)
  816. {
  817. HINSTANCE hInstance = NULL;
  818. WINMAIN_PARAMS pfWinMainFunction = NULL;
  819. DLLREGISTERSERVER_PARAMS pfDllRegisterServer = NULL;
  820. DLLINSTALL_PARAMS pfDllInstall = NULL;
  821. TCHAR szRegKeyPath[MAX_REG_PATH] = TEXT("");
  822. HKEY hCurrentSectionKey = NULL;
  823. BOOL fWx86DLL = FALSE;
  824. // Delete Registry Entry
  825. if ((NULL != szSectionName) && (RRA_DELETE & dwFlags))
  826. {
  827. wsprintf(szRegKeyPath, TEXT("%s\\%s"), szSubkey, szSectionName);
  828. LocalSHDeleteValue(hkeyParent, szRegKeyPath, m_szRunOnceExEntryName);
  829. }
  830. WriteToLog(TEXT("File:%1; Function:%2; Args:%3; Action:"), m_szFileName, m_szFunctionName, m_szCmdLineArgs);
  831. switch(m_ROAction)
  832. {
  833. case eRO_Register:
  834. case eRO_Unregister:
  835. WriteToLog(m_ROAction == eRO_Register ? TEXT("DllRegisterServer()\r\n") : TEXT("DllUnRegisterServer()\r\n"));
  836. hInstance = RunOnceLoadLibrary( m_szFileName, &fWx86DLL );
  837. if (NULL != hInstance)
  838. {
  839. HRESULT hResult = S_OK;
  840. char pcstrDLLToLoad[MAX_PATH];
  841. #ifdef UNICODE
  842. WideCharToMultiByte(CP_ACP, 0, m_szFunctionName, -1,
  843. pcstrDLLToLoad, ARRAYSIZE(pcstrDLLToLoad), NULL, NULL);
  844. #else // UNICODE
  845. lstrcpy(pcstrDLLToLoad, m_szFunctionName);
  846. #endif // UNICODE
  847. if (NULL != (pfDllRegisterServer = (DLLREGISTERSERVER_PARAMS) GetProcAddress(hInstance, pcstrDLLToLoad)))
  848. {
  849. if (RRAEX_NO_EXCEPTION_TRAPPING & dwFlags) // Don't trap exceptions if that's what the user wants.
  850. {
  851. // This flag is off be default because you better have a debugger installed if you don't catch
  852. // these exceptions
  853. #ifdef WX86
  854. if ( fWx86DLL )
  855. {
  856. hResult = Wx86EmulateX86(pfDllRegisterServer, 0, NULL );
  857. }
  858. else
  859. #endif
  860. hResult = (*pfDllRegisterServer)();
  861. }
  862. else
  863. {
  864. _try
  865. {
  866. #ifdef WX86
  867. if ( fWx86DLL )
  868. {
  869. hResult = Wx86EmulateX86(pfDllRegisterServer, 0, NULL );
  870. }
  871. else
  872. #endif
  873. hResult = (*pfDllRegisterServer)();
  874. }
  875. _except(EXCEPTION_EXECUTE_HANDLER) // Catch all exceptions.
  876. {
  877. ReportError(dwFlags, IDS_RUNONCEEX_EXCEPTION, m_szFunctionName, m_szFileName);
  878. }
  879. }
  880. if (FAILED(hResult))
  881. {
  882. WriteToLog(TEXT("An error occurred calling ""%1"" in ""%2"". (HRESULT = %3!lx!)\r\n"), m_szFunctionName, m_szFileName, hResult);
  883. ReportError(dwFlags, IDS_RUNONCEEX_REGISTER_ERROR, m_szFileName);
  884. }
  885. }
  886. else
  887. {
  888. ReportError(dwFlags, IDS_RUNONCEEX_FIND_FUNC_FAILED, m_szFunctionName, m_szFileName);
  889. }
  890. FreeLibrary(hInstance);
  891. }
  892. else
  893. {
  894. ReportError(dwFlags, IDS_RUNONCEEX_CANNOT_LOAD_DLL, m_szFileName);
  895. }
  896. break;
  897. case eRO_Install:
  898. WriteToLog(TEXT("DllInstall()\r\n"));
  899. hInstance = RunOnceLoadLibrary( m_szFileName, &fWx86DLL );
  900. if (NULL != hInstance)
  901. {
  902. char pcstrDLLToLoad[MAX_PATH];
  903. WCHAR pwstrDLLCmdLineArgs[MAX_PATH];
  904. HRESULT hResult = S_OK;
  905. #ifdef UNICODE
  906. WideCharToMultiByte(CP_ACP, 0, m_szFunctionName, -1,
  907. pcstrDLLToLoad, ARRAYSIZE(pcstrDLLToLoad), NULL, NULL);
  908. lstrcpy(pwstrDLLCmdLineArgs, m_szCmdLineArgs);
  909. #else // UNICODE
  910. lstrcpy(pcstrDLLToLoad, m_szFunctionName);
  911. MultiByteToWideChar(CP_ACP, 0, m_szCmdLineArgs, -1, pwstrDLLCmdLineArgs, ARRAYSIZE(pwstrDLLCmdLineArgs));
  912. #endif // UNICODE
  913. if (NULL != (pfDllInstall = (DLLINSTALL_PARAMS) GetProcAddress(hInstance, pcstrDLLToLoad)))
  914. {
  915. BOOL bInstall;
  916. WCHAR *pwszArgs;
  917. // parse the command line
  918. for (pwszArgs = pwstrDLLCmdLineArgs; *pwszArgs != L',' && *pwszArgs != L'\0'; pwszArgs++)
  919. ;
  920. if (*pwszArgs == L',')
  921. *pwszArgs++ = L'\0';
  922. // args to DllInstall is (BOOL, LPWSTR)
  923. bInstall = (*pwstrDLLCmdLineArgs != L'u') && (*pwstrDLLCmdLineArgs != L'U');
  924. if (RRAEX_NO_EXCEPTION_TRAPPING & dwFlags) // Don't trap exceptions if that's what the caller wants.
  925. {
  926. // This flag is off by default because you better have a debugger installed if you don't catch
  927. // these exceptions
  928. #ifdef WX86
  929. if ( fWx86DLL )
  930. {
  931. DWORD dwArgs[ 2 ];
  932. dwArgs[ 0 ] = (DWORD) bInstall;
  933. dwArgs[ 1 ] = (DWORD) pwszArgs;
  934. hResult = Wx86EmulateX86(pfDllInstall, ARRAYSIZE(dwArgs), dwArgs);
  935. }
  936. else
  937. #endif
  938. hResult = (*pfDllInstall)(bInstall, pwszArgs);
  939. }
  940. else
  941. {
  942. _try
  943. {
  944. #ifdef WX86
  945. if ( fWx86DLL )
  946. {
  947. DWORD dwArgs[ 2 ];
  948. dwArgs[ 0 ] = (DWORD) bInstall;
  949. dwArgs[ 1 ] = (DWORD) pwszArgs;
  950. hResult = Wx86EmulateX86(pfDllInstall, ARRAYSIZE(dwArgs), dwArgs);
  951. }
  952. else
  953. #endif
  954. hResult = (*pfDllInstall)(bInstall, pwszArgs);
  955. }
  956. _except(EXCEPTION_EXECUTE_HANDLER) // Catch all exceptions.
  957. {
  958. ReportError(dwFlags, IDS_RUNONCEEX_EXCEPTION, m_szFunctionName, m_szFileName);
  959. }
  960. }
  961. if (FAILED(hResult))
  962. {
  963. WriteToLog(TEXT("An error occurred calling ""%1"" in ""%2"". (HRESULT = %3!lx!)\r\n"), m_szFunctionName, m_szFileName, hResult);
  964. ReportError(dwFlags, IDS_RUNONCEEX_REGISTER_ERROR, m_szFileName);
  965. }
  966. }
  967. else
  968. {
  969. ReportError(dwFlags, IDS_RUNONCEEX_CANNOT_FIND_FUNCTION, m_szFunctionName, m_szFileName);
  970. }
  971. FreeLibrary(hInstance);
  972. }
  973. else
  974. {
  975. ReportError(dwFlags, IDS_RUNONCEEX_CANNOT_LOAD_DLL, m_szFileName);
  976. }
  977. break;
  978. case eRO_WinMainFunction:
  979. WriteToLog(TEXT("WinMain type function\r\n"));
  980. hInstance = RunOnceLoadLibrary( m_szFileName, &fWx86DLL );
  981. if (NULL != hInstance)
  982. {
  983. char pcstrDLLToLoad[MAX_PATH];
  984. char pcstrDLLCmdLineArgs[MAX_PATH];
  985. #ifdef UNICODE
  986. WideCharToMultiByte(CP_ACP, 0, m_szFunctionName, -1,
  987. pcstrDLLToLoad, ARRAYSIZE(pcstrDLLToLoad), NULL, NULL);
  988. WideCharToMultiByte(CP_ACP, 0, m_szCmdLineArgs, -1,
  989. pcstrDLLCmdLineArgs, ARRAYSIZE(pcstrDLLCmdLineArgs), NULL, NULL);
  990. #else // UNICODE
  991. lstrcpy(pcstrDLLToLoad, m_szFunctionName);
  992. lstrcpy(pcstrDLLCmdLineArgs, m_szCmdLineArgs);
  993. #endif // UNICODE
  994. if (NULL != (pfWinMainFunction = (WINMAIN_PARAMS) GetProcAddress(hInstance, pcstrDLLToLoad)))
  995. {
  996. if (RRAEX_NO_EXCEPTION_TRAPPING & dwFlags) // Don't trap exceptions if that's what the caller wants.
  997. {
  998. // This flag is off be default because you better have a debugger installed if you don't catch
  999. // these exceptions
  1000. #ifdef WX86
  1001. if ( fWx86DLL )
  1002. {
  1003. DWORD dwArgs[ 4 ];
  1004. dwArgs[ 0 ] = (DWORD) NULL;
  1005. dwArgs[ 1 ] = (DWORD) NULL;
  1006. dwArgs[ 2 ] = (DWORD) pcstrDLLCmdLineArgs;
  1007. dwArgs[ 3 ] = (DWORD) 0;
  1008. Wx86EmulateX86(pfWinMainFunction, ARRAYSIZE(dwArgs), dwArgs);
  1009. }
  1010. else
  1011. #endif
  1012. (*pfWinMainFunction)(NULL, NULL, pcstrDLLCmdLineArgs, 0);
  1013. }
  1014. else
  1015. {
  1016. _try
  1017. {
  1018. #ifdef WX86
  1019. if ( fWx86DLL )
  1020. {
  1021. DWORD dwArgs[ 4 ];
  1022. dwArgs[ 0 ] = (DWORD) NULL;
  1023. dwArgs[ 1 ] = (DWORD) NULL;
  1024. dwArgs[ 2 ] = (DWORD) pcstrDLLCmdLineArgs;
  1025. dwArgs[ 3 ] = (DWORD) 0;
  1026. Wx86EmulateX86(pfWinMainFunction, ARRAYSIZE(dwArgs), dwArgs);
  1027. }
  1028. else
  1029. #endif
  1030. (*pfWinMainFunction)(NULL, NULL, pcstrDLLCmdLineArgs, 0);
  1031. }
  1032. _except(EXCEPTION_EXECUTE_HANDLER) // Catch all exceptions.
  1033. {
  1034. ReportError(dwFlags, IDS_RUNONCEEX_EXCEPTION, m_szFunctionName, m_szFileName);
  1035. }
  1036. }
  1037. }
  1038. else
  1039. {
  1040. ReportError(dwFlags, IDS_RUNONCEEX_CANNOT_FIND_FUNCTION, m_szFunctionName, m_szFileName);
  1041. }
  1042. FreeLibrary(hInstance);
  1043. }
  1044. else
  1045. {
  1046. ReportError(dwFlags, IDS_RUNONCEEX_CANNOT_LOAD_DLL, m_szFileName);
  1047. }
  1048. break;
  1049. case eRO_Exe:
  1050. WriteToLog(TEXT("ShellExec Command\r\n"));
  1051. {
  1052. if (RRAEX_NO_EXCEPTION_TRAPPING & dwFlags) // Don't trap exceptions if that's what the caller wants.
  1053. {
  1054. // This flag is off be default because you better have a debugger installed if you don't catch
  1055. // these exceptions
  1056. ShellExecuteRegApp(m_szCmdLineArgs, dwFlags);
  1057. }
  1058. else
  1059. {
  1060. _try
  1061. {
  1062. ShellExecuteRegApp(m_szCmdLineArgs, dwFlags);
  1063. }
  1064. _except(EXCEPTION_EXECUTE_HANDLER) // Catch all exceptions.
  1065. {
  1066. ReportError(dwFlags, IDS_RUNONCEEX_EXE_EXCEPTION, m_szCmdLineArgs);
  1067. }
  1068. }
  1069. }
  1070. break;
  1071. case eRO_Unknown:
  1072. default:
  1073. WriteToLog(TEXT("Unknown\r\n"));
  1074. break;
  1075. }
  1076. }
  1077. /////////////////////////////////////////////////////////////////////
  1078. // CLASS: RunOnceExSection
  1079. /////////////////////////////////////////////////////////////////////
  1080. /****************************************************\
  1081. FUNCTION: RunOnceExSection
  1082. PARAMETERS:
  1083. LPTSTR lpszNewSectionName - The name of the new section
  1084. LPTSTR lpszNewDisplayName - The display name of the new section
  1085. DESCRIPTION:
  1086. This constructor will set the name of the newly
  1087. created section.
  1088. \***************************************************/
  1089. RunOnceExSection::RunOnceExSection(LPTSTR lpszNewSectionName, LPTSTR lpszNewDisplayName)
  1090. {
  1091. lstrcpy(m_szRunOnceExSectionName, lpszNewSectionName);
  1092. if ((NULL != lpszNewDisplayName) && (TEXT('\0') != *lpszNewDisplayName))
  1093. {
  1094. // Set the DisplayName as long as it was valid.
  1095. lstrcpy(m_szDisplayName, lpszNewDisplayName);
  1096. }
  1097. else
  1098. {
  1099. // It was invalid, so we use the SectionName for the DisplayName.
  1100. //lstrcpy(m_szDisplayName, lpszNewSectionName);
  1101. *m_szDisplayName = TEXT('\0'); // just don't display anything
  1102. }
  1103. m_hEntryArray = NULL;
  1104. m_NumberOfEntries = 0;
  1105. }
  1106. /****************************************************\
  1107. FUNCTION: ~RunOnceExSection
  1108. PARAMETERS:
  1109. none
  1110. DESCRIPTION:
  1111. This destructor will free memory that it used.
  1112. \***************************************************/
  1113. RunOnceExSection::~RunOnceExSection()
  1114. {
  1115. if (NULL != m_hEntryArray)
  1116. {
  1117. RunOnceExEntry * pFirstSection;
  1118. for (int iIndex = 0; iIndex < m_NumberOfEntries; iIndex++)
  1119. {
  1120. pFirstSection = (RunOnceExEntry *) DPA_GetPtr(m_hEntryArray, iIndex);
  1121. delete pFirstSection;
  1122. }
  1123. DPA_Destroy(m_hEntryArray);
  1124. }
  1125. }
  1126. /****************************************************\
  1127. FUNCTION: RunOnceExSection::Process
  1128. PARAMETERS:
  1129. HKEY hkeyParent - Section of the registry being processed.
  1130. LPCTSTR szSubkey - Path to the registry entries being processed.
  1131. DWORD dwFlags - Flags to determine if we delete command after executing it.
  1132. DESCRIPTION:
  1133. This function will process each entry in it,
  1134. delete the registry key it used, and then
  1135. repeat the process with the next section.
  1136. \***************************************************/
  1137. void RunOnceExSection::Process(HKEY hkeyParent, LPCTSTR szSubkey, DWORD dwFlags)
  1138. {
  1139. RunOnceExEntry * pCurrentEntry = NULL;
  1140. HKEY hSectionKey = NULL;
  1141. int iEntryIndex;
  1142. WriteToLog(TEXT("\r\n"));
  1143. LogDateAndTime();
  1144. WriteToLog(TEXT("Section:%1\r\n"), m_szRunOnceExSectionName);
  1145. // Load the dependency DLLs for this sections
  1146. HandleDependencyDLLs(hkeyParent, szSubkey, m_szRunOnceExSectionName, dwFlags, eHDL_Load);
  1147. if (NULL != m_hEntryArray)
  1148. {
  1149. for (iEntryIndex = 0; iEntryIndex < m_NumberOfEntries; iEntryIndex++)
  1150. {
  1151. pCurrentEntry = (RunOnceExEntry *) DPA_GetPtr(m_hEntryArray, iEntryIndex);
  1152. if (pCurrentEntry)
  1153. pCurrentEntry->Process(hkeyParent, szSubkey, m_szRunOnceExSectionName, dwFlags);
  1154. // If there is a call back, send back information
  1155. if (g_pCallbackProc)
  1156. {
  1157. g_nCurrent++;
  1158. g_pCallbackProc(g_nCurrent, g_nTotal, NULL);
  1159. }
  1160. }
  1161. }
  1162. // Unload the dependency DLLs for this sections
  1163. HandleDependencyDLLs(hkeyParent, szSubkey, m_szRunOnceExSectionName, dwFlags, eHDL_Unload);
  1164. LogDateAndTime();
  1165. if (RRA_DELETE & dwFlags)
  1166. {
  1167. TCHAR szKeyToDelete[MAX_REG_PATH];
  1168. wsprintf(szKeyToDelete, TEXT("%s\\%s"), szSubkey, m_szRunOnceExSectionName);
  1169. LocalSHDeleteKey(hkeyParent, szKeyToDelete);
  1170. }
  1171. }
  1172. /****************************************************\
  1173. FUNCTION: CompareSection
  1174. PARAMETERS:
  1175. RunOnceExSection * pSection1 - The first Section to be compared
  1176. RunOnceExSection * pSection2 - The second Section to be compared
  1177. LPARAM lpNotUsed - Not used.
  1178. int return - (-1) if the first is smaller.
  1179. DESCRIPTION:
  1180. The following function will determine which
  1181. comes first when sorted.
  1182. \***************************************************/
  1183. INT CALLBACK CompareSection(RunOnceExSection * pSection1, RunOnceExSection * pSection2, LPARAM lpNotUsed)
  1184. {
  1185. if (1 == CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, pSection1->m_szRunOnceExSectionName, -1, pSection2->m_szRunOnceExSectionName, -1))
  1186. return(-1);
  1187. return(1);
  1188. }
  1189. /****************************************************\
  1190. FUNCTION: CompareEntries
  1191. PARAMETERS:
  1192. RunOnceExEntry * pEntry1 - The first Entry to be compared
  1193. RunOnceExEntry * pEntry2 - The second Entry to be compared
  1194. LPARAM lpNotUsed - Not used.
  1195. int return - (-1) if the first is smaller.
  1196. DESCRIPTION:
  1197. The following function will determine which
  1198. comes first when sorted.
  1199. \***************************************************/
  1200. INT CALLBACK CompareEntries(RunOnceExEntry * pEntry1, RunOnceExEntry * pEntry2, LPARAM lpNotUsed)
  1201. {
  1202. if (1 == CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, pEntry1->m_szRunOnceExEntryName, -1, pEntry2->m_szRunOnceExEntryName, -1))
  1203. return(-1);
  1204. return(1);
  1205. }
  1206. /****************************************************\
  1207. FUNCTION: GetSections
  1208. PARAMETERS:
  1209. HKEY hkeyParent - Section of the registry being processed.
  1210. LPCTSTR szSubkey - Path to the registry entries being processed.
  1211. DWORD dwFlags - Flags.
  1212. DESCRIPTION:
  1213. This function will open the RunOnceEx registry
  1214. key and read all the sections and entries. It will
  1215. read these into memory and set *ppFirstSection to
  1216. the first (root) section.
  1217. \***************************************************/
  1218. HDPA GetSections(HKEY hkeyParent, LPCTSTR szSubkey, DWORD dwFlags, int * pNumberOfSections)
  1219. {
  1220. TCHAR szCurrentSectionName[MAX_ENTRYNAME] = TEXT("");
  1221. TCHAR szCurrSectionDisplayName[MAX_ENTRYNAME] = TEXT("");
  1222. HKEY hRootKey = NULL;
  1223. DWORD dwCurrentSection = 0;
  1224. DWORD dwRegType = 0;
  1225. long lDisplayNameSize = ARRAYSIZE(szCurrSectionDisplayName);
  1226. long lEnumError;
  1227. HDPA hDPA_Sections = DPA_Create(ARRAY_GROW_RATE);
  1228. RunOnceExSection * pNewSection = NULL;
  1229. *pNumberOfSections = 0;
  1230. if (NULL != hDPA_Sections)
  1231. {
  1232. // Do we have any RegOpenKeyEx sections to process/read?
  1233. if (ERROR_SUCCESS == RegOpenKeyEx(hkeyParent, szSubkey, NULL, KEY_READ, &hRootKey))
  1234. {
  1235. // IthkeyParenterate through each section
  1236. for (dwCurrentSection = 0;
  1237. ERROR_NO_MORE_ITEMS != (lEnumError = RegEnumKey(hRootKey, dwCurrentSection, szCurrentSectionName, ARRAYSIZE(szCurrentSectionName)));
  1238. dwCurrentSection++)
  1239. {
  1240. if (ERROR_SUCCESS == lEnumError)
  1241. {
  1242. lDisplayNameSize = sizeof(szCurrSectionDisplayName);
  1243. if (RegQueryValue(hRootKey, szCurrentSectionName, szCurrSectionDisplayName, &lDisplayNameSize) != ERROR_SUCCESS)
  1244. *szCurrSectionDisplayName = TEXT('\0');
  1245. // Only Process non-"Depend" entries.
  1246. if (2 != CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, szCurrentSectionName, -1, c_szDependencyName, -1))
  1247. {
  1248. pNewSection = new RunOnceExSection(szCurrentSectionName, szCurrSectionDisplayName);
  1249. if (NULL != pNewSection)
  1250. {
  1251. if (*szCurrSectionDisplayName)
  1252. g_iNDisplaySections++;
  1253. DPA_SetPtr(hDPA_Sections, *pNumberOfSections, (void *) pNewSection);
  1254. pNewSection->m_hEntryArray = GetEntries(hRootKey, szCurrentSectionName, dwFlags, &(pNewSection->m_NumberOfEntries));
  1255. (*pNumberOfSections)++;
  1256. }
  1257. }
  1258. }
  1259. }
  1260. RegCloseKey(hRootKey);
  1261. }
  1262. DPA_Sort(hDPA_Sections, (PFNDPACOMPARE) CompareSection, 0);
  1263. }
  1264. return(hDPA_Sections);
  1265. }
  1266. /****************************************************\
  1267. FUNCTION: GetEntries
  1268. PARAMETERS:
  1269. HKEY hkeyParent - Section of the registry being processed.
  1270. LPCTSTR szSubkey - Path to the registry entries being processed.
  1271. DWORD dwFlags - Flags.
  1272. int * pNumberOfEntries - OUT: Number of sections read.
  1273. DESCRIPTION:
  1274. This function will open the RunOnceEx registry
  1275. section key and read all the entries.
  1276. \***************************************************/
  1277. HDPA GetEntries(HKEY hRootKey, LPCTSTR szSectionName, DWORD dwFlags, int * pNumberOfEntries)
  1278. {
  1279. HKEY hCurrentSectionKey = NULL;
  1280. TCHAR szCurrentEntryName[MAX_ENTRYNAME] = TEXT("");
  1281. TCHAR szCurrentEntryCmd[MAX_PATH] = TEXT("");
  1282. DWORD dwEntrySize = 0;
  1283. DWORD dwEntryCmdSize = 0;
  1284. DWORD dwCurrentEntry = 0;
  1285. DWORD dwRegType = 0;
  1286. long lEnumError;
  1287. HDPA hDPA_Entries = DPA_Create(ARRAY_GROW_RATE);
  1288. RunOnceExEntry * pNewEntry = NULL;
  1289. *pNumberOfEntries = 0;
  1290. if (NULL != hDPA_Entries)
  1291. {
  1292. if (ERROR_SUCCESS == RegOpenKeyEx(hRootKey, szSectionName, NULL, KEY_READ, &hCurrentSectionKey))
  1293. {
  1294. DWORD dwKeyType;
  1295. // Iterate through each value
  1296. dwEntrySize = ARRAYSIZE(szCurrentEntryName);
  1297. dwEntryCmdSize = sizeof(szCurrentEntryCmd);
  1298. for (dwCurrentEntry = 0;
  1299. ERROR_NO_MORE_ITEMS != (lEnumError = RegEnumValue(hCurrentSectionKey, dwCurrentEntry, szCurrentEntryName, &dwEntrySize, NULL, &dwKeyType, (LPBYTE) szCurrentEntryCmd, &dwEntryCmdSize));
  1300. dwCurrentEntry++)
  1301. {
  1302. if (ERROR_SUCCESS == lEnumError)
  1303. {
  1304. // An empty Entry Name is not acceptable because that is the Display Name for the section.
  1305. if ((REG_SZ == dwKeyType) && (TEXT('\0') != *szCurrentEntryName))
  1306. {
  1307. pNewEntry = new RunOnceExEntry(szCurrentEntryName, szCurrentEntryCmd, dwFlags);
  1308. if (NULL != pNewEntry)
  1309. {
  1310. if (eRO_Unknown != pNewEntry->m_ROAction)
  1311. {
  1312. DPA_SetPtr(hDPA_Entries, *pNumberOfEntries, (void *) pNewEntry);
  1313. (*pNumberOfEntries)++;
  1314. }
  1315. else
  1316. delete pNewEntry;
  1317. }
  1318. }
  1319. }
  1320. dwEntrySize = ARRAYSIZE(szCurrentEntryName);
  1321. dwEntryCmdSize = sizeof(szCurrentEntryCmd);
  1322. }
  1323. RegCloseKey(hCurrentSectionKey);
  1324. }
  1325. DPA_Sort(hDPA_Entries, (PFNDPACOMPARE) CompareEntries, 0);
  1326. }
  1327. return(hDPA_Entries);
  1328. }
  1329. // taken from \\trango\slmadd\src\shell\shell32\shellprv.h
  1330. #define FillExecInfo(_info, _hwnd, _verb, _file, _params, _dir, _show) \
  1331. (_info).hwnd = _hwnd; \
  1332. (_info).lpVerb = _verb; \
  1333. (_info).lpFile = _file; \
  1334. (_info).lpParameters = _params; \
  1335. (_info).lpDirectory = _dir; \
  1336. (_info).nShow = _show; \
  1337. (_info).fMask = 0; \
  1338. (_info).cbSize = sizeof(SHELLEXECUTEINFO);
  1339. //
  1340. // Path processing function
  1341. //
  1342. #define PPCF_ADDQUOTES 0x00000001 // return a quoted name if required
  1343. #define PPCF_ADDARGUMENTS 0x00000003 // appends arguments (and wraps in quotes if required)
  1344. #define PPCF_NODIRECTORIES 0x00000010 // don't match to directories
  1345. #define PPCF_NORELATIVEOBJECTQUALIFY 0x00000020 // don't return fully qualified relative objects
  1346. #define PPCF_FORCEQUALIFY 0x00000040 // qualify even non-relative names
  1347. typedef LONG WINSHELLAPI (WINAPI * LPPATHPROCESSCOMMAND)(LPCTSTR, LPTSTR, int, DWORD);
  1348. /****************************************************\
  1349. FUNCTION: ShellExecuteRegApp
  1350. PARAMETERS:
  1351. LPCTSTR pszCmdLine - Cmd line to execute
  1352. DWORD dwFlags - Flags to specify if we need to wait for command to finish.
  1353. DESCRIPTION:
  1354. The following handles running an application
  1355. and optionally waiting for it to terminate.
  1356. \***************************************************/
  1357. void ShellExecuteRegApp(LPTSTR pszCmdLine, DWORD dwFlags)
  1358. {
  1359. TCHAR szBuf[MAX_PATH];
  1360. GetSystemDirectory(szBuf, ARRAYSIZE(szBuf));
  1361. if (RunningOnIE4())
  1362. {
  1363. HINSTANCE hShell32DLL = NULL;
  1364. LPPATHPROCESSCOMMAND pfnPathProcessCommand;
  1365. TCHAR szQuotedCmdLine[MAX_PATH+2];
  1366. SHELLEXECUTEINFO ExecInfo;
  1367. LPTSTR lpszArgs;
  1368. BOOL fPPCSuccess = FALSE; // PathProcessCommand succeeded
  1369. AddPath(szBuf, TEXT("shell32.dll"));
  1370. if ((hShell32DLL = LoadLibraryEx(szBuf, NULL, LOAD_WITH_ALTERED_SEARCH_PATH)) != NULL)
  1371. {
  1372. if ((pfnPathProcessCommand = (LPPATHPROCESSCOMMAND) GetProcAddress(hShell32DLL, (LPCSTR) 653)) != NULL)
  1373. {
  1374. //
  1375. // We used to call CreateProcess( NULL, szCmdLine, ...) here,
  1376. // but thats not useful for people with apppaths stuff.
  1377. //
  1378. // Gross, but if the process command fails, copy the command line to let
  1379. // shell execute report the errors
  1380. if ((pfnPathProcessCommand)(pszCmdLine, szQuotedCmdLine, ARRAYSIZE(szQuotedCmdLine), PPCF_ADDARGUMENTS|PPCF_FORCEQUALIFY) != -1)
  1381. fPPCSuccess = TRUE;
  1382. }
  1383. FreeLibrary(hShell32DLL);
  1384. }
  1385. if (!fPPCSuccess)
  1386. lstrcpy(szQuotedCmdLine, pszCmdLine);
  1387. lpszArgs = LocalPathGetArgs(szQuotedCmdLine);
  1388. if (*lpszArgs)
  1389. *(lpszArgs-1) = TEXT('\0'); // Strip args
  1390. LocalPathUnquoteSpaces(szQuotedCmdLine);
  1391. FillExecInfo(ExecInfo, NULL, NULL, szQuotedCmdLine, lpszArgs, szBuf, SW_SHOWNORMAL);
  1392. ExecInfo.fMask |= SEE_MASK_NOCLOSEPROCESS;
  1393. if (dwFlags & RRAEX_NO_ERROR_DIALOGS) // Don't display Error dialog
  1394. ExecInfo.fMask |= SEE_MASK_FLAG_NO_UI;
  1395. if (ShellExecuteEx(&ExecInfo))
  1396. {
  1397. if ((dwFlags & RRA_WAIT) && (ExecInfo.hProcess != NULL))
  1398. {
  1399. MsgWaitForMultipleObjectsLoop(ExecInfo.hProcess, INFINITE);
  1400. }
  1401. CloseHandle(ExecInfo.hProcess);
  1402. }
  1403. }
  1404. else // old Win95 logic -- just call CreateProcess
  1405. {
  1406. STARTUPINFO startup;
  1407. PROCESS_INFORMATION pi;
  1408. ZeroMemory(&startup, sizeof(startup));
  1409. startup.cb = sizeof(startup);
  1410. if (CreateProcess(NULL, pszCmdLine, NULL, NULL, FALSE, CREATE_NEW_PROCESS_GROUP, NULL, szBuf, &startup, &pi))
  1411. {
  1412. if (dwFlags & RRA_WAIT)
  1413. {
  1414. MsgWaitForMultipleObjectsLoop(pi.hProcess, INFINITE);
  1415. }
  1416. CloseHandle(pi.hProcess);
  1417. CloseHandle(pi.hThread);
  1418. }
  1419. }
  1420. }
  1421. #define BUFFER_SIZE 1024
  1422. BOOL HaveDependServices(SC_HANDLE hService)
  1423. {
  1424. HRESULT hr = S_OK;
  1425. LPBYTE lpBuffer = NULL;
  1426. DWORD dwSize = BUFFER_SIZE; // Start with 1k
  1427. DWORD dwBytesNeeded;
  1428. DWORD dwNumServices;
  1429. BOOL bDependServices = FALSE;
  1430. lpBuffer = (LPBYTE) LocalAlloc(LPTR, dwSize);
  1431. if (lpBuffer)
  1432. {
  1433. if (!EnumDependentServices(hService, SERVICE_STATE_ALL,
  1434. (LPENUM_SERVICE_STATUS)lpBuffer, dwSize,
  1435. &dwBytesNeeded, &dwNumServices))
  1436. {
  1437. if (GetLastError() == ERROR_MORE_DATA)
  1438. {
  1439. dwSize = dwBytesNeeded + 32;
  1440. LocalFree(lpBuffer);
  1441. lpBuffer = (LPBYTE) LocalAlloc(LPTR, dwSize);
  1442. if (lpBuffer)
  1443. {
  1444. if (!EnumDependentServices(hService, SERVICE_STATE_ALL,
  1445. (LPENUM_SERVICE_STATUS)lpBuffer, dwSize,
  1446. &dwBytesNeeded, &dwNumServices))
  1447. {
  1448. hr = HRESULT_FROM_WIN32(GetLastError());
  1449. }
  1450. }
  1451. else
  1452. hr = HRESULT_FROM_WIN32(GetLastError());
  1453. }
  1454. else
  1455. hr = HRESULT_FROM_WIN32(GetLastError());
  1456. }
  1457. if (SUCCEEDED(hr))
  1458. {
  1459. // If at least one service depends on this one?
  1460. bDependServices = (dwNumServices != 0);
  1461. }
  1462. if (lpBuffer)
  1463. LocalFree(lpBuffer);
  1464. }
  1465. return bDependServices;
  1466. }
  1467. void CheckServices(DWORD dwFlags)
  1468. {
  1469. SC_HANDLE hSCM = NULL;
  1470. SC_HANDLE hService = NULL;
  1471. BOOL bRebootNeeded = FALSE;
  1472. HKEY hKey = NULL;
  1473. LPSTR pServices = NULL;
  1474. LPSTR pCheckService = NULL;
  1475. LONG lRet = 0;
  1476. DWORD dwSize;
  1477. pServices = (LPSTR)LocalAlloc(LPTR, BUFFER_SIZE);
  1478. if (pServices)
  1479. {
  1480. // Get the services to check for from the registry.
  1481. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_RUNONCEEX, NULL, KEY_READ|KEY_WRITE, &hKey) == ERROR_SUCCESS)
  1482. {
  1483. dwSize = BUFFER_SIZE - 1;
  1484. lRet = RegQueryValueEx(hKey, g_c_szServicesRegValue, NULL, NULL, (LPBYTE)pServices, &dwSize);
  1485. if (lRet == ERROR_MORE_DATA)
  1486. {
  1487. dwSize += 32;
  1488. LocalFree(pServices);
  1489. pServices = (LPSTR)LocalAlloc(LPTR, dwSize);
  1490. if (pServices)
  1491. {
  1492. lRet = RegQueryValueEx(hKey, g_c_szServicesRegValue, NULL, NULL, (LPBYTE)pServices, &dwSize);
  1493. }
  1494. }
  1495. if (lRet != ERROR_SUCCESS)
  1496. {
  1497. if (pServices)
  1498. {
  1499. LocalFree(pServices);
  1500. pServices = NULL;
  1501. }
  1502. }
  1503. RegDeleteValue(hKey, g_c_szServicesRegValue);
  1504. RegCloseKey(hKey);
  1505. }
  1506. if ((pServices) && (*pServices))
  1507. {
  1508. hSCM = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS);
  1509. if (hSCM)
  1510. {
  1511. pCheckService = pServices;
  1512. // zero out all forward slashes to get the services terminated
  1513. while (*pCheckService)
  1514. {
  1515. if (*pCheckService == '/')
  1516. {
  1517. *pCheckService = '\0';
  1518. pCheckService++;
  1519. }
  1520. else
  1521. pCheckService = CharNext(pCheckService);
  1522. }
  1523. // RegQueryValueExA is using the same buffer to convert the UNICODE
  1524. // string to a ANSI string. This will leave some UNICODE characters
  1525. // after the ANSI strings and the services are not realy double 0
  1526. // terminated anymore. This will ensure that the list is still
  1527. // double 0 terminated
  1528. pCheckService++;
  1529. *pCheckService = '\0';
  1530. pCheckService = pServices;
  1531. while (!bRebootNeeded && (*pCheckService))
  1532. {
  1533. hService = OpenService(hSCM,
  1534. (LPCSTR)pCheckService,
  1535. STANDARD_RIGHTS_REQUIRED | SERVICE_ENUMERATE_DEPENDENTS);
  1536. if (hService)
  1537. {
  1538. bRebootNeeded = HaveDependServices(hService);
  1539. CloseServiceHandle(hService);
  1540. }
  1541. pCheckService += lstrlen(pCheckService) + 1;
  1542. }
  1543. CloseServiceHandle(hSCM);
  1544. }
  1545. }
  1546. if (pServices)
  1547. LocalFree(pServices);
  1548. }
  1549. if (bRebootNeeded)
  1550. {
  1551. ReportError((dwFlags & ~RRAEX_NO_ERROR_DIALOGS), IDS_RUNONCEEX_SERVICE_REQUIRES_REBOOT);
  1552. LogOff(TRUE);
  1553. }
  1554. }