/////////////////////////////////////////////////////////////////////////////// // Implementation of class CParseInf // // CParseInf is created to deal with parsing of an INF file. #include #include "ParseInf.h" #include "resource.h" #include "init.h" #include "global.h" #include #include #include #include // for STATUS_CTRL values #include #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; }