windows-nt/Source/XPSP1/NT/com/ole32/olethunk/tools/clschk/classchk.cxx
2020-09-26 16:20:57 +08:00

1115 lines
29 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1993.
//
// File: classchk.cxx
//
// Classchk is a program for verifying that the contents of the registry are
// OKY-DOKY as far as OLE is concerned.
//
// In general, we verify that all CLSID's are of the correct length, all string
// parameters are NULL terminated.
//
// There are several phases of checking.
//
// 1) Checking that PROGID entries that have CLSID sections match.
// 2) Checking that PROGID entries have correct and existing protocol entries
// 3) Checking that PROGID entries
//
// History: 5-31-95 kevinro Created
//
//----------------------------------------------------------------------------
#include <windows.h>
#include <stdio.h>
#include <ctype.h>
#include "classchk.h"
//
// The following registry values are used quite a few times in this program.
// These global variables keep us from needing to open them constantly.
//
HKEY hkey_clsid = 0;
DWORD g_VerbosityLevel = VERB_LEVEL_WARN | VERB_LEVEL_ERROR;
#define StrICmp(x,y) (CompareString(LOCALE_USER_DEFAULT,NORM_IGNORECASE,x,-1,y,-1) - 2)
//+---------------------------------------------------------------------------
//
// Function: ReadRegistryString
//
// Synopsis: Reads a string from the registry
//
// Effects:
//
// This function reads in a string from the registry, and does some basic
// consistency checking on it, such as verifying the length and NULL
// terminatation.
//
//
// Arguments: [hkeyRoot] --
// [pszSubKeyName] --
// [pszValueName] --
// [pszValue] --
// [pcbValue] --
//
// Returns: ERROR_SUCCESS Everything peachy
// ERROR_FILE_NOT_FOUND Couldn't read entry from registry
// CLASSCHK_SOMETHINGODD Something about the string is wrong
// (other) Return value from registry
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: 5-31-95 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
DWORD ReadRegistryString( HKEY hkeyRoot, LPSTR pszSubKeyName, LPSTR pszValueName, LPSTR pszValue, PULONG pcbValue)
{
LONG lRetValue;
DWORD dwType;
DWORD dwReturn = ERROR_SUCCESS;
HKEY hkey = hkeyRoot;
if(pszSubKeyName != NULL)
{
lRetValue = RegOpenKeyEx(hkeyRoot,
pszSubKeyName,
NULL,
KEY_READ,
&hkey);
//
// It is common to see keys that don't exist. Let the caller decide if it is
// important or not.
//
if(lRetValue != ERROR_SUCCESS)
{
VERBOSITY(VERB_LEVEL_TRACE,printf("Unable to open subkey %s to read value %s\n",pszSubKeyName,pszValueName););
return lRetValue;
}
}
lRetValue = RegQueryValueEx(hkey,
pszValueName,
NULL, // Must be NULL according to spec
&dwType,
(BYTE *)pszValue,
pcbValue);
if(hkeyRoot != hkey)
{
//
// Always close the new subkey if we opened it
//
RegCloseKey(hkey);
}
switch(lRetValue)
{
case ERROR_SUCCESS:
//
// Read the value, everything A-OK
//
break;
case ERROR_MORE_DATA:
VERBOSITY(VERB_LEVEL_WARN,printf("The value '%s' is larger than expected. May be a problem\n",pszValueName);)
return lRetValue;
break;
case ERROR_FILE_NOT_FOUND:
//
// This may be expected, so don't report any errors. Let the caller handle that
//
return lRetValue;
default:
VERBOSITY(VERB_LEVEL_WARN,printf("RegQueryValueEx() for '%s' returned unexpected error 0x%x\n",pszValueName,lRetValue);)
return lRetValue;
}
//
// We expect this type to be REG_SZ. If it isn't, complain
//
if(dwType != REG_SZ)
{
VERBOSITY(VERB_LEVEL_WARN,printf("The value for '%s' is type 0x%x, was expecting REG_SZ (0x%x)\n",pszValueName,dwType,REG_SZ);)
dwReturn = CLASSCHK_SOMETHINGODD;
}
//
// We expect the value to be NULL terminated
//
if(pszValue[(*pcbValue)-1] != 0)
{
VERBOSITY(VERB_LEVEL_WARN,printf("The value for '%s' may not be NULL terminated.\n",pszValueName);)
}
//
// We expect strlen to be the same as the length returned (which includes the NULL)
//
if((strlen(pszValue) + 1) != *pcbValue)
{
VERBOSITY(VERB_LEVEL_WARN,printf("The string value for '%s' may not have the correct length\n",pszValueName);)
if(dwType == REG_SZ)
{
VERBOSITY(VERB_LEVEL_WARN,printf("The string value is '%s'\n",pszValue);)
}
dwReturn = CLASSCHK_SOMETHINGODD;
}
return dwReturn;
}
//+---------------------------------------------------------------------------
//
// Function: AllHexDigits
//
// Synopsis: Verify that all of the digits specified are hexidecimal
//
// Effects:
//
// Arguments: [pszString] -- String to check
// [chString] -- Number of characters
//
// Requires:
//
// Returns: TRUE All hex digits
// FALSE Not all hex digits
//
// History: 5-31-95 kevinro Created
//----------------------------------------------------------------------------
BOOL AllHexDigits(LPSTR pszString, ULONG chString)
{
while(chString--)
{
if(!isxdigit(pszString[chString]))
{
return FALSE;
}
}
return TRUE;
}
//+---------------------------------------------------------------------------
//
// Function: CheckForValidGuid
//
// Synopsis: Given a string, determine if the string is a GUID
//
// Arguments: [pszValue] -- String to check
//
// History: 5-31-95 kevinro Created
//
//----------------------------------------------------------------------------
DWORD CheckForValidGuid(LPSTR pszValue)
{
DWORD dwResult = 0;
if((strlen(pszValue) != 38) || (pszValue[0] != '{') || (pszValue[37] != '}'))
{
dwResult = CLASSCHK_SOMETHINGODD;
}
else
{
// Check the internals of the GUID.
if(!AllHexDigits(&pszValue[1],8) ||
pszValue[9] != '-' ||
!AllHexDigits(&pszValue[10],4) ||
pszValue[14] != '-' ||
!AllHexDigits(&pszValue[15],4) ||
pszValue[19] != '-' ||
!AllHexDigits(&pszValue[20],4) ||
pszValue[24] != '-' ||
!AllHexDigits(&pszValue[25],12) )
{
dwResult = CLASSCHK_SOMETHINGODD;
}
}
return dwResult;
}
//+---------------------------------------------------------------------------
//
// Function: ReadRegistryGuid
//
// Synopsis: Read a GUID from the registry, and check to see if it is valid.
//
// Arguments: [hkey] -- Key to start from
// [pszSubKeyName] -- Subkey relative to hkey (NULL if hkey)
// [pszValueName] -- Value name (NULL if default value)
// [pszValue] -- Pointer to return buffer
// [pcbValue] -- Size of return buffer in characters
//
// Returns: ERROR_SUCCESS Read and verified GUID
// (other) Something is wrong
// ERROR_FILE_NOT_FOUND Key didn't exist
// CLASSCHK_SOMETHINGODD Read key, but something is wrong with it.
//
// History: 5-31-95 kevinro Created
//
//----------------------------------------------------------------------------
DWORD ReadRegistryGuid( HKEY hkey, LPSTR pszSubKeyName, LPSTR pszValueName, LPSTR pszValue, PULONG pcbValue)
{
DWORD dwResult;
//
// First, just read the value from the registry.
//
dwResult = ReadRegistryString(hkey, pszSubKeyName, pszValueName, pszValue, pcbValue);
if(dwResult == ERROR_SUCCESS)
{
//
// Do some additional basic checking, such as the length being correct, and the
// GUID correctly formed.
//
dwResult = CheckForValidGuid(pszValue);
if(dwResult != ERROR_SUCCESS)
{
VERBOSITY(VERB_LEVEL_ERROR,printf("*** Malformed GUID '%s' ***\n",pszValue);)
}
}
return dwResult;
}
//+---------------------------------------------------------------------------
//
// Function: ReadRegistryFile
//
// Synopsis: Read a registry entry which is supposed to be a file,
// and determine if the file exists
//
// Arguments: [hkey] -- Key to start from
// [pszSubKeyName] -- Subkey relative to hkey (NULL if hkey)
// [pszValueName] -- Value name (NULL if default value)
// [pszValue] -- Pointer to return buffer
// [pcbValue] -- Size of return buffer in characters
//
// Returns: ERROR_SUCCESS Read and verified
// (other) Something is wrong
// ERROR_FILE_NOT_FOUND Key didn't exist
// CLASSCHK_SOMETHINGODD Read key, but something is wrong with it.
//
// History: 5-31-95 kevinro Created
//
//----------------------------------------------------------------------------
ReadRegistryFile(HKEY hkey, LPSTR pszSubKeyName, LPSTR pszValueName, LPSTR pszValue, PULONG pcbValue)
{
DWORD dwResult;
//
// First, just read the value from the registry.
//
dwResult = ReadRegistryString(hkey, pszSubKeyName, pszValueName, pszValue, pcbValue);
if (dwResult == ERROR_SUCCESS)
{
char achFoundPath[MAX_PATH];
char *pszFileNamePart;
//
// Some apps append a switch at the end.
// If there is a switch, terminate the path at that point
//
if(pszFileNamePart = strchr(pszValue,'/'))
{
*pszFileNamePart = 0;
}
else if(pszFileNamePart = strchr(pszValue,'-'))
{
//
// Some applications also use the '-' character
// as a switch delimiter. If this character is in
// the string, and the previous character is a space,
// then assume it is a delimiter. This isn't foolproof,
// but it should work most of the time.
//
if(pszFileNamePart[-1] == ' ')
{
*pszFileNamePart = 0;
}
}
if(SearchPath(NULL,pszValue,NULL,MAX_PATH,achFoundPath,&pszFileNamePart) == 0)
{
//
// Didn't find the name in the path.
//
VERBOSITY(VERB_LEVEL_ERROR,printf("*** Could not find path '%s' ***\n",pszValue));
dwResult = CLASSCHK_SOMETHINGODD;
}
else
{
VERBOSITY(VERB_LEVEL_TRACE,printf("Found path %s (%s)\n",achFoundPath,pszValue));
}
}
return dwResult;
}
//+---------------------------------------------------------------------------
//
// Function: CheckProgID
//
// Synopsis:
//
// Given a PROGID entry, do some checking to insure the entry is sane. Of the things to check,
// we should check to insure the name string is readable. If there is a CLSID entry, we should
// check to see if it has a CLSID in it.
//
//
// Arguments: [pszProgID] -- PROGID string to check
//
// History: 5-31-95 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
DWORD CheckProgID(LPSTR pszProgID)
{
LONG lRetValue = 0;
char achValue[MAX_PATH];
DWORD cbValue;
DWORD dwRetValue;
HKEY progidKey = NULL;
lRetValue = RegOpenKeyEx(HKEY_CLASSES_ROOT,
pszProgID,
NULL,
KEY_READ,
&progidKey);
if(lRetValue == ERROR_SUCCESS)
{
cbValue = MAX_PATH;
//
// Read the default key value for the PROGID. Normally, this should be the name of the class name
// or program name that would be shown.
//
dwRetValue = ReadRegistryString(progidKey,NULL,NULL,achValue,&cbValue);
if(dwRetValue == CLASSCHK_SOMETHINGODD)
{
VERBOSITY(VERB_LEVEL_WHINE,printf("HKEY_CLASSES_ROOT\\%s found odd description '%s'\n",pszProgID,achValue);)
}
cbValue = MAX_PATH;
dwRetValue = ReadRegistryGuid(progidKey,"CLSID",NULL,achValue,&cbValue);
switch(dwRetValue)
{
case ERROR_SUCCESS:
break;
case ERROR_FILE_NOT_FOUND:
// Not all of these entries will have a CLSID section
break;
default:
VERBOSITY(VERB_LEVEL_ERROR,
printf("*** Possible invalid CLSID in HKEY_CLASSES_ROOT\\%s\\CLSID\n",pszProgID);)
}
if(dwRetValue == ERROR_SUCCESS)
{
char achValue2[MAX_PATH];
DWORD cbValue2 = MAX_PATH;
//
// We appear to have a valid CLSID. Check for existence of the CLSID. We are actually only
// interested in it being found or not.
//
dwRetValue = ReadRegistryString(hkey_clsid,achValue,NULL,achValue2,&cbValue2);
if(dwRetValue == ERROR_FILE_NOT_FOUND)
{
VERBOSITY(VERB_LEVEL_ERROR,printf("ProgID %s has CLSID %s. Unable to find HKEY_CLASSES_ROOT\\CLSID\\%s\n",pszProgID,achValue,achValue);)
}
}
//
// Check to see if there is an OLE 1.0 section that specifies protocol\stdfileediting\server
//
cbValue = MAX_PATH;
dwRetValue = ReadRegistryFile(progidKey,"protocol\\stdfileediting\\server",NULL,achValue,&cbValue);
if(dwRetValue == CLASSCHK_SOMETHINGODD)
{
VERBOSITY(VERB_LEVEL_ERROR,printf("HKEY_CLASSES_ROOT\\%s\\Protocol\\StdFileEditing\\server may have invalid entry\n",pszProgID);)
}
}
else
{
VERBOSITY(VERB_LEVEL_WARN,printf("Unable to open HKEY_CLASSES_ROOT\\%s\n",pszProgID);)
}
if(progidKey != NULL)
{
RegCloseKey(progidKey);
}
return 0;
}
//
// Given an extention (ie something that starts with a '.'), determine if the mapping to PROGID is correct
//
DWORD CheckExtentionToProgID(LPSTR pszExtention)
{
LONG lRetValue = 0;
char achValue[MAX_PATH];
DWORD cbValue;
DWORD dwRetValue;
HKEY subKey = NULL;
HKEY progidKey = NULL;
lRetValue = RegOpenKeyEx(HKEY_CLASSES_ROOT,
pszExtention,
NULL,
KEY_READ,
&subKey);
if(lRetValue == ERROR_SUCCESS)
{
cbValue = MAX_PATH;
//
// Read the default key value for the extention. Normally, this should point to a
// PROGID.
//
dwRetValue = ReadRegistryString(subKey,NULL,NULL,achValue,&cbValue);
VERBOSITY(VERB_LEVEL_TRACE,printf("HKEY_CLASSES_ROOT\\%s = %s\n",pszExtention,achValue);)
if(dwRetValue == CLASSCHK_SOMETHINGODD)
{
VERBOSITY(VERB_LEVEL_WARN,printf("Reading HKEY_CLASSES_ROOT\\%s found something odd\n",pszExtention);)
}
//
// If it is an extension, the value should be a PROGID.Take a look to see if it is.
//
lRetValue = RegOpenKeyEx(HKEY_CLASSES_ROOT,
achValue,
NULL,
KEY_READ,
&progidKey);
//
// It should have been there. If it wasn't, then report a strangeness
//
switch(lRetValue)
{
case ERROR_SUCCESS:
//
// The PROGID actually existed. We will verify its contents later.
//
break;
case ERROR_FILE_NOT_FOUND:
//
// The PROGID doesn't exist. This could be a potential problem in the registry. Report it.
//
VERBOSITY(VERB_LEVEL_ERROR,
printf("HKEY_CLASSES_ROOT\\%s **** PROGID '%s' didn't exist ***\n*** Check Registry ***\n",
pszExtention,
achValue);)
break;
default:
VERBOSITY(VERB_LEVEL_WARN,printf("Unexpected error opening HKEY_CLASSES_ROOT\\%s (error 0x%x)\n",achValue,lRetValue);)
}
}
else
{
VERBOSITY(VERB_LEVEL_WARN,printf("Unable to open HKEY_CLASSES_ROOT\\%s\n",pszExtention);)
}
if(subKey != NULL) RegCloseKey(subKey);
if(progidKey != NULL) RegCloseKey(progidKey);
return 0;
}
//+---------------------------------------------------------------------------
//
// Function: CheckOLE1CLSID
//
// Synopsis: CheckOLE1CLSID looks at CLSID's that are typically OLE 1.0.
// This includes checking for AutoConvert, checking the PROGID,
// and checking that the Ole1Class key exists.
//
// Arguments: [pszCLSID] -- Name of OLE 1.0 CLASSID to check
//
// History: 5-31-95 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
DWORD CheckOLE1CLSID(LPSTR pszCLSID)
{
LONG lRetValue = 0;
char achValue[MAX_PATH];
DWORD cbValue;
DWORD dwRetValue;
HKEY subKey = NULL;
BOOL fFoundAServer = FALSE;
char achValue2[MAX_PATH];
DWORD cbValue2 = MAX_PATH;
lRetValue = RegOpenKeyEx(hkey_clsid,pszCLSID,NULL,KEY_READ,&subKey);
if(lRetValue != ERROR_SUCCESS)
{
VERBOSITY(VERB_LEVEL_ERROR,printf("Unable to open HKEY_CLASSES_ROOT\\CLSID\\%s error 0x%x\n",pszCLSID,lRetValue);)
return CLASSCHK_SOMETHINGODD;
}
//
// Check to insure that there is an Ole1Class entry
//
cbValue = MAX_PATH;
dwRetValue = ReadRegistryString(subKey,"Ole1Class",NULL,achValue,&cbValue);
switch(dwRetValue)
{
case ERROR_FILE_NOT_FOUND:
VERBOSITY(VERB_LEVEL_WARN,printf("HKEY_CLASSES_ROOT\\CLSID\\%s is missing its Ole1Class key\n",pszCLSID);)
break;
case CLASSCHK_SOMETHINGODD:
VERBOSITY(VERB_LEVEL_WARN,printf("HKEY_CLASSES_ROOT\\CLSID\\%s\\Ole1Class key is odd\n",pszCLSID);)
}
//
// Quite often, there will be a AutoConvertTo key, which is supposed to be a GUID
//
cbValue = MAX_PATH;
dwRetValue = ReadRegistryGuid(subKey,"AutoConvertTo",NULL,achValue,&cbValue);
switch(dwRetValue)
{
case ERROR_SUCCESS:
//
// The CLSID should normally point to another class, such as the 2.0 version. Check to
// insure the CLSID exists
//
dwRetValue = ReadRegistryString(hkey_clsid,achValue,NULL,achValue2,&cbValue2);
if(dwRetValue != ERROR_SUCCESS)
{
VERBOSITY(VERB_LEVEL_WARN,printf("HKEY_CLASSES_ROOT\\CLSID\\%s\\AutoConvertTo key is odd\n",pszCLSID);)
switch(dwRetValue)
{
case ERROR_FILE_NOT_FOUND:
VERBOSITY(VERB_LEVEL_WARN,printf("HKEY_CLASSES_ROOT\\CLSID\\%s doesn't appear to exist\n",achValue);)
break;
}
}
break;
case ERROR_FILE_NOT_FOUND:
//
// This entry isn't required. If it doesn't exist, no big deal.
//
break;
case CLASSCHK_SOMETHINGODD:
VERBOSITY(VERB_LEVEL_WARN,printf("HKEY_CLASSES_ROOT\\CLSID\\%s\\AutoConvertTo key is odd\n",pszCLSID);)
break;
}
//
// It would be abnormal to find a missing PROGID key
//
cbValue = MAX_PATH;
dwRetValue = ReadRegistryString(subKey,"ProgID",NULL,achValue,&cbValue);
switch(dwRetValue)
{
case ERROR_SUCCESS:
//
// The PROGID should normally point to a valid PROGID
//
cbValue2 = MAX_PATH;
dwRetValue = ReadRegistryString(HKEY_CLASSES_ROOT,achValue,NULL,achValue2,&cbValue2);
if(dwRetValue != ERROR_SUCCESS)
{
VERBOSITY(VERB_LEVEL_ERROR,printf("HKEY_CLASSES_ROOT\\CLSID\\%s\\PROGID key is odd\n",pszCLSID);)
switch(dwRetValue)
{
case ERROR_FILE_NOT_FOUND:
VERBOSITY(VERB_LEVEL_ERROR,printf("HKEY_CLASSES_ROOT\\%s doesn't appear to exist\n",achValue);)
break;
}
}
break;
case ERROR_FILE_NOT_FOUND:
//
// This entry is required.
//
VERBOSITY(VERB_LEVEL_ERROR,printf("HKEY_CLASSES_ROOT\\CLSID\\%s\\PROGID key is missing\n",pszCLSID);)
break;
case CLASSCHK_SOMETHINGODD:
VERBOSITY(VERB_LEVEL_WARN,printf("HKEY_CLASSES_ROOT\\CLSID\\%s\\Ole1Class key is odd\n",pszCLSID);)
}
return dwRetValue;
}
//+---------------------------------------------------------------------------
//
// Function: CheckForDLL
//
// Synopsis: Given a CLSID, its hkey, and the name of the DLL value,
// determine if the DLL exists as a file, and if the threading
// model value is appropriate.
//
// Arguments: [pszCLSID] -- Name of the CLSID (for debug output)
// [hkeyCLSID] -- HKEY for the clsid
// [pszDLLKey] -- Name of the subkey to check for
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: 5-31-95 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
DWORD CheckForDLL(LPSTR pszCLSID, HKEY hkeyCLSID, LPSTR pszDLLKey)
{
LONG lRetValue = 0;
char achValue[MAX_PATH];
DWORD cbValue;
DWORD dwRetValue;
char achThreadModel[MAX_PATH];
DWORD cbThreadModel = MAX_PATH;
//
// The DLL name should be in
//
cbValue = MAX_PATH;
dwRetValue = ReadRegistryFile(hkeyCLSID,pszDLLKey,NULL,achValue,&cbValue);
switch(dwRetValue)
{
case ERROR_FILE_NOT_FOUND:
//
// The registry key didn't exist. Thats normally OK.
//
return dwRetValue;
case CLASSCHK_SOMETHINGODD:
VERBOSITY(VERB_LEVEL_WARN,printf("HKEY_CLASSES_ROOT\\CLSID\\%s\\%s key is odd\n",pszCLSID,pszDLLKey);)
return dwRetValue;
}
//
// If the DLL exists, check to see if the ThreadingModelKey is valid
//
dwRetValue = ReadRegistryString(hkeyCLSID,pszDLLKey,"ThreadingModel",achThreadModel,&cbThreadModel);
switch(dwRetValue)
{
case ERROR_FILE_NOT_FOUND:
//
// The registry key didn't exist. Thats normally OK.
//
return ERROR_SUCCESS;
case CLASSCHK_SOMETHINGODD:
VERBOSITY(VERB_LEVEL_WARN,printf("HKEY_CLASSES_ROOT\\CLSID\\%s\\%s\\ThreadingModel value is odd\n",pszCLSID,pszDLLKey);)
return dwRetValue;
}
//
// Check to insure the threading model is something we understand
//
if( StrICmp( achThreadModel, "Apartment") &&
StrICmp( achThreadModel, "Both") &&
StrICmp( achThreadModel, "Free"))
{
VERBOSITY(VERB_LEVEL_WARN,printf("HKEY_CLASSES_ROOT\\CLSID\\%s\\%s\\ThreadingModel value is %s\n",pszCLSID,pszDLLKey,achThreadModel);)
VERBOSITY(VERB_LEVEL_WARN,printf("Expected 'Apartment','Both', or 'Free'");)
return CLASSCHK_SOMETHINGODD;
}
return ERROR_SUCCESS;
}
//+---------------------------------------------------------------------------
//
// Function: CheckCLSIDEntry
//
// Synopsis: Given the name of a CLSID entry, verify that the entry is
// valid by looking for the 'usual' key information, and
// cross checking it against things that we assert should be
// true.
// Effects:
//
// Arguments: [pszCLSID] -- CLSID in a string form. Used to open key
//
// History: 5-31-95 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
DWORD CheckCLSIDEntry(LPSTR pszCLSID)
{
LONG lRetValue = 0;
char achValue[MAX_PATH];
DWORD cbValue;
char achPROGID[MAX_PATH];
DWORD cbPROGID = MAX_PATH;
char achPROGIDPath[MAX_PATH];
DWORD cbPROGIDPath = MAX_PATH;
char achPROGIDValue[MAX_PATH];
DWORD cbPROGIDValue = MAX_PATH;
LPSTR pszLocalServer = "LocalServer32";
DWORD dwRetValue;
HKEY subKey = NULL;
BOOL fFoundAServer = FALSE;
lRetValue = RegOpenKeyEx(hkey_clsid,
pszCLSID,
NULL,
KEY_READ,
&subKey);
if(lRetValue != ERROR_SUCCESS)
{
VERBOSITY(VERB_LEVEL_ERROR,printf("Unable to open HKEY_CLASSES_ROOT\\CLSID\\%s error 0x%x\n",pszCLSID,lRetValue);)
return CLASSCHK_SOMETHINGODD;
}
//
// Basic sanity check: Is the description string a valid string
//
cbValue = MAX_PATH;
dwRetValue = ReadRegistryString(subKey,NULL,NULL,achValue,&cbValue);
if(dwRetValue != ERROR_SUCCESS)
{
VERBOSITY(VERB_LEVEL_WHINE,printf("HKEY_CLASSES_ROOT\\CLSID\\%s has odd description string\n",pszCLSID);)
}
//
// A CLSID entry typically has several values. Least of which is supposed to be one or more of the following:
// InprocHandler
// InprocServer
// LocalServer
// InprocHandler32
// InprocServer32
// LocalServer32
//
// It may also have an optional PROGID entry, which we can use to verify that that the LocalServer and
// the protocol\StdFileEditing\server entries match.
//
// Another couple of things to watch for include checking the Inproc entries for ThreadingModel,
//
// Yet another thing to look at is the value of the CLSID itself. If the first 4 digits are 0003 or 0004, then
// the CLSID is for an OLE 1.0 class, and we need to do a seperate check
//
if((strncmp(&pszCLSID[1],"0003",4) == 0) || (strncmp(&pszCLSID[1],"0004",4) == 0) )
{
//
// In theory, this is supposed to be an OLE1CLASS. Check it seperately
//
RegCloseKey(subKey);
return CheckOLE1CLSID(pszCLSID);
}
dwRetValue = CheckForDLL(pszCLSID, subKey, "InprocHandler");
dwRetValue = CheckForDLL(pszCLSID, subKey, "InprocHandler32");
dwRetValue = CheckForDLL(pszCLSID, subKey, "InprocServer");
if(dwRetValue != ERROR_FILE_NOT_FOUND) fFoundAServer++;
dwRetValue = CheckForDLL(pszCLSID, subKey, "InprocServer32");
if(dwRetValue != ERROR_FILE_NOT_FOUND) fFoundAServer++;
//
// First, check for LocalServer32. If that doesn't exist, then try for
// LocalServer.
//
cbValue = MAX_PATH;
dwRetValue = ReadRegistryFile(subKey,pszLocalServer,NULL,achValue,&cbValue);
if(dwRetValue == ERROR_FILE_NOT_FOUND)
{
cbValue = MAX_PATH;
pszLocalServer = "LocalServer";
dwRetValue = ReadRegistryFile(subKey,pszLocalServer,NULL,achValue,&cbValue);
if(dwRetValue == CLASSCHK_SOMETHINGODD)
{
VERBOSITY(VERB_LEVEL_WARN,printf("HKEY_CLASSES_ROOT\\CLSID\\%s\\LocalServer32 is odd\n",pszCLSID);)
}
}
else if(dwRetValue == CLASSCHK_SOMETHINGODD)
{
VERBOSITY(VERB_LEVEL_WARN,printf("HKEY_CLASSES_ROOT\\CLSID\\%s\\%s is odd\n",pszCLSID,pszLocalServer);)
}
if(dwRetValue == ERROR_SUCCESS)
{
fFoundAServer++;
//
// We have a valid LocalServer. Lets get the PROGID's version of the local server, and compare the
// two. They should compare.
//
dwRetValue = ReadRegistryString(subKey,"PROGID",NULL,achPROGID,&cbPROGID);
switch(dwRetValue)
{
case ERROR_FILE_NOT_FOUND:
//
// Most CLSID's should indeed have a PROGID, but it isn't 100% required.
//
VERBOSITY(VERB_LEVEL_WHINE,printf("HKEY_CLASSES_ROOT\\CLSID\\%s missing a PROGID entry\n",pszCLSID);)
break;
case CLASSCHK_SOMETHINGODD:
VERBOSITY(VERB_LEVEL_WARN,printf("HKEY_CLASSES_ROOT\\CLSID\\%s PROGID entry is odd\n",pszCLSID);)
break;
default:
sprintf(achPROGIDPath,"%s\\protocol\\stdfileediting\\server",achPROGID);
dwRetValue = ReadRegistryFile(HKEY_CLASSES_ROOT,achPROGIDPath,NULL,achPROGIDValue,&cbPROGIDValue);
//
// The only thing we are interested in checking is if the two strings compare.
//
if(dwRetValue == ERROR_SUCCESS)
{
if(StrICmp(achPROGIDValue,achValue) != 0)
{
VERBOSITY(VERB_LEVEL_ERROR,printf("HKEY_CLASSES_ROOT\\CLSID\\%s is inconsistent with its PROGID\n",pszCLSID);)
VERBOSITY(VERB_LEVEL_ERROR,printf("HKEY_CLASSES_ROOT\\%s = '%s'\n",achPROGIDPath,achPROGIDValue);)
VERBOSITY(VERB_LEVEL_ERROR,printf("HKEY_CLASSES_ROOT\\CLSID\\%s\\%s = '%s'\n",pszCLSID,pszLocalServer,achValue);)
}
}
}
}
if(!fFoundAServer)
{
VERBOSITY(VERB_LEVEL_ERROR,printf("*** Unable to find a valid server for HKEY_CLASSES_ROOT\\CLSID\\%s ***\n",pszCLSID);)
}
RegCloseKey(subKey);
return dwRetValue;
}
//+---------------------------------------------------------------------------
//
// Function: EnumerateClsidRoot
//
// Synopsis: Enumerate and check each entry in the CLSID section
//
// History: 5-31-95 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
DWORD EnumerateClsidRoot()
{
LONG lRetValue = 0;
DWORD iSubKey = 0;
char achKeyName[MAX_PATH];
DWORD cbKeyName;
FILETIME filetime;
while(1)
{
cbKeyName = MAX_PATH;
lRetValue = RegEnumKeyEx(hkey_clsid,
iSubKey,
achKeyName,
&cbKeyName,
NULL,
NULL,
NULL,
&filetime);
if(lRetValue == ERROR_NO_MORE_ITEMS)
{
// End of enumeration
break;
}
if(lRetValue != ERROR_SUCCESS)
{
VERBOSITY(VERB_LEVEL_ERROR,printf("EnumerateClsidRoot:RegEnumKeyEx returned %x\n",lRetValue);)
return CLASSCHK_ERROR;
}
//
// Each of the sub keys enumerated here is expected to be a GUID. If it isn't a GUID, then it
// might be some other random garbage that we don't care about.
//
if(CheckForValidGuid(achKeyName) == ERROR_SUCCESS)
{
CheckCLSIDEntry(achKeyName);
}
iSubKey++;
}
return 0;
}
//+---------------------------------------------------------------------------
//
// Function: EnumerateClassesRoot
//
// Synopsis: Enumerate the root of HKEY_CLASSES, and check each entry
// based on what we think it should be.
//
// History: 5-31-95 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
DWORD EnumerateClassesRoot()
{
LONG lRetValue = 0;
DWORD iSubKey = 0;
char achKeyName[MAX_PATH];
DWORD cbKeyName;
FILETIME filetime;
while(1)
{
cbKeyName = MAX_PATH;
lRetValue = RegEnumKeyEx(HKEY_CLASSES_ROOT,
iSubKey,
achKeyName,
&cbKeyName,
NULL,
NULL,
NULL,
&filetime);
if(lRetValue == ERROR_NO_MORE_ITEMS)
{
// End of enumeration
break;
}
if(lRetValue != ERROR_SUCCESS)
{
VERBOSITY(VERB_LEVEL_ERROR,printf("EnumerateClassesRoot:RegEnumKeyEx returned %x\n",lRetValue);)
return CLASSCHK_ERROR;
}
//
// We expect to find two basic things at the HKEY_CLASSES_ROOT level.
// First are extention mappings, second are PROGID's. There is also
// the special cases of CLSID, Interface, and FileType, which are
// checked differently
//
if(StrICmp(achKeyName,"CLSID") == 0)
{
// The CLSID section is done later
}
else if(StrICmp(achKeyName,"Interface") == 0)
{
// The interface section is done later
}
else if(StrICmp(achKeyName,"FileType") == 0)
{
// The FileType entry is done later
}
else if(achKeyName[0] == '.')
{
// File extentions start with dots.
// Call the Check Extention function here
CheckExtentionToProgID(achKeyName);
}
else
{
// Assume it may be a PROGID. Call the PROGID function here.
CheckProgID(achKeyName);
}
iSubKey++;
}
return 0;
}
//+---------------------------------------------------------------------------
//
// Function: main
//
// Synopsis: Main entry point for program. Does what main entry points
// usually do.
//
// Arguments: [argc] --
// [argv] --
//
// History: 5-31-95 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
int _cdecl main(int argc, char *argv[])
{
LONG lRetValue = 0;
//
// HKEY_CLASSES_ROOT is often used.
//
lRetValue = RegOpenKeyEx(HKEY_CLASSES_ROOT,
"CLSID",
NULL,
KEY_READ,
&hkey_clsid);
if(lRetValue != ERROR_SUCCESS)
{
printf("Couldn't open HKEY_CLASSES_ROOT\\CLSID\n");
return(1);
}
//
// Enumerate different parts of the registry, reporting
// errors as we go.
//
EnumerateClassesRoot();
EnumerateClsidRoot();
RegCloseKey(hkey_clsid);
return 0;
}