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.

1114 lines
29 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1993.
  5. //
  6. // File: classchk.cxx
  7. //
  8. // Classchk is a program for verifying that the contents of the registry are
  9. // OKY-DOKY as far as OLE is concerned.
  10. //
  11. // In general, we verify that all CLSID's are of the correct length, all string
  12. // parameters are NULL terminated.
  13. //
  14. // There are several phases of checking.
  15. //
  16. // 1) Checking that PROGID entries that have CLSID sections match.
  17. // 2) Checking that PROGID entries have correct and existing protocol entries
  18. // 3) Checking that PROGID entries
  19. //
  20. // History: 5-31-95 kevinro Created
  21. //
  22. //----------------------------------------------------------------------------
  23. #include <windows.h>
  24. #include <stdio.h>
  25. #include <ctype.h>
  26. #include "classchk.h"
  27. //
  28. // The following registry values are used quite a few times in this program.
  29. // These global variables keep us from needing to open them constantly.
  30. //
  31. HKEY hkey_clsid = 0;
  32. DWORD g_VerbosityLevel = VERB_LEVEL_WARN | VERB_LEVEL_ERROR;
  33. #define StrICmp(x,y) (CompareString(LOCALE_USER_DEFAULT,NORM_IGNORECASE,x,-1,y,-1) - 2)
  34. //+---------------------------------------------------------------------------
  35. //
  36. // Function: ReadRegistryString
  37. //
  38. // Synopsis: Reads a string from the registry
  39. //
  40. // Effects:
  41. //
  42. // This function reads in a string from the registry, and does some basic
  43. // consistency checking on it, such as verifying the length and NULL
  44. // terminatation.
  45. //
  46. //
  47. // Arguments: [hkeyRoot] --
  48. // [pszSubKeyName] --
  49. // [pszValueName] --
  50. // [pszValue] --
  51. // [pcbValue] --
  52. //
  53. // Returns: ERROR_SUCCESS Everything peachy
  54. // ERROR_FILE_NOT_FOUND Couldn't read entry from registry
  55. // CLASSCHK_SOMETHINGODD Something about the string is wrong
  56. // (other) Return value from registry
  57. //
  58. // Signals:
  59. //
  60. // Modifies:
  61. //
  62. // Algorithm:
  63. //
  64. // History: 5-31-95 kevinro Created
  65. //
  66. // Notes:
  67. //
  68. //----------------------------------------------------------------------------
  69. DWORD ReadRegistryString( HKEY hkeyRoot, LPSTR pszSubKeyName, LPSTR pszValueName, LPSTR pszValue, PULONG pcbValue)
  70. {
  71. LONG lRetValue;
  72. DWORD dwType;
  73. DWORD dwReturn = ERROR_SUCCESS;
  74. HKEY hkey = hkeyRoot;
  75. if(pszSubKeyName != NULL)
  76. {
  77. lRetValue = RegOpenKeyEx(hkeyRoot,
  78. pszSubKeyName,
  79. NULL,
  80. KEY_READ,
  81. &hkey);
  82. //
  83. // It is common to see keys that don't exist. Let the caller decide if it is
  84. // important or not.
  85. //
  86. if(lRetValue != ERROR_SUCCESS)
  87. {
  88. VERBOSITY(VERB_LEVEL_TRACE,printf("Unable to open subkey %s to read value %s\n",pszSubKeyName,pszValueName););
  89. return lRetValue;
  90. }
  91. }
  92. lRetValue = RegQueryValueEx(hkey,
  93. pszValueName,
  94. NULL, // Must be NULL according to spec
  95. &dwType,
  96. (BYTE *)pszValue,
  97. pcbValue);
  98. if(hkeyRoot != hkey)
  99. {
  100. //
  101. // Always close the new subkey if we opened it
  102. //
  103. RegCloseKey(hkey);
  104. }
  105. switch(lRetValue)
  106. {
  107. case ERROR_SUCCESS:
  108. //
  109. // Read the value, everything A-OK
  110. //
  111. break;
  112. case ERROR_MORE_DATA:
  113. VERBOSITY(VERB_LEVEL_WARN,printf("The value '%s' is larger than expected. May be a problem\n",pszValueName);)
  114. return lRetValue;
  115. break;
  116. case ERROR_FILE_NOT_FOUND:
  117. //
  118. // This may be expected, so don't report any errors. Let the caller handle that
  119. //
  120. return lRetValue;
  121. default:
  122. VERBOSITY(VERB_LEVEL_WARN,printf("RegQueryValueEx() for '%s' returned unexpected error 0x%x\n",pszValueName,lRetValue);)
  123. return lRetValue;
  124. }
  125. //
  126. // We expect this type to be REG_SZ. If it isn't, complain
  127. //
  128. if(dwType != REG_SZ)
  129. {
  130. VERBOSITY(VERB_LEVEL_WARN,printf("The value for '%s' is type 0x%x, was expecting REG_SZ (0x%x)\n",pszValueName,dwType,REG_SZ);)
  131. dwReturn = CLASSCHK_SOMETHINGODD;
  132. }
  133. //
  134. // We expect the value to be NULL terminated
  135. //
  136. if(pszValue[(*pcbValue)-1] != 0)
  137. {
  138. VERBOSITY(VERB_LEVEL_WARN,printf("The value for '%s' may not be NULL terminated.\n",pszValueName);)
  139. }
  140. //
  141. // We expect strlen to be the same as the length returned (which includes the NULL)
  142. //
  143. if((strlen(pszValue) + 1) != *pcbValue)
  144. {
  145. VERBOSITY(VERB_LEVEL_WARN,printf("The string value for '%s' may not have the correct length\n",pszValueName);)
  146. if(dwType == REG_SZ)
  147. {
  148. VERBOSITY(VERB_LEVEL_WARN,printf("The string value is '%s'\n",pszValue);)
  149. }
  150. dwReturn = CLASSCHK_SOMETHINGODD;
  151. }
  152. return dwReturn;
  153. }
  154. //+---------------------------------------------------------------------------
  155. //
  156. // Function: AllHexDigits
  157. //
  158. // Synopsis: Verify that all of the digits specified are hexidecimal
  159. //
  160. // Effects:
  161. //
  162. // Arguments: [pszString] -- String to check
  163. // [chString] -- Number of characters
  164. //
  165. // Requires:
  166. //
  167. // Returns: TRUE All hex digits
  168. // FALSE Not all hex digits
  169. //
  170. // History: 5-31-95 kevinro Created
  171. //----------------------------------------------------------------------------
  172. BOOL AllHexDigits(LPSTR pszString, ULONG chString)
  173. {
  174. while(chString--)
  175. {
  176. if(!isxdigit(pszString[chString]))
  177. {
  178. return FALSE;
  179. }
  180. }
  181. return TRUE;
  182. }
  183. //+---------------------------------------------------------------------------
  184. //
  185. // Function: CheckForValidGuid
  186. //
  187. // Synopsis: Given a string, determine if the string is a GUID
  188. //
  189. // Arguments: [pszValue] -- String to check
  190. //
  191. // History: 5-31-95 kevinro Created
  192. //
  193. //----------------------------------------------------------------------------
  194. DWORD CheckForValidGuid(LPSTR pszValue)
  195. {
  196. DWORD dwResult = 0;
  197. if((strlen(pszValue) != 38) || (pszValue[0] != '{') || (pszValue[37] != '}'))
  198. {
  199. dwResult = CLASSCHK_SOMETHINGODD;
  200. }
  201. else
  202. {
  203. // Check the internals of the GUID.
  204. if(!AllHexDigits(&pszValue[1],8) ||
  205. pszValue[9] != '-' ||
  206. !AllHexDigits(&pszValue[10],4) ||
  207. pszValue[14] != '-' ||
  208. !AllHexDigits(&pszValue[15],4) ||
  209. pszValue[19] != '-' ||
  210. !AllHexDigits(&pszValue[20],4) ||
  211. pszValue[24] != '-' ||
  212. !AllHexDigits(&pszValue[25],12) )
  213. {
  214. dwResult = CLASSCHK_SOMETHINGODD;
  215. }
  216. }
  217. return dwResult;
  218. }
  219. //+---------------------------------------------------------------------------
  220. //
  221. // Function: ReadRegistryGuid
  222. //
  223. // Synopsis: Read a GUID from the registry, and check to see if it is valid.
  224. //
  225. // Arguments: [hkey] -- Key to start from
  226. // [pszSubKeyName] -- Subkey relative to hkey (NULL if hkey)
  227. // [pszValueName] -- Value name (NULL if default value)
  228. // [pszValue] -- Pointer to return buffer
  229. // [pcbValue] -- Size of return buffer in characters
  230. //
  231. // Returns: ERROR_SUCCESS Read and verified GUID
  232. // (other) Something is wrong
  233. // ERROR_FILE_NOT_FOUND Key didn't exist
  234. // CLASSCHK_SOMETHINGODD Read key, but something is wrong with it.
  235. //
  236. // History: 5-31-95 kevinro Created
  237. //
  238. //----------------------------------------------------------------------------
  239. DWORD ReadRegistryGuid( HKEY hkey, LPSTR pszSubKeyName, LPSTR pszValueName, LPSTR pszValue, PULONG pcbValue)
  240. {
  241. DWORD dwResult;
  242. //
  243. // First, just read the value from the registry.
  244. //
  245. dwResult = ReadRegistryString(hkey, pszSubKeyName, pszValueName, pszValue, pcbValue);
  246. if(dwResult == ERROR_SUCCESS)
  247. {
  248. //
  249. // Do some additional basic checking, such as the length being correct, and the
  250. // GUID correctly formed.
  251. //
  252. dwResult = CheckForValidGuid(pszValue);
  253. if(dwResult != ERROR_SUCCESS)
  254. {
  255. VERBOSITY(VERB_LEVEL_ERROR,printf("*** Malformed GUID '%s' ***\n",pszValue);)
  256. }
  257. }
  258. return dwResult;
  259. }
  260. //+---------------------------------------------------------------------------
  261. //
  262. // Function: ReadRegistryFile
  263. //
  264. // Synopsis: Read a registry entry which is supposed to be a file,
  265. // and determine if the file exists
  266. //
  267. // Arguments: [hkey] -- Key to start from
  268. // [pszSubKeyName] -- Subkey relative to hkey (NULL if hkey)
  269. // [pszValueName] -- Value name (NULL if default value)
  270. // [pszValue] -- Pointer to return buffer
  271. // [pcbValue] -- Size of return buffer in characters
  272. //
  273. // Returns: ERROR_SUCCESS Read and verified
  274. // (other) Something is wrong
  275. // ERROR_FILE_NOT_FOUND Key didn't exist
  276. // CLASSCHK_SOMETHINGODD Read key, but something is wrong with it.
  277. //
  278. // History: 5-31-95 kevinro Created
  279. //
  280. //----------------------------------------------------------------------------
  281. ReadRegistryFile(HKEY hkey, LPSTR pszSubKeyName, LPSTR pszValueName, LPSTR pszValue, PULONG pcbValue)
  282. {
  283. DWORD dwResult;
  284. //
  285. // First, just read the value from the registry.
  286. //
  287. dwResult = ReadRegistryString(hkey, pszSubKeyName, pszValueName, pszValue, pcbValue);
  288. if (dwResult == ERROR_SUCCESS)
  289. {
  290. char achFoundPath[MAX_PATH];
  291. char *pszFileNamePart;
  292. //
  293. // Some apps append a switch at the end.
  294. // If there is a switch, terminate the path at that point
  295. //
  296. if(pszFileNamePart = strchr(pszValue,'/'))
  297. {
  298. *pszFileNamePart = 0;
  299. }
  300. else if(pszFileNamePart = strchr(pszValue,'-'))
  301. {
  302. //
  303. // Some applications also use the '-' character
  304. // as a switch delimiter. If this character is in
  305. // the string, and the previous character is a space,
  306. // then assume it is a delimiter. This isn't foolproof,
  307. // but it should work most of the time.
  308. //
  309. if(pszFileNamePart[-1] == ' ')
  310. {
  311. *pszFileNamePart = 0;
  312. }
  313. }
  314. if(SearchPath(NULL,pszValue,NULL,MAX_PATH,achFoundPath,&pszFileNamePart) == 0)
  315. {
  316. //
  317. // Didn't find the name in the path.
  318. //
  319. VERBOSITY(VERB_LEVEL_ERROR,printf("*** Could not find path '%s' ***\n",pszValue));
  320. dwResult = CLASSCHK_SOMETHINGODD;
  321. }
  322. else
  323. {
  324. VERBOSITY(VERB_LEVEL_TRACE,printf("Found path %s (%s)\n",achFoundPath,pszValue));
  325. }
  326. }
  327. return dwResult;
  328. }
  329. //+---------------------------------------------------------------------------
  330. //
  331. // Function: CheckProgID
  332. //
  333. // Synopsis:
  334. //
  335. // Given a PROGID entry, do some checking to insure the entry is sane. Of the things to check,
  336. // we should check to insure the name string is readable. If there is a CLSID entry, we should
  337. // check to see if it has a CLSID in it.
  338. //
  339. //
  340. // Arguments: [pszProgID] -- PROGID string to check
  341. //
  342. // History: 5-31-95 kevinro Created
  343. //
  344. // Notes:
  345. //
  346. //----------------------------------------------------------------------------
  347. DWORD CheckProgID(LPSTR pszProgID)
  348. {
  349. LONG lRetValue = 0;
  350. char achValue[MAX_PATH];
  351. DWORD cbValue;
  352. DWORD dwRetValue;
  353. HKEY progidKey = NULL;
  354. lRetValue = RegOpenKeyEx(HKEY_CLASSES_ROOT,
  355. pszProgID,
  356. NULL,
  357. KEY_READ,
  358. &progidKey);
  359. if(lRetValue == ERROR_SUCCESS)
  360. {
  361. cbValue = MAX_PATH;
  362. //
  363. // Read the default key value for the PROGID. Normally, this should be the name of the class name
  364. // or program name that would be shown.
  365. //
  366. dwRetValue = ReadRegistryString(progidKey,NULL,NULL,achValue,&cbValue);
  367. if(dwRetValue == CLASSCHK_SOMETHINGODD)
  368. {
  369. VERBOSITY(VERB_LEVEL_WHINE,printf("HKEY_CLASSES_ROOT\\%s found odd description '%s'\n",pszProgID,achValue);)
  370. }
  371. cbValue = MAX_PATH;
  372. dwRetValue = ReadRegistryGuid(progidKey,"CLSID",NULL,achValue,&cbValue);
  373. switch(dwRetValue)
  374. {
  375. case ERROR_SUCCESS:
  376. break;
  377. case ERROR_FILE_NOT_FOUND:
  378. // Not all of these entries will have a CLSID section
  379. break;
  380. default:
  381. VERBOSITY(VERB_LEVEL_ERROR,
  382. printf("*** Possible invalid CLSID in HKEY_CLASSES_ROOT\\%s\\CLSID\n",pszProgID);)
  383. }
  384. if(dwRetValue == ERROR_SUCCESS)
  385. {
  386. char achValue2[MAX_PATH];
  387. DWORD cbValue2 = MAX_PATH;
  388. //
  389. // We appear to have a valid CLSID. Check for existence of the CLSID. We are actually only
  390. // interested in it being found or not.
  391. //
  392. dwRetValue = ReadRegistryString(hkey_clsid,achValue,NULL,achValue2,&cbValue2);
  393. if(dwRetValue == ERROR_FILE_NOT_FOUND)
  394. {
  395. VERBOSITY(VERB_LEVEL_ERROR,printf("ProgID %s has CLSID %s. Unable to find HKEY_CLASSES_ROOT\\CLSID\\%s\n",pszProgID,achValue,achValue);)
  396. }
  397. }
  398. //
  399. // Check to see if there is an OLE 1.0 section that specifies protocol\stdfileediting\server
  400. //
  401. cbValue = MAX_PATH;
  402. dwRetValue = ReadRegistryFile(progidKey,"protocol\\stdfileediting\\server",NULL,achValue,&cbValue);
  403. if(dwRetValue == CLASSCHK_SOMETHINGODD)
  404. {
  405. VERBOSITY(VERB_LEVEL_ERROR,printf("HKEY_CLASSES_ROOT\\%s\\Protocol\\StdFileEditing\\server may have invalid entry\n",pszProgID);)
  406. }
  407. }
  408. else
  409. {
  410. VERBOSITY(VERB_LEVEL_WARN,printf("Unable to open HKEY_CLASSES_ROOT\\%s\n",pszProgID);)
  411. }
  412. if(progidKey != NULL)
  413. {
  414. RegCloseKey(progidKey);
  415. }
  416. return 0;
  417. }
  418. //
  419. // Given an extention (ie something that starts with a '.'), determine if the mapping to PROGID is correct
  420. //
  421. DWORD CheckExtentionToProgID(LPSTR pszExtention)
  422. {
  423. LONG lRetValue = 0;
  424. char achValue[MAX_PATH];
  425. DWORD cbValue;
  426. DWORD dwRetValue;
  427. HKEY subKey = NULL;
  428. HKEY progidKey = NULL;
  429. lRetValue = RegOpenKeyEx(HKEY_CLASSES_ROOT,
  430. pszExtention,
  431. NULL,
  432. KEY_READ,
  433. &subKey);
  434. if(lRetValue == ERROR_SUCCESS)
  435. {
  436. cbValue = MAX_PATH;
  437. //
  438. // Read the default key value for the extention. Normally, this should point to a
  439. // PROGID.
  440. //
  441. dwRetValue = ReadRegistryString(subKey,NULL,NULL,achValue,&cbValue);
  442. VERBOSITY(VERB_LEVEL_TRACE,printf("HKEY_CLASSES_ROOT\\%s = %s\n",pszExtention,achValue);)
  443. if(dwRetValue == CLASSCHK_SOMETHINGODD)
  444. {
  445. VERBOSITY(VERB_LEVEL_WARN,printf("Reading HKEY_CLASSES_ROOT\\%s found something odd\n",pszExtention);)
  446. }
  447. //
  448. // If it is an extension, the value should be a PROGID.Take a look to see if it is.
  449. //
  450. lRetValue = RegOpenKeyEx(HKEY_CLASSES_ROOT,
  451. achValue,
  452. NULL,
  453. KEY_READ,
  454. &progidKey);
  455. //
  456. // It should have been there. If it wasn't, then report a strangeness
  457. //
  458. switch(lRetValue)
  459. {
  460. case ERROR_SUCCESS:
  461. //
  462. // The PROGID actually existed. We will verify its contents later.
  463. //
  464. break;
  465. case ERROR_FILE_NOT_FOUND:
  466. //
  467. // The PROGID doesn't exist. This could be a potential problem in the registry. Report it.
  468. //
  469. VERBOSITY(VERB_LEVEL_ERROR,
  470. printf("HKEY_CLASSES_ROOT\\%s **** PROGID '%s' didn't exist ***\n*** Check Registry ***\n",
  471. pszExtention,
  472. achValue);)
  473. break;
  474. default:
  475. VERBOSITY(VERB_LEVEL_WARN,printf("Unexpected error opening HKEY_CLASSES_ROOT\\%s (error 0x%x)\n",achValue,lRetValue);)
  476. }
  477. }
  478. else
  479. {
  480. VERBOSITY(VERB_LEVEL_WARN,printf("Unable to open HKEY_CLASSES_ROOT\\%s\n",pszExtention);)
  481. }
  482. if(subKey != NULL) RegCloseKey(subKey);
  483. if(progidKey != NULL) RegCloseKey(progidKey);
  484. return 0;
  485. }
  486. //+---------------------------------------------------------------------------
  487. //
  488. // Function: CheckOLE1CLSID
  489. //
  490. // Synopsis: CheckOLE1CLSID looks at CLSID's that are typically OLE 1.0.
  491. // This includes checking for AutoConvert, checking the PROGID,
  492. // and checking that the Ole1Class key exists.
  493. //
  494. // Arguments: [pszCLSID] -- Name of OLE 1.0 CLASSID to check
  495. //
  496. // History: 5-31-95 kevinro Created
  497. //
  498. // Notes:
  499. //
  500. //----------------------------------------------------------------------------
  501. DWORD CheckOLE1CLSID(LPSTR pszCLSID)
  502. {
  503. LONG lRetValue = 0;
  504. char achValue[MAX_PATH];
  505. DWORD cbValue;
  506. DWORD dwRetValue;
  507. HKEY subKey = NULL;
  508. BOOL fFoundAServer = FALSE;
  509. char achValue2[MAX_PATH];
  510. DWORD cbValue2 = MAX_PATH;
  511. lRetValue = RegOpenKeyEx(hkey_clsid,pszCLSID,NULL,KEY_READ,&subKey);
  512. if(lRetValue != ERROR_SUCCESS)
  513. {
  514. VERBOSITY(VERB_LEVEL_ERROR,printf("Unable to open HKEY_CLASSES_ROOT\\CLSID\\%s error 0x%x\n",pszCLSID,lRetValue);)
  515. return CLASSCHK_SOMETHINGODD;
  516. }
  517. //
  518. // Check to insure that there is an Ole1Class entry
  519. //
  520. cbValue = MAX_PATH;
  521. dwRetValue = ReadRegistryString(subKey,"Ole1Class",NULL,achValue,&cbValue);
  522. switch(dwRetValue)
  523. {
  524. case ERROR_FILE_NOT_FOUND:
  525. VERBOSITY(VERB_LEVEL_WARN,printf("HKEY_CLASSES_ROOT\\CLSID\\%s is missing its Ole1Class key\n",pszCLSID);)
  526. break;
  527. case CLASSCHK_SOMETHINGODD:
  528. VERBOSITY(VERB_LEVEL_WARN,printf("HKEY_CLASSES_ROOT\\CLSID\\%s\\Ole1Class key is odd\n",pszCLSID);)
  529. }
  530. //
  531. // Quite often, there will be a AutoConvertTo key, which is supposed to be a GUID
  532. //
  533. cbValue = MAX_PATH;
  534. dwRetValue = ReadRegistryGuid(subKey,"AutoConvertTo",NULL,achValue,&cbValue);
  535. switch(dwRetValue)
  536. {
  537. case ERROR_SUCCESS:
  538. //
  539. // The CLSID should normally point to another class, such as the 2.0 version. Check to
  540. // insure the CLSID exists
  541. //
  542. dwRetValue = ReadRegistryString(hkey_clsid,achValue,NULL,achValue2,&cbValue2);
  543. if(dwRetValue != ERROR_SUCCESS)
  544. {
  545. VERBOSITY(VERB_LEVEL_WARN,printf("HKEY_CLASSES_ROOT\\CLSID\\%s\\AutoConvertTo key is odd\n",pszCLSID);)
  546. switch(dwRetValue)
  547. {
  548. case ERROR_FILE_NOT_FOUND:
  549. VERBOSITY(VERB_LEVEL_WARN,printf("HKEY_CLASSES_ROOT\\CLSID\\%s doesn't appear to exist\n",achValue);)
  550. break;
  551. }
  552. }
  553. break;
  554. case ERROR_FILE_NOT_FOUND:
  555. //
  556. // This entry isn't required. If it doesn't exist, no big deal.
  557. //
  558. break;
  559. case CLASSCHK_SOMETHINGODD:
  560. VERBOSITY(VERB_LEVEL_WARN,printf("HKEY_CLASSES_ROOT\\CLSID\\%s\\AutoConvertTo key is odd\n",pszCLSID);)
  561. break;
  562. }
  563. //
  564. // It would be abnormal to find a missing PROGID key
  565. //
  566. cbValue = MAX_PATH;
  567. dwRetValue = ReadRegistryString(subKey,"ProgID",NULL,achValue,&cbValue);
  568. switch(dwRetValue)
  569. {
  570. case ERROR_SUCCESS:
  571. //
  572. // The PROGID should normally point to a valid PROGID
  573. //
  574. cbValue2 = MAX_PATH;
  575. dwRetValue = ReadRegistryString(HKEY_CLASSES_ROOT,achValue,NULL,achValue2,&cbValue2);
  576. if(dwRetValue != ERROR_SUCCESS)
  577. {
  578. VERBOSITY(VERB_LEVEL_ERROR,printf("HKEY_CLASSES_ROOT\\CLSID\\%s\\PROGID key is odd\n",pszCLSID);)
  579. switch(dwRetValue)
  580. {
  581. case ERROR_FILE_NOT_FOUND:
  582. VERBOSITY(VERB_LEVEL_ERROR,printf("HKEY_CLASSES_ROOT\\%s doesn't appear to exist\n",achValue);)
  583. break;
  584. }
  585. }
  586. break;
  587. case ERROR_FILE_NOT_FOUND:
  588. //
  589. // This entry is required.
  590. //
  591. VERBOSITY(VERB_LEVEL_ERROR,printf("HKEY_CLASSES_ROOT\\CLSID\\%s\\PROGID key is missing\n",pszCLSID);)
  592. break;
  593. case CLASSCHK_SOMETHINGODD:
  594. VERBOSITY(VERB_LEVEL_WARN,printf("HKEY_CLASSES_ROOT\\CLSID\\%s\\Ole1Class key is odd\n",pszCLSID);)
  595. }
  596. return dwRetValue;
  597. }
  598. //+---------------------------------------------------------------------------
  599. //
  600. // Function: CheckForDLL
  601. //
  602. // Synopsis: Given a CLSID, its hkey, and the name of the DLL value,
  603. // determine if the DLL exists as a file, and if the threading
  604. // model value is appropriate.
  605. //
  606. // Arguments: [pszCLSID] -- Name of the CLSID (for debug output)
  607. // [hkeyCLSID] -- HKEY for the clsid
  608. // [pszDLLKey] -- Name of the subkey to check for
  609. //
  610. // Requires:
  611. //
  612. // Returns:
  613. //
  614. // Signals:
  615. //
  616. // Modifies:
  617. //
  618. // Algorithm:
  619. //
  620. // History: 5-31-95 kevinro Created
  621. //
  622. // Notes:
  623. //
  624. //----------------------------------------------------------------------------
  625. DWORD CheckForDLL(LPSTR pszCLSID, HKEY hkeyCLSID, LPSTR pszDLLKey)
  626. {
  627. LONG lRetValue = 0;
  628. char achValue[MAX_PATH];
  629. DWORD cbValue;
  630. DWORD dwRetValue;
  631. char achThreadModel[MAX_PATH];
  632. DWORD cbThreadModel = MAX_PATH;
  633. //
  634. // The DLL name should be in
  635. //
  636. cbValue = MAX_PATH;
  637. dwRetValue = ReadRegistryFile(hkeyCLSID,pszDLLKey,NULL,achValue,&cbValue);
  638. switch(dwRetValue)
  639. {
  640. case ERROR_FILE_NOT_FOUND:
  641. //
  642. // The registry key didn't exist. Thats normally OK.
  643. //
  644. return dwRetValue;
  645. case CLASSCHK_SOMETHINGODD:
  646. VERBOSITY(VERB_LEVEL_WARN,printf("HKEY_CLASSES_ROOT\\CLSID\\%s\\%s key is odd\n",pszCLSID,pszDLLKey);)
  647. return dwRetValue;
  648. }
  649. //
  650. // If the DLL exists, check to see if the ThreadingModelKey is valid
  651. //
  652. dwRetValue = ReadRegistryString(hkeyCLSID,pszDLLKey,"ThreadingModel",achThreadModel,&cbThreadModel);
  653. switch(dwRetValue)
  654. {
  655. case ERROR_FILE_NOT_FOUND:
  656. //
  657. // The registry key didn't exist. Thats normally OK.
  658. //
  659. return ERROR_SUCCESS;
  660. case CLASSCHK_SOMETHINGODD:
  661. VERBOSITY(VERB_LEVEL_WARN,printf("HKEY_CLASSES_ROOT\\CLSID\\%s\\%s\\ThreadingModel value is odd\n",pszCLSID,pszDLLKey);)
  662. return dwRetValue;
  663. }
  664. //
  665. // Check to insure the threading model is something we understand
  666. //
  667. if( StrICmp( achThreadModel, "Apartment") &&
  668. StrICmp( achThreadModel, "Both") &&
  669. StrICmp( achThreadModel, "Free"))
  670. {
  671. VERBOSITY(VERB_LEVEL_WARN,printf("HKEY_CLASSES_ROOT\\CLSID\\%s\\%s\\ThreadingModel value is %s\n",pszCLSID,pszDLLKey,achThreadModel);)
  672. VERBOSITY(VERB_LEVEL_WARN,printf("Expected 'Apartment','Both', or 'Free'");)
  673. return CLASSCHK_SOMETHINGODD;
  674. }
  675. return ERROR_SUCCESS;
  676. }
  677. //+---------------------------------------------------------------------------
  678. //
  679. // Function: CheckCLSIDEntry
  680. //
  681. // Synopsis: Given the name of a CLSID entry, verify that the entry is
  682. // valid by looking for the 'usual' key information, and
  683. // cross checking it against things that we assert should be
  684. // true.
  685. // Effects:
  686. //
  687. // Arguments: [pszCLSID] -- CLSID in a string form. Used to open key
  688. //
  689. // History: 5-31-95 kevinro Created
  690. //
  691. // Notes:
  692. //
  693. //----------------------------------------------------------------------------
  694. DWORD CheckCLSIDEntry(LPSTR pszCLSID)
  695. {
  696. LONG lRetValue = 0;
  697. char achValue[MAX_PATH];
  698. DWORD cbValue;
  699. char achPROGID[MAX_PATH];
  700. DWORD cbPROGID = MAX_PATH;
  701. char achPROGIDPath[MAX_PATH];
  702. DWORD cbPROGIDPath = MAX_PATH;
  703. char achPROGIDValue[MAX_PATH];
  704. DWORD cbPROGIDValue = MAX_PATH;
  705. LPSTR pszLocalServer = "LocalServer32";
  706. DWORD dwRetValue;
  707. HKEY subKey = NULL;
  708. BOOL fFoundAServer = FALSE;
  709. lRetValue = RegOpenKeyEx(hkey_clsid,
  710. pszCLSID,
  711. NULL,
  712. KEY_READ,
  713. &subKey);
  714. if(lRetValue != ERROR_SUCCESS)
  715. {
  716. VERBOSITY(VERB_LEVEL_ERROR,printf("Unable to open HKEY_CLASSES_ROOT\\CLSID\\%s error 0x%x\n",pszCLSID,lRetValue);)
  717. return CLASSCHK_SOMETHINGODD;
  718. }
  719. //
  720. // Basic sanity check: Is the description string a valid string
  721. //
  722. cbValue = MAX_PATH;
  723. dwRetValue = ReadRegistryString(subKey,NULL,NULL,achValue,&cbValue);
  724. if(dwRetValue != ERROR_SUCCESS)
  725. {
  726. VERBOSITY(VERB_LEVEL_WHINE,printf("HKEY_CLASSES_ROOT\\CLSID\\%s has odd description string\n",pszCLSID);)
  727. }
  728. //
  729. // A CLSID entry typically has several values. Least of which is supposed to be one or more of the following:
  730. // InprocHandler
  731. // InprocServer
  732. // LocalServer
  733. // InprocHandler32
  734. // InprocServer32
  735. // LocalServer32
  736. //
  737. // It may also have an optional PROGID entry, which we can use to verify that that the LocalServer and
  738. // the protocol\StdFileEditing\server entries match.
  739. //
  740. // Another couple of things to watch for include checking the Inproc entries for ThreadingModel,
  741. //
  742. // Yet another thing to look at is the value of the CLSID itself. If the first 4 digits are 0003 or 0004, then
  743. // the CLSID is for an OLE 1.0 class, and we need to do a seperate check
  744. //
  745. if((strncmp(&pszCLSID[1],"0003",4) == 0) || (strncmp(&pszCLSID[1],"0004",4) == 0) )
  746. {
  747. //
  748. // In theory, this is supposed to be an OLE1CLASS. Check it seperately
  749. //
  750. RegCloseKey(subKey);
  751. return CheckOLE1CLSID(pszCLSID);
  752. }
  753. dwRetValue = CheckForDLL(pszCLSID, subKey, "InprocHandler");
  754. dwRetValue = CheckForDLL(pszCLSID, subKey, "InprocHandler32");
  755. dwRetValue = CheckForDLL(pszCLSID, subKey, "InprocServer");
  756. if(dwRetValue != ERROR_FILE_NOT_FOUND) fFoundAServer++;
  757. dwRetValue = CheckForDLL(pszCLSID, subKey, "InprocServer32");
  758. if(dwRetValue != ERROR_FILE_NOT_FOUND) fFoundAServer++;
  759. //
  760. // First, check for LocalServer32. If that doesn't exist, then try for
  761. // LocalServer.
  762. //
  763. cbValue = MAX_PATH;
  764. dwRetValue = ReadRegistryFile(subKey,pszLocalServer,NULL,achValue,&cbValue);
  765. if(dwRetValue == ERROR_FILE_NOT_FOUND)
  766. {
  767. cbValue = MAX_PATH;
  768. pszLocalServer = "LocalServer";
  769. dwRetValue = ReadRegistryFile(subKey,pszLocalServer,NULL,achValue,&cbValue);
  770. if(dwRetValue == CLASSCHK_SOMETHINGODD)
  771. {
  772. VERBOSITY(VERB_LEVEL_WARN,printf("HKEY_CLASSES_ROOT\\CLSID\\%s\\LocalServer32 is odd\n",pszCLSID);)
  773. }
  774. }
  775. else if(dwRetValue == CLASSCHK_SOMETHINGODD)
  776. {
  777. VERBOSITY(VERB_LEVEL_WARN,printf("HKEY_CLASSES_ROOT\\CLSID\\%s\\%s is odd\n",pszCLSID,pszLocalServer);)
  778. }
  779. if(dwRetValue == ERROR_SUCCESS)
  780. {
  781. fFoundAServer++;
  782. //
  783. // We have a valid LocalServer. Lets get the PROGID's version of the local server, and compare the
  784. // two. They should compare.
  785. //
  786. dwRetValue = ReadRegistryString(subKey,"PROGID",NULL,achPROGID,&cbPROGID);
  787. switch(dwRetValue)
  788. {
  789. case ERROR_FILE_NOT_FOUND:
  790. //
  791. // Most CLSID's should indeed have a PROGID, but it isn't 100% required.
  792. //
  793. VERBOSITY(VERB_LEVEL_WHINE,printf("HKEY_CLASSES_ROOT\\CLSID\\%s missing a PROGID entry\n",pszCLSID);)
  794. break;
  795. case CLASSCHK_SOMETHINGODD:
  796. VERBOSITY(VERB_LEVEL_WARN,printf("HKEY_CLASSES_ROOT\\CLSID\\%s PROGID entry is odd\n",pszCLSID);)
  797. break;
  798. default:
  799. sprintf(achPROGIDPath,"%s\\protocol\\stdfileediting\\server",achPROGID);
  800. dwRetValue = ReadRegistryFile(HKEY_CLASSES_ROOT,achPROGIDPath,NULL,achPROGIDValue,&cbPROGIDValue);
  801. //
  802. // The only thing we are interested in checking is if the two strings compare.
  803. //
  804. if(dwRetValue == ERROR_SUCCESS)
  805. {
  806. if(StrICmp(achPROGIDValue,achValue) != 0)
  807. {
  808. VERBOSITY(VERB_LEVEL_ERROR,printf("HKEY_CLASSES_ROOT\\CLSID\\%s is inconsistent with its PROGID\n",pszCLSID);)
  809. VERBOSITY(VERB_LEVEL_ERROR,printf("HKEY_CLASSES_ROOT\\%s = '%s'\n",achPROGIDPath,achPROGIDValue);)
  810. VERBOSITY(VERB_LEVEL_ERROR,printf("HKEY_CLASSES_ROOT\\CLSID\\%s\\%s = '%s'\n",pszCLSID,pszLocalServer,achValue);)
  811. }
  812. }
  813. }
  814. }
  815. if(!fFoundAServer)
  816. {
  817. VERBOSITY(VERB_LEVEL_ERROR,printf("*** Unable to find a valid server for HKEY_CLASSES_ROOT\\CLSID\\%s ***\n",pszCLSID);)
  818. }
  819. RegCloseKey(subKey);
  820. return dwRetValue;
  821. }
  822. //+---------------------------------------------------------------------------
  823. //
  824. // Function: EnumerateClsidRoot
  825. //
  826. // Synopsis: Enumerate and check each entry in the CLSID section
  827. //
  828. // History: 5-31-95 kevinro Created
  829. //
  830. // Notes:
  831. //
  832. //----------------------------------------------------------------------------
  833. DWORD EnumerateClsidRoot()
  834. {
  835. LONG lRetValue = 0;
  836. DWORD iSubKey = 0;
  837. char achKeyName[MAX_PATH];
  838. DWORD cbKeyName;
  839. FILETIME filetime;
  840. while(1)
  841. {
  842. cbKeyName = MAX_PATH;
  843. lRetValue = RegEnumKeyEx(hkey_clsid,
  844. iSubKey,
  845. achKeyName,
  846. &cbKeyName,
  847. NULL,
  848. NULL,
  849. NULL,
  850. &filetime);
  851. if(lRetValue == ERROR_NO_MORE_ITEMS)
  852. {
  853. // End of enumeration
  854. break;
  855. }
  856. if(lRetValue != ERROR_SUCCESS)
  857. {
  858. VERBOSITY(VERB_LEVEL_ERROR,printf("EnumerateClsidRoot:RegEnumKeyEx returned %x\n",lRetValue);)
  859. return CLASSCHK_ERROR;
  860. }
  861. //
  862. // Each of the sub keys enumerated here is expected to be a GUID. If it isn't a GUID, then it
  863. // might be some other random garbage that we don't care about.
  864. //
  865. if(CheckForValidGuid(achKeyName) == ERROR_SUCCESS)
  866. {
  867. CheckCLSIDEntry(achKeyName);
  868. }
  869. iSubKey++;
  870. }
  871. return 0;
  872. }
  873. //+---------------------------------------------------------------------------
  874. //
  875. // Function: EnumerateClassesRoot
  876. //
  877. // Synopsis: Enumerate the root of HKEY_CLASSES, and check each entry
  878. // based on what we think it should be.
  879. //
  880. // History: 5-31-95 kevinro Created
  881. //
  882. // Notes:
  883. //
  884. //----------------------------------------------------------------------------
  885. DWORD EnumerateClassesRoot()
  886. {
  887. LONG lRetValue = 0;
  888. DWORD iSubKey = 0;
  889. char achKeyName[MAX_PATH];
  890. DWORD cbKeyName;
  891. FILETIME filetime;
  892. while(1)
  893. {
  894. cbKeyName = MAX_PATH;
  895. lRetValue = RegEnumKeyEx(HKEY_CLASSES_ROOT,
  896. iSubKey,
  897. achKeyName,
  898. &cbKeyName,
  899. NULL,
  900. NULL,
  901. NULL,
  902. &filetime);
  903. if(lRetValue == ERROR_NO_MORE_ITEMS)
  904. {
  905. // End of enumeration
  906. break;
  907. }
  908. if(lRetValue != ERROR_SUCCESS)
  909. {
  910. VERBOSITY(VERB_LEVEL_ERROR,printf("EnumerateClassesRoot:RegEnumKeyEx returned %x\n",lRetValue);)
  911. return CLASSCHK_ERROR;
  912. }
  913. //
  914. // We expect to find two basic things at the HKEY_CLASSES_ROOT level.
  915. // First are extention mappings, second are PROGID's. There is also
  916. // the special cases of CLSID, Interface, and FileType, which are
  917. // checked differently
  918. //
  919. if(StrICmp(achKeyName,"CLSID") == 0)
  920. {
  921. // The CLSID section is done later
  922. }
  923. else if(StrICmp(achKeyName,"Interface") == 0)
  924. {
  925. // The interface section is done later
  926. }
  927. else if(StrICmp(achKeyName,"FileType") == 0)
  928. {
  929. // The FileType entry is done later
  930. }
  931. else if(achKeyName[0] == '.')
  932. {
  933. // File extentions start with dots.
  934. // Call the Check Extention function here
  935. CheckExtentionToProgID(achKeyName);
  936. }
  937. else
  938. {
  939. // Assume it may be a PROGID. Call the PROGID function here.
  940. CheckProgID(achKeyName);
  941. }
  942. iSubKey++;
  943. }
  944. return 0;
  945. }
  946. //+---------------------------------------------------------------------------
  947. //
  948. // Function: main
  949. //
  950. // Synopsis: Main entry point for program. Does what main entry points
  951. // usually do.
  952. //
  953. // Arguments: [argc] --
  954. // [argv] --
  955. //
  956. // History: 5-31-95 kevinro Created
  957. //
  958. // Notes:
  959. //
  960. //----------------------------------------------------------------------------
  961. int _cdecl main(int argc, char *argv[])
  962. {
  963. LONG lRetValue = 0;
  964. //
  965. // HKEY_CLASSES_ROOT is often used.
  966. //
  967. lRetValue = RegOpenKeyEx(HKEY_CLASSES_ROOT,
  968. "CLSID",
  969. NULL,
  970. KEY_READ,
  971. &hkey_clsid);
  972. if(lRetValue != ERROR_SUCCESS)
  973. {
  974. printf("Couldn't open HKEY_CLASSES_ROOT\\CLSID\n");
  975. return(1);
  976. }
  977. //
  978. // Enumerate different parts of the registry, reporting
  979. // errors as we go.
  980. //
  981. EnumerateClassesRoot();
  982. EnumerateClsidRoot();
  983. RegCloseKey(hkey_clsid);
  984. return 0;
  985. }