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

1093 lines
35 KiB

  1. // MSInfo.cpp : Implementation of DLL Exports, main application object
  2. // and Registry Object Map. All registry information (apart from
  3. // MIDL) lives here.
  4. //
  5. // Copyright (c) 1998-1999 Microsoft Corporation
  6. #include "stdafx.h"
  7. #include "resource.h"
  8. #include "initguid.h"
  9. // This hack is required because we may be building in an environment
  10. // which doesn't have a late enough version of rpcndr.h
  11. #if __RPCNDR_H_VERSION__ < 440
  12. #define __RPCNDR_H_VERSION__ 440
  13. #define MIDL_INTERFACE(x) interface
  14. #endif
  15. #include "MSInfo.h"
  16. #include "DataObj.h"
  17. #include "CompData.h"
  18. #include "About.h"
  19. #include "Toolset.h"
  20. #include "Dispatch.h"
  21. #include "MSInfo_i.c"
  22. static LPCTSTR cszBasePath = _T("Software\\Microsoft\\MMC");
  23. static LPCTSTR cszBaseSnapinPath = _T("Software\\Microsoft\\MMC\\Snapins");
  24. static LPCTSTR cszBaseNodeTypePath = _T("Software\\Microsoft\\MMC\\NodeTypes");
  25. static LPCTSTR cszNameString = _T("NameString");
  26. static LPCTSTR cszProvider = _T("Provider");
  27. static LPCTSTR cszVersion = _T("Version");
  28. static LPCTSTR cszAbout = _T("About");
  29. static LPCTSTR cszStandAlone = _T("StandAlone");
  30. static LPCTSTR cszNodeTypes = _T("NodeTypes");
  31. static LPCTSTR cszExtensions = _T("Extensions");
  32. static LPCTSTR cszNameSpace = _T("NameSpace");
  33. static LPCTSTR cszTask = _T("Task");
  34. static LPCTSTR cszMSInfoBaseKey = _T("Software\\Microsoft\\Shared Tools");
  35. static LPCTSTR cszMSInfoSubKey = _T("MSInfo");
  36. static LPCTSTR cszMSInfoKey = _T("Software\\Microsoft\\Shared Tools\\MSInfo");
  37. static LPCTSTR cszPathValue = _T("Path");
  38. static LPCTSTR cszRunKey = _T("Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\msinfo32.exe");
  39. static LPCTSTR cszRunRootKey = _T("Software\\Microsoft\\Windows\\CurrentVersion\\App Paths");
  40. static LPCTSTR cszRunSubKey = _T("msinfo32.exe");
  41. static LPCTSTR cszNFOExtension = _T(".nfo");
  42. static LPCTSTR cszOLERegistration = _T("MSInfo.Document");
  43. static LPCTSTR cszDefaultIconKey = _T("DefaultIcon");
  44. static LPCTSTR cszCLSIDKey = _T("CLSID");
  45. static LPCTSTR cszShellKey = _T("shell");
  46. static LPCTSTR cszOpenCommandKey = _T("open\\command");
  47. static LPCTSTR cszPrintCommandKey = _T("print\\command");
  48. static LPCTSTR cszPrintToKey = _T("printto\\command");
  49. // note trailing quotes on file path
  50. static LPCTSTR cszMSInfoPath = _T("Microsoft Shared\\MSInfo\\MSInfo32.exe\"");
  51. static LPCTSTR cszDefaultIconValue = _T("Microsoft Shared\\MSInfo\\MSInfo32.exe\",0");
  52. static LPCTSTR cszOpenCommand = _T("Microsoft Shared\\MSInfo\\MSInfo32.exe\" /msinfo_file \"%1\"");
  53. // these are never referenced
  54. static LPCTSTR cszMSInfoDir = _T("Common Files\\Microsoft Shared\\MSInfo\\");
  55. static LPCTSTR cszPrintToCommand = _T("Common Files\\Microsoft Shared\\MSInfo\\MSInfo32.exe /pt \"%1\" \"%2\" \"%3\" \"%4\"");
  56. //static LPCTSTR cszPrintCommand = _T("Common Files\\Microsoft Shared\\MSInfo\\MSInfo32.exe /p \"%1\"");
  57. //a-kjaw
  58. static LPCTSTR cszPrintCommand = _T("Microsoft Shared\\MSInfo\\MSInfo32.exe\" /p \"%1\"");
  59. //a-kjaw
  60. // Nodes we extend
  61. static LPCTSTR cszCompMgrNode = _T("{476E6448-AAFF-11D0-B944-00C04FD8D5B0}");
  62. CComModule _Module;
  63. /*
  64. * Object Map of Registered objects, allowing the Active Template Library to
  65. * register our CLSIDs.
  66. */
  67. BEGIN_OBJECT_MAP(ObjectMap)
  68. OBJECT_ENTRY(CLSID_MSInfo, CSystemInfoScopePrimary)
  69. OBJECT_ENTRY(CLSID_Extension, CSystemInfoScopeExtension)
  70. OBJECT_ENTRY(CLSID_About, CAboutImpl)
  71. OBJECT_ENTRY(CLSID_SystemInfo, CMSInfo)
  72. END_OBJECT_MAP()
  73. /*
  74. * The MSInfo application object.
  75. *
  76. * History: a-jsari 10/1/97 Initial version.
  77. */
  78. class CMSInfoApp : public CWinApp
  79. {
  80. public:
  81. virtual BOOL InitInstance();
  82. virtual int ExitInstance();
  83. };
  84. CMSInfoApp theApp;
  85. /*
  86. * InitInstance - Initialize an instance of the application.
  87. *
  88. * History: a-jsari 10/1/97 Initial version.
  89. */
  90. extern void LoadDialogResources();
  91. BOOL CMSInfoApp::InitInstance()
  92. {
  93. _Module.Init(ObjectMap, m_hInstance);
  94. LoadDialogResources(); // loads dialog strings from resources
  95. return CWinApp::InitInstance();
  96. }
  97. /*
  98. * ExitInstance - Deconstruct an instance of the application.
  99. *
  100. * History: a-jsari 10/1/97 Initial version.
  101. */
  102. int CMSInfoApp::ExitInstance()
  103. {
  104. _Module.Term();
  105. return CWinApp::ExitInstance();
  106. }
  107. /////////////////////////////////////////////////////////////////////////////
  108. // Used to determine whether the DLL can be unloaded by OLE
  109. STDAPI DllCanUnloadNow(void)
  110. {
  111. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  112. return (AfxDllCanUnloadNow()==S_OK && _Module.GetLockCount()==0) ? S_OK : S_FALSE;
  113. }
  114. /////////////////////////////////////////////////////////////////////////////
  115. // Returns a class factory to create an object of the requested type
  116. STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
  117. {
  118. return _Module.GetClassObject(rclsid, riid, ppv);
  119. }
  120. /*
  121. * RegOpenMMCRoot - Open CRegKey
  122. *
  123. * History: a-jsari 9/9/97 Initial version
  124. */
  125. static inline long RegOpenMMCRoot(CRegKey *pcrkMMCRoot)
  126. {
  127. return pcrkMMCRoot->Open(HKEY_LOCAL_MACHINE, cszBasePath);
  128. }
  129. /*
  130. * RegOpenMMCSnapinRoot - Return MMC's registry Snapin root
  131. *
  132. * History: a-jsari 9/9/97 Initial version
  133. */
  134. static inline long RegOpenMMCSnapinRoot(CRegKey *pcrkSnapinRoot)
  135. {
  136. return pcrkSnapinRoot->Open(HKEY_LOCAL_MACHINE, cszBaseSnapinPath);
  137. }
  138. /*
  139. * RegOpenMMCNodeTypeRoot - Return MMC's registry NodeType root
  140. *
  141. * History: a-jsari 9/9/97 Initial version
  142. */
  143. static inline long RegOpenMMCNodeTypeRoot(CRegKey *pcrkNodeTypesRoot)
  144. {
  145. return pcrkNodeTypesRoot->Open(HKEY_LOCAL_MACHINE, cszBaseNodeTypePath);
  146. }
  147. /*
  148. * RegisterStandaloneSnapin() - Do all registration for the Standalone
  149. * portion of the snapin.
  150. *
  151. * History: a-jsari 9/9/97 Initial version
  152. *
  153. * Note: Would require AFX_MANAGE_STATE, except that the calling function
  154. * handles it.
  155. */
  156. static inline HRESULT RegisterStandaloneSnapin()
  157. {
  158. CRegKey crkRoot;
  159. CRegKey crkClsid;
  160. CRegKey crkIterator;
  161. CString szResourceLoader;
  162. HRESULT hr = E_FAIL;
  163. do {
  164. // HKEY_CLASSES_ROOT\.nfo
  165. long lRegOpenResult = crkRoot.Create(HKEY_CLASSES_ROOT, cszNFOExtension);
  166. if (lRegOpenResult != ERROR_SUCCESS) break;
  167. lRegOpenResult = crkRoot.SetValue(cszOLERegistration);
  168. if (lRegOpenResult != ERROR_SUCCESS) break;
  169. crkRoot.Close();
  170. // HKEY_CLASSES_ROOT\MSInfo.Document
  171. lRegOpenResult = crkRoot.Create(HKEY_CLASSES_ROOT, cszOLERegistration);
  172. if (lRegOpenResult != ERROR_SUCCESS) break;
  173. // This originally set the default value for the key to "msinfo.document", which
  174. // is what would show up in the UI for a description of the NFO filetype. Fixing
  175. // this to load the string from a resource (bug 10442).
  176. //
  177. // lRegOpenResult = crkRoot.SetValue(cszOLERegistration);
  178. // if (lRegOpenResult != ERROR_SUCCESS) break;
  179. CString strDescription;
  180. strDescription.LoadString(IDS_NFODESCRIPTION);
  181. crkRoot.SetValue((LPCTSTR)strDescription);
  182. lRegOpenResult = crkIterator.Create(crkRoot, cszCLSIDKey);
  183. if (lRegOpenResult != ERROR_SUCCESS) break;
  184. lRegOpenResult = crkIterator.SetValue(cszClsidMSInfoSnapin);
  185. if (lRegOpenResult != ERROR_SUCCESS) break;
  186. crkIterator.Close();
  187. TCHAR szWindowsPath[MAX_PATH];
  188. DWORD dwSize;
  189. CString szPathValue;
  190. dwSize = sizeof(szWindowsPath);
  191. lRegOpenResult = crkIterator.Open(HKEY_LOCAL_MACHINE, cszWindowsCurrentKey);
  192. if (lRegOpenResult != ERROR_SUCCESS) break;
  193. lRegOpenResult = crkIterator.QueryValue(szWindowsPath, cszCommonFilesValue, &dwSize);
  194. if (lRegOpenResult != ERROR_SUCCESS) break;
  195. lRegOpenResult = crkIterator.Create(crkRoot, cszDefaultIconKey);
  196. if (lRegOpenResult != ERROR_SUCCESS) break;
  197. szPathValue = _T("\"");
  198. szPathValue += szWindowsPath;
  199. szPathValue += _T("\\");
  200. szPathValue += cszDefaultIconValue;
  201. lRegOpenResult = crkIterator.SetValue(szPathValue);
  202. if (lRegOpenResult != ERROR_SUCCESS) break;
  203. crkIterator.Close();
  204. lRegOpenResult = crkRoot.Create(crkRoot, cszShellKey);
  205. if (lRegOpenResult != ERROR_SUCCESS) break;
  206. lRegOpenResult = crkIterator.Create(crkRoot, cszOpenCommandKey);
  207. if (lRegOpenResult != ERROR_SUCCESS) break;
  208. szPathValue = _T("\"");
  209. szPathValue += szWindowsPath;
  210. szPathValue += _T("\\");
  211. szPathValue += cszOpenCommand;
  212. lRegOpenResult = crkIterator.SetValue(szPathValue);
  213. if (lRegOpenResult != ERROR_SUCCESS) break;
  214. crkIterator.Close();
  215. //crkRoot.Close();
  216. //a-kjaw
  217. lRegOpenResult = crkIterator.Create(crkRoot, cszPrintCommandKey);
  218. if (lRegOpenResult != ERROR_SUCCESS) break;
  219. szPathValue = _T("\"");
  220. szPathValue += szWindowsPath;
  221. szPathValue += _T("\\");
  222. szPathValue += cszPrintCommand;
  223. lRegOpenResult = crkIterator.SetValue(szPathValue);
  224. if (lRegOpenResult != ERROR_SUCCESS) break;
  225. crkIterator.Close();
  226. crkRoot.Close();
  227. //a-kjaw
  228. // HKEY_LOCAL_MACHINE\Software\Microsoft\MMC\Snapins
  229. lRegOpenResult = RegOpenMMCSnapinRoot(&crkRoot);
  230. // FIX: This fail should behave differently (registering the DLL
  231. // w/o MMC registered).
  232. if (lRegOpenResult != ERROR_SUCCESS) break;
  233. // {45ac8c63-23e2-11e1-a696-00c04fd58bc3}
  234. lRegOpenResult = crkClsid.Create(crkRoot, cszClsidMSInfoSnapin);
  235. if (lRegOpenResult != ERROR_SUCCESS) break;
  236. // NameString = REG_SZ "Microsoft System Information"
  237. VERIFY(szResourceLoader.LoadString(IDS_DESCRIPTION));
  238. lRegOpenResult = crkClsid.SetValue(szResourceLoader, cszNameString);
  239. if (lRegOpenResult != ERROR_SUCCESS) break;
  240. // About = REG_SZ "{45ac8c65-23e2-11e1-a696-00c04fd58bc3}"
  241. lRegOpenResult = crkClsid.SetValue(cszClsidAboutMSInfo, cszAbout);
  242. if (lRegOpenResult != ERROR_SUCCESS) break;
  243. // Provider = REG_SZ "Microsoft Corporation"
  244. VERIFY(szResourceLoader.LoadString(IDS_COMPANY));
  245. lRegOpenResult = crkClsid.SetValue(szResourceLoader, cszProvider);
  246. if (lRegOpenResult != ERROR_SUCCESS) break;
  247. // Version = REG_SZ "5.0"
  248. VERIFY(szResourceLoader.LoadString(IDS_VERSION));
  249. lRegOpenResult = crkClsid.SetValue(szResourceLoader, cszVersion);
  250. if (lRegOpenResult != ERROR_SUCCESS) break;
  251. // StandAlone
  252. lRegOpenResult = crkIterator.Create(crkClsid, cszStandAlone);
  253. if (lRegOpenResult != ERROR_SUCCESS) break;
  254. // NodeTypes
  255. lRegOpenResult = crkIterator.Create(crkClsid, cszNodeTypes);
  256. if (lRegOpenResult != ERROR_SUCCESS) break;
  257. // {45ac8c66-23e2-11e1-a696-00c04fd58bc3}
  258. CRegKey crkNodeType;
  259. lRegOpenResult = crkNodeType.Create(crkIterator, cszNodeTypeStatic);
  260. if (lRegOpenResult != ERROR_SUCCESS) break;
  261. // Replace the SnapinRoot with the NodeType root;
  262. // work from the new base.
  263. // HKEY_LOCAL_MACHINE\Software\Microsoft\MMC\NodeTypes
  264. lRegOpenResult = RegOpenMMCNodeTypeRoot(&crkRoot);
  265. if (lRegOpenResult != ERROR_SUCCESS) break;
  266. // {45ac8c66-23e2-11e1-a696-00c04fd58bc3}
  267. // = REG_SZ "Microsoft System Information Root"
  268. VERIFY(szResourceLoader.LoadString(IDS_NODEDESCRIPTION));
  269. lRegOpenResult = crkRoot.SetKeyValue(cszNodeTypeStatic, szResourceLoader);
  270. if (lRegOpenResult != ERROR_SUCCESS) break;
  271. hr = S_OK;
  272. } while (0);
  273. return hr;
  274. }
  275. /*
  276. * HRESULT UnregisterStandaloneSnapin - Remove all registry entries for
  277. * the standalone portion of the snapin.
  278. *
  279. * History: a-jsari 9/9/97 Initial version.
  280. */
  281. static inline HRESULT UnregisterStandaloneSnapin()
  282. {
  283. CRegKey crkSnapinRoot;
  284. // Remove HKEY_CLASSES_ROOT\.nfo
  285. long lRegOpenResult = crkSnapinRoot.Open(HKEY_CLASSES_ROOT, NULL);
  286. if (lRegOpenResult != ERROR_SUCCESS) {
  287. if (lRegOpenResult != ERROR_FILE_NOT_FOUND) return E_FAIL;
  288. } else {
  289. lRegOpenResult = crkSnapinRoot.RecurseDeleteKey(cszNFOExtension);
  290. // It's not really an error to not find a key we were deleting anyhow.
  291. if (lRegOpenResult != ERROR_SUCCESS
  292. && lRegOpenResult != ERROR_FILE_NOT_FOUND) {
  293. return E_FAIL;
  294. }
  295. crkSnapinRoot.Close();
  296. }
  297. // Remove HKEY_CLASSES_ROOT\MSInfo.Document
  298. lRegOpenResult = crkSnapinRoot.Open(HKEY_CLASSES_ROOT, NULL);
  299. if (lRegOpenResult != ERROR_SUCCESS) {
  300. if (lRegOpenResult != ERROR_FILE_NOT_FOUND) return E_FAIL;
  301. } else {
  302. lRegOpenResult = crkSnapinRoot.RecurseDeleteKey(cszOLERegistration);
  303. // It's not really an error to not find a key we were deleting anyhow.
  304. if (lRegOpenResult != ERROR_SUCCESS
  305. && lRegOpenResult != ERROR_FILE_NOT_FOUND) {
  306. return E_FAIL;
  307. }
  308. crkSnapinRoot.Close();
  309. }
  310. lRegOpenResult = RegOpenMMCSnapinRoot(&crkSnapinRoot);
  311. if (lRegOpenResult != ERROR_SUCCESS) {
  312. if (lRegOpenResult != ERROR_FILE_NOT_FOUND) return E_FAIL;
  313. } else {
  314. // Just recursively delete our root. Extensions will be automatically
  315. // deleted as well.
  316. lRegOpenResult = crkSnapinRoot.RecurseDeleteKey(cszClsidMSInfoSnapin);
  317. // It's not really an error to not find a key we were deleting anyhow.
  318. if (lRegOpenResult != ERROR_SUCCESS
  319. && lRegOpenResult != ERROR_FILE_NOT_FOUND) {
  320. return E_FAIL;
  321. }
  322. }
  323. lRegOpenResult = RegOpenMMCNodeTypeRoot(&crkSnapinRoot);
  324. if (lRegOpenResult != ERROR_SUCCESS) {
  325. if (lRegOpenResult != ERROR_FILE_NOT_FOUND) return E_FAIL;
  326. } else {
  327. lRegOpenResult = crkSnapinRoot.RecurseDeleteKey(cszNodeTypeStatic);
  328. ASSERT(lRegOpenResult == ERROR_SUCCESS);
  329. // It's not really an error to not find a key we were deleting anyhow.
  330. if (lRegOpenResult != ERROR_SUCCESS
  331. && lRegOpenResult != ERROR_FILE_NOT_FOUND) {
  332. return E_FAIL;
  333. }
  334. }
  335. return S_OK;
  336. }
  337. /*
  338. * OpenExtendKeyForNodeType - Return in pcrkExtension the Registry Key for
  339. * HKEY_LOCAL_MACHINE\Software\Microsoft\MMC\NodeTypes\
  340. * cszNodeTypeGuid (GUID string) \Extensions
  341. * If fCreateIfNonextistent is TRUE, the Extensions key (only) is created
  342. * if it doesn't exist.
  343. *
  344. * Return Codes:
  345. * S_OK - All operations succeeded.
  346. * E_FAIL - A critical registry operation failed.
  347. * E_ABORT - The GUID string could not be opened, or
  348. * the Extensions key could not be opened (and
  349. * fCreateIfNonexistent is FALSE).
  350. *
  351. * History: a-jsari 9/18/97 Initial version
  352. */
  353. static inline HRESULT OpenExtendKeyForNodeType(LPCTSTR cszNodeTypeGuid,
  354. CRegKey *pcrkExtension, BOOL fCreateIfNonexistent = TRUE)
  355. {
  356. CRegKey crkRoot;
  357. CRegKey crkNodeToExtend;
  358. ASSERT(pcrkExtension != NULL);
  359. long lRegOpenResult = RegOpenMMCNodeTypeRoot(&crkRoot);
  360. if (lRegOpenResult != ERROR_SUCCESS) return E_FAIL;
  361. // Not finding the proper nodetype is a different kind of error
  362. // than not finding MMC, or not creating a new one.
  363. if (fCreateIfNonexistent) {
  364. lRegOpenResult = crkNodeToExtend.Create(crkRoot, cszNodeTypeGuid);
  365. if (lRegOpenResult != ERROR_SUCCESS) return E_FAIL;
  366. lRegOpenResult = pcrkExtension->Create(crkNodeToExtend, cszExtensions);
  367. } else {
  368. lRegOpenResult = crkNodeToExtend.Open(crkRoot, cszNodeTypeGuid);
  369. if (lRegOpenResult != ERROR_SUCCESS) return E_ABORT;
  370. lRegOpenResult = pcrkExtension->Open(crkNodeToExtend, cszExtensions);
  371. if (lRegOpenResult == ERROR_FILE_NOT_FOUND) return E_ABORT;
  372. }
  373. if (lRegOpenResult != ERROR_SUCCESS) return E_FAIL;
  374. return S_OK;
  375. }
  376. /*
  377. * ExtendNode - Does all registry extension required for the NodeType
  378. * cszNodeTypeGuid.
  379. *
  380. * Return Codes:
  381. * S_OK - Upon successful completion, or if cszNodeTypeGuid can't
  382. * be found.
  383. * E_FAIL - If MMC cannot be found or NodeTypeGuid can't be opened.
  384. *
  385. * History: a-jsari 9/9/97 Initial version
  386. */
  387. static HRESULT ExtendNode(LPCTSTR cszNodeTypeGuid)
  388. {
  389. CRegKey crkExtension;
  390. CRegKey crkIterator;
  391. CString szResourceLoader;
  392. HRESULT hrOpenExtension = OpenExtendKeyForNodeType(cszNodeTypeGuid,
  393. &crkExtension, TRUE);
  394. // Don't return an error if we can't open the cszNodeTypeGuid
  395. if (hrOpenExtension != S_OK)
  396. return hrOpenExtension == E_ABORT ? S_OK : hrOpenExtension;
  397. // NameSpace
  398. // {GUID} = "System Information Extension"
  399. long lRegOpenResult = crkIterator.Create(crkExtension, cszNameSpace);
  400. if (lRegOpenResult != ERROR_SUCCESS) return E_FAIL;
  401. VERIFY(szResourceLoader.LoadString(IDS_EXTENSIONDESCRIPTION));
  402. lRegOpenResult = crkIterator.SetValue(szResourceLoader, cszClsidMSInfoExtension);
  403. if (lRegOpenResult != ERROR_SUCCESS) return E_FAIL;
  404. // Task
  405. // {GUID} = "System Information Extension"
  406. lRegOpenResult = crkIterator.Create(crkExtension, cszTask);
  407. if (lRegOpenResult != ERROR_SUCCESS) return E_FAIL;
  408. lRegOpenResult = crkIterator.SetValue(szResourceLoader, cszClsidMSInfoExtension);
  409. return S_OK;
  410. }
  411. /*
  412. * HRESULT UnregisterNode - Remove all registry entries for a specific
  413. * Nodetype.
  414. *
  415. * Return Codes:
  416. * S_OK - If all of the essential nodes are found.
  417. * E_FAIL - If any of the path to the keys to remove fail to be found.
  418. *
  419. * History: a-jsari 9/9/97 Initial version.
  420. */
  421. static HRESULT UnregisterNode(LPCTSTR cszNodeTypeGuid)
  422. {
  423. CRegKey crkExtension;
  424. CRegKey crkIterator;
  425. HRESULT hrOpenExtension = OpenExtendKeyForNodeType(cszNodeTypeGuid,
  426. &crkExtension);
  427. ASSERT(hrOpenExtension == S_OK);
  428. // Don't return an error if we can't open the cszNodeTypeGuid
  429. if (hrOpenExtension != S_OK)
  430. return (hrOpenExtension == E_ABORT) ? S_OK : hrOpenExtension;
  431. long lRegOpenResult = crkIterator.Open(crkExtension, cszNameSpace);
  432. if (lRegOpenResult != ERROR_SUCCESS)
  433. return (lRegOpenResult == ERROR_FILE_NOT_FOUND) ? S_OK : E_FAIL;
  434. lRegOpenResult = crkIterator.DeleteValue(cszClsidMSInfoExtension);
  435. // It's not really an error to not find a key we were deleting anyhow.
  436. if (lRegOpenResult != ERROR_SUCCESS
  437. && lRegOpenResult != ERROR_FILE_NOT_FOUND) {
  438. return E_FAIL;
  439. }
  440. return S_OK;
  441. }
  442. /*
  443. * RegisterExtensionSnapin - Do all registration required for the extension side
  444. * of the snapin.
  445. *
  446. * History: a-jsari 9/9/97 Initial version
  447. */
  448. static inline HRESULT RegisterExtensionSnapin()
  449. {
  450. const HRESULT hrErrorReturn = E_FAIL;
  451. CRegKey crkRoot;
  452. CRegKey crkClsid;
  453. CRegKey crkIterator;
  454. CString szResourceLoader;
  455. // HKEY_LOCAL_MACHINE\Software\Microsoft\MMC\Snapins
  456. long lRegOpenResult = RegOpenMMCSnapinRoot(&crkRoot);
  457. // FIX: This fail should behave differently (registering the DLL
  458. // w/o MMC registered).
  459. if (lRegOpenResult != ERROR_SUCCESS) return hrErrorReturn;
  460. // {45ac8c63-23e2-11e1-a696-00c04fd58bc3}
  461. lRegOpenResult = crkClsid.Create(crkRoot, cszClsidMSInfoExtension);
  462. if (lRegOpenResult != ERROR_SUCCESS) return hrErrorReturn;
  463. // NameString = REG_SZ "System Information Extension"
  464. VERIFY(szResourceLoader.LoadString(IDS_EXTENSIONDESCRIPTION));
  465. lRegOpenResult = crkClsid.SetValue(szResourceLoader, cszNameString);
  466. if (lRegOpenResult != ERROR_SUCCESS) return hrErrorReturn;
  467. // Provider = REG_SZ "Microsoft Corporation"
  468. VERIFY(szResourceLoader.LoadString(IDS_COMPANY));
  469. lRegOpenResult = crkClsid.SetValue(szResourceLoader, cszProvider);
  470. if (lRegOpenResult != ERROR_SUCCESS) return hrErrorReturn;
  471. // Version = REG_SZ "5.0"
  472. VERIFY(szResourceLoader.LoadString(IDS_VERSION));
  473. lRegOpenResult = crkClsid.SetValue(szResourceLoader, cszVersion);
  474. if (lRegOpenResult != ERROR_SUCCESS) return hrErrorReturn;
  475. // Register our about interface CLSID under the "About" value, so when
  476. // we're being added as an extension, our information shows up.
  477. lRegOpenResult = crkClsid.SetValue(cszClsidAboutMSInfo, cszAbout);
  478. if (lRegOpenResult != ERROR_SUCCESS)
  479. return hrErrorReturn;
  480. // NodeTypes
  481. lRegOpenResult = crkIterator.Create(crkClsid, cszNodeTypes);
  482. if (lRegOpenResult != ERROR_SUCCESS) return hrErrorReturn;
  483. // {45ac8c66-23e2-11e1-a696-00c04fd58bc3}
  484. CRegKey crkNodeType;
  485. lRegOpenResult = crkNodeType.Create(crkIterator, cszNodeTypeStatic);
  486. if (lRegOpenResult != ERROR_SUCCESS) return hrErrorReturn;
  487. // We no longer want to extend the computer management node (138503).
  488. // So we won't make this call to create the extension key. Also, we'll
  489. // delete it if it exists.
  490. //
  491. // HRESULT hrExtend = ExtendNode(cszCompMgrNode);
  492. // ASSERT(hrExtend == S_OK);
  493. // return hrExtend;
  494. CRegKey crkCompmgmtExtension;
  495. if (SUCCEEDED(OpenExtendKeyForNodeType(cszCompMgrNode, &crkCompmgmtExtension, TRUE)))
  496. {
  497. CRegKey crkNameSpace, crkTaskPad;
  498. if (ERROR_SUCCESS == crkNameSpace.Open((HKEY)crkCompmgmtExtension, cszNameSpace))
  499. crkNameSpace.DeleteValue(cszClsidMSInfoExtension);
  500. if (ERROR_SUCCESS == crkTaskPad.Open((HKEY)crkCompmgmtExtension, cszTask))
  501. crkTaskPad.DeleteValue(cszClsidMSInfoExtension);
  502. }
  503. return S_OK;
  504. }
  505. /*
  506. * UnregisterExtensionSnapin - Unregister the extension part of the snapin.
  507. *
  508. * History: a-jsari 9/9/97 Initial version
  509. */
  510. static inline HRESULT UnregisterExtensionSnapin()
  511. {
  512. CRegKey crkSnapinRoot;
  513. long lRegOpenResult = RegOpenMMCSnapinRoot(&crkSnapinRoot);
  514. if (lRegOpenResult != ERROR_SUCCESS) {
  515. if (lRegOpenResult != ERROR_FILE_NOT_FOUND) return E_FAIL;
  516. } else {
  517. // CHECK: Is this appropriate for potential extensions (probably)
  518. lRegOpenResult = crkSnapinRoot.RecurseDeleteKey(cszClsidMSInfoExtension);
  519. ASSERT(lRegOpenResult == ERROR_SUCCESS);
  520. // It's not really an error to not find a key we were deleting anyhow.
  521. if (lRegOpenResult != ERROR_SUCCESS
  522. && lRegOpenResult != ERROR_FILE_NOT_FOUND) {
  523. return E_FAIL;
  524. }
  525. }
  526. HRESULT hrUnregister = UnregisterNode(cszCompMgrNode);
  527. return hrUnregister;
  528. }
  529. /*
  530. * RegisterPaths - Perform registration for path items
  531. *
  532. * History: a-jsari 12/4/97 Initial version.
  533. */
  534. static inline HRESULT RegisterPaths()
  535. {
  536. CRegKey crkSnapinRoot;
  537. CRegKey crkProgramFilesKey;
  538. TCHAR szBuffer[MAX_PATH];
  539. CString strPath;
  540. DWORD dwSize;
  541. long lResult;
  542. do {
  543. // Register MSInfo's Windows App Path.
  544. lResult = crkSnapinRoot.Open(HKEY_LOCAL_MACHINE, cszMSInfoKey);
  545. ASSERT(lResult == ERROR_SUCCESS);
  546. if (lResult != ERROR_SUCCESS) break;
  547. dwSize = sizeof(szBuffer);
  548. // Get the Path registry value into szBuffer
  549. lResult = crkSnapinRoot.QueryValue(szBuffer, cszPathValue, &dwSize);
  550. if (lResult != ERROR_SUCCESS) {
  551. // The path can't be read from the registry; register it.
  552. lResult = crkProgramFilesKey.Open(HKEY_LOCAL_MACHINE, cszWindowsCurrentKey);
  553. ASSERT(lResult == ERROR_SUCCESS);
  554. if (lResult != ERROR_SUCCESS) break;
  555. dwSize = sizeof(szBuffer);
  556. lResult = crkProgramFilesKey.QueryValue(szBuffer, cszCommonFilesValue, &dwSize);
  557. ASSERT(lResult == ERROR_SUCCESS);
  558. if (lResult != ERROR_SUCCESS) break;
  559. // Remove the quotes from around the path. Bug #363834.
  560. // strPath = "\"";
  561. strPath = CString(_T(""));
  562. strPath += szBuffer;
  563. strPath += _T("\\");
  564. strPath += cszMSInfoPath;
  565. // Remove the quotes from around the path to mimic the behaviour of MSInfo 4.10
  566. // (so that apps which used this key to launch MSInfo will continue to work). Bug #363834.
  567. if (strPath.Right(1) == CString(_T("\"")))
  568. strPath = strPath.Left(strPath.GetLength() - 1);
  569. // Set the path value: Path = <Path to MSInfo32.exe>
  570. lResult = crkSnapinRoot.SetValue(strPath, cszPathValue);
  571. ASSERT(lResult == ERROR_SUCCESS);
  572. if (lResult != ERROR_SUCCESS) break;
  573. } else {
  574. // Read the path from the previously registered location and set the variable
  575. // equal to it.
  576. strPath = szBuffer;
  577. // Remove the quotes from around the path. Bug #363834.
  578. if (strPath.Left(1) == CString(_T("\"")))
  579. {
  580. strPath = strPath.Right(strPath.GetLength() - 1);
  581. if (strPath.Right(1) == CString(_T("\"")))
  582. strPath = strPath.Left(strPath.GetLength() - 1);
  583. lResult = crkSnapinRoot.SetValue(strPath, cszPathValue);
  584. }
  585. }
  586. // Bug #363834 - we still want quotes around the string when it's written elsewhere in the registry.
  587. if (strPath.Left(1) != CString(_T("\"")))
  588. strPath = _T("\"") + strPath;
  589. if (strPath.Right(1) != CString(_T("\"")))
  590. strPath += _T("\"");
  591. lResult = crkSnapinRoot.Create(HKEY_LOCAL_MACHINE, cszRunKey);
  592. ASSERT(lResult == ERROR_SUCCESS);
  593. if (lResult != ERROR_SUCCESS) break;
  594. lResult = crkSnapinRoot.SetValue(strPath);
  595. ASSERT(lResult == ERROR_SUCCESS);
  596. if (lResult != ERROR_SUCCESS) break;
  597. int iValue = strPath.ReverseFind((TCHAR)'\\');
  598. strPath = strPath.Left(iValue);
  599. strPath += "\"";
  600. lResult = crkSnapinRoot.SetValue(strPath, cszPathValue);
  601. ASSERT(lResult == ERROR_SUCCESS);
  602. if (lResult != ERROR_SUCCESS) break;
  603. #if 0
  604. // We are now assuming that the path to MSInfo will be registered by some
  605. // outside agent, presumably setup.
  606. // Register the Path in the MSInfo directory
  607. lResult = crkSnapinRoot.Open(HKEY_LOCAL_MACHINE, cszMSInfoKey);
  608. ASSERT(lResult == ERROR_SUCCESS);
  609. if (lResult != ERROR_SUCCESS) break;
  610. // Reuse the saved szPath value.
  611. lResult = crkSnapinRoot.SetValue(szPath, cszPathValue);
  612. ASSERT(lResult == ERROR_SUCCESS);
  613. #endif
  614. } while (FALSE);
  615. return HRESULT_FROM_WIN32(lResult);
  616. }
  617. /*
  618. * UnregisterPaths - Remove registry entries for path items.
  619. *
  620. * History: a-jsari 12/4/97 Initial version
  621. */
  622. static inline HRESULT UnregisterPaths()
  623. {
  624. CRegKey crkRootKey;
  625. long lResult;
  626. do {
  627. // Remove the App Path
  628. lResult = crkRootKey.Open(HKEY_LOCAL_MACHINE, cszRunRootKey);
  629. if (lResult != ERROR_SUCCESS) break;
  630. lResult = crkRootKey.DeleteSubKey(cszRunSubKey);
  631. ASSERT(lResult == ERROR_SUCCESS);
  632. if (lResult != ERROR_SUCCESS) break;
  633. // Unregister the MSInfo Key
  634. lResult = crkRootKey.Open(HKEY_LOCAL_MACHINE, cszMSInfoBaseKey);
  635. ASSERT(lResult == ERROR_SUCCESS);
  636. if (lResult != ERROR_SUCCESS) break;
  637. lResult = crkRootKey.DeleteSubKey(cszMSInfoSubKey);
  638. ASSERT(lResult == ERROR_SUCCESS);
  639. } while (FALSE);
  640. return HRESULT_FROM_WIN32(lResult);
  641. }
  642. /*
  643. * DllRegisterServer - Registers the snapin in
  644. * HKEY_LOCAL_MACHINE\Software\Microsoft\MMC
  645. * HKEY_LOCAL_MACHINE\Software\Classes\MSInfo.*
  646. * HKEY_CLASSES_ROOT\CLSID\{45AC8C6...}
  647. *
  648. * History: a-jsari 9/9/97 Initial version.
  649. */
  650. STDAPI DllRegisterServer(void)
  651. {
  652. long lToolResult;
  653. if ((lToolResult = CToolList::Register(TRUE)) != ERROR_SUCCESS)
  654. return HRESULT_FROM_WIN32(lToolResult);
  655. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  656. HRESULT hrRegister = RegisterStandaloneSnapin();
  657. if (hrRegister != S_OK) return hrRegister;
  658. hrRegister = RegisterExtensionSnapin();
  659. if (hrRegister != S_OK) return hrRegister;
  660. hrRegister = RegisterPaths();
  661. if (hrRegister != S_OK) return hrRegister;
  662. // Registers object and all interfaces in typelib
  663. #if 0
  664. // This version has been failing
  665. return _Module.RegisterServer(TRUE);
  666. #else
  667. // So use this method.
  668. return _Module.RegisterServer(FALSE);
  669. #endif
  670. }
  671. /*
  672. * DllUnregisterServer - Removes entries from the system registry
  673. *
  674. * History: a-jsari 9/9/97 Initial version.
  675. */
  676. STDAPI DllUnregisterServer(void)
  677. {
  678. HRESULT hrReturn = S_OK;
  679. long lToolResult;
  680. if ((lToolResult = CToolList::Register(FALSE)) != ERROR_SUCCESS)
  681. hrReturn = HRESULT_FROM_WIN32(lToolResult);
  682. HRESULT hrUnregister = UnregisterStandaloneSnapin();
  683. if (hrUnregister != S_OK) hrReturn = hrUnregister;
  684. hrUnregister = UnregisterExtensionSnapin();
  685. if (hrUnregister != S_OK) hrReturn = hrUnregister;
  686. hrUnregister = UnregisterPaths();
  687. if (hrUnregister != S_OK) hrReturn = hrUnregister;
  688. _Module.UnregisterServer();
  689. return hrReturn;
  690. }
  691. //-----------------------------------------------------------------------------
  692. // Implementation for the CMSInfoLog class, used to keep a log of MSInfo
  693. // activities.
  694. //
  695. // This global variable can be used elsewhere in the snap-in to write log
  696. // entries. Only one should be created.
  697. //-----------------------------------------------------------------------------
  698. CMSInfoLog msiLog;
  699. //-----------------------------------------------------------------------------
  700. // The constructor needs to read the logging state information from the
  701. // registry.
  702. //-----------------------------------------------------------------------------
  703. CMSInfoLog::CMSInfoLog()
  704. {
  705. m_pLogFile = NULL;
  706. m_strEndMarker = _T("###"); // see note in OpenLogFile
  707. ReadLoggingStatus();
  708. }
  709. //-----------------------------------------------------------------------------
  710. // The destructor needs to close the file (if it was ever created and opened).
  711. // Also, this is a good place to put the exit MSInfo log entry.
  712. //-----------------------------------------------------------------------------
  713. CMSInfoLog::~CMSInfoLog()
  714. {
  715. if (this->IsLogging())
  716. this->WriteLog(CMSInfoLog::BASIC, _T("EXIT MSInfo\r\n"));
  717. try
  718. {
  719. if (m_pLogFile)
  720. {
  721. // Advance past the marker we wrote.
  722. m_pLogFile->Seek(m_strEndMarker.GetLength() * sizeof(TCHAR), CFile::current);
  723. // If we aren't at the end of the file, then we've at some point wrapped
  724. // to the beginning and are overwriting entries. To make it so there are
  725. // no incomplete entries, write spaces until the end of the file or until
  726. // we find a '\r' character.
  727. DWORD dwLength = m_pLogFile->GetLength();
  728. DWORD dwPosition = m_pLogFile->GetPosition();
  729. if (dwPosition < dwLength)
  730. {
  731. DWORD dwBytesRead = 0;
  732. TCHAR cRead;
  733. do
  734. {
  735. if (m_pLogFile->Read((void *) &cRead, sizeof(TCHAR)) < sizeof(TCHAR))
  736. break;
  737. dwBytesRead += sizeof(TCHAR);
  738. } while (cRead && cRead != _T('\r') && (dwPosition + dwBytesRead) < dwLength);
  739. if (dwBytesRead)
  740. {
  741. m_pLogFile->Seek(dwBytesRead * -1, CFile::current);
  742. // Write the spaces (but don't overwrite the '\r' character).
  743. if (cRead == _T('\0') || cRead == _T('\r'))
  744. dwBytesRead -= sizeof(TCHAR);
  745. WriteSpaces(dwBytesRead / sizeof(TCHAR));
  746. }
  747. }
  748. m_pLogFile->Close();
  749. delete m_pLogFile;
  750. m_pLogFile = NULL;
  751. }
  752. }
  753. catch (CFileException *e)
  754. {
  755. // Some sort of file error - turn off logging.
  756. m_fLoggingEnabled = FALSE;
  757. m_iLoggingMask = 0;
  758. }
  759. }
  760. //-----------------------------------------------------------------------------
  761. // These two WriteLog functions are for writing a log entry directly to the
  762. // file. The second (with two string parameters) assumes that the first
  763. // string is a format with a '%s' and the second is the replacement string.
  764. //
  765. // The iType parameter is used to indicate what sort of log entry this is.
  766. // The const int values from the class definition should be used.
  767. //
  768. // If fContinuation is TRUE, then this write is to terminate a log entry which
  769. // spans an operation, and should not have a timestamp added.
  770. //-----------------------------------------------------------------------------
  771. BOOL CMSInfoLog::WriteLog(int iType, const CString & strMessage, BOOL fContinuation)
  772. {
  773. if (!OpenLogFile())
  774. return FALSE;
  775. if ((m_iLoggingMask & iType) == 0)
  776. return FALSE;
  777. CString strWorking(strMessage);
  778. // If this isn't the continuation of a previous log entry, then
  779. // possibly add a timestamp.
  780. if (!fContinuation && m_fTimestamp)
  781. {
  782. CTime time = CTime::GetCurrentTime();
  783. strWorking = time.Format(_T("%Y-%m-%d %H:%M:%S ")) + strWorking;
  784. }
  785. return WriteLogInternal(strWorking);
  786. }
  787. BOOL CMSInfoLog::WriteLog(int iType, const CString & strFormat, const CString & strReplace1)
  788. {
  789. CString strCombined;
  790. strCombined.Format(strFormat, strReplace1);
  791. return WriteLog(iType, strCombined);
  792. }
  793. //-----------------------------------------------------------------------------
  794. // This function is called each time a log entry is written, to insure that
  795. // the log file is open. If we can't open the file, or shouldn't be logging in
  796. // the first place, this function should return FALSE.
  797. //-----------------------------------------------------------------------------
  798. BOOL CMSInfoLog::OpenLogFile()
  799. {
  800. if (!m_fLoggingEnabled)
  801. return FALSE;
  802. if (m_pLogFile == NULL)
  803. {
  804. m_pLogFile = new CFile;
  805. if (m_pLogFile == NULL)
  806. return FALSE;
  807. try
  808. {
  809. if (!m_pLogFile->Open(m_strFilename, CFile::modeCreate | CFile::modeNoTruncate | CFile::modeReadWrite))
  810. {
  811. delete m_pLogFile;
  812. m_pLogFile = NULL;
  813. m_fLoggingEnabled = FALSE;
  814. m_iLoggingMask = 0;
  815. return FALSE;
  816. }
  817. // Move to the right place in the file. If the file is empty, we're
  818. // already there. If not, look for the end marker (from the last time
  819. // we added log entries). If there is one, we should be positioned over
  820. // its first character. Otherwise, just move to the end of the file.
  821. if (m_pLogFile->GetLength() != 0)
  822. {
  823. // IMPORTANT NOTE: We assume here (for efficiency) that the end
  824. // marker is structured so that we don't need to back up when
  825. // searching the file. For example, no markers of the form "aaab".
  826. TCHAR cRead;
  827. int iMarker = 0;
  828. while (iMarker != m_strEndMarker.GetLength())
  829. {
  830. if (m_pLogFile->Read((void *) &cRead, sizeof(TCHAR)) < sizeof(TCHAR))
  831. break;
  832. if (cRead != m_strEndMarker[iMarker])
  833. iMarker = 0;
  834. if (cRead == m_strEndMarker[iMarker])
  835. iMarker++;
  836. }
  837. if (iMarker == m_strEndMarker.GetLength())
  838. m_pLogFile->Seek(m_strEndMarker.GetLength() * sizeof(TCHAR) * -1, CFile::current);
  839. else
  840. m_pLogFile->SeekToEnd();
  841. }
  842. }
  843. catch (CFileException *e)
  844. {
  845. // Some sort of file error - turn off logging.
  846. m_fLoggingEnabled = FALSE;
  847. m_iLoggingMask = 0;
  848. return FALSE;
  849. }
  850. }
  851. return TRUE;
  852. }
  853. //-----------------------------------------------------------------------------
  854. // This function reads information about logging (what to log, where to log,
  855. // etc.) out of the registry.
  856. //-----------------------------------------------------------------------------
  857. void CMSInfoLog::ReadLoggingStatus()
  858. {
  859. m_fLoggingEnabled = FALSE;
  860. m_fTimestamp = TRUE;
  861. m_strFilename = _T("");
  862. m_iLoggingMask = 0;
  863. m_dwMaxFileSize = 32 * 1024;
  864. CString strRegkey = CString(cszMSInfoKey) + CString(_T("\\Logging"));
  865. CRegKey regkey;
  866. if (ERROR_SUCCESS == regkey.Open(HKEY_LOCAL_MACHINE, strRegkey, KEY_READ))
  867. {
  868. DWORD dwTemp;
  869. dwTemp = 0;
  870. if (ERROR_SUCCESS == regkey.QueryValue(dwTemp, _T("LogMask")))
  871. m_iLoggingMask = (int) dwTemp;
  872. dwTemp = 0;
  873. if (ERROR_SUCCESS == regkey.QueryValue(dwTemp, _T("LogFileMaxSize")))
  874. m_dwMaxFileSize = dwTemp;
  875. dwTemp = 0;
  876. if (ERROR_SUCCESS == regkey.QueryValue(dwTemp, _T("LogTimestamp")))
  877. m_fTimestamp = (dwTemp) ? TRUE : FALSE;
  878. TCHAR szFilename[MAX_PATH];
  879. dwTemp = MAX_PATH;
  880. if (ERROR_SUCCESS == regkey.QueryValue(szFilename, _T("LogFilename"), &dwTemp))
  881. m_strFilename = szFilename;
  882. regkey.Close();
  883. }
  884. m_fLoggingEnabled = ((m_iLoggingMask != 0) && !m_strFilename.IsEmpty());
  885. }
  886. //-----------------------------------------------------------------------------
  887. // Write a string to the log file. We should be positioned before the end
  888. // marker in the file. Overwrite this, and add the end marker onto the end
  889. // of the string we are writing.
  890. //
  891. // If we are too close to the maximum file size, then fill the rest of the
  892. // file (to that point) with spaces, and wrap to the start of the file.
  893. //
  894. // After we've written the output, back up so that we are positioned over the
  895. // start of the end marker.
  896. //-----------------------------------------------------------------------------
  897. BOOL CMSInfoLog::WriteLogInternal(const CString & strMessage)
  898. {
  899. CString strTerminated = strMessage + m_strEndMarker;
  900. DWORD dwLength = strTerminated.GetLength() * sizeof(TCHAR);
  901. if (m_pLogFile == NULL)
  902. return FALSE;
  903. try
  904. {
  905. // If we are too close to the max size of the file, write spaces to that
  906. // size and start from the beginning.
  907. DWORD dwPosition = m_pLogFile->GetPosition();
  908. if (m_dwMaxFileSize && ((dwPosition + dwLength) > m_dwMaxFileSize))
  909. {
  910. WriteSpaces((m_dwMaxFileSize - dwPosition) / sizeof(TCHAR));
  911. m_pLogFile->SeekToBegin();
  912. }
  913. // Write the string to the file.
  914. m_pLogFile->Write((const void *) (LPCTSTR) strTerminated, strTerminated.GetLength() * sizeof(TCHAR));
  915. // Finally, back up over the terminating characters.
  916. m_pLogFile->Seek(m_strEndMarker.GetLength() * sizeof(TCHAR) * -1, CFile::current);
  917. }
  918. catch (CFileException *e)
  919. {
  920. // Some sort of file error - turn off logging.
  921. m_fLoggingEnabled = FALSE;
  922. m_iLoggingMask = 0;
  923. }
  924. return TRUE;
  925. }
  926. //-----------------------------------------------------------------------------
  927. // Write the specified number of spaces to the file. (Note, this will already
  928. // be inside an exeption handling block from the caller.)
  929. //-----------------------------------------------------------------------------
  930. void CMSInfoLog::WriteSpaces(DWORD dwCount)
  931. {
  932. TCHAR * szSpaceBuffer = NULL;
  933. szSpaceBuffer = new TCHAR[dwCount];
  934. if (szSpaceBuffer)
  935. {
  936. _tcsnset(szSpaceBuffer, _T(' '), dwCount);
  937. m_pLogFile->Write((const void *) (LPCTSTR) szSpaceBuffer, dwCount * sizeof(TCHAR));
  938. delete [] szSpaceBuffer;
  939. }
  940. }