1959 lines
60 KiB
C++
1959 lines
60 KiB
C++
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
// 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;
|
||
|
}
|
||
|
|