windows-nt/Source/XPSP1/NT/enduser/windows.com/wuv3/wuv3is/detect.cpp
2020-09-26 16:20:57 +08:00

863 lines
17 KiB
C++

//=======================================================================
//
// Copyright (c) 1999 Microsoft Corporation. All Rights Reserved.
//
// File: detect.cpp
//
// Purpose: Component Detection
//
// History: 3/9/99 YAsmi Created
//
//=======================================================================
#include "stdafx.h"
#include "detect.h"
#include "debug.h"
#include <wustl.h>
#include "log.h"
LPCSTR ParseField(LPCSTR pszStr, LPSTR pszTokOut, int cTokSize);
BOOL UninstallKeyExists(LPCSTR pszUninstallKey);
static int CompareLocales(LPCSTR pszLoc1, LPCSTR pszLoc2);
//
// CComponentDetection
//
CComponentDetection::CComponentDetection() :
m_dwDetectStatus(ICI_NOTINITIALIZED),
m_dwDLLReturnValue(0),
m_dwInstalledVer(0),
m_dwInstalledBuild(0)
{
int i;
for (i = 0; i < (int)ccValueCount; i++)
{
m_ppValue[i] = (char*)malloc(ccMaxSize);
}
// custom data has its own size and can be longer than MaxSize
m_dwCustomDataSize = ccMaxSize;
for (i = 0; i < (int)ccDLLCount; i++)
{
m_pDLLs[i].bUsed = FALSE;
}
ClearValues();
}
CComponentDetection::~CComponentDetection()
{
int i;
//
// free cached libraries
//
for (i = 0; i < (int)ccDLLCount; i++)
{
if (m_pDLLs[i].bUsed)
FreeLibrary(m_pDLLs[i].hLib);
}
for (i = 0; i < (int)ccValueCount; i++)
{
free(m_ppValue[i]);
}
}
void CComponentDetection::ClearValues()
{
for (int i = 0; i < (int)ccValueCount; i++)
{
m_ppValue[i][0] = '\0';
}
}
BOOL CComponentDetection::SetValue(enumCCValue cc, LPCSTR pszValue)
{
if (cc <= ccLastValue)
{
if (cc == ccCustomData)
{
// we allow custom data to be as long as possible so we will reallocate if neccessory
DWORD l = strlen(pszValue) + 1;
if (l > m_dwCustomDataSize)
{
free(m_ppValue[cc]);
m_ppValue[cc] = _strdup(pszValue);
m_dwCustomDataSize = l;
}
else
{
strncpy(m_ppValue[cc], pszValue, m_dwCustomDataSize);
}
return TRUE;
}
else
{
// we don't copy more than ccMaxSize
strncpy(m_ppValue[cc], pszValue, ccMaxSize - 1);
return TRUE;
}
}
return FALSE;
}
BOOL CComponentDetection::GetValue(enumCCValue cc, LPSTR pszValue, DWORD dwSize)
{
if (cc <= ccLastValue)
{
strncpy(pszValue, m_ppValue[cc], dwSize);
if (pszValue[0] != '\0')
return TRUE;
}
return FALSE;
}
// detects using the current values specified by SetValue
// clears all the values after detection but we can still retrieve the
// status of last detection using GetLastDetectStatus
//
// Returns the status of detection ICI_INSTALLED etc
DWORD CComponentDetection::Detect()
{
DWORD dwDetStat;
m_dwDLLReturnValue = 0;
dwDetStat = IsComponentInstalled();
ClearValues();
return dwDetStat;
}
DWORD CComponentDetection::GetLastDetectStatus()
{
return m_dwDetectStatus;
}
DWORD CComponentDetection::GetLastDLLReturnValue()
{
return m_dwDLLReturnValue;
}
void CComponentDetection::GetInstalledVersion(LPDWORD pdwVer, LPDWORD pdwBuild)
{
*pdwVer = m_dwInstalledVer;
*pdwBuild = m_dwInstalledBuild;
}
HINSTANCE CComponentDetection::CacheLoadLibrary(LPCSTR pszDLLName, LPCTSTR pszDLLFullPath)
{
int iAvailable = -1;
int iFound = -1;
//
// check the cache to see if we already have loaded it
//
for (int i = 0; i < (int)ccDLLCount; i++)
{
if (m_pDLLs[i].bUsed)
{
if (_stricmp(pszDLLName, m_pDLLs[i].szDLLName) == 0)
{
iFound = i;
break;
}
}
else
{
if (iAvailable == -1)
iAvailable = i;
}
}
if (iFound != -1)
{
//
// returned the cached libary instance
//
return m_pDLLs[iFound].hLib;
}
else
{
//
// load and cache the libary
//
if (iAvailable == -1)
{
iAvailable = 0;
if (m_pDLLs[iAvailable].bUsed)
{
FreeLibrary(m_pDLLs[iAvailable].hLib);
m_pDLLs[iAvailable].bUsed = FALSE;
}
}
m_pDLLs[iAvailable].hLib = LoadLibrary(pszDLLFullPath);
if (m_pDLLs[iAvailable].hLib != NULL)
{
strcpy(m_pDLLs[iAvailable].szDLLName, pszDLLName);
m_pDLLs[iAvailable].bUsed = TRUE;
}
else
{
TRACE_HR(HRESULT_FROM_WIN32(GetLastError()), "Could not load %s (%d)", pszDLLFullPath, HRESULT_FROM_WIN32(GetLastError()));
}
return m_pDLLs[iAvailable].hLib;
}
}
HRESULT CComponentDetection::CallDetectDLL(LPCSTR pszDll, LPCSTR pszEntry)
{
USES_CONVERSION;
HRESULT hr = E_FAIL;
HINSTANCE hLib;
DETECTION_STRUCT Det;
DWORD dwCifVer, dwCifBuild;
char szLocale[8];
char szGUID[128];
m_dwDetectStatus = ICI_NOTINSTALLED;
GetValue(ccGUID, szGUID, sizeof(szGUID));
GetLocale(szLocale, sizeof(szLocale));
GetVersion(&dwCifVer, &dwCifBuild);
//
// init the Detection structure
//
Det.dwSize = sizeof(DETECTION_STRUCT);
Det.pdwInstalledVer = &m_dwInstalledVer;
Det.pdwInstalledBuild = &m_dwInstalledBuild;
Det.pszLocale = szLocale;
Det.pszGUID = szGUID;
Det.dwAskVer = dwCifVer;
Det.dwAskBuild = dwCifBuild;
Det.pCifFile = NULL;
Det.pCifComp = (ICifComponent*)this;
TCHAR szDllFile[MAX_PATH];
GetWindowsUpdateDirectory(szDllFile);
lstrcat(szDllFile, A2T(pszDll));
//
// load the detection dll
//
hLib = CacheLoadLibrary(pszDll, szDllFile);
if (hLib)
{
DETECTVERSION fpDetVer = (DETECTVERSION)GetProcAddress(hLib, pszEntry);
if (fpDetVer)
{
//
// call the entry point
//
m_dwDLLReturnValue = fpDetVer(&Det);
switch(m_dwDLLReturnValue)
{
case DET_NOTINSTALLED:
m_dwDetectStatus = ICI_NOTINSTALLED;
break;
case DET_INSTALLED:
m_dwDetectStatus = ICI_INSTALLED;
break;
case DET_NEWVERSIONINSTALLED:
m_dwDetectStatus = ICI_OLDVERSIONAVAILABLE;
break;
case DET_OLDVERSIONINSTALLED:
m_dwDetectStatus = ICI_NEWVERSIONAVAILABLE;
break;
}
hr = NOERROR;
}
else
{
TRACE_HR(HRESULT_FROM_WIN32(GetLastError()), "Detection DLL call failed %s %s", pszDll, pszEntry);
}
}
return hr;
}
STDMETHODIMP_(DWORD) CComponentDetection::IsComponentInstalled()
{
USES_CONVERSION;
char szDllName[32];
char szDllEntry[32];
DWORD dwUnused;
DWORD dwIsInstalled;
char szGUID[128];
HKEY hComponentKey = NULL;
m_dwDetectStatus = ICI_NOTINSTALLED;
//
// if we need to call detection dll, call it
//
if (SUCCEEDED(GetDetVersion(szDllName, sizeof(szDllName), szDllEntry, sizeof(szDllEntry))))
{
if (SUCCEEDED(CallDetectDLL(szDllName, szDllEntry)))
{
if (m_dwDetectStatus == ICI_OLDVERSIONAVAILABLE)
m_dwDetectStatus = ICI_INSTALLED;
return m_dwDetectStatus;
}
}
//
// build GUID registry key
//
GetValue(ccGUID, szGUID, sizeof(szGUID));
TCHAR szKeyName[MAX_PATH];
lstrcpy(szKeyName, COMPONENT_KEY);
lstrcat(szKeyName, _T("\\"));
lstrcat(szKeyName, A2T(szGUID));
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKeyName, 0, KEY_READ, &hComponentKey) == ERROR_SUCCESS)
{
//
// first check for the IsInstalled valuename
// if the valuename is there AND equals zero, we say not installed.
// otherwise continue.
//
// NOTE: We default to ISINSTALLED_YES if valuename not present to be Back-compatible
//
dwUnused = sizeof(dwIsInstalled);
if (RegQueryValueEx(hComponentKey, ISINSTALLED_KEY, 0, NULL, (LPBYTE) (&dwIsInstalled), &dwUnused) != ERROR_SUCCESS)
dwIsInstalled = ISINSTALLED_YES;
if (dwIsInstalled == ISINSTALLED_YES)
{
//
// next check for a locale match (no locale entry uses default)
//
DWORD dwType;
TCHAR szRegLocale[8];
dwUnused = sizeof(szRegLocale);
if (RegQueryValueEx(hComponentKey, LOCALE_KEY, 0, NULL, (LPBYTE)szRegLocale, &dwUnused) != ERROR_SUCCESS)
lstrcpy(szRegLocale, DEFAULT_LOCALE);
char szAskLocale[8];
GetValue(ccLocale, szAskLocale, sizeof(szAskLocale));
if (CompareLocales(T2A(szRegLocale), szAskLocale) == 0)
{
//
// locales match so go check the QFEversio, version in that order
//
BOOL bVersionFound = FALSE;
TCHAR szRegVer[128];
DWORD dwCifVer, dwCifBuild;
dwUnused = sizeof(szRegVer);
bVersionFound = (RegQueryValueEx(hComponentKey, QFE_VERSION_KEY, 0, &dwType, (LPBYTE)szRegVer, &dwUnused) == ERROR_SUCCESS);
if (!bVersionFound)
{
dwUnused = sizeof(szRegVer);
bVersionFound = (RegQueryValueEx(hComponentKey, VERSION_KEY, 0, &dwType, (LPBYTE)szRegVer, &dwUnused) == ERROR_SUCCESS);
}
if (bVersionFound)
{
if (dwType == REG_SZ)
{
ConvertVersionStrToDwords(szRegVer, &m_dwInstalledVer, &m_dwInstalledBuild);
GetVersion(&dwCifVer, &dwCifBuild);
if ((m_dwInstalledVer > dwCifVer) ||
((m_dwInstalledVer == dwCifVer) && (m_dwInstalledBuild >= dwCifBuild)))
{
m_dwDetectStatus = ICI_INSTALLED;
}
else
{
m_dwDetectStatus = ICI_NEWVERSIONAVAILABLE;
}
}
else
{
// if a string field is not found assume we have a newer version
m_dwDetectStatus = ICI_NEWVERSIONAVAILABLE;
}
} //version found
} //locales match
} // installed key
}
if (hComponentKey)
RegCloseKey(hComponentKey);
//
// we think its installed, now try to verify using uninstall key
//
if (m_dwDetectStatus != ICI_NOTINSTALLED)
{
char szUninstallKey[ccMaxSize];
if (GetValue(ccUninstallKey, szUninstallKey, sizeof(szUninstallKey)))
{
if (!UninstallKeyExists(szUninstallKey))
{
m_dwDetectStatus = ICI_NOTINSTALLED;
}
}
}
return m_dwDetectStatus;
}
STDMETHODIMP CComponentDetection::GetDetVersion(LPSTR pszDll, DWORD dwdllSize, LPSTR pszEntry, DWORD dwentSize)
{
char szBuf[ccMaxSize];
if (pszDll && pszEntry)
*pszDll = *pszEntry = '\0';
else
return E_FAIL;
if (GetValue(ccDetectVersion, szBuf, sizeof(szBuf)))
{
LPCSTR pszParse = szBuf;
pszParse = ParseField(pszParse, pszDll, dwdllSize);
pszParse = ParseField(pszParse, pszEntry, dwentSize);
}
if (pszDll[0] == '\0' || pszEntry[0] == '\0')
return E_FAIL;
else
return NOERROR;
}
STDMETHODIMP CComponentDetection::GetCustomData(LPSTR pszKey, LPSTR pszData, DWORD dwSize)
{
USES_CONVERSION;
if (_stricmp(pszKey, "DetectVersion") == 0)
{
strncpy(pszData, m_ppValue[ccDetectVersion], dwSize);
return NOERROR;
}
char szKeyName[128];
LPCSTR pCus = m_ppValue[ccCustomData];
LPCSTR pBeg = pCus;
LPCSTR pEq;
strcpy(szKeyName, "_");
strcat(szKeyName, pszKey);
// look for the _ key name
pBeg = stristr(pBeg, szKeyName);
while (pBeg != NULL)
{
// we found a match ensure that its at the begining of a line
if ((pBeg == pCus) || (*(pBeg - 1) == '\n'))
{
// point to equal sign
pEq = pBeg + strlen(szKeyName);
// skip spaces
while (*pEq == ' ')
pEq++;
if (*pEq == '=')
{
// skip the equal sign
pEq++;
// copy the value into pszData
LPSTR p = pszData;
int i = dwSize - 1;
while ((*pEq != '\n') && (i > 0))
{
*p++ = *pEq++;
i--;
}
*p = '\0';
TRACE("Detect GetCustomData %s returns %s", A2T(pszKey), A2T(pszData));
return NOERROR;
}
} // not the begining of the line
}
TRACE("Detect GetCustomData %s not found", A2T(pszKey));
return E_FAIL;
}
STDMETHODIMP CComponentDetection::GetVersion(LPDWORD pdwVersion, LPDWORD pdwBuild)
{
USES_CONVERSION;
char szBuf[ccMaxSize];
if (GetValue(ccVersion, szBuf, sizeof(szBuf)))
{
ConvertVersionStrToDwords(A2T(szBuf), pdwVersion, pdwBuild);
return NOERROR;
}
else
return E_FAIL;
}
STDMETHODIMP CComponentDetection::GetGUID(LPSTR pszGUID, DWORD dwSize)
{
if (GetValue(ccGUID, pszGUID, dwSize))
return NOERROR;
else
return E_FAIL;
}
STDMETHODIMP CComponentDetection::GetLocale(LPSTR pszLocale, DWORD dwSize)
{
if (GetValue(ccLocale, pszLocale, dwSize))
return NOERROR;
else
return E_FAIL;
}
STDMETHODIMP CComponentDetection::GetUninstallKey(LPSTR pszKey, DWORD dwSize)
{
if (GetValue(ccUninstallKey, pszKey, dwSize))
return NOERROR;
else
return E_FAIL;
}
STDMETHODIMP CComponentDetection::GetID(LPSTR pszID, DWORD dwSize)
{
return E_NOTIMPL;
}
STDMETHODIMP CComponentDetection::GetDescription(LPSTR pszDesc, DWORD dwSize)
{
return E_NOTIMPL;
}
STDMETHODIMP CComponentDetection::GetDetails(LPSTR pszDetails, DWORD dwSize)
{
return E_NOTIMPL;
}
STDMETHODIMP CComponentDetection::GetUrl(UINT uUrlNum, LPSTR pszUrl, DWORD dwSize, LPDWORD pdwUrlFlags)
{
return E_NOTIMPL;
}
STDMETHODIMP CComponentDetection::GetFileExtractList(UINT uUrlNum, LPSTR pszExtract, DWORD dwSize)
{
return E_NOTIMPL;
}
STDMETHODIMP CComponentDetection::GetUrlCheckRange(UINT uUrlNum, LPDWORD pdwMin, LPDWORD pdwMax)
{
return E_NOTIMPL;
}
STDMETHODIMP CComponentDetection::GetCommand(UINT uCmdNum, LPSTR pszCmd, DWORD dwCmdSize,
LPSTR pszSwitches, DWORD dwSwitchSize, LPDWORD pdwType)
{
return E_NOTIMPL;
}
STDMETHODIMP CComponentDetection::GetInstalledSize(LPDWORD pdwWin, LPDWORD pdwApp)
{
return E_NOTIMPL;
}
STDMETHODIMP_(DWORD) CComponentDetection::GetDownloadSize()
{
return E_NOTIMPL;
}
STDMETHODIMP_(DWORD) CComponentDetection::GetExtractSize()
{
return E_NOTIMPL;
}
STDMETHODIMP CComponentDetection::GetSuccessKey(LPSTR pszKey, DWORD dwSize)
{
return E_NOTIMPL;
}
STDMETHODIMP CComponentDetection::GetProgressKeys(LPSTR pszProgress, DWORD dwProgSize, LPSTR pszCancel, DWORD dwCancelSize)
{
return E_NOTIMPL;
}
STDMETHODIMP CComponentDetection::IsActiveSetupAware()
{
return E_NOTIMPL;
}
STDMETHODIMP CComponentDetection::IsRebootRequired()
{
return E_NOTIMPL;
}
STDMETHODIMP CComponentDetection::RequiresAdminRights()
{
return E_NOTIMPL;
}
STDMETHODIMP_(DWORD) CComponentDetection::GetPriority()
{
return E_NOTIMPL;
}
STDMETHODIMP CComponentDetection::GetDependency(UINT uDepNum, LPSTR pszID, DWORD dwBuf, char *pchType, LPDWORD pdwVer, LPDWORD pdwBuild)
{
return E_NOTIMPL;
}
STDMETHODIMP_(DWORD) CComponentDetection::GetPlatform()
{
return E_NOTIMPL;
}
STDMETHODIMP_(BOOL) CComponentDetection::DisableComponent()
{
return E_NOTIMPL;
}
STDMETHODIMP CComponentDetection::GetMode(UINT uModeNum, LPSTR pszMode, DWORD dwSize)
{
return E_NOTIMPL;
}
STDMETHODIMP CComponentDetection::GetGroup(LPSTR pszID, DWORD dwSize)
{
return E_NOTIMPL;
}
STDMETHODIMP CComponentDetection::IsUIVisible()
{
return E_NOTIMPL;
}
STDMETHODIMP CComponentDetection::GetPatchID(LPSTR pszID, DWORD dwSize)
{
return E_NOTIMPL;
}
STDMETHODIMP CComponentDetection::GetTreatAsOneComponents(UINT uNum, LPSTR pszID, DWORD dwBuf)
{
return E_NOTIMPL;
}
STDMETHODIMP_(DWORD) CComponentDetection::GetCurrentPriority()
{
return E_NOTIMPL;
}
STDMETHODIMP CComponentDetection::SetCurrentPriority(DWORD dwPriority)
{
return E_NOTIMPL;
}
STDMETHODIMP_(DWORD) CComponentDetection::GetActualDownloadSize()
{
return E_NOTIMPL;
}
STDMETHODIMP CComponentDetection::IsComponentDownloaded()
{
return E_NOTIMPL;
}
STDMETHODIMP_(DWORD) CComponentDetection::IsThisVersionInstalled(DWORD dwAskVer, DWORD dwAskBld, LPDWORD pdwVersion, LPDWORD pdwBuild)
{
return E_NOTIMPL;
}
STDMETHODIMP_(DWORD) CComponentDetection::GetInstallQueueState()
{
return E_NOTIMPL;
}
STDMETHODIMP CComponentDetection::SetInstallQueueState(DWORD dwState)
{
return E_NOTIMPL;
}
LPCSTR ParseField(LPCSTR pszStr, LPSTR pszTokOut, int cTokSize)
{
LPCSTR pszRetVal = NULL;
LPSTR p;
LPSTR p2;
if (pszStr == NULL || *pszStr == '\0')
{
pszTokOut[0] = '\0';
return NULL;
}
// look for a comma separator
p = strstr(pszStr, ",");
if (p != NULL)
{
int l = p - pszStr;
if (l >= cTokSize)
l = cTokSize - 1;
strncpy(pszTokOut, pszStr, l);
pszTokOut[l] = '\0';
pszRetVal = p + 1;
}
else
{
strncpy(pszTokOut, pszStr, cTokSize - 1);
}
//strip spaces
p = pszTokOut;
p2 = pszTokOut;
while (*p2)
{
if (*p2 != ' ')
*p++ = *p2++;
else
p2++;
}
*p = '\0';
return pszRetVal;
}
static int CompareLocales(LPCSTR pszLoc1, LPCSTR pszLoc2)
{
if (pszLoc1[0] == '*' || pszLoc2[0] == '*')
return 0;
else
return _stricmp(pszLoc1, pszLoc2);
}
void ConvertVersionStrToDwords(LPCTSTR pszVer, LPDWORD pdwVer, LPDWORD pdwBuild)
{
USES_CONVERSION;
DWORD dwTemp1,dwTemp2;
LPCSTR pszParse = T2A(pszVer);
char szBuf[20];
pszParse = ParseField(pszParse, szBuf, sizeof(szBuf));
dwTemp1 = atoi(szBuf);
pszParse = ParseField(pszParse, szBuf, sizeof(szBuf));
dwTemp2 = atoi(szBuf);
*pdwVer = (dwTemp1 << 16) + dwTemp2;
pszParse = ParseField(pszParse, szBuf, sizeof(szBuf));
dwTemp1 = atoi(szBuf);
pszParse = ParseField(pszParse, szBuf, sizeof(szBuf));
dwTemp2 = atoi(szBuf);
*pdwBuild = (dwTemp1 << 16) + dwTemp2;
}
BOOL UninstallKeyExists(LPCSTR pszUninstallKey)
{
USES_CONVERSION;
HKEY hUninstallKey = NULL;
TCHAR szUninstallKey[MAX_PATH];
if (!pszUninstallKey) //if the pointer is NULL, assume installed
return TRUE;
lstrcpy(szUninstallKey, UNINSTALL_BRANCH);
lstrcat(szUninstallKey, _T("\\"));
lstrcat(szUninstallKey, A2T(pszUninstallKey));
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, szUninstallKey, 0, KEY_READ, &hUninstallKey) == ERROR_SUCCESS)
{
RegCloseKey(hUninstallKey);
return TRUE;
}
else
return FALSE;
}
//reterives file version
BOOL GetFileVersionDwords(LPCTSTR pszFilename, LPDWORD pdwMSVer, LPDWORD pdwLSVer)
{
BOOL bRetVal = FALSE;
DWORD dwHandle;
DWORD dwVerInfoSize = GetFileVersionInfoSize((LPTSTR)pszFilename, &dwHandle);
if (dwVerInfoSize)
{
LPVOID lpBuffer = LocalAlloc(LPTR, dwVerInfoSize);
if (lpBuffer)
{
// Read version stamping info
if (GetFileVersionInfo((LPTSTR)pszFilename, dwHandle, dwVerInfoSize, lpBuffer))
{
// Get the value for Translation
UINT uiSize;
VS_FIXEDFILEINFO* lpVSFixedFileInfo;
if (VerQueryValue(lpBuffer, _T("\\"), (LPVOID*)&lpVSFixedFileInfo, &uiSize) && (uiSize))
{
*pdwMSVer = lpVSFixedFileInfo->dwFileVersionMS;
*pdwLSVer = lpVSFixedFileInfo->dwFileVersionLS;
bRetVal = TRUE;
}
}
LocalFree(lpBuffer);
}
}
if (!bRetVal)
{
*pdwMSVer = *pdwLSVer = 0L;
}
return bRetVal;
}