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.

1308 lines
40 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation
  3. Module Name:
  4. globals.cpp
  5. Abstract:
  6. This module implements global functions needed for the program.
  7. It also contain global variables/classes.
  8. Author:
  9. William Hsieh (williamh) created
  10. Revision History:
  11. --*/
  12. #include "devmgr.h"
  13. #include <shlobj.h>
  14. #define NO_SHELL_TREE_TYPE
  15. #include <shlobjp.h>
  16. //
  17. // global classes and variables
  18. //
  19. // this, of course, our dll's instance handle.
  20. HINSTANCE g_hInstance = NULL;
  21. //
  22. // A CMachineList is created for each instance of DLL. It is shared
  23. // by all the CComponentData the instance might create. The class CMachine
  24. // contains all the information about all the classes and devices on the
  25. // machine. Each CComponent should register itself to CMachine. This way,
  26. // A CComponent will get notification whenever there are changes in
  27. // the CMachine(Refresh, Property changes on a device, for example).
  28. // We do not rely on MMC's view notification(UpdatAllView) because
  29. // it only reaches all the CComponents created by a CComponenetData.
  30. //
  31. CMachineList g_MachineList;
  32. CMemoryException g_MemoryException(TRUE);
  33. String g_strStartupMachineName;
  34. String g_strStartupDeviceId;
  35. String g_strStartupCommand;
  36. String g_strDevMgr;
  37. BOOL g_IsAdmin = FALSE;
  38. CPrintDialog g_PrintDlg;
  39. //
  40. // UUID consts
  41. //
  42. const CLSID CLSID_DEVMGR = {0x74246BFC,0x4C96,0x11D0,{0xAB,0xEF,0x00,0x20,0xAF,0x6B,0x0B,0x7A}};
  43. const CLSID CLSID_DEVMGR_EXTENSION = {0x90087284,0xd6d6,0x11d0,{0x83,0x53,0x00,0xa0,0xc9,0x06,0x40,0xbf}};
  44. const CLSID CLSID_SYSTOOLS = {0x476e6448,0xaaff,0x11d0,{0xb9,0x44,0x00,0xc0,0x4f,0xd8,0xd5,0xb0}};
  45. const CLSID CLSID_DEVMGR_ABOUT = {0x94abaf2a,0x892a,0x11d1,{0xbb,0xc4,0x00,0xa0,0xc9,0x06,0x40,0xbf}};
  46. const TCHAR* const CLSID_STRING_DEVMGR = TEXT("{74246bfc-4c96-11d0-abef-0020af6b0b7a}");
  47. const TCHAR* const CLSID_STRING_DEVMGR_EXTENSION = TEXT("{90087284-d6d6-11d0-8353-00a0c90640bf}");
  48. const TCHAR* const CLSID_STRING_SYSTOOLS = TEXT("{476e6448-aaff-11d0-b944-00c04fd8d5b0}");
  49. const TCHAR* const CLSID_STRING_DEVMGR_ABOUT = TEXT("{94abaf2a-892a-11d1-bbc4-00a0c90640bf}");
  50. //
  51. // ProgID
  52. //
  53. const TCHAR* const PROGID_DEVMGR = TEXT("DevMgrSnapin.DevMgrSnapin.1");
  54. const TCHAR* const PROGID_DEVMGREXT = TEXT("DevMgrExtension.DevMgrExtension.1");
  55. const TCHAR* const PROGID_DEVMGR_ABOUT = TEXT("DevMgrAbout.DevMgrAbout.1");
  56. //
  57. // Node types const
  58. //
  59. const NODEINFO NodeInfo[TOTAL_COOKIE_TYPES] =
  60. {
  61. { COOKIE_TYPE_SCOPEITEM_DEVMGR,
  62. IDS_NAME_DEVMGR,
  63. IDS_DISPLAYNAME_SCOPE_DEVMGR,
  64. {0xc41dfb2a,0x4d5b,0x11d0,{0xab,0xef,0x00,0x20,0xaf,0x6b,0x0b,0x7a}},
  65. TEXT("{c41dfb2a-4d5b-11d0-abef-0020af6b0b7a}")
  66. },
  67. { COOKIE_TYPE_RESULTITEM_RESOURCE_IRQ,
  68. IDS_NAME_IRQ,
  69. 0,
  70. {0x494535fe,0x5aa2,0x11d0,{0xab,0xf0,0x00,0x20,0xaf,0x6b,0x0b,0x7a}},
  71. TEXT("{494535fe-5aa2-11d0-abf0-0020af6b0b7a}")
  72. },
  73. { COOKIE_TYPE_RESULTITEM_RESOURCE_DMA,
  74. IDS_NAME_DMA,
  75. 0,
  76. {0x49f0df4e,0x5aa2,0x11d0,{0xab,0xf0,0x00,0x20,0xaf,0x6b,0x0b,0x7a}},
  77. TEXT("{49f0df4e-5aa2-11d0-abf0-0020af6b0b7a}")
  78. },
  79. { COOKIE_TYPE_RESULTITEM_RESOURCE_IO,
  80. IDS_NAME_IO,
  81. 0,
  82. {0xa2958d7a,0x5aa2,0x11d0,{0xab,0xf0,0x00,0x20,0xaf,0x6b,0x0b,0x7a}},
  83. TEXT("{a2958d7a-5aa2-11d0-abf0-0020af6b0b7a}")
  84. },
  85. { COOKIE_TYPE_RESULTITEM_RESOURCE_MEMORY,
  86. IDS_NAME_MEMORY,
  87. 0,
  88. {0xa2958d7b,0x5aa2,0x11d0,{0xab,0xf0,0x00,0x20,0xaf,0x6b,0x0b,0x7a}},
  89. TEXT("{a2958d7b-5aa2-11d0-abf0-0020af6b0b7a}")
  90. },
  91. { COOKIE_TYPE_RESULTITEM_COMPUTER,
  92. IDS_NAME_COMPUTER,
  93. 0,
  94. {0xa2958d7c,0x5aa2,0x11d0,{0xab,0xf0,0x00,0x20,0xaf,0x6b,0x0b,0x7a}},
  95. TEXT("{a2958d7c-5aa2-11d0-abf0-0020af6b0b7a}")
  96. },
  97. { COOKIE_TYPE_RESULTITEM_DEVICE,
  98. IDS_NAME_DEVICE,
  99. 0,
  100. {0xa2958d7d,0x5aa2,0x11d0,{0xab,0xf0,0x00,0x20,0xaf,0x6b,0x0b,0x7a}},
  101. TEXT("{a2958d7d-5aa2-11d0-abf0-0020af6b0b7a}")
  102. },
  103. { COOKIE_TYPE_RESULTITEM_CLASS,
  104. IDS_NAME_CLASS,
  105. 0,
  106. {0xe677e204,0x5aa2,0x11d0,{0xab,0xf0,0x00,0x20,0xaf,0x6b,0x0b,0x7a}},
  107. TEXT("{e677e204-5aa2-11d0-abf0-0020af6b0b7a}")
  108. },
  109. { COOKIE_TYPE_RESULTITEM_RESTYPE,
  110. IDS_NAME_RESOURCES,
  111. 0,
  112. {0xa2958d7e,0x5aa2,0x11d0,{0xab,0xf0,0x00,0x20,0xaf,0x6b,0x0b,0x7a}},
  113. TEXT("{a2958d7e-5aa2-11d0-abf0-0020af6b0b7a}")
  114. }
  115. };
  116. const IID IID_IDMTVOCX = \
  117. {0x142525f2,0x59d8,0x11d0,{0xab,0xf0,0x00,0x20,0xaf,0x6b,0x0b,0x7a}};
  118. const IID IID_ISnapinCallback = \
  119. {0x8e0ba98a,0xd161,0x11d0,{0x83,0x53,0x00,0xa0,0xc9,0x06,0x40,0xbf}};
  120. //
  121. // cliboard format strings
  122. //
  123. const TCHAR* const MMC_SNAPIN_MACHINE_NAME = TEXT("MMC_SNAPIN_MACHINE_NAME");
  124. const TCHAR* const SNAPIN_INTERNAL = TEXT("SNAPIN_INTERNAL");
  125. const TCHAR* const DEVMGR_SNAPIN_CLASS_GUID = TEXT("DEVMGR_SNAPIN_CLASS_GUID");
  126. const TCHAR* const DEVMGR_SNAPIN_DEVICE_ID = TEXT("DEVMGR_SNAPIN_DEVICE_ID");
  127. const TCHAR* const DEVMGR_COMMAND_PROPERTY = TEXT("Property");
  128. const TCHAR* const REG_PATH_DEVICE_MANAGER = TEXT("SOFTWARE\\Microsoft\\DeviceManager");
  129. const TCHAR* const REG_STR_BUS_TYPES = TEXT("BusTypes");
  130. const TCHAR* const REG_STR_TROUBLESHOOTERS = TEXT("TroubleShooters");
  131. const TCHAR* const DEVMGR_HELP_FILE_NAME = TEXT("devmgr.hlp");
  132. const TCHAR* const DEVMGR_HTML_HELP_FILE_NAME = TEXT("\\help\\devmgr.chm");
  133. // lookup table to translate problem number to its text resource id.
  134. const PROBLEMINFO g_ProblemInfo[] =
  135. {
  136. {IDS_PROB_NOPROBLEM, 0}, // NO PROBLEM
  137. {IDS_PROB_NOT_CONFIGURED, PIF_CODE_EMBEDDED}, // CM_PROB_NOT_CONFIGURED
  138. {IDS_PROB_DEVLOADERFAILED, PIF_CODE_EMBEDDED}, // CM_PROB_DEVLOADER_FAILED
  139. {IDS_PROB_OUT_OF_MEMORY, PIF_CODE_EMBEDDED}, // CM_PROB_OUT_OF_MEMORY
  140. {IDS_PROB_WRONG_TYPE, PIF_CODE_EMBEDDED}, // CM_PROB_ENTRY_IS_WRONG_TYPE
  141. {IDS_PROB_LACKEDARBITRATOR, PIF_CODE_EMBEDDED}, // CM_PROB_LACKED_ARBITRATOR
  142. {IDS_PROB_BOOT_CONFIG_CONFLICT, PIF_CODE_EMBEDDED}, // CM_PROB_BOOT_CONFIG_CONFLICT
  143. {IDS_PROB_FAILED_FILTER, PIF_CODE_EMBEDDED}, // CM_PROB_FAILED_FILTER
  144. {IDS_PROB_DEVLOADER_NOT_FOUND, PIF_CODE_EMBEDDED}, // CM_PROB_DEVLOADER_NOT_FOUND
  145. {IDS_PROB_INVALID_DATA, PIF_CODE_EMBEDDED}, // CM_PROB_INVALID_DATA
  146. {IDS_PROB_FAILED_START, PIF_CODE_EMBEDDED}, // CM_PROB_FAILED_START
  147. {IDS_PROB_LIAR, PIF_CODE_EMBEDDED}, // CM_PROB_LIAR
  148. {IDS_PROB_NORMAL_CONFLICT, PIF_CODE_EMBEDDED}, // CM_PROB_NORMAL_CONFLICT
  149. {IDS_PROB_NOT_VERIFIED, PIF_CODE_EMBEDDED}, // CM_PROB_NOT_VERIFIED
  150. {IDS_PROB_NEEDRESTART, PIF_CODE_EMBEDDED}, // CM_PROB_NEED_RESTART
  151. {IDS_PROB_REENUMERATION, PIF_CODE_EMBEDDED}, // CM_PROB_REENUMERATION
  152. {IDS_PROB_PARTIALCONFIG, PIF_CODE_EMBEDDED}, // CM_PROB_PARTIAL_LOG_CONF
  153. {IDS_PROB_UNKNOWN_RESOURCE, PIF_CODE_EMBEDDED}, // CM_PROB_UNKNOWN_RESOURCE
  154. {IDS_PROB_REINSTALL, PIF_CODE_EMBEDDED}, // CM_PROB_REINSTALL
  155. {IDS_PROB_REGISTRY, PIF_CODE_EMBEDDED}, // CM_PROB_REGISTRY
  156. {IDS_PROB_SYSTEMFAILURE, PIF_CODE_EMBEDDED}, // CM_PROB_VXDLDR
  157. {IDS_PROB_WILL_BE_REMOVED, PIF_CODE_EMBEDDED}, // CM_PROB_WILL_BE_REMOVED
  158. {IDS_PROB_DISABLED, PIF_CODE_EMBEDDED}, // CM_PROB_DISABLED
  159. {IDS_PROB_SYSTEMFAILURE, PIF_CODE_EMBEDDED}, // CM_PROB_DEVLOADER_NOT_READY
  160. {IDS_DEVICE_NOT_THERE, PIF_CODE_EMBEDDED}, // CM_PROB_DEVICE_NOT_THERE
  161. {IDS_PROB_MOVED, PIF_CODE_EMBEDDED}, // CM_PROB_MOVED
  162. {IDS_PROB_TOO_EARLY, PIF_CODE_EMBEDDED}, // CM_PROB_TOO_EARLY
  163. {IDS_PROB_NO_VALID_LOG_CONF, PIF_CODE_EMBEDDED}, // CM_PROB_NO_VALID_LOG_CONF
  164. {IDS_PROB_FAILEDINSTALL, PIF_CODE_EMBEDDED}, // CM_PROB_FAILED_INSTALL
  165. {IDS_PROB_HARDWAREDISABLED, PIF_CODE_EMBEDDED}, // CM_PROB_HARDWARE_DISABLED
  166. {IDS_PROB_CANT_SHARE_IRQ, PIF_CODE_EMBEDDED}, // CM_PROB_CANT_SHARE_IRQ
  167. {IDS_PROB_FAILED_ADD, PIF_CODE_EMBEDDED}, // CM_PROB_FAILED_ADD
  168. {IDS_PROB_DISABLED_SERVICE, PIF_CODE_EMBEDDED}, // CM_PROB_DISABLED_SERVICE
  169. {IDS_PROB_TRANSLATION_FAILED, PIF_CODE_EMBEDDED}, // CM_PROB_TRANSLATION_FAILED
  170. {IDS_PROB_NO_SOFTCONFIG, PIF_CODE_EMBEDDED}, // CM_PROB_NO_SOFTCONFIG
  171. {IDS_PROB_BIOS_TABLE, PIF_CODE_EMBEDDED}, // CM_PROB_BIOS_TABLE
  172. {IDS_PROB_IRQ_TRANSLATION_FAILED, PIF_CODE_EMBEDDED}, // CM_PROB_IRQ_TRANSLATION_FAILED
  173. {IDS_PROB_FAILED_DRIVER_ENTRY, PIF_CODE_EMBEDDED}, // CM_PROB_FAILED_DRIVER_ENTRY
  174. {IDS_PROB_DRIVER_FAILED_PRIOR_UNLOAD, PIF_CODE_EMBEDDED}, // CM_PROB_DRIVER_FAILED_PRIOR_UNLOAD
  175. {IDS_PROB_DRIVER_FAILED_LOAD, PIF_CODE_EMBEDDED}, // CM_PROB_DRIVER_FAILED_LOAD
  176. {IDS_PROB_DRIVER_SERVICE_KEY_INVALID, PIF_CODE_EMBEDDED}, // CM_PROB_DRIVER_SERVICE_KEY_INVALID
  177. {IDS_PROB_LEGACY_SERVICE_NO_DEVICES, PIF_CODE_EMBEDDED}, // CM_PROB_LEGACY_SERVICE_NO_DEVICES
  178. {IDS_PROB_DUPLICATE_DEVICE, PIF_CODE_EMBEDDED}, // CM_PROB_DUPLICATE_DEVICE
  179. {IDS_PROB_FAILED_POST_START, PIF_CODE_EMBEDDED}, // CM_PROB_FAILED_POST_START
  180. {IDS_PROB_HALTED, PIF_CODE_EMBEDDED}, // CM_PROB_HALTED
  181. {IDS_PROB_PHANTOM, PIF_CODE_EMBEDDED}, // CM_PROB_PHANTOM
  182. {IDS_PROB_SYSTEM_SHUTDOWN, PIF_CODE_EMBEDDED}, // CM_PROB_SYSTEM_SHUTDOWN
  183. {IDS_PROB_HELD_FOR_EJECT, PIF_CODE_EMBEDDED}, // CM_PROB_HELD_FOR_EJECT
  184. {IDS_PROB_DRIVER_BLOCKED, PIF_CODE_EMBEDDED}, // CM_PROB_DRIVER_BLOCKED
  185. {IDS_PROB_REGISTRY_TOO_LARGE, PIF_CODE_EMBEDDED}, // CM_PROB_REGISTRY_TOO_LARGE
  186. {IDS_PROB_SETPROPERTIES_FAILED, PIF_CODE_EMBEDDED}, // CM_PROB_SETPROPERTIES_FAILED
  187. {IDS_PROB_UNKNOWN_WITHCODE, PIF_CODE_EMBEDDED} // UNKNOWN PROBLEM
  188. };
  189. //
  190. // Global functions
  191. //
  192. #if DBG
  193. //
  194. // Debugging aids
  195. //
  196. void
  197. Trace(
  198. LPCTSTR format,
  199. ...
  200. )
  201. {
  202. // according to wsprintf specification, the max buffer size is
  203. // 1024
  204. TCHAR Buffer[1024];
  205. va_list arglist;
  206. va_start(arglist, format);
  207. StringCchVPrintf(Buffer, ARRAYLEN(Buffer), format, arglist);
  208. va_end(arglist);
  209. OutputDebugString(TEXT("DEVMGR: "));
  210. OutputDebugString(Buffer);
  211. OutputDebugString(TEXT("\r\n"));
  212. }
  213. #endif
  214. inline
  215. BOOL
  216. IsBlankChar(TCHAR ch)
  217. {
  218. return (_T(' ') == ch || _T('\t') == ch);
  219. }
  220. inline
  221. LPTSTR
  222. SkipBlankChars(
  223. LPTSTR psz
  224. )
  225. {
  226. while (IsBlankChar(*psz))
  227. psz++;
  228. return psz;
  229. }
  230. //
  231. // This function converts a given string to GUID
  232. // INPUT:
  233. // GuidString -- the null terminated guid string
  234. // LPGUID -- buffer to receive the GUID
  235. // OUTPUT:
  236. // TRUE if the conversion succeeded.
  237. // FALSE if failed.
  238. //
  239. inline
  240. BOOL
  241. GuidFromString(
  242. LPCTSTR GuidString,
  243. LPGUID pGuid
  244. )
  245. {
  246. return ERROR_SUCCESS == pSetupGuidFromString(GuidString, pGuid);
  247. }
  248. // This function converts the given GUID to a string
  249. // INPUT:
  250. // pGuid -- the guid
  251. // Buffer -- the buffer to receive the string
  252. // BufferLen -- the buffer size in char unit
  253. // OUTPUT:
  254. // TRUE if the conversion succeeded.
  255. // FLASE if failed.
  256. //
  257. //
  258. inline
  259. BOOL
  260. GuidToString(
  261. LPGUID pGuid,
  262. LPTSTR Buffer,
  263. DWORD BufferLen
  264. )
  265. {
  266. return ERROR_SUCCESS == pSetupStringFromGuid(pGuid, Buffer, BufferLen);
  267. }
  268. //
  269. // This function allocates an OLE string from OLE task memory pool
  270. // It does necessary char set conversion before copying the string.
  271. //
  272. // INPUT: LPCTSTR str -- the initial string
  273. // OUTPUT: LPOLESTR -- the result OLE string. NULL if the function fails.
  274. //
  275. LPOLESTR
  276. AllocOleTaskString(
  277. LPCTSTR str
  278. )
  279. {
  280. if (!str)
  281. {
  282. SetLastError(ERROR_INVALID_PARAMETER);
  283. return NULL;
  284. }
  285. size_t Len = lstrlen(str);
  286. // allocate the null terminate char also.
  287. LPOLESTR olestr = (LPOLESTR)::CoTaskMemAlloc((Len + 1) * sizeof(TCHAR));
  288. if (olestr)
  289. {
  290. StringCchCopy((LPTSTR)olestr, Len + 1, str);
  291. return olestr;
  292. }
  293. return NULL;
  294. }
  295. inline
  296. void
  297. FreeOleTaskString(
  298. LPOLESTR olestr
  299. )
  300. {
  301. if (olestr)
  302. {
  303. ::CoTaskMemFree(olestr);
  304. }
  305. }
  306. //
  307. // This function addes the given menu item to snapin
  308. // INPUT:
  309. // iNameStringId -- menu item text resource id
  310. // iStatusBarStringId -- status bar text resource id.
  311. // iCommandId -- command id to be assigned to the menu item
  312. // InsertionPointId -- Insertion point id
  313. // Flags -- flags
  314. // SpecialFlags -- special flags
  315. // OUTPUT:
  316. // HRESULT
  317. //
  318. HRESULT
  319. AddMenuItem(
  320. LPCONTEXTMENUCALLBACK pCallback,
  321. int iNameStringId,
  322. int iStatusBarStringId,
  323. long lCommandId,
  324. long InsertionPointId,
  325. long Flags,
  326. long SpecialFlags
  327. )
  328. {
  329. ASSERT(pCallback);
  330. CONTEXTMENUITEM tCMI;
  331. memset(&tCMI, 0, sizeof(tCMI));
  332. tCMI.lCommandID = lCommandId;
  333. tCMI.lInsertionPointID = InsertionPointId;
  334. tCMI.fFlags = Flags;
  335. tCMI.fSpecialFlags = SpecialFlags;
  336. TCHAR Name[MAX_PATH];
  337. TCHAR Status[MAX_PATH];
  338. if (::LoadString(g_hInstance, iNameStringId, Name, ARRAYLEN(Name)) != 0) {
  339. tCMI.strName = Name;
  340. }
  341. if (iStatusBarStringId &&
  342. (::LoadString(g_hInstance, iStatusBarStringId, Status, ARRAYLEN(Status)) != 0)) {
  343. tCMI.strStatusBarText = Status;
  344. }
  345. return pCallback->AddItem(&tCMI);
  346. }
  347. //
  348. // This function verifies the given machine name can be accessed remotely.
  349. // INPUT:
  350. // MachineName -- the machine name. The machine name must be
  351. // led by "\\\\".
  352. // OUTPUT:
  353. // BOOL TRUE for success and FALSE for failure. Check GetLastError() for failure
  354. // reason.
  355. //
  356. BOOL
  357. VerifyMachineName(
  358. LPCTSTR MachineName
  359. )
  360. {
  361. CONFIGRET cr = CR_SUCCESS;
  362. HMACHINE hMachine = NULL;
  363. HKEY hRemoteKey = NULL;
  364. HKEY hClass = NULL;
  365. String m_strMachineFullName;
  366. if (MachineName && (_T('\0') != MachineName[0]))
  367. {
  368. if (_T('\\') == MachineName[0] && _T('\\') == MachineName[1]) {
  369. m_strMachineFullName = MachineName;
  370. } else {
  371. m_strMachineFullName = TEXT("\\\\");
  372. m_strMachineFullName+=MachineName;
  373. }
  374. //
  375. // make sure we can connect the machine using cfgmgr32.
  376. //
  377. cr = CM_Connect_Machine((LPTSTR)m_strMachineFullName, &hMachine);
  378. //
  379. // We could not connect to the machine using cfgmgr32
  380. //
  381. if (CR_SUCCESS != cr)
  382. {
  383. goto clean0;
  384. }
  385. //
  386. // make sure we can connect to the registry of the remote machine
  387. //
  388. if (RegConnectRegistry((LPTSTR)m_strMachineFullName,
  389. HKEY_LOCAL_MACHINE,
  390. &hRemoteKey) != ERROR_SUCCESS) {
  391. cr = CR_REGISTRY_ERROR;
  392. goto clean0;
  393. }
  394. cr = CM_Open_Class_Key_Ex(NULL,
  395. NULL,
  396. KEY_READ,
  397. RegDisposition_OpenExisting,
  398. &hClass,
  399. CM_OPEN_CLASS_KEY_INSTALLER,
  400. hMachine
  401. );
  402. }
  403. clean0:
  404. if (hMachine) {
  405. CM_Disconnect_Machine(hMachine);
  406. }
  407. if (hRemoteKey) {
  408. RegCloseKey(hRemoteKey);
  409. }
  410. if (hClass) {
  411. RegCloseKey(hClass);
  412. }
  413. //
  414. // We will basically set two different error codes for this API, since we need
  415. // to present this information to the user.
  416. // 1) ERROR_MACHINE_UNABAILABLE
  417. // 2) ERROR_ACCESS_DENIED.
  418. //
  419. if (CR_SUCCESS == cr) {
  420. SetLastError(NO_ERROR);
  421. } else if (CR_MACHINE_UNAVAILABLE == cr) {
  422. SetLastError(ERROR_MACHINE_UNAVAILABLE);
  423. } else {
  424. SetLastError(ERROR_ACCESS_DENIED);
  425. }
  426. return (CR_SUCCESS == cr);
  427. }
  428. // This function loads the string designated by the given
  429. // string id(resource id) from the module's resource to the provided
  430. // buffer. It returns the required buffer size(in chars) to hold the string,
  431. // not including the terminated NULL chars. Last error will be set
  432. // appropaitely.
  433. //
  434. // input: int StringId -- the resource id of the string to be loaded.
  435. // LPTSTR Buffer -- provided buffer to receive the string
  436. // UINT BufferSize -- the size of Buffer in chars
  437. // output:
  438. // UINT the required buffer size to receive the string
  439. // if it returns 0, GetLastError() returns the error code.
  440. //
  441. UINT
  442. LoadResourceString(
  443. int StringId,
  444. LPTSTR Buffer,
  445. UINT BufferSize
  446. )
  447. {
  448. // do some trivial tests.
  449. if (BufferSize && !Buffer)
  450. {
  451. SetLastError(ERROR_INVALID_PARAMETER);
  452. return 0;
  453. }
  454. // if caller provides buffer, try to load the string with the given buffer
  455. // and length.
  456. UINT FinalLen;
  457. if (Buffer)
  458. {
  459. FinalLen = ::LoadString(g_hInstance, StringId, Buffer, BufferSize);
  460. if (BufferSize > FinalLen)
  461. {
  462. return FinalLen;
  463. }
  464. }
  465. // Either the caller does not provide the buffer or the given buffer
  466. // is too small. Try to figure out the requried size.
  467. //
  468. // first use a stack-based buffer to get the string. If the buffer
  469. // is big enough, we are happy.
  470. TCHAR Temp[256];
  471. UINT ArrayLen = ARRAYLEN(Temp);
  472. FinalLen = ::LoadString(g_hInstance, StringId, Temp, ArrayLen);
  473. DWORD LastError = ERROR_SUCCESS;
  474. if (ArrayLen <= FinalLen)
  475. {
  476. // the stack-based buffer is too small, use heap-based buffer.
  477. // we have not got all the chars. we increase the buffer size of 256
  478. // chars each time it fails. The initial size is 512(256+256)
  479. // the max size is 32K
  480. ArrayLen = 256;
  481. TCHAR* HeapBuffer;
  482. FinalLen = 0;
  483. while (ArrayLen < 0x8000)
  484. {
  485. ArrayLen += 256;
  486. HeapBuffer = new TCHAR[ArrayLen];
  487. if (HeapBuffer)
  488. {
  489. FinalLen = ::LoadString(g_hInstance, StringId, HeapBuffer, ArrayLen);
  490. delete [] HeapBuffer;
  491. if (FinalLen < ArrayLen)
  492. break;
  493. }
  494. else
  495. {
  496. LastError = ERROR_NOT_ENOUGH_MEMORY;
  497. break;
  498. }
  499. }
  500. if (ERROR_SUCCESS != LastError)
  501. {
  502. SetLastError(LastError);
  503. FinalLen = 0;
  504. }
  505. }
  506. return FinalLen;
  507. }
  508. // This function get the problem text designated by the given problem number
  509. // for the given devnode on the given machine.
  510. //
  511. //
  512. // input:
  513. // ULONG ProblemNumber -- the problem number
  514. // LPTSTR Buffer -- provided buffer to receive the string
  515. // UINT BufferSize -- the size of Buffer in chars
  516. // output:
  517. // UINT the required buffer size to receive the string
  518. // if it returns 0, GetLastError() returns the error code.
  519. //
  520. UINT
  521. GetDeviceProblemText(
  522. ULONG ProblemNumber,
  523. LPTSTR Buffer,
  524. UINT BufferSize
  525. )
  526. {
  527. //
  528. // first does a trivial test
  529. //
  530. if (!Buffer && BufferSize)
  531. {
  532. SetLastError(ERROR_INVALID_PARAMETER);
  533. return 0;
  534. }
  535. String strMainText;
  536. UINT RequiredSize = 0;
  537. PROBLEMINFO pi;
  538. //
  539. // Get the PROBLEMINFO for the problem number
  540. //
  541. pi = g_ProblemInfo[min(ProblemNumber, DEVMGR_NUM_CM_PROB-1)];
  542. try
  543. {
  544. String strProblemDesc;
  545. strProblemDesc.LoadString(g_hInstance, pi.StringId);
  546. if (pi.Flags & PIF_CODE_EMBEDDED)
  547. {
  548. String strFormat;
  549. strFormat.LoadString(g_hInstance, IDS_PROB_CODE);
  550. String strCodeText;
  551. strCodeText.Format((LPTSTR)strFormat, ProblemNumber);
  552. strMainText.Format((LPTSTR)strProblemDesc, (LPTSTR)strCodeText);
  553. }
  554. else
  555. {
  556. strMainText = strProblemDesc;
  557. }
  558. RequiredSize = strMainText.GetLength() + 1;
  559. //
  560. // copy the main text
  561. //
  562. if (RequiredSize && (BufferSize > RequiredSize))
  563. {
  564. StringCchCopy(Buffer, BufferSize, (LPTSTR)strMainText);
  565. }
  566. }
  567. catch (CMemoryException* e)
  568. {
  569. e->Delete();
  570. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  571. RequiredSize = 0;
  572. }
  573. return RequiredSize;
  574. }
  575. //
  576. // This function creates and shows a message box
  577. // INPUT:
  578. // hwndParent -- the window handle servers as the parent window to the
  579. // message box
  580. // MsgId -- string id for message box body. The string can be
  581. // a format string.
  582. // CaptionId -- string id for caption. if 0, default is device manager
  583. // Type -- the standard message box flags(MB_xxxx)
  584. // ... -- parameters to MsgId string if it contains any
  585. // format chars.
  586. //OUTPUT:
  587. // return value from MessageBox(IDOK, IDYES...)
  588. int MsgBoxParam(
  589. HWND hwndParent,
  590. int MsgId,
  591. int CaptionId,
  592. DWORD Type,
  593. ...
  594. )
  595. {
  596. TCHAR szMsg[MAX_PATH * 4], szCaption[MAX_PATH];;
  597. LPCTSTR pCaption;
  598. va_list parg;
  599. int Result;
  600. // if no MsgId is given, it is for no memory error;
  601. if (MsgId)
  602. {
  603. va_start(parg, Type);
  604. // load the msg string to szCaption(temp). The text may contain
  605. // format information
  606. if (!::LoadString(g_hInstance, MsgId, szCaption, ARRAYLEN(szCaption)))
  607. {
  608. goto NoMemory;
  609. }
  610. //finish up format string
  611. StringCchVPrintf(szMsg, ARRAYLEN(szMsg), szCaption, parg);
  612. // if caption id is given, load it.
  613. if (CaptionId)
  614. {
  615. if (!::LoadString(g_hInstance, CaptionId, szCaption, ARRAYLEN(szCaption)))
  616. {
  617. goto NoMemory;
  618. }
  619. pCaption = szCaption;
  620. }
  621. else
  622. {
  623. pCaption = g_strDevMgr;
  624. }
  625. if ((Result = MessageBox(hwndParent, szMsg, pCaption, Type)) == 0)
  626. {
  627. goto NoMemory;
  628. }
  629. return Result;
  630. }
  631. NoMemory:
  632. g_MemoryException.ReportError(hwndParent);
  633. return 0;
  634. }
  635. // This functin prompts for restart.
  636. // INPUT:
  637. // hwndParent -- the window handle to be used as the parent window
  638. // to the restart dialog
  639. // RestartFlags -- flags(RESTART/REBOOT/POWERRECYCLE)
  640. // ResId -- designated string resource id. If 0, default will
  641. // be used.
  642. // OUTPUT:
  643. // ID returned from the MessageBox. IDYES if the user said Yes to the restart
  644. // dialog and IDNO if they said NO.
  645. INT
  646. PromptForRestart(
  647. HWND hwndParent,
  648. DWORD RestartFlags,
  649. int ResId
  650. )
  651. {
  652. INT id = 0;
  653. if (RestartFlags & (DI_NEEDRESTART | DI_NEEDREBOOT))
  654. {
  655. DWORD ExitWinCode = 0;
  656. try
  657. {
  658. String str;
  659. if (RestartFlags & DI_NEEDRESTART)
  660. {
  661. if (!ResId)
  662. {
  663. ResId = IDS_DEVCHANGE_RESTART;
  664. }
  665. str.LoadString(g_hInstance, ResId);
  666. ExitWinCode = EWX_REBOOT;
  667. }
  668. else
  669. {
  670. if (!ResId && RestartFlags & DI_NEEDPOWERCYCLE)
  671. {
  672. String str2;
  673. str.LoadString(g_hInstance, IDS_POWERCYC1);
  674. str2.LoadString(g_hInstance, IDS_POWERCYC2);
  675. str += str2;
  676. ExitWinCode = EWX_SHUTDOWN;
  677. }
  678. else
  679. {
  680. if (!ResId)
  681. {
  682. ResId = IDS_DEVCHANGE_RESTART;
  683. }
  684. str.LoadString(g_hInstance, ResId);
  685. ExitWinCode = EWX_REBOOT;
  686. }
  687. }
  688. if (ExitWinCode != 0) {
  689. id = RestartDialogEx(hwndParent,
  690. str,
  691. ExitWinCode,
  692. REASON_PLANNED_FLAG | REASON_HWINSTALL
  693. );
  694. }
  695. }
  696. catch(CMemoryException* e)
  697. {
  698. e->Delete();
  699. MsgBoxParam(hwndParent, 0, 0, 0);
  700. }
  701. }
  702. return id;
  703. }
  704. BOOL
  705. LoadEnumPropPage32(
  706. LPCTSTR RegString,
  707. HMODULE* pDll,
  708. FARPROC* pProcAddress
  709. )
  710. {
  711. // verify parameters
  712. if (!RegString || _T('\0') == RegString[0] || !pDll || !pProcAddress)
  713. {
  714. SetLastError(ERROR_INVALID_PARAMETER);
  715. return FALSE;
  716. }
  717. // make a copy of the string because we have to party on it
  718. ULONG Len = lstrlen(RegString) + 1;
  719. TCHAR* psz = new TCHAR[Len];
  720. if (!psz)
  721. {
  722. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  723. return FALSE;
  724. }
  725. StringCchCopy(psz, Len, RegString);
  726. LPTSTR DllName = NULL;
  727. LPTSTR DllNameEnd = NULL;
  728. LPTSTR FunctionName = NULL;
  729. LPTSTR FunctionNameEnd = NULL;
  730. LPTSTR p;
  731. p = psz;
  732. SetLastError(ERROR_SUCCESS);
  733. // the format of the string is "dllname, dllentryname"
  734. p = SkipBlankChars(p);
  735. if (_T('\0') != *p)
  736. {
  737. // looking for dllname which could be enclosed
  738. // inside double quote chars.
  739. // NOTE: not double quote chars inside double quoted string is allowed.
  740. if (_T('\"') == *p)
  741. {
  742. DllName = ++p;
  743. while (_T('\"') != *p && _T('\0') != *p)
  744. p++;
  745. DllNameEnd = p;
  746. if (_T('\"') == *p)
  747. p++;
  748. }
  749. else
  750. {
  751. DllName = p;
  752. while (!IsBlankChar(*p) && _T(',') != *p)
  753. p++;
  754. DllNameEnd = p;
  755. }
  756. // looking for ','
  757. p = SkipBlankChars(p);
  758. if (_T('\0') != *p && _T(',') == *p)
  759. {
  760. p = SkipBlankChars(p + 1);
  761. if (_T('\0') != *p)
  762. {
  763. FunctionName = p++;
  764. while (!IsBlankChar(*p) && _T('\0') != *p)
  765. p++;
  766. FunctionNameEnd = p;
  767. }
  768. }
  769. }
  770. if (DllName && FunctionName)
  771. {
  772. if (DllNameEnd) {
  773. *DllNameEnd = _T('\0');
  774. }
  775. if (FunctionNameEnd) {
  776. *FunctionNameEnd = _T('\0');
  777. }
  778. *pDll = LoadLibrary(DllName);
  779. if (*pDll)
  780. {
  781. // convert Wide char to ANSI which is GetProcAddress expected.
  782. // We do not append a 'A" or a "W' here.
  783. CHAR FuncNameA[256];
  784. WideCharToMultiByte(CP_ACP, 0,
  785. FunctionName,
  786. (int)wcslen(FunctionName) + 1,
  787. FuncNameA,
  788. sizeof(FuncNameA),
  789. NULL, NULL);
  790. *pProcAddress = GetProcAddress(*pDll, FuncNameA);
  791. }
  792. }
  793. delete [] psz;
  794. if (!*pProcAddress && *pDll)
  795. FreeLibrary(*pDll);
  796. return (*pDll && *pProcAddress);
  797. }
  798. BOOL
  799. AddPropPageCallback(
  800. HPROPSHEETPAGE hPage,
  801. LPARAM lParam
  802. )
  803. {
  804. CPropSheetData* ppsData = (CPropSheetData*)lParam;
  805. ASSERT(ppsData);
  806. return ppsData->InsertPage(hPage);
  807. }
  808. BOOL
  809. AddToolTips(
  810. HWND hDlg,
  811. UINT id,
  812. LPCTSTR pszText,
  813. HWND *phwnd
  814. )
  815. {
  816. if (*phwnd == NULL)
  817. {
  818. *phwnd = CreateWindow(TOOLTIPS_CLASS,
  819. TEXT(""),
  820. WS_POPUP | TTS_NOPREFIX,
  821. CW_USEDEFAULT,
  822. CW_USEDEFAULT,
  823. CW_USEDEFAULT,
  824. CW_USEDEFAULT,
  825. hDlg,
  826. NULL,
  827. g_hInstance,
  828. NULL);
  829. if (*phwnd)
  830. {
  831. TOOLINFO ti;
  832. ti.cbSize = sizeof(ti);
  833. ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
  834. ti.hwnd = hDlg;
  835. ti.uId = (UINT_PTR)GetDlgItem(hDlg, id);
  836. ti.lpszText = (LPTSTR)pszText; // const -> non const
  837. ti.hinst = g_hInstance;
  838. SendMessage(*phwnd, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
  839. }
  840. }
  841. return (*phwnd) ? TRUE : FALSE;
  842. }
  843. void Int64ToStr(LONGLONG n, LPTSTR lpBuffer)
  844. {
  845. TCHAR szTemp[40];
  846. LONGLONG iChr = 0;
  847. do {
  848. szTemp[iChr++] = TEXT('0') + (TCHAR)(n % 10);
  849. n = n / 10;
  850. } while (n != 0);
  851. do {
  852. iChr--;
  853. *lpBuffer++ = szTemp[iChr];
  854. } while (iChr != 0);
  855. *lpBuffer++ = '\0';
  856. }
  857. //
  858. // Obtain NLS info about how numbers should be grouped.
  859. //
  860. // The annoying thing is that LOCALE_SGROUPING and NUMBERFORMAT
  861. // have different ways of specifying number grouping.
  862. //
  863. // LOCALE NUMBERFMT Sample Country
  864. //
  865. // 3;0 3 1,234,567 United States
  866. // 3;2;0 32 12,34,567 India
  867. // 3 30 1234,567 ??
  868. //
  869. // Not my idea. That's the way it works.
  870. //
  871. UINT GetNLSGrouping(void)
  872. {
  873. TCHAR szGrouping[32];
  874. // If no locale info, then assume Western style thousands
  875. if (!GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, szGrouping, ARRAYLEN(szGrouping)))
  876. return 3;
  877. UINT grouping = 0;
  878. LPTSTR psz = szGrouping;
  879. for (;;)
  880. {
  881. if (*psz == '0') break; // zero - stop
  882. else if ((UINT)(*psz - '0') < 10) // digit - accumulate it
  883. grouping = grouping * 10 + (UINT)(*psz - '0');
  884. else if (*psz) // punctuation - ignore it
  885. { }
  886. else // end of string, no "0" found
  887. {
  888. grouping = grouping * 10; // put zero on end (see examples)
  889. break; // and finished
  890. }
  891. psz++;
  892. }
  893. return grouping;
  894. }
  895. STDAPI_(LPTSTR)
  896. AddCommas64(
  897. LONGLONG n,
  898. LPTSTR pszResult,
  899. UINT cchResult
  900. )
  901. {
  902. TCHAR szTemp[MAX_COMMA_NUMBER_SIZE];
  903. TCHAR szSep[5];
  904. NUMBERFMT nfmt;
  905. nfmt.NumDigits=0;
  906. nfmt.LeadingZero=0;
  907. nfmt.Grouping = GetNLSGrouping();
  908. GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, szSep, ARRAYLEN(szSep));
  909. nfmt.lpDecimalSep = nfmt.lpThousandSep = szSep;
  910. nfmt.NegativeOrder= 0;
  911. Int64ToStr(n, szTemp);
  912. if (GetNumberFormat(LOCALE_USER_DEFAULT, 0, szTemp, &nfmt, pszResult, cchResult) == 0)
  913. StringCchCopy(pszResult, cchResult, szTemp);
  914. return pszResult;
  915. }
  916. LPTSTR
  917. FormatString(
  918. LPCTSTR format,
  919. ...
  920. )
  921. {
  922. LPTSTR str = NULL;
  923. va_list arglist;
  924. va_start(arglist, format);
  925. if (FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
  926. format,
  927. 0,
  928. MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
  929. (LPTSTR)&str,
  930. 0,
  931. &arglist
  932. ) == 0) {
  933. str = NULL;
  934. }
  935. va_end(arglist);
  936. return str;
  937. }
  938. STDAPI_(CONFIGRET) GetLocationInformation(
  939. DEVNODE dn,
  940. LPTSTR Location,
  941. ULONG LocationLen,
  942. HMACHINE hMachine
  943. )
  944. /*++
  945. Slot x (LocationInformation)
  946. Slot x
  947. LocationInformation
  948. on parent bus
  949. --*/
  950. {
  951. CONFIGRET LastCR;
  952. DEVNODE dnParent;
  953. ULONG ulSize;
  954. DWORD UINumber;
  955. TCHAR Buffer[MAX_PATH];
  956. TCHAR UINumberDescFormat[MAX_PATH];
  957. TCHAR Format[MAX_PATH];
  958. Buffer[0] = TEXT('\0');
  959. //
  960. // We will first get any LocationInformation for the device. This will either
  961. // be in the LocationInformationOverride value in the devices driver (software) key
  962. // or if that is not present we will look for the LocationInformation value in
  963. // the devices device (hardware) key.
  964. //
  965. HKEY hKey;
  966. DWORD Type = REG_SZ;
  967. ulSize = sizeof(Buffer);
  968. if (CR_SUCCESS == CM_Open_DevNode_Key_Ex(dn,
  969. KEY_READ,
  970. 0,
  971. RegDisposition_OpenExisting,
  972. &hKey,
  973. CM_REGISTRY_SOFTWARE,
  974. hMachine
  975. )) {
  976. if (RegQueryValueEx(hKey,
  977. REGSTR_VAL_LOCATION_INFORMATION_OVERRIDE,
  978. NULL,
  979. &Type,
  980. (const PBYTE)Buffer,
  981. &ulSize) != ERROR_SUCCESS) {
  982. Buffer[0] = TEXT('\0');
  983. }
  984. RegCloseKey(hKey);
  985. }
  986. //
  987. // If the buffer is empty then we didn't get the LocationInformationOverride
  988. // value in the device's software key. So, we will see if their is a
  989. // LocationInformation value in the device's hardware key.
  990. //
  991. if (Buffer[0] == TEXT('\0')) {
  992. ulSize = sizeof(Buffer);
  993. if (CM_Get_DevNode_Registry_Property_Ex(dn,
  994. CM_DRP_LOCATION_INFORMATION,
  995. NULL,
  996. Buffer,
  997. &ulSize,
  998. 0,
  999. hMachine) != CR_SUCCESS) {
  1000. Buffer[0] = TEXT('\0');
  1001. }
  1002. }
  1003. //
  1004. // UINumber has precedence over all other location information so check if this
  1005. // device has a UINumber.
  1006. //
  1007. ulSize = sizeof(UINumber);
  1008. if (((LastCR = CM_Get_DevNode_Registry_Property_Ex(dn,
  1009. CM_DRP_UI_NUMBER,
  1010. NULL,
  1011. &UINumber,
  1012. &ulSize,
  1013. 0,
  1014. hMachine
  1015. )) == CR_SUCCESS) &&
  1016. (ulSize == sizeof(ULONG))) {
  1017. UINumberDescFormat[0] = TEXT('\0');
  1018. ulSize = sizeof(UINumberDescFormat);
  1019. //
  1020. // Get the UINumber description format string from the device's parent,
  1021. // if there is one, otherwise default to 'Location %1'
  1022. if ((CM_Get_Parent_Ex(&dnParent, dn, 0, hMachine) == CR_SUCCESS) &&
  1023. (CM_Get_DevNode_Registry_Property_Ex(dnParent,
  1024. CM_DRP_UI_NUMBER_DESC_FORMAT,
  1025. NULL,
  1026. UINumberDescFormat,
  1027. &ulSize,
  1028. 0,
  1029. hMachine) == CR_SUCCESS) &&
  1030. *UINumberDescFormat) {
  1031. } else {
  1032. ::LoadString(g_hInstance, IDS_UI_NUMBER_DESC_FORMAT, UINumberDescFormat, ARRAYLEN(UINumberDescFormat));
  1033. }
  1034. LPTSTR UINumberBuffer = NULL;
  1035. //
  1036. // Fill in the UINumber string
  1037. //
  1038. UINumberBuffer = FormatString(UINumberDescFormat, UINumber);
  1039. if (UINumberBuffer) {
  1040. StringCchCopy((LPTSTR)Location, LocationLen, UINumberBuffer);
  1041. LocalFree(UINumberBuffer);
  1042. } else {
  1043. Location[0] = TEXT('\0');
  1044. }
  1045. //
  1046. // If we also have LocationInformation then tack that on the end of the string
  1047. // as well.
  1048. //
  1049. if (*Buffer) {
  1050. StringCchCat((LPTSTR)Location, LocationLen, TEXT(" ("));
  1051. StringCchCat((LPTSTR)Location, LocationLen, Buffer);
  1052. StringCchCat((LPTSTR)Location, LocationLen, TEXT(")"));
  1053. }
  1054. }
  1055. //
  1056. // We don't have a UINumber but we do have LocationInformation
  1057. //
  1058. else if (*Buffer &&
  1059. (::LoadString(g_hInstance, IDS_LOCATION, Format, sizeof(Format)/sizeof(TCHAR)) != 0)) {
  1060. StringCchPrintf((LPTSTR)Location, LocationLen, Format, Buffer);
  1061. }
  1062. //
  1063. // We don't have a UINumber or LocationInformation so we need to get a description
  1064. // of the parent of this device.
  1065. //
  1066. else {
  1067. if ((LastCR = CM_Get_Parent_Ex(&dnParent, dn, 0, hMachine)) == CR_SUCCESS) {
  1068. //
  1069. // Try the registry for FRIENDLYNAME
  1070. //
  1071. Buffer[0] = TEXT('\0');
  1072. ulSize = sizeof(Buffer);
  1073. if (((LastCR = CM_Get_DevNode_Registry_Property_Ex(dnParent,
  1074. CM_DRP_FRIENDLYNAME,
  1075. NULL,
  1076. Buffer,
  1077. &ulSize,
  1078. 0,
  1079. hMachine
  1080. )) != CR_SUCCESS) ||
  1081. !*Buffer) {
  1082. //
  1083. // Try the registry for DEVICEDESC
  1084. //
  1085. ulSize = sizeof(Buffer);
  1086. if (((LastCR = CM_Get_DevNode_Registry_Property_Ex(dnParent,
  1087. CM_DRP_DEVICEDESC,
  1088. NULL,
  1089. Buffer,
  1090. &ulSize,
  1091. 0,
  1092. hMachine
  1093. )) != CR_SUCCESS) ||
  1094. !*Buffer) {
  1095. ulSize = sizeof(Buffer);
  1096. if (((LastCR = CM_Get_DevNode_Registry_Property_Ex(dnParent,
  1097. CM_DRP_CLASS,
  1098. NULL,
  1099. Buffer,
  1100. &ulSize,
  1101. 0,
  1102. hMachine
  1103. )) != CR_SUCCESS) ||
  1104. !*Buffer) {
  1105. //
  1106. // no parent, or parent name.
  1107. //
  1108. Buffer[0] = TEXT('\0');
  1109. }
  1110. }
  1111. }
  1112. }
  1113. if (*Buffer &&
  1114. (::LoadString(g_hInstance, IDS_LOCATION_NOUINUMBER, Format, sizeof(Format)/sizeof(TCHAR)) != 0)) {
  1115. //
  1116. // We have a description of the parent
  1117. //
  1118. StringCchPrintf((LPTSTR)Location, LocationLen, Format, Buffer);
  1119. } else {
  1120. //
  1121. // We don't have any information so we will just say Unknown
  1122. //
  1123. ::LoadString(g_hInstance, IDS_UNKNOWN, Location, LocationLen);
  1124. }
  1125. }
  1126. //
  1127. // Make sure the Location string is NULL terminated.
  1128. //
  1129. Location[LocationLen - 1] = TEXT('\0');
  1130. return CR_SUCCESS;
  1131. }