1761 lines
51 KiB
C++
1761 lines
51 KiB
C++
#include "general.h"
|
|
#include "ParseInf.h"
|
|
#include "resource.h"
|
|
#include "FileNode.h"
|
|
#include <shlwapi.h>
|
|
#include <shlobj.h>
|
|
|
|
|
|
//#define USE_SHORT_PATH_NAME 1
|
|
|
|
// also defined in \nt\private\inet\urlmon\download\isctrl.cxx
|
|
LPCTSTR g_lpszUpdateInfo = TEXT("UpdateInfo");
|
|
LPCTSTR g_lpszCookieValue = TEXT("Cookie");
|
|
LPCTSTR g_lpszSavedValue = TEXT("LastSpecifiedInterval");
|
|
|
|
// This is a 'private' entry point into URLMON that we use
|
|
// to convert paths from their current, possibly short-file-name
|
|
// form to their canonical long-file-name form.
|
|
|
|
extern "C" {
|
|
#ifdef UNICODE
|
|
#define STR_CDLGETLONGPATHNAME "CDLGetLongPathNameW"
|
|
|
|
typedef DWORD (STDAPICALLTYPE *CDLGetLongPathNamePtr)(LPWSTR lpszLongPath, LPCWSTR lpszShortPath, DWORD cchBuffer);
|
|
|
|
#else // not UNICODE
|
|
|
|
#define STR_CDLGETLONGPATHNAME "CDLGetLongPathNameA"
|
|
|
|
typedef DWORD (STDAPICALLTYPE *CDLGetLongPathNamePtr)(LPSTR lpszLong, LPCSTR lpszShort, DWORD cchBuffer);
|
|
#endif // else not UNICODE
|
|
}
|
|
|
|
|
|
// given the typelib id, loop through HKEY_CLASSES_ROOT\Interface section and
|
|
// remove those entries with "TypeLib" subkey equal to the given type lib id
|
|
HRESULT CleanInterfaceEntries(LPCTSTR lpszTypeLibCLSID)
|
|
{
|
|
Assert(lpszTypeLibCLSID != NULL);
|
|
if (lpszTypeLibCLSID == NULL || lpszTypeLibCLSID[0] == '\0')
|
|
return HRESULT_FROM_WIN32(ERROR_BAD_ARGUMENTS);
|
|
|
|
HRESULT hr = S_OK;
|
|
HKEY hkey = NULL;
|
|
DWORD cStrings = 0;
|
|
LONG lResult = ERROR_SUCCESS, lSize = 0;
|
|
TCHAR szKeyName[OLEUI_CCHKEYMAX];
|
|
TCHAR szTmpID[MAX_PATH];
|
|
|
|
// open key HKEY_CLASS_ROOT\Interface
|
|
if (RegOpenKeyEx(
|
|
HKEY_CLASSES_ROOT,
|
|
HKCR_INTERFACE,
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
&hkey) == ERROR_SUCCESS)
|
|
{
|
|
// loop through all entries
|
|
while ((lResult = RegEnumKey(
|
|
hkey,
|
|
cStrings,
|
|
szKeyName,
|
|
OLEUI_CCHKEYMAX)) == ERROR_SUCCESS)
|
|
{
|
|
lSize = MAX_PATH;
|
|
lstrcat(szKeyName, TEXT("\\"));
|
|
lstrcat(szKeyName, HKCR_TYPELIB);
|
|
|
|
// if typelib id's match, remove the key
|
|
if ((RegQueryValue(
|
|
hkey,
|
|
szKeyName,
|
|
szTmpID,
|
|
&lSize) == ERROR_SUCCESS) &&
|
|
(lstrcmpi(szTmpID, lpszTypeLibCLSID) == 0))
|
|
{
|
|
hr = NullLastSlash(szKeyName, 0);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
DeleteKeyAndSubKeys(hkey, szKeyName);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cStrings += 1;
|
|
}
|
|
}
|
|
|
|
RegCloseKey(hkey);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (lResult != ERROR_NO_MORE_ITEMS)
|
|
hr = HRESULT_FROM_WIN32(lResult);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// If the OCX file being removed does not exist, then we cannot prompt the control
|
|
// to unregister itself. In this case, we call this function of clean up as many
|
|
// registry entries as we could for the control.
|
|
HRESULT CleanOrphanedRegistry(
|
|
LPCTSTR szFileName,
|
|
LPCTSTR szClientClsId,
|
|
LPCTSTR szTypeLibCLSID)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LONG lResult = 0;
|
|
TCHAR szTmpID[MAX_PATH];
|
|
TCHAR szTmpRev[MAX_PATH];
|
|
TCHAR szKeyName[OLEUI_CCHKEYMAX+50];
|
|
HKEY hkey = NULL, hkeyCLSID = NULL;
|
|
int nKeyLen = 0;
|
|
DWORD cStrings = 0;
|
|
long lSize = MAX_PATH;
|
|
|
|
Assert(lstrlen(szFileName) > 0);
|
|
Assert(lstrlen(szClientClsId) > 0);
|
|
|
|
// Delete CLSID keys
|
|
CatPathStrN( szTmpID, HKCR_CLSID, szClientClsId, MAX_PATH );
|
|
|
|
if (DeleteKeyAndSubKeys(HKEY_CLASSES_ROOT, szTmpID) != ERROR_SUCCESS)
|
|
hr = S_FALSE; // Keep going, but mark that there was a failure
|
|
|
|
// Delete TypeLib info
|
|
if (szTypeLibCLSID != NULL && szTypeLibCLSID[0] != '\0')
|
|
{
|
|
CatPathStrN( szTmpID, HKCR_TYPELIB, szTypeLibCLSID, MAX_PATH);
|
|
if (DeleteKeyAndSubKeys(HKEY_CLASSES_ROOT, szTmpID) != ERROR_SUCCESS)
|
|
hr = S_FALSE;
|
|
}
|
|
|
|
// Delete ModuleUsage keys
|
|
// The canonicalizer can fail if the target file isn't there, so in that case, fall back
|
|
// on szFileName, which may well have come in canonical form from the DU file list.
|
|
if ( OCCGetLongPathName(szTmpRev, szFileName, MAX_PATH) == 0 )
|
|
lstrcpy( szTmpRev, szFileName );
|
|
ReverseSlashes(szTmpRev);
|
|
|
|
// Guard against the subkey name being empty, as this will cause us to nuke
|
|
// the entire Module Usage subtree, which is a bad thing to do.
|
|
if ( szTmpRev[0] != '\0' )
|
|
{
|
|
CatPathStrN(szTmpID, REGSTR_PATH_MODULE_USAGE, szTmpRev, MAX_PATH);
|
|
if (DeleteKeyAndSubKeys(HKEY_LOCAL_MACHINE, szTmpID) != ERROR_SUCCESS)
|
|
hr = S_FALSE;
|
|
|
|
// Delete SharedDLL value
|
|
if (RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
REGSTR_PATH_SHAREDDLLS,
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
&hkey) == ERROR_SUCCESS)
|
|
{
|
|
hr = (RegDeleteValue(hkey, szFileName) == ERROR_SUCCESS ? hr : S_FALSE);
|
|
RegCloseKey(hkey);
|
|
}
|
|
else
|
|
{
|
|
hr = S_FALSE;
|
|
}
|
|
}
|
|
|
|
// loop through entries under HKEY_CLASSES_ROOT to clear entries
|
|
// whose CLSID subsection is equal to the CLSID of the control
|
|
// being removed
|
|
while ((lResult = RegEnumKey(
|
|
HKEY_CLASSES_ROOT,
|
|
cStrings++,
|
|
szKeyName,
|
|
OLEUI_CCHKEYMAX)) == ERROR_SUCCESS)
|
|
{
|
|
lSize = MAX_PATH;
|
|
nKeyLen = lstrlen(szKeyName);
|
|
lstrcat(szKeyName, "\\");
|
|
lstrcat(szKeyName, HKCR_CLSID);
|
|
if ((RegQueryValue(
|
|
HKEY_CLASSES_ROOT,
|
|
szKeyName,
|
|
szTmpID, &lSize) == ERROR_SUCCESS) &&
|
|
(lstrcmpi(szTmpID, szClientClsId) == 0))
|
|
{
|
|
szKeyName[nKeyLen] = '\0';
|
|
DeleteKeyAndSubKeys(HKEY_CLASSES_ROOT, szKeyName);
|
|
lResult = ERROR_NO_MORE_ITEMS;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
Assert(lResult == ERROR_NO_MORE_ITEMS);
|
|
if (lResult != ERROR_NO_MORE_ITEMS)
|
|
hr = S_FALSE;
|
|
|
|
// loop through all HKEY_CLASSES_ROOT\CLSID entries and remove
|
|
// those with InprocServer32 subsection equal to the name of
|
|
// the OCX file being removed
|
|
if (RegOpenKeyEx(
|
|
HKEY_CLASSES_ROOT,
|
|
HKCR_CLSID,
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
&hkey) == ERROR_SUCCESS)
|
|
{
|
|
cStrings = 0;
|
|
while ((lResult = RegEnumKey(
|
|
hkey,
|
|
cStrings,
|
|
szKeyName,
|
|
OLEUI_CCHKEYMAX)) == ERROR_SUCCESS)
|
|
{
|
|
// check InprocServer32
|
|
lSize = MAX_PATH;
|
|
lstrcat(szKeyName, "\\");
|
|
lstrcat(szKeyName, INPROCSERVER32);
|
|
if ((RegQueryValue(
|
|
hkey,
|
|
szKeyName,
|
|
szTmpID,
|
|
&lSize) == ERROR_SUCCESS) &&
|
|
(lstrcmpi(szTmpID, szFileName) == 0))
|
|
{
|
|
hr = NullLastSlash(szKeyName, 0);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
DeleteKeyAndSubKeys(hkey, szKeyName);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// check LocalServer32
|
|
hr = NullLastSlash(szKeyName, 1);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
lstrcat(szKeyName, LOCALSERVER32);
|
|
if ((RegQueryValue(
|
|
hkey,
|
|
szKeyName,
|
|
szTmpID,
|
|
&lSize) == ERROR_SUCCESS) &&
|
|
(lstrcmpi(szTmpID, szFileName) == 0))
|
|
{
|
|
hr = NullLastSlash(szKeyName, 0);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
DeleteKeyAndSubKeys(hkey, szKeyName);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// check LocalServerX86
|
|
hr = NullLastSlash(szKeyName, 1);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
lstrcat(szKeyName, LOCALSERVERX86);
|
|
if ((RegQueryValue(
|
|
hkey,
|
|
szKeyName,
|
|
szTmpID,
|
|
&lSize) == ERROR_SUCCESS) &&
|
|
(lstrcmpi(szTmpID, szFileName) == 0))
|
|
{
|
|
hr = NullLastSlash(szKeyName, 0);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
DeleteKeyAndSubKeys(hkey, szKeyName);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// check InProcServerX86
|
|
hr = NullLastSlash(szKeyName, 1);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
lstrcat(szKeyName, INPROCSERVERX86);
|
|
if ((RegQueryValue(
|
|
hkey,
|
|
szKeyName,
|
|
szTmpID,
|
|
&lSize) == ERROR_SUCCESS) &&
|
|
(lstrcmpi(szTmpID, szFileName) == 0))
|
|
{
|
|
hr = NullLastSlash(szKeyName, 0);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
DeleteKeyAndSubKeys(hkey, szKeyName);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
cStrings += 1;
|
|
}
|
|
|
|
RegCloseKey(hkey);
|
|
|
|
Assert(lResult == ERROR_NO_MORE_ITEMS);
|
|
if (lResult != ERROR_NO_MORE_ITEMS)
|
|
hr = S_FALSE;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Get from a abbreviated filename its full, long name
|
|
// eg. from C:\DOC\MyMath~1.txt to C:\DOC\MyMathFile.txt
|
|
// lpszShortFileName must has in it both the file name and its full path
|
|
// if bToUpper is TRUE, the name returned will be in uppercase
|
|
HRESULT ConvertToLongFileName(
|
|
LPTSTR lpszShortFileName,
|
|
BOOL bToUpper /* = FALSE */)
|
|
{
|
|
Assert(lpszShortFileName != NULL);
|
|
if (lpszShortFileName == NULL)
|
|
return HRESULT_FROM_WIN32(ERROR_BAD_ARGUMENTS);
|
|
|
|
HRESULT hr = S_OK;
|
|
WIN32_FIND_DATA filedata;
|
|
TCHAR *pEndPath = NULL;
|
|
HANDLE h = FindFirstFile(lpszShortFileName, &filedata);
|
|
|
|
if (h != INVALID_HANDLE_VALUE)
|
|
{
|
|
FindClose(h);
|
|
|
|
// separate filename from path
|
|
pEndPath = ReverseStrchr(lpszShortFileName, '\\');
|
|
if (pEndPath != NULL)
|
|
{
|
|
*(++pEndPath) = '\0';
|
|
lstrcat(pEndPath, filedata.cFileName);
|
|
}
|
|
else
|
|
{
|
|
lstrcpy(lpszShortFileName, filedata.cFileName);
|
|
}
|
|
|
|
// to upper case if requested
|
|
if (bToUpper)
|
|
CharUpper(lpszShortFileName);
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// DeleteKeyAndSubKeys
|
|
//=--------------------------------------------------------------------------=
|
|
// delete's a key and all of it's subkeys.
|
|
//
|
|
// Parameters:
|
|
// HKEY - [in] delete the descendant specified
|
|
// LPSTR - [in] i'm the descendant specified
|
|
//
|
|
// Output:
|
|
// LONG - ERROR_SUCCESS if successful
|
|
// - else, a nonzero error code defined in WINERROR.H
|
|
//
|
|
// Notes:
|
|
// - I don't feel too bad about implementing this recursively, since the
|
|
// depth isn't likely to get all the great.
|
|
// - Manually removing subkeys is needed for NT. Win95 does that
|
|
// automatically
|
|
//
|
|
// This code was stolen from the ActiveX framework (util.cpp).
|
|
LONG DeleteKeyAndSubKeys(HKEY hkIn, LPCTSTR pszSubKey)
|
|
{
|
|
HKEY hk;
|
|
TCHAR szTmp[MAX_PATH];
|
|
DWORD dwTmpSize;
|
|
LONG lResult;
|
|
|
|
lResult = RegOpenKeyEx(hkIn, pszSubKey, 0, KEY_ALL_ACCESS, &hk);
|
|
if (lResult != ERROR_SUCCESS)
|
|
return lResult;
|
|
|
|
// loop through all subkeys, blowing them away.
|
|
for (/* DWORD c = 0 */; lResult == ERROR_SUCCESS; /* c++ */)
|
|
{
|
|
dwTmpSize = MAX_PATH;
|
|
lResult = RegEnumKeyEx(hk, 0, szTmp, &dwTmpSize, 0, NULL, NULL, NULL);
|
|
if (lResult == ERROR_NO_MORE_ITEMS)
|
|
break;
|
|
lResult = DeleteKeyAndSubKeys(hk, szTmp);
|
|
}
|
|
|
|
// there are no subkeys left, [or we'll just generate an error and return FALSE].
|
|
// let's go blow this dude away.
|
|
//
|
|
dwTmpSize = MAX_PATH;
|
|
Assert(RegEnumKeyEx(hk, 0, szTmp, &dwTmpSize, 0, NULL, NULL, NULL) == ERROR_NO_MORE_ITEMS);
|
|
RegCloseKey(hk);
|
|
|
|
lResult = RegDeleteKey(hkIn, pszSubKey);
|
|
|
|
return lResult;
|
|
}
|
|
|
|
// return TRUE if file szFileName exists, FALSE otherwise
|
|
BOOL FileExist(LPCTSTR lpszFileName)
|
|
{
|
|
DWORD dwErrMode;
|
|
BOOL fResult;
|
|
|
|
dwErrMode = SetErrorMode(SEM_FAILCRITICALERRORS);
|
|
|
|
fResult = ((UINT)GetFileAttributes(lpszFileName) != (UINT)-1);
|
|
|
|
SetErrorMode(dwErrMode);
|
|
|
|
return fResult;
|
|
}
|
|
|
|
// given a flag, return the appropriate directory
|
|
// possible flags are:
|
|
// GD_WINDOWDIR : return WINDOWS directory
|
|
// GD_SYSTEMDIR : return SYSTEM directory
|
|
// GD_CONTAINERDIR : return directory of app used to view control (ie IE)
|
|
// GD_CACHEDIR : return OCX cache directory, read from registry
|
|
// GD_CONFLICTDIR : return OCX conflict directory, read from registry
|
|
// GD_EXTRACTDIR : require an extra parameter szOCXFullName,
|
|
// extract and return its path
|
|
HRESULT GetDirectory(
|
|
UINT nDirType,
|
|
LPTSTR szDirBuffer,
|
|
int nBufSize,
|
|
LPCTSTR szOCXFullName /* = NULL */)
|
|
{
|
|
LONG lResult = 0;
|
|
TCHAR *pCh = NULL, *pszKeyName = NULL;
|
|
HRESULT hr = S_OK;
|
|
HKEY hkeyIntSetting = NULL;
|
|
unsigned long ulSize = nBufSize;
|
|
|
|
switch (nDirType)
|
|
{
|
|
|
|
case GD_WINDOWSDIR:
|
|
if (GetWindowsDirectory(szDirBuffer, nBufSize) == 0)
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
break;
|
|
|
|
case GD_SYSTEMDIR:
|
|
if (GetSystemDirectory(szDirBuffer, nBufSize) == 0)
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
break;
|
|
|
|
case GD_EXTRACTDIR:
|
|
if (szOCXFullName == NULL)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_BAD_ARGUMENTS);
|
|
break;
|
|
}
|
|
lstrcpy(szDirBuffer, szOCXFullName);
|
|
pCh = ReverseStrchr(szDirBuffer, '\\');
|
|
if (pCh == NULL)
|
|
hr = HRESULT_FROM_WIN32(ERROR_BAD_ARGUMENTS);
|
|
else
|
|
pCh[0] = '\0';
|
|
break;
|
|
|
|
case GD_CONTAINERDIR:
|
|
pszKeyName = new TCHAR[MAX_PATH];
|
|
if (pszKeyName == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
CatPathStrN(pszKeyName, REGSTR_PATH_IE, CONTAINER_APP, MAX_PATH);
|
|
if ((lResult = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
pszKeyName,
|
|
0x0,
|
|
KEY_READ,
|
|
&hkeyIntSetting)) == ERROR_SUCCESS)
|
|
{
|
|
lResult = RegQueryValueEx(
|
|
hkeyIntSetting,
|
|
VALUE_PATH,
|
|
NULL,
|
|
NULL,
|
|
(unsigned char*)szDirBuffer,
|
|
&ulSize);
|
|
}
|
|
if (lResult != ERROR_SUCCESS)
|
|
hr = HRESULT_FROM_WIN32(lResult);
|
|
else
|
|
szDirBuffer[lstrlen(szDirBuffer)-1] = '\0'; // take away the ending ';'
|
|
delete [] pszKeyName;
|
|
break;
|
|
|
|
case GD_CACHEDIR:
|
|
case GD_CONFLICTDIR:
|
|
if ((lResult = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
REGSTR_PATH_IE_SETTINGS,
|
|
0x0,
|
|
KEY_READ,
|
|
&hkeyIntSetting)) == ERROR_SUCCESS)
|
|
{
|
|
lResult = RegQueryValueEx(
|
|
hkeyIntSetting,
|
|
VALUE_ACTIVEXCACHE,
|
|
NULL,
|
|
NULL,
|
|
(unsigned char*)szDirBuffer,
|
|
&ulSize);
|
|
}
|
|
|
|
hr = (lResult == ERROR_SUCCESS ? S_OK : HRESULT_FROM_WIN32(lResult));
|
|
|
|
// if looking for cache dir, append "\\CONFLICT"
|
|
if (SUCCEEDED(hr) && nDirType == GD_CONFLICTDIR)
|
|
lstrcat(szDirBuffer, DEFAULT_CONFLICT);
|
|
|
|
break;
|
|
|
|
default:
|
|
Assert(FALSE);
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
|
|
if (hkeyIntSetting != NULL)
|
|
RegCloseKey(hkeyIntSetting);
|
|
|
|
if (FAILED(hr))
|
|
szDirBuffer[0] = '\0';
|
|
|
|
return hr;
|
|
}
|
|
|
|
// retrieve file size for file szFile. Size returne in pSize.
|
|
HRESULT GetSizeOfFile(LPCTSTR lpszFile, LPDWORD lpSize)
|
|
{
|
|
HANDLE hFile = NULL;
|
|
WIN32_FIND_DATA fileData;
|
|
|
|
*lpSize = 0;
|
|
Assert(lpszFile != NULL);
|
|
|
|
hFile = FindFirstFile(lpszFile, &fileData);
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
FindClose(hFile);
|
|
*lpSize = fileData.nFileSizeLow;
|
|
|
|
// Get cluster size to calculate the real # of bytes
|
|
// taken up by the file
|
|
|
|
DWORD dwSectorPerCluster, dwBytePerSector;
|
|
DWORD dwFreeCluster, dwTotalCluster;
|
|
TCHAR szRoot[4];
|
|
lstrcpyn(szRoot, lpszFile, 4);
|
|
|
|
if (!GetDiskFreeSpace(
|
|
szRoot, &dwSectorPerCluster, &dwBytePerSector,
|
|
&dwFreeCluster, &dwTotalCluster))
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
DWORD dwClusterSize = dwSectorPerCluster * dwBytePerSector;
|
|
*lpSize = ((*lpSize/dwClusterSize) * dwClusterSize +
|
|
(*lpSize % dwClusterSize ? dwClusterSize : 0));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// Return S_OK is lpszCLSID is in ModuleUsage section of lpszFileName.
|
|
// Return E_... otherwise.
|
|
// lpszCLSID can be NULL, in this case it does not search for the CLSID.
|
|
// If lpszOwner is not NULL, it must point to a buffer which will be
|
|
// used to store the owner of the ModuleUsage section for lpszFileName
|
|
// dwOwnerSize is the size of the buffer pointed to by lpszOwner .
|
|
HRESULT LookUpModuleUsage(
|
|
LPCTSTR lpszFileName,
|
|
LPCTSTR lpszCLSID,
|
|
LPTSTR lpszOwner /* = NULL */,
|
|
DWORD dwOwnerSize /* = 0 */)
|
|
{
|
|
HKEY hkey = NULL, hkeyMod = NULL;
|
|
HRESULT hr = S_OK;
|
|
TCHAR szBuf[MAX_PATH];
|
|
LONG lResult = ERROR_SUCCESS;
|
|
|
|
if ((lResult = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
REGSTR_PATH_MODULE_USAGE,
|
|
0,
|
|
KEY_READ,
|
|
&hkeyMod)) != ERROR_SUCCESS)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(lResult);
|
|
goto EXITLOOKUPMODULEUSAGE;
|
|
}
|
|
|
|
|
|
if ( OCCGetLongPathName(szBuf, lpszFileName, MAX_PATH) == 0 )
|
|
lstrcpyn( szBuf, lpszFileName, MAX_PATH );
|
|
szBuf[256] = '\0'; // truncate if longer than 255 ude to win95 registry bug
|
|
|
|
|
|
lResult = RegOpenKeyEx(
|
|
hkeyMod,
|
|
szBuf,
|
|
0,
|
|
KEY_READ,
|
|
&hkey);
|
|
if (lResult != ERROR_SUCCESS)
|
|
{
|
|
ReverseSlashes(szBuf);
|
|
lResult = RegOpenKeyEx(hkeyMod, szBuf, 0, KEY_READ, &hkey);
|
|
if (lResult != ERROR_SUCCESS)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(lResult);
|
|
goto EXITLOOKUPMODULEUSAGE;
|
|
}
|
|
}
|
|
|
|
// Get owner if requested
|
|
if (lpszOwner != NULL)
|
|
{
|
|
DWORD dwSize = dwOwnerSize;
|
|
lResult = RegQueryValueEx(
|
|
hkey,
|
|
VALUE_OWNER,
|
|
NULL,
|
|
NULL,
|
|
(unsigned char*)lpszOwner,
|
|
&dwSize);
|
|
if (lResult != ERROR_SUCCESS)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(lResult);
|
|
lpszOwner[0] = '\0';
|
|
goto EXITLOOKUPMODULEUSAGE;
|
|
}
|
|
}
|
|
|
|
// see if lpszCLSID is a client of this module usage section
|
|
if (lpszCLSID != NULL)
|
|
{
|
|
lResult = RegQueryValueEx(
|
|
hkey,
|
|
lpszCLSID,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
if (lResult != ERROR_SUCCESS)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(lResult);
|
|
goto EXITLOOKUPMODULEUSAGE;
|
|
}
|
|
}
|
|
|
|
EXITLOOKUPMODULEUSAGE:
|
|
|
|
if (hkey)
|
|
RegCloseKey(hkey);
|
|
|
|
if (hkeyMod)
|
|
RegCloseKey(hkeyMod);
|
|
|
|
return hr;
|
|
}
|
|
|
|
// ReverseSlashes() takes a string, that's assumed to be pointing to a
|
|
// valid string and is null-terminated, and reverses all forward slashes
|
|
// to backslashes and all backslashes to forward slashes.
|
|
void ReverseSlashes(LPTSTR pszStr)
|
|
{
|
|
while (*pszStr)
|
|
{
|
|
if (*pszStr == '\\') *pszStr = '/';
|
|
else if (*pszStr == '/') *pszStr = '\\';
|
|
pszStr++;
|
|
}
|
|
}
|
|
|
|
// find the last occurance of ch in string szString
|
|
TCHAR* ReverseStrchr(LPCTSTR szString, TCHAR ch)
|
|
{
|
|
if (szString == NULL || szString[0] == '\0')
|
|
return NULL;
|
|
TCHAR *pCh = (TCHAR*)(szString + lstrlen(szString));
|
|
for (;pCh != szString && *pCh != ch; pCh--);
|
|
return (*pCh == ch ? pCh : NULL);
|
|
}
|
|
|
|
// set the last backslash (or the character offset from that by 1) to NULL
|
|
// returns S_OK if fine, E_FAIL if last backslash not found
|
|
HRESULT NullLastSlash(LPTSTR pszString, UINT uiOffset)
|
|
{
|
|
LPTSTR pszLastSlash;
|
|
HRESULT hr;
|
|
|
|
ASSERT(pszString);
|
|
ASSERT((uiOffset == 0) || (uiOffset == 1));
|
|
|
|
pszLastSlash = ReverseStrchr(pszString, TEXT('\\'));
|
|
|
|
if (!pszLastSlash)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
*(pszLastSlash + uiOffset) = TEXT('\0');
|
|
hr = S_OK;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
// If lpszGUID is an owner of the module lpszFileName in Module Usage,
|
|
// remove it, updating the .Owner as necessary. If we remove an owner,
|
|
// then decrement the SharedDlls count. Never drop the SharedDlls count
|
|
// below 1 if the owner is 'Unknown Owner'.
|
|
// If modules usage drops to zero, remove MU. If SharedDlls count drops
|
|
// to zero, remove that value.
|
|
// Return the resulting owner count.
|
|
|
|
DWORD SubtractModuleOwner( LPCTSTR lpszFileName, LPCTSTR lpszGUID )
|
|
{
|
|
LONG cRef = 0;
|
|
HRESULT hr = S_OK;
|
|
LONG lResult;
|
|
HKEY hkeyMU = NULL;
|
|
HKEY hkeyMod = NULL;
|
|
TCHAR szBuf[MAX_PATH];
|
|
TCHAR szOwner[MAX_PATH];
|
|
DWORD dwSize = MAX_PATH;
|
|
BOOL bHasUnknownOwner;
|
|
BOOL bGUIDIsOwner;
|
|
|
|
Assert(lpszFileName != NULL);
|
|
Assert(lpszGUID != NULL);
|
|
|
|
// Get the current ref count, passing -1 to set is a get. Go figure.
|
|
hr = SetSharedDllsCount( lpszFileName, -1, &cRef );
|
|
if ( FAILED(hr) )
|
|
return 1; // in event of failure, say something safe
|
|
|
|
// check if Usage section is present for this dll
|
|
// open the file's section we are concerned with
|
|
|
|
if ((lResult = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
REGSTR_PATH_MODULE_USAGE,
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
&hkeyMU)) != ERROR_SUCCESS)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(lResult);
|
|
goto ExitSubtractModuleOwner;
|
|
}
|
|
|
|
|
|
if ( OCCGetLongPathName(szBuf, lpszFileName, MAX_PATH) == 0 )
|
|
lstrcpyn( szBuf, lpszFileName, MAX_PATH );
|
|
|
|
szBuf[256] = '\0'; // truncate if longer than 255 ude to win95 registry bug
|
|
ReverseSlashes(szBuf);
|
|
|
|
// open section for szFileName under ModuleUsage
|
|
if ((lResult = RegOpenKeyEx(
|
|
hkeyMU,
|
|
szBuf,
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
&hkeyMod)) != ERROR_SUCCESS)
|
|
{
|
|
goto ExitSubtractModuleOwner;
|
|
}
|
|
|
|
dwSize = MAX_PATH;
|
|
if ((lResult = RegQueryValueEx(
|
|
hkeyMod,
|
|
VALUE_OWNER,
|
|
NULL,
|
|
NULL,
|
|
(unsigned char*)szOwner,
|
|
&dwSize)) != ERROR_SUCCESS)
|
|
{
|
|
goto ExitSubtractModuleOwner;
|
|
}
|
|
|
|
bHasUnknownOwner = lstrcmp( szOwner, MODULE_UNKNOWN_OWNER ) == 0;
|
|
|
|
bGUIDIsOwner = lstrcmp( szOwner, lpszGUID ) == 0;
|
|
|
|
// remove the owner value entry, if any.
|
|
lResult = RegDeleteValue(hkeyMod, lpszGUID);
|
|
// if this worked, then we'll need to drop the SharedDlls count,
|
|
// being careful not to let it fall below 1 if bHasUnknownOwner
|
|
if ( lResult == ERROR_SUCCESS )
|
|
{
|
|
if ( !bHasUnknownOwner || cRef > 1 )
|
|
SetSharedDllsCount( lpszFileName, --cRef, NULL );
|
|
|
|
if ( cRef > 0 && bGUIDIsOwner )
|
|
{
|
|
DWORD dwEnumIndex;
|
|
// lpszGUID was the .Owner, now that it's gone, replace it
|
|
// with another owner
|
|
for ( dwEnumIndex = 0, dwSize = MAX_PATH;
|
|
RegEnumValue(hkeyMod, dwEnumIndex, (char *)szOwner,
|
|
&dwSize, NULL, NULL, NULL, NULL) == ERROR_SUCCESS;
|
|
dwEnumIndex++, dwSize = MAX_PATH )
|
|
{
|
|
if (szOwner[0] != '.')
|
|
{
|
|
lResult = RegSetValueEx( hkeyMod,VALUE_OWNER, 0,
|
|
REG_SZ, (LPBYTE)szOwner,
|
|
(lstrlen( szOwner ) + 1) * sizeof(TCHAR) );
|
|
break; // we've done our job
|
|
}
|
|
} // for find a new owner
|
|
} // if there are still owners, but we've nuked the owner of record.
|
|
else if ( cRef == 0 )
|
|
{
|
|
// that was the last ref, so nuke the MU entry
|
|
RegCloseKey( hkeyMod );
|
|
hkeyMod = NULL;
|
|
RegDeleteKey( hkeyMU, szBuf ); // note - we assume this key has no subkeys.
|
|
|
|
// Take out the shared DLL's value
|
|
HKEY hkey;
|
|
|
|
lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
REGSTR_PATH_SHAREDDLLS, 0, KEY_ALL_ACCESS,
|
|
&hkey);
|
|
if ( lResult == ERROR_SUCCESS )
|
|
{
|
|
ReverseSlashes(szBuf); // revert to file sys
|
|
lResult = RegDeleteValue( hkey, szBuf );
|
|
RegCloseKey( hkey );
|
|
} // if opened SharedDlls
|
|
} // else last reference
|
|
} // if removed an owner
|
|
|
|
ExitSubtractModuleOwner:
|
|
|
|
if (hkeyMU)
|
|
RegCloseKey(hkeyMU);
|
|
|
|
if (hkeyMod)
|
|
RegCloseKey(hkeyMod);
|
|
|
|
return cRef;
|
|
}
|
|
|
|
// Set manually the count in SharedDlls.
|
|
// If dwCount is < 0, nothing is set.
|
|
// If pdwOldCount is non-null, the old count is returned
|
|
HRESULT SetSharedDllsCount(
|
|
LPCTSTR lpszFileName,
|
|
LONG cRef,
|
|
LONG *pcRefOld /* = NULL */)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LONG lResult = ERROR_SUCCESS;
|
|
DWORD dwSize = 0;
|
|
HKEY hkey = NULL;
|
|
|
|
Assert(lpszFileName != NULL);
|
|
if (lpszFileName == NULL)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_BAD_ARGUMENTS);
|
|
goto EXITSETSHAREDDLLSCOUNT;
|
|
}
|
|
|
|
if (cRef < 0 && pcRefOld == NULL)
|
|
{
|
|
goto EXITSETSHAREDDLLSCOUNT;
|
|
}
|
|
|
|
// open HKLM, Microsoft\Windows\CurrentVersion\SharedDlls
|
|
lResult = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
REGSTR_PATH_SHAREDDLLS, 0, KEY_ALL_ACCESS,
|
|
&hkey);
|
|
|
|
if (lResult != ERROR_SUCCESS)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(lResult);
|
|
goto EXITSETSHAREDDLLSCOUNT;
|
|
}
|
|
|
|
// if pdwOldCount is not NULL, save the old count in it
|
|
if (pcRefOld != NULL)
|
|
{
|
|
dwSize = sizeof(DWORD);
|
|
lResult = RegQueryValueEx(
|
|
hkey,
|
|
lpszFileName,
|
|
0,
|
|
NULL,
|
|
(unsigned char*)pcRefOld,
|
|
&dwSize);
|
|
if (lResult != ERROR_SUCCESS)
|
|
{
|
|
*pcRefOld = 0;
|
|
hr = S_FALSE;
|
|
goto EXITSETSHAREDDLLSCOUNT;
|
|
}
|
|
}
|
|
|
|
// if dwCount >= 0, set it as the new count
|
|
if (cRef >= 0)
|
|
{
|
|
lResult = RegSetValueEx(
|
|
hkey,
|
|
lpszFileName,
|
|
0,
|
|
REG_DWORD,
|
|
(unsigned char*)&cRef,
|
|
sizeof(DWORD));
|
|
if (lResult != ERROR_SUCCESS)
|
|
{
|
|
hr = S_FALSE;
|
|
goto EXITSETSHAREDDLLSCOUNT;
|
|
}
|
|
}
|
|
|
|
EXITSETSHAREDDLLSCOUNT:
|
|
|
|
if (hkey != NULL)
|
|
RegCloseKey(hkey);
|
|
|
|
return hr;
|
|
}
|
|
|
|
// UnregisterOCX() attempts to unregister a DLL or OCX by calling LoadLibrary
|
|
// and then DllUnregisterServer if the LoadLibrary succeeds. This function
|
|
// returns TRUE if the DLL or OCX could be unregistered or if the file isn't
|
|
// a loadable module.
|
|
HRESULT UnregisterOCX(LPCTSTR pszFile)
|
|
{
|
|
HINSTANCE hLib;
|
|
HRESULT hr = S_OK;
|
|
HRESULT (FAR STDAPICALLTYPE * pUnregisterEntry)(void);
|
|
|
|
hLib = LoadLibrary(pszFile);
|
|
|
|
if (hLib == NULL)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
else
|
|
{
|
|
(FARPROC &) pUnregisterEntry = GetProcAddress(
|
|
hLib,
|
|
"DllUnregisterServer"
|
|
);
|
|
|
|
if (pUnregisterEntry != NULL)
|
|
{
|
|
hr = (*pUnregisterEntry)();
|
|
}
|
|
|
|
FreeLibrary(hLib);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Return S_OK if dll can be removed, or S_FALSE if it cannot.
|
|
// Return E_... if an error has occured
|
|
HRESULT UpdateSharedDlls(LPCTSTR szFileName, BOOL bUpdate)
|
|
{
|
|
HKEY hkeySD = NULL;
|
|
HRESULT hr = S_OK;
|
|
DWORD dwType;
|
|
DWORD dwRef = 1;
|
|
DWORD dwSize = sizeof(DWORD);
|
|
LONG lResult;
|
|
|
|
// get the main SHAREDDLLS key ready; this is never freed!
|
|
if ((lResult = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE, REGSTR_PATH_SHAREDDLLS,
|
|
0, KEY_ALL_ACCESS, &hkeySD)) != ERROR_SUCCESS)
|
|
{
|
|
hkeySD = NULL;
|
|
hr = HRESULT_FROM_WIN32(lResult);
|
|
goto ExitUpdateSharedDlls;
|
|
}
|
|
|
|
// now look for szFileName
|
|
lResult = RegQueryValueEx(hkeySD, szFileName, NULL, &dwType,
|
|
(unsigned char*)&dwRef, &dwSize);
|
|
|
|
if (lResult != ERROR_SUCCESS)
|
|
{
|
|
hr = S_FALSE;
|
|
goto ExitUpdateSharedDlls;
|
|
}
|
|
|
|
// decrement reference count by 1.
|
|
//
|
|
// if (count equals to 0)
|
|
// if (bUpdate is TRUE)
|
|
// remove the key from SharedDlls
|
|
// return S_OK
|
|
// otherwise
|
|
// if (bUpdate is TRUE)
|
|
// update the count
|
|
// return S_FALSE
|
|
|
|
if ((--dwRef) > 0)
|
|
{
|
|
hr = S_FALSE;
|
|
if (bUpdate &&
|
|
(lResult = RegSetValueEx(hkeySD, szFileName, 0, REG_DWORD,
|
|
(unsigned char *)&dwRef, sizeof(DWORD))) != ERROR_SUCCESS)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(lResult);
|
|
}
|
|
goto ExitUpdateSharedDlls;
|
|
}
|
|
|
|
Assert(dwRef == 0);
|
|
|
|
// remove entry from SharedDlls
|
|
if (bUpdate &&
|
|
(lResult = RegDeleteValue(hkeySD, szFileName)) != ERROR_SUCCESS)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(lResult);
|
|
goto ExitUpdateSharedDlls;
|
|
}
|
|
|
|
ExitUpdateSharedDlls:
|
|
|
|
if (hkeySD)
|
|
RegCloseKey(hkeySD);
|
|
|
|
return hr;
|
|
}
|
|
|
|
void RemoveList(LPCLSIDLIST_ITEM lpListHead)
|
|
{
|
|
LPCLSIDLIST_ITEM lpItemPtr = NULL;
|
|
while (TRUE)
|
|
{
|
|
lpItemPtr = lpListHead;
|
|
if (lpItemPtr == NULL)
|
|
break;
|
|
lpListHead = lpItemPtr->pNext;
|
|
delete lpItemPtr;
|
|
}
|
|
lpListHead = NULL;
|
|
}
|
|
|
|
BOOL ReadInfFileNameFromRegistry(
|
|
LPCTSTR lpszCLSID,
|
|
LPTSTR lpszInf,
|
|
LONG nBufLen)
|
|
{
|
|
if (lpszCLSID == NULL || lpszInf == NULL)
|
|
return FALSE;
|
|
|
|
HKEY hkey = NULL;
|
|
TCHAR szKey[100];
|
|
LONG lResult = ERROR_SUCCESS;
|
|
|
|
CatPathStrN( szKey, HKCR_CLSID, lpszCLSID, 100);
|
|
CatPathStrN( szKey, szKey, INFFILE, 100);
|
|
|
|
if (RegOpenKeyEx(HKEY_CLASSES_ROOT, szKey, 0, KEY_READ, &hkey) != ERROR_SUCCESS)
|
|
return FALSE;
|
|
|
|
lResult = RegQueryValue(hkey, NULL, lpszInf, &nBufLen);
|
|
RegCloseKey(hkey);
|
|
|
|
if (lResult != ERROR_SUCCESS)
|
|
{
|
|
lpszInf[0] = '\0';
|
|
}
|
|
|
|
if (lpszInf[0] == '\0')
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL WriteInfFileNameToRegistry(LPCTSTR lpszCLSID, LPTSTR lpszInf)
|
|
{
|
|
if (lpszCLSID == NULL)
|
|
return FALSE;
|
|
|
|
HKEY hkey = NULL;
|
|
LONG lResult = ERROR_SUCCESS;
|
|
TCHAR szKey[100];
|
|
|
|
CatPathStrN(szKey, HKCR_CLSID, lpszCLSID, 100);
|
|
CatPathStrN(szKey, szKey, INFFILE, 100);
|
|
|
|
if (RegCreateKey(HKEY_CLASSES_ROOT, szKey, &hkey) != ERROR_SUCCESS)
|
|
return FALSE;
|
|
|
|
lResult = RegSetValue(
|
|
hkey,
|
|
NULL,
|
|
REG_SZ,
|
|
(lpszInf == NULL ? TEXT("") : lpszInf),
|
|
(lpszInf == NULL ? 0 : lstrlen(lpszInf)));
|
|
RegCloseKey(hkey);
|
|
|
|
return (lResult == ERROR_SUCCESS);
|
|
}
|
|
|
|
// Define a macro to make life easier
|
|
#define QUIT_IF_FAIL if (FAILED(hr)) goto Exit
|
|
|
|
|
|
HRESULT
|
|
ExpandVar(
|
|
LPCSTR& pchSrc, // passed by ref!
|
|
LPSTR& pchOut, // passed by ref!
|
|
DWORD& cbLen, // passed by ref!
|
|
DWORD cbBuffer,
|
|
const char * szVars[],
|
|
const char * szValues[])
|
|
{
|
|
HRESULT hr = S_FALSE;
|
|
int cbvar = 0;
|
|
|
|
Assert (*pchSrc == '%');
|
|
|
|
for (int i=0; szVars[i] && (cbvar = lstrlen(szVars[i])) ; i++) { // for each variable
|
|
|
|
int cbneed = 0;
|
|
|
|
if ( (szValues[i] == NULL) || !(cbneed = lstrlen(szValues[i])))
|
|
continue;
|
|
|
|
cbneed++; // add for nul
|
|
|
|
if (0 == strncmp(szVars[i], pchSrc, cbvar)) {
|
|
|
|
// found something we can expand
|
|
|
|
if ((cbLen + cbneed) >= cbBuffer) {
|
|
// out of buffer space
|
|
*pchOut = '\0'; // term
|
|
hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
|
goto Exit;
|
|
}
|
|
|
|
lstrcpy(pchOut, szValues[i]);
|
|
cbLen += (cbneed -1); //don't count the nul
|
|
|
|
pchSrc += cbvar; // skip past the var in pchSrc
|
|
pchOut += (cbneed -1); // skip past dir in pchOut
|
|
|
|
hr = S_OK;
|
|
goto Exit;
|
|
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
// from urlmon\download\hooks.cxx (ExpandCommandLine and ExpandVars)
|
|
// used to expand variables
|
|
HRESULT
|
|
ExpandCommandLine(
|
|
LPCSTR szSrc,
|
|
LPSTR szBuf,
|
|
DWORD cbBuffer,
|
|
const char * szVars[],
|
|
const char * szValues[])
|
|
{
|
|
Assert(cbBuffer);
|
|
|
|
|
|
HRESULT hr = S_FALSE;
|
|
|
|
LPCSTR pchSrc = szSrc; // start parsing at begining of cmdline
|
|
|
|
LPSTR pchOut = szBuf; // set at begin of out buffer
|
|
DWORD cbLen = 0;
|
|
|
|
while (*pchSrc) {
|
|
|
|
// look for match of any of our env vars
|
|
if (*pchSrc == '%') {
|
|
|
|
HRESULT hr1 = ExpandVar(pchSrc, pchOut, cbLen, // all passed by ref!
|
|
cbBuffer, szVars, szValues);
|
|
|
|
if (FAILED(hr1)) {
|
|
hr = hr1;
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
if (hr1 == S_OK) { // expand var expanded this
|
|
hr = hr1;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// copy till the next % or nul
|
|
if ((cbLen + 1) < cbBuffer) {
|
|
|
|
*pchOut++ = *pchSrc++;
|
|
cbLen++;
|
|
|
|
} else {
|
|
|
|
// out of buffer space
|
|
*pchOut = '\0'; // term
|
|
hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
|
goto Exit;
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
*pchOut = '\0'; // term
|
|
|
|
|
|
Exit:
|
|
|
|
return hr;
|
|
}
|
|
// Find dependent DLLs in ModuleUsage
|
|
// given the clsid it will enumerate all the DLLs in the ModuleUsage
|
|
// that were used by this clsid
|
|
HRESULT FindDLLInModuleUsage(
|
|
LPTSTR lpszFileName,
|
|
LPCTSTR lpszCLSID,
|
|
DWORD &iSubKey)
|
|
{
|
|
HKEY hkey = NULL, hkeyMod = NULL;
|
|
HRESULT hr = S_OK;
|
|
TCHAR szBuf[MAX_PATH];
|
|
LONG lResult = ERROR_SUCCESS;
|
|
|
|
if (lpszCLSID == NULL) {
|
|
hr = E_INVALIDARG; // req clsid
|
|
goto Exit;
|
|
}
|
|
|
|
// get the main MODULEUSAGE key
|
|
if ((lResult = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
REGSTR_PATH_MODULE_USAGE,
|
|
0,
|
|
KEY_READ,
|
|
&hkeyMod)) != ERROR_SUCCESS)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(lResult);
|
|
goto Exit;
|
|
}
|
|
|
|
while ( ((lResult = RegEnumKey(
|
|
hkeyMod,
|
|
iSubKey++,
|
|
szBuf,
|
|
MAX_PATH)) == ERROR_SUCCESS) ) {
|
|
|
|
lResult = RegOpenKeyEx(
|
|
hkeyMod,
|
|
szBuf,
|
|
0,
|
|
KEY_READ,
|
|
&hkey);
|
|
|
|
if (lResult != ERROR_SUCCESS)
|
|
break;
|
|
|
|
// see if lpszCLSID is a client of this module usage section
|
|
lResult = RegQueryValueEx(
|
|
hkey,
|
|
lpszCLSID,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
if (lResult == ERROR_SUCCESS)
|
|
{
|
|
// got the filename, return it
|
|
lstrcpy(lpszFileName, szBuf);
|
|
goto Exit;
|
|
}
|
|
|
|
if (hkey) {
|
|
RegCloseKey(hkey);
|
|
hkey = NULL;
|
|
}
|
|
|
|
} // while
|
|
|
|
if (lResult != ERROR_SUCCESS) {
|
|
hr = HRESULT_FROM_WIN32(lResult);
|
|
}
|
|
|
|
Exit:
|
|
|
|
if (hkey)
|
|
RegCloseKey(hkey);
|
|
|
|
if (hkeyMod)
|
|
RegCloseKey(hkeyMod);
|
|
|
|
return hr;
|
|
}
|
|
|
|
BOOL PatternMatch(LPCTSTR szModName, LPTSTR szSectionName)
|
|
{
|
|
|
|
LPCTSTR pch = ReverseStrchr(szModName, '/');
|
|
DWORD len = 0;
|
|
|
|
if (!pch)
|
|
pch = szModName;
|
|
else
|
|
pch++;
|
|
|
|
// pch points at base name of module
|
|
|
|
if ((len = lstrlen(pch)) != (DWORD)lstrlen(szSectionName))
|
|
return FALSE;
|
|
|
|
LPTSTR pchSecStar = StrChr(szSectionName, '*');
|
|
|
|
Assert(pchSecStar);
|
|
|
|
DWORD cbLen1 = (DWORD) (pchSecStar - szSectionName);
|
|
|
|
// compare upto '*'
|
|
if (StrCmpNI(szSectionName, pch, cbLen1) != 0)
|
|
return FALSE;
|
|
|
|
// compare after the 3 stars
|
|
if ( (cbLen1 + 3) < len) // *s not at end
|
|
if (StrCmpNI(pchSecStar+3, pch + (cbLen1+3), len -(cbLen1+3)) != 0)
|
|
return FALSE;
|
|
|
|
// simlar strings but for the stars.
|
|
|
|
// modify the szSectionName to hold the value for the stars
|
|
// in-effect this will substitute the original variable with
|
|
// the value that was used when installing the OCX
|
|
|
|
lstrcpy(pchSecStar, pch + cbLen1);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
DWORD OCCGetLongPathName( LPTSTR szLong, LPCTSTR szShort, DWORD cchBuffer )
|
|
{
|
|
DWORD dwLen = 0;
|
|
HMODULE hmodUrlMon;
|
|
CDLGetLongPathNamePtr pfnGetLongPathName = NULL;
|
|
|
|
hmodUrlMon = LoadLibrary( "URLMON.DLL" );
|
|
|
|
// Set up our globals with short and long versions of the base cache path
|
|
if ( hmodUrlMon != NULL ) {
|
|
pfnGetLongPathName = (CDLGetLongPathNamePtr)GetProcAddress(hmodUrlMon, (LPCSTR)STR_CDLGETLONGPATHNAME );
|
|
|
|
if ( pfnGetLongPathName != NULL ) {
|
|
dwLen = pfnGetLongPathName( szLong, szShort, cchBuffer );
|
|
}
|
|
FreeLibrary( hmodUrlMon );
|
|
}
|
|
|
|
return dwLen;
|
|
}
|
|
|
|
TCHAR *CatPathStrN( TCHAR *szDst, const TCHAR *szHead, const TCHAR *szTail, int cchDst )
|
|
{
|
|
TCHAR *szRet = szDst;
|
|
int cchHead = lstrlen(szHead);
|
|
int cchTail = lstrlen(szTail);
|
|
|
|
if ( cchHead + cchTail >= (cchDst - 2) ) {// - 2 for / and null
|
|
Assert(FALSE);
|
|
szRet = NULL;
|
|
*szDst = 0;
|
|
}
|
|
else { // we know the whole thing is safe
|
|
lstrcpy(szDst, szHead);
|
|
lstrcpy(&szDst[cchHead], TEXT("\\"));
|
|
lstrcpy(&szDst[cchHead + 1], szTail);
|
|
}
|
|
|
|
return szRet;
|
|
}
|
|
|
|
BOOL IsCanonicalName( LPTSTR szName )
|
|
{
|
|
// simple test - if there's a ~ in it, it has a contraction in it
|
|
// and is therefore non-canonical
|
|
for ( ; *szName != '\0' && *szName != '~'; szName++ );
|
|
|
|
return *szName != '~';
|
|
};
|
|
|
|
struct RegPathName {
|
|
LPTSTR m_szName;
|
|
LPTSTR m_szCanonicalName;
|
|
|
|
RegPathName(void) : m_szName(NULL), m_szCanonicalName(NULL)
|
|
{};
|
|
~RegPathName()
|
|
{
|
|
if ( m_szName )
|
|
delete m_szName;
|
|
|
|
if ( m_szCanonicalName )
|
|
delete m_szCanonicalName;
|
|
};
|
|
|
|
void MakeRegFriendly( LPTSTR szName )
|
|
{
|
|
TCHAR *pch;
|
|
// If szName is going to be a reg key name, we can't have it lookin' like a path
|
|
for ( pch = szName; *pch != '\0'; pch++ )
|
|
if ( *pch == '\\' ) *pch = '/';
|
|
}
|
|
|
|
void MakeFileSysFriendly( LPTSTR szName )
|
|
{
|
|
TCHAR *pch;
|
|
// change the slashes back into DOS
|
|
// directory \'s
|
|
for ( pch = szName; *pch != '\0'; pch++ )
|
|
if ( *pch == '/' ) *pch = '\\';
|
|
}
|
|
|
|
BOOL FSetCanonicalName(void)
|
|
{
|
|
BOOL fSet = FALSE;
|
|
TCHAR *szT = new TCHAR[MAX_PATH];
|
|
|
|
if ( m_szName != NULL && szT != NULL ) {
|
|
LPITEMIDLIST pidl = NULL;
|
|
// WE jump through some hoops to get the all-long
|
|
// name version of szName. First we convert it to
|
|
// an ITEMIDLIST.
|
|
|
|
// but first, we must change the slashes back into DOS
|
|
// directory \'s
|
|
MakeFileSysFriendly( m_szName );
|
|
if ( OCCGetLongPathName( szT, m_szName, MAX_PATH ) != 0 ) {
|
|
m_szCanonicalName = szT;
|
|
fSet = TRUE;
|
|
} else
|
|
delete [] szT;
|
|
|
|
// restore m_szName to it's registry-friendly form
|
|
MakeRegFriendly( m_szName );
|
|
|
|
} // if we can get our temp string
|
|
|
|
if ( fSet ) { // whatever its source, our canonical form has reversed slashes
|
|
MakeRegFriendly( m_szCanonicalName );
|
|
}
|
|
return fSet;
|
|
};
|
|
|
|
BOOL FSetName( LPTSTR szName, int cchName )
|
|
{
|
|
BOOL fSet = FALSE;
|
|
|
|
if ( m_szName != NULL ) {
|
|
delete m_szName;
|
|
m_szName = NULL;
|
|
}
|
|
|
|
if ( m_szCanonicalName != NULL ) {
|
|
delete m_szCanonicalName;
|
|
m_szCanonicalName = NULL;
|
|
}
|
|
|
|
// we got a short name, so szName is the short name
|
|
m_szName = new TCHAR[cchName + 1];
|
|
if ( m_szName != NULL ) {
|
|
lstrcpy( m_szName, szName );
|
|
fSet = FSetCanonicalName();
|
|
}
|
|
|
|
return fSet;
|
|
};
|
|
};
|
|
|
|
struct ModuleUsageKeys : public RegPathName {
|
|
ModuleUsageKeys *m_pmukNext;
|
|
HKEY m_hkeyShort; // key with the short file name name
|
|
|
|
ModuleUsageKeys(void) : m_pmukNext(NULL), m_hkeyShort(NULL) {};
|
|
~ModuleUsageKeys(void)
|
|
{
|
|
if ( m_hkeyShort )
|
|
RegCloseKey( m_hkeyShort );
|
|
};
|
|
|
|
HRESULT MergeMU( HKEY hkeyCanon, HKEY hkeyMU )
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
DWORD dwIndex = 0;
|
|
DWORD cchNameMax;
|
|
DWORD cbValueMax;
|
|
|
|
if ( RegQueryInfoKey( m_hkeyShort,
|
|
NULL, NULL, NULL, NULL, NULL, NULL,
|
|
&dwIndex, &cchNameMax, &cbValueMax,
|
|
NULL, NULL ) == ERROR_SUCCESS ) {
|
|
LPTSTR szName = new TCHAR[cchNameMax + 1];
|
|
|
|
if (szName != NULL)
|
|
{
|
|
LPBYTE lpbValue = new BYTE[cbValueMax];
|
|
|
|
if (lpbValue != NULL)
|
|
{
|
|
// Examine each value.
|
|
for ( dwIndex--, hr = S_OK; (LONG)dwIndex >= 0 && SUCCEEDED(hr); dwIndex-- ) {
|
|
LONG lResult;
|
|
DWORD cchName = cchNameMax + 1;
|
|
DWORD dwType;
|
|
DWORD dwSize = cbValueMax;
|
|
|
|
// fetch key and value
|
|
lResult = RegEnumValue( m_hkeyShort,
|
|
dwIndex,
|
|
szName,
|
|
&cchName,
|
|
0,
|
|
&dwType,
|
|
lpbValue,
|
|
&dwSize );
|
|
|
|
if ( lResult == ERROR_SUCCESS ) {
|
|
// Do not replace if the canonical entry already has a
|
|
// .Owner value that is "Unknown Owner"
|
|
if ( lstrcmp( szName, ".Owner" ) == 0 ) {
|
|
TCHAR szCanonValue[MAX_PATH];
|
|
DWORD dwType;
|
|
DWORD lcbCanonValue = MAX_PATH;
|
|
if ( RegQueryValueEx( hkeyCanon, ".Owner", NULL, &dwType,
|
|
(LPBYTE)szCanonValue, &lcbCanonValue ) == ERROR_SUCCESS &&
|
|
lstrcmp( szCanonValue, "Unknown Owner" ) == 0 )
|
|
continue;
|
|
}
|
|
|
|
// Add the value to the canonical version of the key
|
|
if ( RegSetValueEx( hkeyCanon, szName, NULL, dwType,
|
|
lpbValue, dwSize ) != ERROR_SUCCESS )
|
|
hr = E_FAIL;
|
|
|
|
} else
|
|
hr = E_FAIL;
|
|
} // for each value in the non-canoncical key
|
|
|
|
// Now we are finished with the non-canonical key
|
|
if ( SUCCEEDED(hr) &&
|
|
RegDeleteKey( hkeyMU, m_szName ) != ERROR_SUCCESS )
|
|
hr = E_FAIL;
|
|
}
|
|
else // lpbValue.
|
|
{
|
|
delete [] szName;
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
else // szName
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
};
|
|
|
|
HRESULT MergeSharedDlls( HKEY hkeySD )
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
DWORD dwShortVal = 0;
|
|
DWORD dwCanonicalVal = 0;
|
|
DWORD dwType;
|
|
DWORD dwSize;
|
|
|
|
// The value names under shared DLLs are raw paths
|
|
MakeFileSysFriendly( m_szName );
|
|
MakeFileSysFriendly( m_szCanonicalName );
|
|
|
|
dwSize = sizeof(DWORD);
|
|
if ( RegQueryValueEx( hkeySD, m_szName, NULL,
|
|
&dwType, (LPBYTE)&dwShortVal, &dwSize ) == ERROR_SUCCESS &&
|
|
dwType == REG_DWORD ) {
|
|
dwCanonicalVal = 0;
|
|
dwSize = sizeof(DWORD);
|
|
// the canonical form may not be there, so we don't care if this
|
|
// fails.
|
|
RegQueryValueEx( hkeySD, m_szCanonicalName, NULL,
|
|
&dwType, (LPBYTE)&dwCanonicalVal, &dwSize );
|
|
dwCanonicalVal += dwShortVal;
|
|
dwSize = sizeof(DWORD);
|
|
if ( RegSetValueEx( hkeySD, m_szCanonicalName, NULL, REG_DWORD,
|
|
(LPBYTE)&dwCanonicalVal, dwSize ) == ERROR_SUCCESS ) {
|
|
RegDeleteValue( hkeySD, m_szName );
|
|
}
|
|
} else {
|
|
dwCanonicalVal = 1;
|
|
dwSize = sizeof(DWORD);
|
|
if ( RegSetValueEx( hkeySD, m_szCanonicalName, NULL, REG_DWORD,
|
|
(LPBYTE)&dwCanonicalVal, dwSize ) == ERROR_SUCCESS )
|
|
hr = S_OK;
|
|
}
|
|
|
|
MakeRegFriendly( m_szName );
|
|
MakeRegFriendly( m_szCanonicalName );
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CanonicalizeMU( HKEY hkeyMU, HKEY hkeySD )
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
HKEY hkeyCanon;
|
|
LONG lResult = RegOpenKeyEx( hkeyMU, m_szCanonicalName, 0, KEY_ALL_ACCESS, &hkeyCanon);
|
|
|
|
|
|
if ( lResult != ERROR_SUCCESS )
|
|
lResult = RegCreateKey( hkeyMU,
|
|
m_szCanonicalName,
|
|
&hkeyCanon );
|
|
|
|
if ( lResult == ERROR_SUCCESS ) {
|
|
hr = MergeMU( hkeyCanon, hkeyMU );
|
|
if ( SUCCEEDED(hr) )
|
|
hr = MergeSharedDlls( hkeySD );
|
|
RegCloseKey( hkeyCanon );
|
|
} else
|
|
hr = E_FAIL;
|
|
|
|
return S_OK;
|
|
};
|
|
};
|
|
|
|
// FAddModuleUsageKeys adds a module usage key to the list.
|
|
|
|
BOOL FAddModuleUsageKeys( ModuleUsageKeys*&pmuk, // head of ModuleUsageKeys list
|
|
LPTSTR szName, // name of key value
|
|
DWORD cchName, // length of szName, minus null terminator
|
|
HKEY hkeyMU // hkey of parent
|
|
)
|
|
{
|
|
BOOL fAdd = FALSE;
|
|
ModuleUsageKeys* pmukNew;
|
|
HKEY hkeySub = NULL;
|
|
LRESULT lr;
|
|
|
|
pmukNew = new ModuleUsageKeys;
|
|
if ( pmukNew &&
|
|
(lr = RegOpenKeyEx( hkeyMU, szName, 0, KEY_ALL_ACCESS, &hkeySub)) == ERROR_SUCCESS ) {
|
|
|
|
fAdd = pmukNew->FSetName( szName, cchName );
|
|
|
|
if ( fAdd ) {
|
|
// append to head of the list
|
|
pmukNew->m_hkeyShort = hkeySub;
|
|
pmukNew->m_pmukNext = pmuk;
|
|
pmuk = pmukNew;
|
|
}
|
|
}
|
|
|
|
if ( !fAdd ) {
|
|
if ( hkeySub )
|
|
RegCloseKey( hkeySub );
|
|
if ( pmukNew != NULL )
|
|
delete pmukNew;
|
|
}
|
|
|
|
return fAdd;
|
|
}
|
|
|
|
EXTERN_C HRESULT
|
|
CanonicalizeModuleUsage(void)
|
|
{
|
|
HKEY hkeyMU = NULL;
|
|
HKEY hkeySD = NULL;
|
|
HRESULT hr = S_OK;
|
|
LONG lResult;
|
|
|
|
// get the main SHAREDDLLS key ready; this is never freed!
|
|
|
|
|
|
if ((lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE, REGSTR_PATH_SHAREDDLLS,
|
|
0, KEY_ALL_ACCESS, &hkeySD)) == ERROR_SUCCESS &&
|
|
(lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE, REGSTR_PATH_MODULE_USAGE,
|
|
0, KEY_ALL_ACCESS, &hkeyMU)) == ERROR_SUCCESS )
|
|
{
|
|
DWORD dwIndex = 0;
|
|
ModuleUsageKeys *pmukUpdate = NULL; // records for values we want to update.
|
|
ModuleUsageKeys *pmuk;
|
|
ModuleUsageKeys *pmukNext;
|
|
|
|
// Examine each value.
|
|
do {
|
|
TCHAR szName[MAX_PATH]; // Value name
|
|
DWORD cchName = MAX_PATH;
|
|
FILETIME ftT;
|
|
|
|
// fetch key and value
|
|
lResult = RegEnumKeyEx( hkeyMU, dwIndex, szName, &cchName,
|
|
0, NULL, NULL, &ftT );
|
|
|
|
if ( lResult == ERROR_SUCCESS ) {
|
|
|
|
if ( !IsCanonicalName( szName ) )
|
|
if ( !FAddModuleUsageKeys( pmukUpdate, szName, cchName, hkeyMU ) )
|
|
hr = E_OUTOFMEMORY;
|
|
dwIndex++;
|
|
} else if ( lResult == ERROR_NO_MORE_ITEMS )
|
|
hr = S_FALSE;
|
|
else
|
|
hr = E_FAIL;
|
|
} while ( hr == S_OK );
|
|
|
|
|
|
if ( SUCCEEDED(hr) ) {
|
|
hr = S_OK; // don't need S_FALSE any longer
|
|
for ( pmuk = pmukUpdate; pmuk != NULL; pmuk = pmukNext ) {
|
|
HRESULT hr2 = pmuk->CanonicalizeMU( hkeyMU, hkeySD );
|
|
if ( FAILED(hr2) )
|
|
hr = hr2;
|
|
pmukNext = pmuk->m_pmukNext;
|
|
delete pmuk;
|
|
} // for
|
|
} // if enumeration succeeded
|
|
} // if keys opened
|
|
|
|
if (hkeyMU)
|
|
RegCloseKey(hkeyMU);
|
|
|
|
if ( hkeySD )
|
|
RegCloseKey( hkeySD );
|
|
|
|
return hr;
|
|
}
|
|
|