windows-nt/Source/XPSP1/NT/shell/ext/occache/parseinf.cpp

1959 lines
60 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
///////////////////////////////////////////////////////////////////////////////
// Implementation of class CParseInf
//
// CParseInf is created to deal with parsing of an INF file.
#include <ole2.h>
#include "ParseInf.h"
#include "resource.h"
#include "init.h"
#include "global.h"
#include <shlwapi.h>
#include <initguid.h>
#include <pkgguid.h>
#include <cleanoc.h> // for STATUS_CTRL values
#include <mluisupp.h>
#define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))
static BOOL FGetCLSIDFile( LPTSTR szFile, LPCTSTR szCLSID )
{
BOOL fGotIt = FALSE;
HKEY hkeyClsid;
TCHAR szT[MAX_PATH];
TCHAR *szPath = CatPathStrN( szT, HKCR_CLSID, szCLSID, MAX_PATH );
if ( RegOpenKeyEx( HKEY_CLASSES_ROOT, szPath, 0, KEY_READ, &hkeyClsid ) == ERROR_SUCCESS )
{
DWORD dw;
LRESULT lResult;
// Look for InprocServer[32] or LocalServer[32] key
dw = MAX_PATH;
lResult = RegQueryValue(hkeyClsid, INPROCSERVER32, szT, (PLONG)&dw);
if (lResult != ERROR_SUCCESS)
{
dw = MAX_PATH;
lResult = RegQueryValue(hkeyClsid, LOCALSERVER32, szT, (PLONG)&dw);
}
if (lResult != ERROR_SUCCESS)
{
dw = MAX_PATH;
lResult = RegQueryValue(hkeyClsid, INPROCSERVERX86, szT, (PLONG)&dw);
}
if (lResult != ERROR_SUCCESS)
{
dw = MAX_PATH;
lResult = RegQueryValue(hkeyClsid, LOCALSERVERX86, szT, (PLONG)&dw);
}
if ( lResult == ERROR_SUCCESS )
{
if ( OCCGetLongPathName( szFile, szT, MAX_PATH ) == 0 )
lstrcpy( szFile, szT );
fGotIt = TRUE;
}
RegCloseKey( hkeyClsid );
}
return fGotIt;
}
// constructor
CParseInf::CParseInf()
{
m_pHeadFileList = NULL;
m_pCurFileNode = NULL;
m_pFileRetrievalPtr = NULL;
m_pHeadPackageList = NULL;
m_pCurPackageNode = NULL;
m_pPackageRetrievalPtr = NULL;
m_bIsDistUnit = FALSE;
m_bHasActiveX = FALSE;
m_bHasJava = FALSE;
m_pijpm = NULL;
m_bCoInit = FALSE;
m_dwStatus = STATUS_CTRL_UNKNOWN;
GetDaysBeforeExpireGeneral( &m_cExpireDays );
}
// destructor
CParseInf::~CParseInf()
{
DestroyFileList();
DestroyPackageList();
if ( m_pijpm != NULL )
m_pijpm->Release();
if ( m_bCoInit )
CoUninitialize();
}
// initialization
void CParseInf::Init()
{
m_dwFileSizeSaved = 0;
m_dwTotalFileSize = 0;
m_nTotalFiles = 0;
m_pHeadFileList = m_pCurFileNode = NULL;
m_pHeadPackageList = m_pCurPackageNode = NULL;
lstrcpyn(m_szInf, m_szFileName, ARRAYSIZE(m_szInf));
TCHAR *pCh = ReverseStrchr(m_szInf, '.');
if (pCh != NULL)
*pCh = '\0';
if ( lstrlen(m_szInf) + lstrlen(INF_EXTENSION) < ARRAYSIZE(m_szInf))
lstrcat(m_szInf, INF_EXTENSION);
else
m_szInf[0] = 0; // if can't hold it, we can't hold it.
}
// release memory used by a linked list of files
void CParseInf::DestroyFileList()
{
if (m_pHeadFileList != NULL)
delete m_pHeadFileList;
m_pHeadFileList = m_pCurFileNode = NULL;
}
void CParseInf::DestroyPackageList()
{
if (m_pHeadPackageList != NULL)
delete m_pHeadPackageList;
m_pHeadPackageList = m_pCurPackageNode = NULL;
}
// find inf from cache directory if one with the
// same name as the OCX is not found
HRESULT CParseInf::FindInf(LPTSTR szInf)
{
HRESULT hr = S_OK;
WIN32_FIND_DATA dataFile;
HANDLE h = INVALID_HANDLE_VALUE;
DWORD dwLen = 0;
TCHAR szValueBuf[MAX_PATH];
TCHAR *szOcxFileName = ReverseStrchr(m_szFileName, '\\');
int nCachePathLength = 0, i = 0;
Assert(szOcxFileName != NULL);
szOcxFileName += 1;
Assert (szInf != NULL);
if (szInf == NULL)
goto ExitFindInf;
// search for inf file in two directories. First the dir where the
// OCX is, then the OC cache dir.
for (i = 0; dwLen == 0 && i < 2; i++)
{
if (i == 0)
hr = GetDirectory(GD_EXTRACTDIR, szInf, ARRAYSIZE(szInf), m_szFileName);
else
{
TCHAR szTemp[MAX_PATH];
hr = GetDirectory(GD_CACHEDIR, szTemp, ARRAYSIZE(szTemp));
if (lstrcmpi(szTemp, szInf) == 0)
continue;
lstrcpy(szInf, szTemp);
}
if (FAILED(hr))
goto ExitFindInf;
lstrcat(szInf, TEXT("\\"));
nCachePathLength = lstrlen(szInf);
lstrcat(szInf, TEXT("*"));
lstrcat(szInf, INF_EXTENSION);
h = FindFirstFile(szInf, &dataFile);
if (h == INVALID_HANDLE_VALUE)
{
goto ExitFindInf;
}
// find an inf file with a section in [Add.Code] dedicated
// to the OCX file in question
do {
szInf[nCachePathLength] = '\0';
lstrcat(szInf, (LPCTSTR)dataFile.cFileName);
dwLen = GetPrivateProfileString(
KEY_ADDCODE,
szOcxFileName,
DEFAULT_VALUE,
szValueBuf,
MAX_PATH,
szInf);
} while(dwLen == 0 && FindNextFile(h, &dataFile));
}
hr = (dwLen != 0 ? hr : S_FALSE);
ExitFindInf:
if (h != INVALID_HANDLE_VALUE)
FindClose(h);
if (hr != S_OK)
szInf[0] = '\0';
return hr;
}
// initiate parsing of INF file
// szCLSID -- address to a buffer storing CLSID of control
// szOCXFileName -- full path and name (ie. long file name) of OCX file
HRESULT CParseInf::DoParse(
LPCTSTR szOCXFileName,
LPCTSTR szCLSID)
{
Assert(szOCXFileName != NULL);
Assert(szCLSID != NULL);
HRESULT hr = S_OK;
const TCHAR *pszPath = NULL;
TCHAR szFileName[MAX_PATH];
DWORD dwFileSize = 0;
if ( FGetCLSIDFile( szFileName, szCLSID ) &&
lstrcmpi( szFileName, szOCXFileName ) != 0 )
m_dwStatus = STATUS_CTRL_UNPLUGGED;
// If DoParse was called, we are assumed to be a legacy control and not
// a distribution unit (subsequent call to DoParseDU will change the
// status). This information is required for control removal purposes.
m_bIsDistUnit = FALSE;
m_bHasActiveX = TRUE; // all legacy controls are ActiveX
// initialization
if ( OCCGetLongPathName(m_szFileName, szOCXFileName, MAX_PATH) == 0 )
lstrcpyn( m_szFileName, szOCXFileName, MAX_PATH );
lstrcpyn(m_szCLSID, szCLSID, MAX_CLSID_LEN);
DestroyFileList();
Init();
BOOL bOCXRemovable = IsModuleRemovable(m_szFileName);
// test INF file existance, if not, try to find one in OC cache dir.
if (!FileExist(m_szInf))
{
if (!ReadInfFileNameFromRegistry(m_szCLSID, m_szInf, MAX_PATH))
{
FindInf(m_szInf);
// record inf file name into the registry
WriteInfFileNameToRegistry(
m_szCLSID,
(m_szInf[0] == '\0' ? NULL : m_szInf));
}
}
// enumerate files assocated with a particular OCX
if (FAILED(hr = EnumSections()))
goto ExitDoParse;
// S_FALSE is returned when an ocx has no inf file
if (hr == S_FALSE)
{
m_nTotalFiles = 1;
if (FAILED(GetSizeOfFile(m_szFileName, &m_dwFileSizeSaved)))
{
m_dwFileSizeSaved = 0;
m_dwTotalFileSize = 0;
}
else
{
m_dwTotalFileSize = m_dwFileSizeSaved;
}
hr = S_OK;
if ( !PathFileExists( m_szFileName ) )
m_dwStatus = STATUS_CTRL_DAMAGED;
else
m_dwStatus = STATUS_CTRL_INSTALLED;
goto ExitDoParse;
}
// OCX has an corresponding INF file.
// Loop through the list of assocated files to dig out info for each
// from their corresponding section in the INF file
for (m_pCurFileNode = m_pHeadFileList;
m_pCurFileNode != NULL;
m_pCurFileNode = m_pCurFileNode->GetNextFileNode(), hr = S_OK)
{
// if m_pCurFileNode->GetNextFileNode() == NULL => it's the inf file itself,
// which does not need to be processed.
if (m_pCurFileNode->GetNextFileNode() != NULL)
{
pszPath = m_pCurFileNode->GetPath();
Assert(pszPath != NULL);
if (pszPath == NULL)
{
hr = E_UNEXPECTED;
goto ExitDoParse;
}
CatPathStrN( szFileName, pszPath, m_pCurFileNode->GetName(), ARRAYSIZE(szFileName));
}
else
{
lstrcpyn(szFileName, m_szInf, ARRAYSIZE(szFileName));
pszPath = NULL;
}
// hr might either be S_OK or S_FALSE
// S_OK means file can be removed as it has a SharedDlls count of 1
// S_FALSE if the count is greater than 1
// calculate total num of files and their sizes
if (SUCCEEDED(hr = GetSizeOfFile(szFileName, &dwFileSize)))
{
if (pszPath == NULL ||
IsModuleRemovable(szFileName) ||
lstrcmpi(szFileName, m_szFileName) == 0)
{
m_dwFileSizeSaved += dwFileSize;
}
m_dwTotalFileSize += dwFileSize;
} else
m_dwStatus = STATUS_CTRL_DAMAGED; // failure to get size indicative of missing file.
m_nTotalFiles += 1;
}
// if we didn't detect a problem, flag the control as installed.
if ( m_dwStatus == STATUS_CTRL_UNKNOWN )
m_dwStatus = STATUS_CTRL_INSTALLED;
ExitDoParse:
return hr;
}
HRESULT CParseInf::BuildDUFileList( HKEY hKeyDU )
{
HRESULT hr = S_OK;
LRESULT lResult;
HKEY hkeyFiles;
TCHAR szDUFileName[MAX_PATH + 1];
DWORD dwStrSize = MAX_PATH;
int cFilesEnum = 0;
lResult = RegOpenKeyEx(hKeyDU, REGSTR_DU_CONTAINS_FILES, 0,
KEY_READ, &hkeyFiles);
if ( lResult != ERROR_SUCCESS ) // if no files, maybe there's Java
return hr;
while ((lResult = RegEnumValue(hkeyFiles, cFilesEnum++, szDUFileName,
&dwStrSize, NULL, NULL, NULL, NULL)) == ERROR_SUCCESS)
{
TCHAR szPath[MAX_PATH + 1];
CFileNode *pFileNode;
lstrcpyn(szPath, szDUFileName, MAX_PATH);
TCHAR *szFName = ReverseStrchr(szPath, '\\');
Assert(szFName != NULL);
// long ago and far away, in the IE4, PP1-2 timeframe, there was a horrible
// bug that corrupted these entries on Memphis and NT5. We suspect that GetLongPathName
// was doing something wrong for code download, but repro scenarios were not
// to be found. Anywho, the damaged registries are out there, so we need to
// cope with them more gracefully than faulting at the *szFName = NULL;
if ( szFName == NULL )
continue;
*szFName = NULL;
szFName++;
pFileNode = new CFileNode(szFName, "", szPath);
if (pFileNode == NULL)
{
hr = E_OUTOFMEMORY;
break;
}
// create and add node to list
if (m_pHeadFileList == NULL)
{
m_pHeadFileList = pFileNode;
m_pCurFileNode = m_pHeadFileList;
}
else
{
hr = m_pCurFileNode->Insert(pFileNode);
m_pCurFileNode = m_pCurFileNode->GetNextFileNode();
}
dwStrSize = MAX_PATH;
}
RegCloseKey( hkeyFiles );
return hr;
}
HRESULT CParseInf::BuildDUPackageList( HKEY hKeyDU )
{
HRESULT hr = S_OK;
LRESULT lResult;
HKEY hkeyJava;
ICreateJavaPackageMgr *picjpm;
DestroyPackageList();
lResult = RegOpenKeyEx(hKeyDU, REGSTR_DU_CONTAINS_JAVA, 0,
KEY_READ, &hkeyJava);
if ( lResult != ERROR_SUCCESS ) // it's OK if there's no Java
return hr;
if ( !m_bCoInit )
m_bCoInit = SUCCEEDED(hr = CoInitialize(NULL));
if ( m_bCoInit )
{
hr=CoCreateInstance(CLSID_JavaPackageManager,NULL,CLSCTX_INPROC_SERVER,
IID_ICreateJavaPackageMgr,(LPVOID *) &picjpm);
if (SUCCEEDED(hr))
{
hr = picjpm->GetPackageManager(&m_pijpm);
picjpm->Release();
}
}
if (FAILED(hr))
return S_OK; // hr; // quietly fail until we're sure the JavaVM with package manager support is in the build.
// list the packages under Contains/Java - these are in the gobal namespace
hr = BuildNamespacePackageList(hkeyJava, "");
// add packages for each namespace key under Contains\Java
if ( SUCCEEDED(hr) )
{
DWORD dwIndex;
TCHAR szNamespace[MAX_PATH + 1]; //
DWORD dwStrSize;
for ( dwIndex = 0, dwStrSize = MAX_PATH;
RegEnumKey( hkeyJava, dwIndex, szNamespace, dwStrSize ) == ERROR_SUCCESS &&
SUCCEEDED(hr);
dwIndex++, dwStrSize = MAX_PATH )
{
HKEY hkeyNamespace;
lResult = RegOpenKeyEx(hkeyJava, szNamespace, 0, KEY_READ, &hkeyNamespace);
if ( lResult == ERROR_SUCCESS )
{
hr = BuildNamespacePackageList(hkeyNamespace, szNamespace );
RegCloseKey( hkeyNamespace );
}
else
{
hr = HRESULT_FROM_WIN32(lResult);
break;
}
}
}
RegCloseKey( hkeyJava );
m_bHasJava = m_pHeadPackageList != NULL;
return hr;
}
HRESULT CParseInf::BuildNamespacePackageList( HKEY hKeyNS, LPCTSTR szNamespace )
{
HRESULT hr = S_OK;
LRESULT lResult;
int cPackagesEnum = 0;
TCHAR szDUPackageName[MAX_PATH + 1];
DWORD dwStrSize = MAX_PATH;
BOOL fIsSystemClass = FALSE;
while ((lResult = RegEnumValue(hKeyNS, cPackagesEnum++, szDUPackageName,
&dwStrSize, NULL, NULL, NULL, NULL)) == ERROR_SUCCESS)
{
IJavaPackage *pijp;
#ifndef UNICODE
MAKE_WIDEPTR_FROMANSI(swzPackage, szDUPackageName );
MAKE_WIDEPTR_FROMANSI(swzNamespace, szNamespace );
#else
OLESTR swzPackage = szDUPackageName;
OLESTR swzNamespace = szNamespace;
#endif
hr = m_pijpm->GetPackage( swzPackage,
((*szNamespace == '\0')? NULL : swzNamespace),
&pijp );
if ( SUCCEEDED(hr) )
{
BSTR bstrPath;
hr = pijp->GetFilePath( &bstrPath );
if ( SUCCEEDED(hr) ) {
CPackageNode *pPackageNode;
pPackageNode = new CPackageNode(szDUPackageName, szNamespace);
if (pPackageNode == NULL)
{
hr = E_OUTOFMEMORY;
pijp->Release();
break;
}
#ifndef UNICODE
MAKE_ANSIPTR_FROMWIDE(szPath, bstrPath );
#else
TCHAR *szPath = bstrPath;
#endif
pPackageNode->SetPath( szPath );
pijp->IsSystemClass(&fIsSystemClass);
pPackageNode->SetIsSystemClass(fIsSystemClass);
if (m_pHeadPackageList == NULL)
{
m_pHeadPackageList = pPackageNode;
m_pCurPackageNode = m_pHeadPackageList;
}
else
{
hr = m_pCurPackageNode->Insert(pPackageNode);
m_pCurPackageNode = m_pCurPackageNode->GetNextPackageNode();
}
SysFreeString( bstrPath );
pijp->Release(); // we're done with the package
}
}
else
{
m_dwStatus = STATUS_CTRL_DAMAGED;
hr = S_OK; // don't barf if this doesn't work, some villain might have uninstalled it
}
dwStrSize = MAX_PATH;
}
return hr;
}
HRESULT CParseInf::DoParseDU(LPCTSTR szOCXFileName, LPCTSTR szCLSID)
{
HRESULT hr = S_OK;
TCHAR szFileName[MAX_PATH];
TCHAR szDUSvrName[MAX_PATH];
const TCHAR *pszSvrFile = NULL;
DWORD dwFileSize = 0;
HKEY hKeyFiles = 0;
HKEY hKeyDU = 0;
HKEY hKeyDLInfo = 0;
TCHAR szDistUnit[MAX_REGPATH_LEN];
HRESULT lResult;
CFileNode *pFileNode = NULL;
DWORD dwExpire;
DWORD dw;
Assert(szCLSID != NULL);
// Since this function was called, we must be a distribution unit.
// Set a member flag so that all other member functions realize that
// we are really part of a DU now.
m_bIsDistUnit = TRUE;
// initialization
if ( szOCXFileName != NULL )
lstrcpyn(m_szFileName, szOCXFileName, ARRAYSIZE(m_szFileName));
lstrcpyn(m_szCLSID, szCLSID, ARRAYSIZE(m_szCLSID));
Init();
// Add files from ...\Distribution Units\{Name}\Contains\Files
CatPathStrN( szDistUnit, REGSTR_PATH_DIST_UNITS, szCLSID, ARRAYSIZE(szDistUnit));
lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szDistUnit, 0, KEY_READ,
&hKeyDU);
if (lResult != ERROR_SUCCESS)
{
hr = E_FAIL;
goto ExitDoParseDU;
}
hr = BuildDUFileList( hKeyDU );
if (FAILED(hr))
{
goto ExitDoParseDU;
}
hr = BuildDUPackageList( hKeyDU );
if (FAILED(hr))
{
goto ExitDoParseDU;
}
// Now add the OSD and INF files
lResult = RegOpenKeyEx(hKeyDU, REGSTR_DOWNLOAD_INFORMATION, 0,
KEY_READ, &hKeyDLInfo);
if (lResult == ERROR_SUCCESS)
{
TCHAR *pFileName = NULL;
TCHAR szBuffer[MAX_PATH + 1];
dw = MAX_PATH;
lResult = RegQueryValueEx(hKeyDLInfo, REGSTR_VALUE_INF, NULL, NULL,
(unsigned char*)szBuffer, &dw);
if (lResult == ERROR_SUCCESS)
{
pFileName = ReverseStrchr(szBuffer, '\\');
if (pFileName != NULL)
{
pFileName++;
// set INF member variable
lstrcpyn(m_szInf, szBuffer, ARRAYSIZE(m_szInf));
pFileNode = new CFileNode(szBuffer, "", NULL);
if (pFileNode == NULL)
{
hr = E_OUTOFMEMORY;
goto ExitDoParseDU;
}
// create and add node to list
if (m_pHeadFileList == NULL)
{
m_pHeadFileList = pFileNode;
m_pCurFileNode = m_pHeadFileList;
}
else
{
hr = m_pCurFileNode->Insert(pFileNode);
m_pCurFileNode = m_pCurFileNode->GetNextFileNode();
}
}
}
pFileName = NULL;
dw = MAX_PATH;
lResult = RegQueryValueEx(hKeyDLInfo, REGSTR_VALUE_OSD, NULL, NULL,
(unsigned char*)szBuffer, &dw);
if (lResult == ERROR_SUCCESS)
{
pFileName = ReverseStrchr(szBuffer, '\\');
if (pFileName != NULL)
{
pFileName++;
pFileNode = new CFileNode(szBuffer, "", NULL);
// create and add node to list
if (m_pHeadFileList == NULL)
{
m_pHeadFileList = pFileNode;
m_pCurFileNode = m_pHeadFileList;
}
else
{
hr = m_pCurFileNode->Insert(pFileNode);
m_pCurFileNode = m_pCurFileNode->GetNextFileNode();
}
}
}
}
// See if there's an Expire value, and if so, override the default/general expire.
dw = sizeof(DWORD);
dwExpire = 0;
if ( RegQueryValueEx(hKeyDU, REGSTR_VALUE_EXPIRE, NULL, NULL, (LPBYTE)&dwExpire, &dw) == ERROR_SUCCESS )
{
if ( dwExpire )
m_cExpireDays = dwExpire;
else
GetDaysBeforeExpireAuto(&m_cExpireDays);
}
// Find out where COM thinks our CLSID is, and what the server name is.
if ( FGetCLSIDFile( szDUSvrName, szCLSID ) )
{
m_bHasActiveX = TRUE;
pszSvrFile = PathFindFileName(szDUSvrName);
}
else
szDUSvrName[0] = '\0';
for (m_pCurFileNode = m_pHeadFileList;
m_pCurFileNode != NULL;
m_pCurFileNode = m_pCurFileNode->GetNextFileNode(), hr = S_OK)
{
const TCHAR *pszPath = m_pCurFileNode->GetPath();
if (pszPath != NULL)
{
CatPathStrN( szFileName, m_pCurFileNode->GetPath(), m_pCurFileNode->GetName(), ARRAYSIZE(szFileName));
}
else
{
lstrcpyn(szFileName, m_pCurFileNode->GetName(),ARRAYSIZE(szFileName));
}
if (SUCCEEDED(hr = GetSizeOfFile(szFileName, &dwFileSize)))
{
if (pszPath == NULL ||
IsModuleRemovable(szFileName) ||
lstrcmpi(szFileName, m_szFileName) == 0)
{
m_dwFileSizeSaved += dwFileSize;
}
// only play with the status if we haven't already flagged the installation
// as damaged and we're looking at the the file that should be the host for
// our control, if any.
if ( m_dwStatus != STATUS_CTRL_DAMAGED && pszSvrFile != NULL &&
lstrcmpi( pszSvrFile, m_pCurFileNode->GetName() ) == 0 )
{
TCHAR szDUSvrNameSPN[MAX_PATH];
TCHAR szFileNameSPN[MAX_PATH];
GetShortPathName(szDUSvrName, szDUSvrNameSPN, MAX_PATH);
GetShortPathName(szFileName, szFileNameSPN, MAX_PATH);
if ( lstrcmpi( szDUSvrNameSPN, szFileNameSPN ) == 0 )
m_dwStatus = STATUS_CTRL_INSTALLED; // no, we're not unplugged
else // server and our file are in different directories - unplugged scenario
m_dwStatus = STATUS_CTRL_UNPLUGGED;
}
m_dwTotalFileSize += dwFileSize;
} else if ( !PathFileExists( szFileName ) ) // if a DU file is missing, then the installation is damaged.
m_dwStatus = STATUS_CTRL_DAMAGED;
m_nTotalFiles += 1;
}
// If we're still unsure, and there are packages, then this is a pure Java
// DU and will say we're installed unless a check of the package files indicates otherwise.
if ( m_pHeadPackageList != NULL && m_dwStatus == STATUS_CTRL_UNKNOWN )
m_dwStatus = STATUS_CTRL_INSTALLED;
// Accumulate package sizes and such into our running total
for (m_pCurPackageNode = m_pHeadPackageList;
m_pCurPackageNode != NULL;
m_pCurPackageNode = m_pCurPackageNode->GetNextPackageNode(), hr = S_OK)
{
// the files can hold more than one of our packages, so only add a package
// path file to the totals if we haven't already counted it.
// N^2 to be sure, but the numbers will be small.
CPackageNode *ppn;
LPCTSTR szPackagePath = m_pCurPackageNode->GetPath();
BOOL bAlreadySeen = FALSE;
for ( ppn = m_pHeadPackageList;
ppn != m_pCurPackageNode && !bAlreadySeen;
ppn = ppn->GetNextPackageNode() )
bAlreadySeen = lstrcmp( szPackagePath, ppn->GetPath() ) == 0;
if ( bAlreadySeen )
continue;
// Must be a new file,
if ( SUCCEEDED(GetSizeOfFile(szPackagePath, &dwFileSize)) )
{
m_dwFileSizeSaved += dwFileSize;
m_dwTotalFileSize += dwFileSize;
}
else
m_dwStatus = STATUS_CTRL_DAMAGED;
// m_nTotalFiles += 1; don't count these files, or the dependency file list will have a bunch of blank entries
}
// Some DUs, like SportsZone or Shockwave, have no Contains subkeys.
// If status is still unknown here, but the server is in place, consider it
// installed.
if ( m_dwStatus == STATUS_CTRL_UNKNOWN && PathFileExists( szDUSvrName ) )
m_dwStatus = STATUS_CTRL_INSTALLED;
ExitDoParseDU:
if (hKeyDU)
{
RegCloseKey(hKeyDU);
}
if (hKeyDLInfo)
{
RegCloseKey(hKeyDLInfo);
}
return hr;
}
// ---------------------------------------------------------------------------
// CParseInf::IsSectionInINF
// Checks if a section is in the INF
// returns:
// S_OK: lpCurCode has the satellite binary name
// S_FALSE: ignore this code and use default resources in main dll
// E_XXX: any other error
BOOL
CParseInf::IsSectionInINF(
LPCSTR lpCurCode)
{
const char *szDefault = "";
DWORD len;
#define FAKE_BUF_SIZE 3
char szBuf[FAKE_BUF_SIZE];
len = GetPrivateProfileString(lpCurCode, NULL, szDefault,
szBuf, FAKE_BUF_SIZE, m_szInf);
if (len == (FAKE_BUF_SIZE - 2)) { // returns Out Of Buffer Space?
// yes, section found
return TRUE;
} else {
return FALSE;
}
}
// loop through the keys in [Add.Code} section and enumerate the
// files and their corresponding sections.
HRESULT CParseInf::HandleSatellites(LPCTSTR pszFileName)
{
HRESULT hr = S_OK;
// BEGIN NOTE: add vars and values in matching order
// add a var by adding a new define VAR_NEW_VAR = NUM_VARS++
const char *szVars[] = {
#define VAR_LANG 0 // expands to 3 letter lang code based on lcid
"%LANG%",
#define NUM_VARS 1
""
};
const char *szValues[NUM_VARS + 1];
szValues[VAR_LANG] = "***"; // unint magic
szValues[NUM_VARS] = NULL;
// END NOTE: add vars and values in matching order
// look for and substitute variables like %EXTRACT_DIR%
// and expand out the command line
TCHAR szSectionName[MAX_PATH];
TCHAR szSectionNameCopy[MAX_PATH];
hr = ExpandCommandLine(pszFileName, szSectionName, MAX_PATH, szVars, szValues);
if (hr != S_OK)
return hr; // no vars to expand ignore section
lstrcpy(szSectionNameCopy, szSectionName); // preserve
// OK, this is a satellite DLL. Now we need to find the section(s) that
// got installed.
// we first enum the registry's Module Usage looking for DLLs that were
// installed by (or used by) this CLSID. For each of those we need to
// check if the base filename matches the pattern of the section,
// if it does then we process those sections
DWORD iSubKey = 0;
TCHAR szModName[MAX_PATH];
while ( SUCCEEDED(hr = FindDLLInModuleUsage( szModName, m_szCLSID, iSubKey)) ) {
if (PatternMatch(szModName, szSectionName) &&
IsSectionInINF(szSectionName) ) {
// create new node
CFileNode *pFileNode = new CFileNode(szSectionName, szSectionName);
if (pFileNode == NULL)
{
hr = E_OUTOFMEMORY;
goto Exit;
}
// don't insert file into list if it's path cannot be found
if (FAILED(GetFilePath(pFileNode)))
{
delete pFileNode;
continue;
}
// create and add node to list
if (m_pHeadFileList == NULL)
{
m_pHeadFileList = pFileNode;
m_pCurFileNode = m_pHeadFileList;
}
else if (SUCCEEDED(hr = m_pCurFileNode->Insert(pFileNode)))
{
m_pCurFileNode = m_pCurFileNode->GetNextFileNode();
}
else
{
goto Exit;
}
lstrcpy(szSectionName, szSectionNameCopy); // restore
}
}
if ( hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS)) {
hr = S_OK;
}
Exit:
return hr;
}
// loop through the keys in [Add.Code} section and enumerate the
// files and their corresponding sections.
HRESULT CParseInf::EnumSections()
{
HRESULT hr = S_OK;
TCHAR szSectionBuffer[MAX_INF_SECTION_SIZE];
TCHAR szValueBuffer[MAX_PATH];
TCHAR *pszFileName = NULL;
CFileNode *pFileNode = NULL;
DWORD dwLen = GetPrivateProfileString(
KEY_ADDCODE,
NULL,
DEFAULT_VALUE,
szSectionBuffer,
MAX_INF_SECTION_SIZE,
m_szInf);
if (dwLen == 0)
{
// if inf file or [Add.Code] section
// does not exist, just delete the OCX
Assert (m_pHeadFileList == NULL);
// separate file name from its directory
Assert( lstrlen(m_szFileName) < ARRAYSIZE(szValueBuffer) );
lstrcpy(szValueBuffer, m_szFileName);
TCHAR *szName = ReverseStrchr(szValueBuffer, '\\');
Assert (szName != NULL);
if (szName == NULL)
{
hr = E_UNEXPECTED;
goto ExitEnumSections;
}
// create a node of the OCX and put it in a linked list
m_pHeadFileList = new CFileNode(szName + 1, DEFAULT_VALUE);
if (m_pHeadFileList == NULL)
{
hr = E_OUTOFMEMORY;
goto ExitEnumSections;
}
m_pCurFileNode = m_pHeadFileList;
*szName = '\0';
if (FAILED(hr = m_pHeadFileList->SetPath(szValueBuffer)))
{
goto ExitEnumSections;
}
hr = S_FALSE;
goto ExitEnumSections;
}
// For OCX's that have an INF file and [Add.Code] section, loop
// through the section to get filenames and section names. Store
// each file and its section in a node and add the node to a
// linked list
for (pszFileName = szSectionBuffer;
pszFileName[0] != '\0';
pszFileName += lstrlen(pszFileName) + 1)
{
dwLen = GetPrivateProfileString(
KEY_ADDCODE,
pszFileName,
DEFAULT_VALUE,
szValueBuffer,
MAX_PATH,
m_szInf);
// skip the file if no section is specified for it
if (dwLen == 0) {
continue;
}
if (StrChr(pszFileName, '%')) {
// if section not found and it contains a %
// could be a variable like %LANG% that gets
// substituted to install satellite DLLs
// check if it has any vars that we know about
// and expand them and add filenodes if reqd.
if (HandleSatellites(pszFileName) == S_OK) {
// if this expanded to a satellite dll name then
// we would have already added that
// as a node in HandleSatellites
continue;
}
}
// create new node
pFileNode = new CFileNode(pszFileName, szValueBuffer);
if (pFileNode == NULL)
{
hr = E_OUTOFMEMORY;
goto ExitEnumSections;
}
// don't insert file into list if it's path cannot be found
if (FAILED(GetFilePath(pFileNode)))
{
delete pFileNode;
continue;
}
// create and add node to list
if (m_pHeadFileList == NULL)
{
m_pHeadFileList = pFileNode;
m_pCurFileNode = m_pHeadFileList;
}
else if (SUCCEEDED(hr = m_pCurFileNode->Insert(pFileNode)))
{
m_pCurFileNode = m_pCurFileNode->GetNextFileNode();
}
else
{
goto ExitEnumSections;
}
}
// include inf file into file list
if (m_pHeadFileList && m_pCurFileNode)
{
hr = m_pCurFileNode->Insert(new CFileNode(m_szInf, DEFAULT_VALUE));
if (SUCCEEDED(hr))
m_pCurFileNode = m_pCurFileNode->GetNextFileNode();
}
ExitEnumSections:
return hr;
}
// Loop through all the sections in [Setup Hooks]. For each
// section, call ParseUninstallSection to find its UNINSTALL section
// and execute it.
HRESULT CParseInf::ParseSetupHook()
{
HRESULT hr = S_FALSE; // Return S_FALSE if we don't run into any errors, but also don't do any work.
TCHAR szSectionBuffer[MAX_INF_SECTION_SIZE];
TCHAR szSection[MAX_PATH];
TCHAR *pszKey = NULL;
DWORD dwLen = GetPrivateProfileString(
KEY_SETUPHOOK,
NULL,
DEFAULT_VALUE,
szSectionBuffer,
MAX_INF_SECTION_SIZE,
m_szInf);
// no Setup Hook section found
if (dwLen == 0)
goto EXITPARSESETUPHOOK;
for (pszKey = szSectionBuffer;
pszKey[0] != '\0';
pszKey += lstrlen(pszKey) + 1)
{
// For each key, get the section and run the section with RunSetupCommand
dwLen = GetPrivateProfileString(
KEY_SETUPHOOK,
pszKey,
DEFAULT_VALUE,
szSection,
MAX_PATH,
m_szInf);
if (dwLen == 0)
continue;
hr = ParseUninstallSection(szSection);
if (FAILED(hr))
goto EXITPARSESETUPHOOK;
}
EXITPARSESETUPHOOK:
return hr;
}
// Go to each file's section, find its conditional hook section, then
// call ParseUninstallSection to execute the conditional hook section.
HRESULT CParseInf::ParseConditionalHook()
{
HRESULT hr = S_FALSE; // Return S_FALSE if we don't run into any errors, but also don't do any work.
TCHAR szHookSection[MAX_PATH];
const TCHAR *pszSection = NULL;
CFileNode *pNode = NULL;
if (m_pHeadFileList == NULL)
{
hr = S_FALSE;
goto EXITPARSECONDITIONALHOOK;
}
pNode = m_pHeadFileList;
for (pNode = m_pHeadFileList; pNode != NULL; pNode = pNode->GetNextFileNode())
{
pszSection = pNode->GetSection();
if (pszSection == NULL)
continue;
if (GetPrivateProfileString(
pszSection,
KEY_HOOK,
DEFAULT_VALUE,
szHookSection,
MAX_PATH,
m_szInf) == 0)
continue;
hr = ParseUninstallSection(szHookSection);
if (FAILED(hr))
goto EXITPARSECONDITIONALHOOK;
}
EXITPARSECONDITIONALHOOK:
return hr;
}
// Given a file section, find its UNINSTALL section, go to the
// section and executes the commands there
HRESULT CParseInf::ParseUninstallSection(LPCTSTR lpszSection)
{
HRESULT hr = S_OK;
TCHAR szUninstallSection[MAX_PATH];
TCHAR szBuf[MAX_PATH];
TCHAR szInfSection[MAX_PATH];
TCHAR szCacheDir[MAX_PATH];
HANDLE hExe = INVALID_HANDLE_VALUE;
HINSTANCE hInst = NULL;
// check for "UNINSTALL" key
DWORD dwLen = GetPrivateProfileString(
lpszSection,
KEY_UNINSTALL,
DEFAULT_VALUE,
szUninstallSection,
ARRAYSIZE(szUninstallSection),
m_szInf);
// UNINSTALL key not found, quit.
if (dwLen == 0)
{
return S_FALSE;
}
// There are 4 possible combinations inside the uninstall section
// 1) Both inffile and infsection are specified -> simply to go those
// 2) Only inffile is given -> go to inffile and do DefaultInstall
// 3) Only infsection is given -> do infsection in this inf file
// 4) Nothing is specified -> simply do this section
GetDirectory(GD_EXTRACTDIR, szCacheDir, ARRAYSIZE(szCacheDir), m_szFileName);
lstrcpyn(szBuf, szCacheDir, MAX_PATH - 1);
lstrcat(szBuf, TEXT("\\"));
int cch = lstrlen(szBuf);
dwLen = GetPrivateProfileString(
szUninstallSection,
KEY_INFFILE,
DEFAULT_VALUE,
szBuf + cch,
MAX_PATH - cch,
m_szInf);
if (dwLen == 0)
{
szBuf[0] = '\0';
}
// get inf section
dwLen = GetPrivateProfileString(
szUninstallSection,
KEY_INFSECTION,
DEFAULT_VALUE,
szInfSection,
ARRAYSIZE(szInfSection),
m_szInf);
if (dwLen == 0)
{
if (szBuf[0] != '\0')
lstrcpyn(szInfSection, KEY_DEFAULTUNINSTALL,ARRAYSIZE(szInfSection));
else
{
lstrcpyn(szBuf, m_szInf,ARRAYSIZE(szBuf));
lstrcpyn(szInfSection, szUninstallSection,ARRAYSIZE(szInfSection));
}
}
// load advpack.dll and call RunSetupCommand() to process
// any special uninstall commands
hr = STG_E_FILENOTFOUND;
HINSTANCE hinstAdvPack = LoadLibrary(TEXT("ADVPACK.DLL"));
if (hinstAdvPack)
{
RUNSETUPCOMMAND pfnRunSetup = (RUNSETUPCOMMAND)GetProcAddress(
hinstAdvPack, achRUNSETUPCOMMANDFUNCTION);
if (pfnRunSetup)
{
hr = pfnRunSetup(NULL, szBuf, szInfSection,
szCacheDir, NULL, &hExe, 1, NULL);
}
}
return hr;
}
// For each file specified in the INF file, find its
// path in this order
// 1) OCX path
// 2) System dir
// 3) Windows dir
// 4) PATH directories
HRESULT CParseInf::GetFilePath(CFileNode *pFileNode)
{
Assert (pFileNode != NULL);
HRESULT hr = S_OK;
TCHAR szValueBuf[MAX_PATH];
TCHAR *pszPathPtr = NULL;
TCHAR *pszPathEnv = NULL;
TCHAR *pchPathEnd = NULL;
DWORD dwLenPATH = 0;
// ocx directory
hr = GetDirectory(GD_EXTRACTDIR, szValueBuf, ARRAYSIZE(szValueBuf), m_szFileName);
CatPathStrN( szValueBuf, szValueBuf, pFileNode->GetName(), ARRAYSIZE(szValueBuf));
// if file being searched for now is the OCX itself, just leave
if (lstrcmpi(szValueBuf, m_szFileName) == 0)
{
goto EXITGETFILEPATH;
}
if (SUCCEEDED(hr) &&
SUCCEEDED(LookUpModuleUsage(szValueBuf, m_szCLSID)))
{
goto EXITGETFILEPATH;
}
// system directory
hr = GetDirectory(GD_SYSTEMDIR, szValueBuf, ARRAYSIZE(szValueBuf));
if (SUCCEEDED(hr) && CatPathStrN( szValueBuf, szValueBuf, pFileNode->GetName(), ARRAYSIZE(szValueBuf)) &&
SUCCEEDED(LookUpModuleUsage(szValueBuf, m_szCLSID)))
{
goto EXITGETFILEPATH;
}
// windows directory
hr = GetDirectory(GD_WINDOWSDIR, szValueBuf, ARRAYSIZE(szValueBuf));
if (SUCCEEDED(hr) && CatPathStrN( szValueBuf, szValueBuf, pFileNode->GetName(), ARRAYSIZE(szValueBuf)) &&
SUCCEEDED(LookUpModuleUsage(szValueBuf, m_szCLSID)))
{
goto EXITGETFILEPATH;
}
// get PATH envirnment variable
dwLenPATH = GetEnvironmentVariable(ENV_PATH, szValueBuf, 0);
if (dwLenPATH == 0)
{
hr = E_FAIL;
goto EXITGETFILEPATH;
}
pszPathEnv = new TCHAR[dwLenPATH];
if (pszPathEnv == NULL)
{
hr = E_OUTOFMEMORY;
goto EXITGETFILEPATH;
}
GetEnvironmentVariable(ENV_PATH, pszPathEnv, dwLenPATH);
pchPathEnd = pszPathPtr = pszPathEnv;
// walk all directories in PATH and see if file is found
// in any of them
while (pchPathEnd != NULL)
{
pchPathEnd = StrChr(pszPathPtr, ';');
if (pchPathEnd != NULL)
*pchPathEnd = '\0';
CatPathStrN( szValueBuf, pszPathPtr, pFileNode->GetName(), ARRAYSIZE(szValueBuf));
if (SUCCEEDED(LookUpModuleUsage(szValueBuf, m_szCLSID)))
goto EXITGETFILEPATH;
if (pchPathEnd != NULL)
*(pchPathEnd++) = ';';
pszPathPtr = pchPathEnd;
}
// file not found anywhere
hr = E_FAIL;
EXITGETFILEPATH:
if (pszPathEnv != NULL)
delete [] pszPathEnv;
if (SUCCEEDED(hr))
{
hr = NullLastSlash(szValueBuf, 0);
if (SUCCEEDED(hr))
{
hr = pFileNode->SetPath(szValueBuf);
}
}
return hr;
}
HRESULT CParseInf::CheckFilesRemovability(void)
{
HRESULT hr = S_OK;
TCHAR szFullName[MAX_PATH];
const TCHAR *pszPath = NULL;
BOOL bFileExist;
// Walk through every file and see if it is deletable. If so,
// then check if for sharing violations on that file.
for (m_pCurFileNode = m_pHeadFileList;
m_pCurFileNode != NULL && SUCCEEDED(hr);
m_pCurFileNode = m_pCurFileNode->GetNextFileNode())
{
pszPath = m_pCurFileNode->GetPath();
if (pszPath == NULL || pszPath[0] == '\0')
continue;
CatPathStrN( szFullName, pszPath, m_pCurFileNode->GetName(), ARRAYSIZE(szFullName) );
if (IsModuleRemovable(szFullName))
{
HANDLE h = CreateFile(
szFullName,
GENERIC_READ|GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_NO_BUFFERING,
NULL);
if (h == INVALID_HANDLE_VALUE)
{
bFileExist = (GetLastError() != ERROR_FILE_NOT_FOUND);
if (bFileExist)
{
hr = STG_E_SHAREVIOLATION;
break;
}
}
else
{
CloseHandle(h);
m_pCurFileNode->SetRemovable( TRUE );
}
}
}
return hr;
}
HRESULT CParseInf::CheckLegacyRemovability(LONG *cOldSharedCount )
{
HRESULT hr = S_OK;
BOOL bFileExist;
HANDLE h = CreateFile(
m_szFileName,
GENERIC_READ|GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_NO_BUFFERING,
NULL);
if (h == INVALID_HANDLE_VALUE)
{
bFileExist = (GetLastError() != ERROR_FILE_NOT_FOUND);
if (bFileExist)
{
hr = STG_E_SHAREVIOLATION;
} else
hr = S_FALSE;
}
else
{
CloseHandle(h);
}
if ( SUCCEEDED(hr) )
hr = CheckFilesRemovability();
return hr;
}
HRESULT CParseInf::CheckDURemovability(HKEY hkeyDUDB, BOOL bSilent)
{
HRESULT hr = S_OK;
BOOL bAskSystemClass = TRUE;
hr = CheckFilesRemovability();
if (FAILED(hr)) {
goto CheckDURemovabilityExit;
}
hr = CheckDUDependencies(hkeyDUDB, bSilent);
if (FAILED(hr)) {
goto CheckDURemovabilityExit;
}
// Check for package removability.
// We shouldn't remove a package if another DU also uses it.
// TODO: Some sort of package-currently-in-use test. Either test the path file, as above,
// or use some groovy new IJavaPackage(Manager) method.
for (m_pCurPackageNode = m_pHeadPackageList;
m_pCurPackageNode != NULL;
m_pCurPackageNode = m_pCurPackageNode->GetNextPackageNode())
{
TCHAR szT[MAX_PATH];
LRESULT lResult;
BOOL bFoundInOtherDU = FALSE;
int cDistUnitEnum = 0;
if (!bAskSystemClass && m_pCurPackageNode->GetIsSystemClass()) {
char lpszBuf[MAX_MSGBOX_STRING_LEN];
char lpszBufTitle[MAX_MSGBOX_TITLE_LEN];
MLLoadString(IDS_OCCACHE_WARNING_JAVA_SYSTEM_CLASS,
lpszBuf, MAX_MSGBOX_STRING_LEN);
MLLoadString(IDS_REMOVAL_WARNING,
lpszBufTitle, MAX_MSGBOX_TITLE_LEN);
// Attempting to remove system class. Warn user.
if ( bSilent ||
MessageBox(NULL, lpszBuf, lpszBufTitle,
MB_YESNO | MB_ICONWARNING) != IDYES) {
hr = E_FAIL;
goto CheckDURemovabilityExit;
}
bAskSystemClass = FALSE;
}
// Enumerate distribution units
while ( (lResult = RegEnumKey(hkeyDUDB, cDistUnitEnum++, szT, MAX_PATH)) == ERROR_SUCCESS &&
!bFoundInOtherDU )
{
if ( lstrcmp(szT, m_szCLSID) != 0 ) // skip the current DU
{
HKEY hkeyDUCJ;
DWORD dw = MAX_PATH;
lstrcat(szT, REGSTR_DU_CONTAINS_JAVA );
lResult = RegOpenKeyEx( hkeyDUDB, szT, 0, KEY_READ, &hkeyDUCJ );
if ( lResult == ERROR_SUCCESS )
{
lResult = RegQueryValueEx(hkeyDUCJ, REGSTR_VALUE_INF, NULL, NULL,
(unsigned char*)szT, &dw);
// To be safe, assume that anything other than value not found means
// that the other DU also uses the package.
bFoundInOtherDU = lResult != ERROR_FILE_NOT_FOUND;
RegCloseKey( hkeyDUCJ );
} // if we could open other key's Contains\Java subkey
} // if it's a different DU
} // while enumerating DUs
// if we found it in another DU, then we shouldn't remove this package with this DU
m_pCurPackageNode->SetRemovable( !bFoundInOtherDU );
} // for each package
CheckDURemovabilityExit:
return hr;
}
HRESULT CParseInf::RemoveLegacyControl( LPCTSTR lpszTypeLibID, BOOL bSilent )
{
HRESULT hr = S_FALSE;
const TCHAR *pszPath;
BOOL bUnplug = m_dwStatus != STATUS_CTRL_UNPLUGGED;
BOOL bFileMissing = !PathFileExists( m_szFileName );
BOOL bDidRemove = FALSE;
TCHAR szFullName[MAX_PATH];
// loop through the list of assocated files, remove them as
// well as their registry entries.
for (m_pCurFileNode = m_pHeadFileList;
m_pCurFileNode != NULL;
m_pCurFileNode = m_pCurFileNode->GetNextFileNode())
{
int cOwners;
pszPath = m_pCurFileNode->GetPath();
// Process INF file, which as no path since it's not described in INF
if (pszPath == NULL || pszPath[0] == '\0')
{
if ( DeleteFile(m_pCurFileNode->GetName()) )
hr = S_OK; // hey, we did _something_ - averts the "not enough info" message
continue;
}
// If we're where, we had some other file besides the INF.
// Even if we don't remove it, we still knock down its module
// usage, which has gotta count for having done something.
hr = S_OK;
CatPathStrN( szFullName, pszPath, m_pCurFileNode->GetName(), MAX_PATH);
cOwners = SubtractModuleOwner( szFullName, m_szCLSID );
if (m_pCurFileNode->GetRemovable() && cOwners == 0)
{
if ( bUnplug )
UnregisterOCX(szFullName);
DeleteFile(szFullName);
bDidRemove = bDidRemove || StrCmpI(szFullName,m_szFileName) == 0;
}
}
if (hr == S_OK && bDidRemove && lpszTypeLibID != NULL)
CleanInterfaceEntries(lpszTypeLibID);
if ( bUnplug && bFileMissing )
{
if ( m_szFileName[0] != '\0' ) // only do this if there is an ocx to clean up after
CleanOrphanedRegistry(m_szFileName, m_szCLSID, lpszTypeLibID);
}
return hr;
}
HRESULT CParseInf::RemoveDU( LPTSTR szFullName, LPCTSTR lpszTypeLibID, HKEY hkeyDUDB, BOOL bSilent )
{
HRESULT hr = S_FALSE; // only say S_OK if we actually do something beyond yanking the INF
const TCHAR *pszPath = NULL;
hr = RemoveLegacyControl( lpszTypeLibID, bSilent );
if (SUCCEEDED(hr))
{
// Remove the packages that we have determined are safe to remove.
for (m_pCurPackageNode = m_pHeadPackageList;
m_pCurPackageNode != NULL;
m_pCurPackageNode = m_pCurPackageNode->GetNextPackageNode())
{
if ( m_pCurPackageNode->GetRemovable() )
{
Assert(m_pijpm != NULL);
#ifdef UNICODE
OLECHAR *swzPkg = m_pCurPackageNode->GetName();
OLECHAR *swzNamespace = m_pCurPackageNode->GetNamespace();
#else
MAKE_WIDEPTR_FROMANSI(swzPkg, m_pCurPackageNode->GetName());
MAKE_WIDEPTR_FROMANSI(swzNamespace, m_pCurPackageNode->GetNamespace());
#endif
hr = m_pijpm->UninstallPackage( swzPkg,
((*swzNamespace == 0)? NULL : swzNamespace),
0 );
}
}
}
DeleteKeyAndSubKeys(hkeyDUDB, m_szCLSID);
return hr;
}
HRESULT CParseInf::CheckDUDependencies(HKEY hKeyDUDB, BOOL bSilent )
{
long lrDist = 0;
long lResult = 0;
long lr = 0;
int iSubKey = 0;
HKEY hkeyCurrent = 0;
HKEY hkeyCurDU = 0;
char szName[MAX_REGPATH_LEN];
int iValue = 0;
unsigned long ulSize;
char szDependency[MAX_REGPATH_LEN];
HKEY hkeyCOM = 0;
DWORD dwType = 0;
char szDepName[MAX_CONTROL_NAME_LEN];
char szDepWarnBuf[MAX_MSGBOX_STRING_LEN];
char szCOMControl[MAX_REGPATH_LEN];
DWORD dwSize = 0;
HRESULT hr = S_OK;
// Iterate through DUs that have a ...\contains\Distribution Units
// key in the registry and compare the entries inside with the DU
// being removed.
while ((lResult = RegEnumKey(hKeyDUDB, iSubKey++, szName,
MAX_REGPATH_LEN)) == ERROR_SUCCESS)
{
if (!lstrcmpi(szName, m_szCLSID))
{
// Skip ourselves
continue;
}
if (RegOpenKeyEx(hKeyDUDB, szName, 0, KEY_READ, &hkeyCurrent) ==
ERROR_SUCCESS)
{
lr = RegOpenKeyEx(hkeyCurrent, REGSTR_DU_CONTAINS_DIST_UNITS,
0, KEY_READ, &hkeyCurDU);
if (lr != ERROR_SUCCESS)
{
RegCloseKey(hkeyCurrent);
continue;
}
ulSize = MAX_REGPATH_LEN;
while ((lResult = RegEnumValue(hkeyCurDU, iValue++, szDependency,
&ulSize, NULL, NULL, NULL, NULL)) == ERROR_SUCCESS)
{
if (!lstrcmpi(szDependency, m_szCLSID))
{
// dependency found
// Try to get a friendly name for the dependency control
dwSize = MAX_CONTROL_NAME_LEN;
lResult = RegQueryValueEx(hkeyCurrent, NULL, NULL,
&dwType, (unsigned char *)szDepName,
&dwSize);
if (lResult != ERROR_SUCCESS || szDepName[0] == '\0') {
// Couldn't get a friendly name. Try the COM branch.
// Technically, this could overflow because
// szName and szCOMControl are the same size, but
// this is already at our defined maximum size for reg
// entries.
wsprintf(szCOMControl, "%s\\%s", REGSTR_COM_BRANCH, szName);
lResult = RegOpenKeyEx(HKEY_CLASSES_ROOT, szCOMControl,
0, KEY_READ, &hkeyCOM);
if (lResult != ERROR_SUCCESS) {
MLLoadString(IDS_OCCACHE_WARNING_DEP_REMOVAL_NAME_UNKNOWN,
szDepWarnBuf, MAX_MSGBOX_STRING_LEN);
}
else {
dwSize = MAX_CONTROL_NAME_LEN;
lResult = RegQueryValueEx(hkeyCOM, NULL, NULL,
&dwType, (unsigned char *)szDepName,
&dwSize);
if (lResult != ERROR_SUCCESS || szDepName[0] == '\0') {
MLLoadString(IDS_OCCACHE_WARNING_DEP_REMOVAL_NAME_UNKNOWN,
szDepWarnBuf, MAX_MSGBOX_STRING_LEN);
}
else {
char lpszBuf[MAX_MSGBOX_STRING_LEN];
MLLoadString(IDS_OCCACHE_WARNING_DEPENDENCY_REMOVAL,
lpszBuf, MAX_MSGBOX_STRING_LEN);
wsprintf(szDepWarnBuf, lpszBuf, szDepName);
}
if (hkeyCOM) {
RegCloseKey(hkeyCOM);
}
}
}
else {
char lpszBuf[MAX_MSGBOX_STRING_LEN];
MLLoadString(IDS_OCCACHE_WARNING_DEPENDENCY_REMOVAL,
lpszBuf, MAX_MSGBOX_STRING_LEN);
wsprintf(szDepWarnBuf, lpszBuf, szDepName);
}
// TODO: Consider using better HWND than desktop
char lpszBufTitle[MAX_MSGBOX_TITLE_LEN];
MLLoadString(IDS_REMOVAL_WARNING,
lpszBufTitle, MAX_MSGBOX_TITLE_LEN);
if (bSilent ||
MessageBox(NULL, szDepWarnBuf, lpszBufTitle,
MB_YESNO | MB_ICONWARNING) != IDYES)
{
hr = E_FAIL;
RegCloseKey(hkeyCurDU);
RegCloseKey(hkeyCurrent);
RegCloseKey(hKeyDUDB);
goto ReturnCheckDUDependencies;
}
}
ulSize = MAX_REGPATH_LEN;
}
RegCloseKey(hkeyCurDU);
RegCloseKey(hkeyCurrent);
}
}
ReturnCheckDUDependencies:
return hr;
}
// uninstall OCX and its associated files
HRESULT CParseInf::RemoveFiles(
LPCTSTR lpszTypeLibID /* = NULL */,
BOOL bForceRemove, /* = FALSE */
DWORD dwIsDistUnit,
BOOL bSilent)
{
HRESULT hr = S_OK;
HRESULT hrInf1;
HRESULT hrInf2;
TCHAR szFullName[MAX_PATH];
const TCHAR *pszPath = NULL;
BOOL bRemovable = (dwIsDistUnit) ? (TRUE) : (IsModuleRemovable(m_szFileName));
BOOL bIsOCX = FALSE;
LONG cRefOld = 0;
HKEY hKeyDUDB = 0;
BOOL bUnplug = m_dwStatus == STATUS_CTRL_DAMAGED || m_dwStatus == STATUS_CTRL_INSTALLED;
if ( !g_fAllAccess || (!bForceRemove && !bRemovable))
{
hr = E_ACCESSDENIED;
goto ExitRemoveFiles;
}
// Check sharing violation (if it is a legacy control)
if (!dwIsDistUnit)
{
hr = CheckLegacyRemovability( &cRefOld );
// set SharedDlls count to 1 and save up the old
// count in case the removal fails
if (hr == S_OK && !bRemovable &&
FAILED(hr = SetSharedDllsCount(m_szFileName, 1, &cRefOld)))
{
hr = (!PathFileExists( m_szFileName ) ? S_OK : hr);
goto ExitRemoveFiles;
}
if ( FAILED(hr) )
goto ExitRemoveFiles;
}
else
{
long lResultDist;
lResultDist = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_DIST_UNITS, 0,
KEY_READ, &hKeyDUDB);
if (lResultDist == ERROR_SUCCESS)
hr = CheckDURemovability( hKeyDUDB, bSilent );
else
hr = E_FAIL;
if ( FAILED(hr) )
goto ReturnRemoveFiles;
}
// ** keyword UNINSTALL -- new feature that hasn't been implemented yet **
// parse [Setup Hook], look for "UNINSTALL" key
if (FAILED(hrInf1 = ParseSetupHook()))
{
goto ExitRemoveFiles;
}
// parse conditional hooks in each of the file sections
if (FAILED(hrInf2 = ParseConditionalHook()))
{
goto ExitRemoveFiles;
}
// Okay, if the both didn't do anything, we'll try the DefaultUninstall
if ( hrInf2 == S_FALSE && hrInf2 == S_FALSE && PathFileExists( m_szInf ) )
{
// see if there's anybody home in the default uninstall section
DWORD dwSize = GetPrivateProfileString( KEY_DEFAULTUNINSTALL,
NULL,
DEFAULT_VALUE,
szFullName,
MAX_PATH,
m_szInf );
if ( dwSize > 0 )
{
HINSTANCE hinstAdvPack = LoadLibrary(TEXT("ADVPACK.DLL"));
HANDLE hExe = INVALID_HANDLE_VALUE;
GetDirectory(GD_EXTRACTDIR, szFullName, ARRAYSIZE(szFullName), m_szInf);
if (hinstAdvPack)
{
RUNSETUPCOMMAND pfnRunSetup = (RUNSETUPCOMMAND)GetProcAddress(
hinstAdvPack, achRUNSETUPCOMMANDFUNCTION);
if (pfnRunSetup)
{
// reset hrINf2 to reflect the success of running the default
// uninstall section. This will prevent us from pointing to the
// Add/Remove control panel in some cases, like Shockwave.
hrInf2 = pfnRunSetup(NULL, m_szInf, KEY_DEFAULTUNINSTALL,
szFullName, NULL, &hExe, RSC_FLAG_INF, NULL);
}
FreeLibrary( hinstAdvPack );
}
}
}
if ( !dwIsDistUnit )
hr = RemoveLegacyControl( lpszTypeLibID, bSilent );
else
hr = RemoveDU( szFullName, lpszTypeLibID, hKeyDUDB, bSilent );
if ( FAILED(hr) )
goto ExitRemoveFiles;
// Return S_FALSE iff none of our uninstall efforts succeeded
if ( hr == S_FALSE && (hrInf1 == S_OK || hrInf2 == S_OK) )
hr = S_OK;
// remove conflict directory
if (SUCCEEDED(GetDirectory(GD_CONFLICTDIR, szFullName, ARRAYSIZE(szFullName))) &&
LStrNICmp(m_szFileName, szFullName, lstrlen(szFullName)) == 0)
{
TCHAR *pCh = ReverseStrchr(m_szFileName, '\\');
Assert (pCh != NULL);
TCHAR chTemp = *pCh;
*pCh = '\0';
RemoveDirectory(m_szFileName);
*pCh = chTemp;
}
DestroyFileList();
ExitRemoveFiles:
// set shared dlls count back to where it was if OCX cannot be removed
if (cRefOld > 0 && FileExist(m_szFileName))
{
if (SUCCEEDED(hr))
hr = SetSharedDllsCount(m_szFileName, cRefOld);
else
SetSharedDllsCount(m_szFileName, cRefOld);
}
if ( hKeyDUDB )
RegCloseKey( hKeyDUDB );
ReturnRemoveFiles:
return hr;
}
void CParseInf::SetIsDistUnit(BOOL bDist)
{
m_bIsDistUnit = bDist;
}
BOOL CParseInf::GetIsDistUnit() const
{
return m_bIsDistUnit;
}
// return total size of OCX and its associated files
DWORD CParseInf::GetTotalFileSize() const
{
return m_dwTotalFileSize;
}
DWORD CParseInf::GetTotalSizeSaved() const
{
return m_dwFileSizeSaved;
}
DWORD CParseInf::GetStatus() const
{
return m_dwStatus;
}
// return total number of files which will be removed
// together with the OCX
int CParseInf::GetTotalFiles() const
{
return m_nTotalFiles;
}
// return first file in the list of associated files
CFileNode* CParseInf::GetFirstFile()
{
m_pFileRetrievalPtr = m_pHeadFileList;
return m_pFileRetrievalPtr;
}
// get the next file in the list of associated files
CFileNode* CParseInf::GetNextFile()
{
m_pFileRetrievalPtr = m_pFileRetrievalPtr->GetNextFileNode();
return m_pFileRetrievalPtr;
}
// return first file in the list of associated files
CPackageNode* CParseInf::GetFirstPackage()
{
m_pPackageRetrievalPtr = m_pHeadPackageList;
return m_pPackageRetrievalPtr;
}
// get the next file in the list of associated files
CPackageNode* CParseInf::GetNextPackage()
{
m_pPackageRetrievalPtr = (m_pPackageRetrievalPtr != NULL)?
m_pPackageRetrievalPtr->GetNextPackageNode() :
NULL;
return m_pPackageRetrievalPtr;
}