3731 lines
93 KiB
C++
3731 lines
93 KiB
C++
//=======================================================================
|
|
//
|
|
// Copyright (c) 1998-1999 Microsoft Corporation. All Rights Reserved.
|
|
//
|
|
// File: cv3.cpp
|
|
//
|
|
// Purpose: V3 control main code
|
|
//
|
|
//=======================================================================
|
|
|
|
#include "stdafx.h"
|
|
#include "WUV3IS.h"
|
|
#include <stdio.h>
|
|
#include <initguid.h>
|
|
#include <inseng.h>
|
|
#include <shlwapi.h>
|
|
|
|
#define USEWUV3INCLUDES
|
|
#include <wuv3.h>
|
|
#undef USEWUV3INCLUDES
|
|
|
|
#include <winspool.h>
|
|
#include <cstate.h>
|
|
#include <wustl.h>
|
|
#include <osdet.h>
|
|
#include "printers.h"
|
|
#include "progress.h"
|
|
#include "newtrust.h"
|
|
#include "history.h"
|
|
#include "CV3.h"
|
|
#include "detect.h"
|
|
#include "callback.h"
|
|
#include "locstr.h"
|
|
#include "safearr.h"
|
|
#include "install.h"
|
|
#include "log.h"
|
|
#include "template.h"
|
|
#include "filecrc.h"
|
|
#include <shlguid.h>
|
|
#include <wininet.h>
|
|
#include <servpaus.h>
|
|
#include "..\..\inc\wuverp.h"
|
|
|
|
|
|
|
|
const TCHAR REGPATH_EXPLORER[] = _T("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer");
|
|
const TCHAR REGKEY_WINUPD_DISABLED[] = _T("NoWindowsUpdate");
|
|
|
|
#define EXENAME_128BIT _T("128BIT.EXE")
|
|
|
|
|
|
//
|
|
// State management class. All retrieved catalogs are stored here. When the
|
|
// control exits then this class cleans up the memory used by each catalog
|
|
// stored with the state module. So the application must not delete any catalogs
|
|
// that have been added to state storage.
|
|
//
|
|
CState g_v3state;
|
|
TCHAR CCV3::s_szControlVer[20] = _T("\0");
|
|
|
|
|
|
void DownloadDLL(CDiamond *pDiamond, CWUDownload *pDownload, LPCTSTR pszDLLName, LPCTSTR pszPath);
|
|
void DownloadFileVerInfo(CWUDownload* pDownload);
|
|
PBYTE DownloadOemInfo(CWUDownload *pDownload, CDiamond *pDiamond);;
|
|
void DownloadCabs(LPCTSTR szLocalDir, CWUDownload* pDownload, CDiamond* pDiamond, PBYTE pCabList, PBYTE pCRCList, IWUProgress* pProgress, BOOL bUnCab);
|
|
void DownloadCif(LPCTSTR szLocalDir, CDiamond* pDiamond, PINSTALLINFOSTRUCT pInstallInfo);
|
|
void Download128Bit(LPCTSTR pszLocalDir, PINVENTORY_ITEM pItem, IWUProgress* pProgress);
|
|
|
|
|
|
void DetectPlatAndLang();
|
|
void DetectActiveSetupWU(CWUDownload* pDownload, CDiamond* pDiamond, CCatalog* pCatalog);
|
|
void DownloadItem(LPCTSTR pszLocalDir, CWUDownload *pDownload, CDiamond *pDiamond, PINSTALLINFOSTRUCT pInstallInfo, IWUProgress* pProgress);
|
|
void InstallItem(LPCTSTR pszLocalDir, PSELECTITEMINFO pStatusInfo, PINSTALLINFOSTRUCT pInstallInfo, IWUProgress* pProgress);
|
|
DWORD WINAPI RebootThreadProc(LPVOID pv);
|
|
void CheckDescDiagInfo(CCatalog* pCatalog);
|
|
|
|
bool IsArabicOrHebrew()
|
|
{
|
|
WORD wCurPrimeLang = PRIMARYLANGID(LOWORD(g_v3state.GetBrowserLocale()));
|
|
return LANG_HEBREW == wCurPrimeLang || LANG_ARABIC == wCurPrimeLang;
|
|
}
|
|
|
|
|
|
//
|
|
// CDescriptionMerger class
|
|
//
|
|
// a local class used to merge description files
|
|
//
|
|
class CDescriptionMerger
|
|
{
|
|
public:
|
|
CDescriptionMerger()
|
|
: m_pMap(NULL),
|
|
m_pDiamond(NULL)
|
|
{
|
|
}
|
|
|
|
~CDescriptionMerger()
|
|
{
|
|
if (m_pMap != NULL)
|
|
delete m_pMap;
|
|
if (m_pDiamond != NULL)
|
|
delete m_pDiamond;
|
|
}
|
|
|
|
HRESULT CheckMerge(PINSTALLINFOSTRUCT pInstallInfo);
|
|
|
|
private:
|
|
CCRCMapFile* m_pMap;
|
|
CDiamond* m_pDiamond;
|
|
|
|
HRESULT ReadMapFile(CCatalog* pCatalog);
|
|
};
|
|
|
|
|
|
//
|
|
// CCV3 class
|
|
//
|
|
|
|
// this function is called when our ref count becomes zero
|
|
void CCV3::FinalRelease()
|
|
{
|
|
LOG_block("CCV3::FinalRelease");
|
|
|
|
//
|
|
// reset state
|
|
//
|
|
g_v3state.Reset();
|
|
|
|
//
|
|
// make sure we undo registration of MS trust key if we registred it
|
|
//
|
|
CConnSpeed::WriteToRegistry();
|
|
}
|
|
|
|
|
|
// this function is called after our object is created, this is the prefered
|
|
// place to do initialization
|
|
HRESULT CCV3::FinalConstruct()
|
|
{
|
|
LOG_block("CCV3::FinalConstruct");
|
|
wsprintf(CCV3::s_szControlVer, _T("5,04,%d,%d"), VER_PRODUCTBUILD,VER_PRODUCTBUILD_QFE);
|
|
|
|
TCHAR szLogFN[MAX_PATH];
|
|
TCHAR szDate[50];
|
|
TCHAR szTime[50];
|
|
GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, NULL, NULL, szDate, sizeof(szDate)/sizeof(szDate[0]));
|
|
GetTimeFormat(LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER, NULL, NULL, szTime, sizeof(szTime)/sizeof(szTime[0]));
|
|
|
|
LOG_out("Windows Update V3 Internet Site Control, Version %s",s_szControlVer);
|
|
LOG_out("Session starting %s at %s", szDate, szTime);
|
|
|
|
//
|
|
// set up the application log
|
|
//
|
|
GetWindowsUpdateDirectory(szLogFN);
|
|
lstrcat(szLogFN, HISTORY_FILENAME);
|
|
g_v3state.AppLog().SetLogFile(szLogFN);
|
|
|
|
CConnSpeed::ReadFromRegistry();
|
|
|
|
m_bValidInstance = FALSE;
|
|
|
|
//
|
|
// seed the random number generator
|
|
//
|
|
srand((int)GetTickCount());
|
|
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// The get catalog method retrieves a catalog array from the server. The get catalog method only
|
|
// accesses the server if the catalog is not already resident on the clients computer system.
|
|
// This allows the VB script page call this method to quickly obtain filtered catalog record information.
|
|
STDMETHODIMP CCV3::GetCatalog(
|
|
IN long puidCatalog,
|
|
IN BSTR bstrServerUrl,
|
|
IN long platformId,
|
|
IN BSTR bstrBrowserLanguage, // BUGBUG - why is browser locale a string and not a long
|
|
IN long lFilters,
|
|
IN long lFlags,
|
|
OUT RETVAL VARIANT *pCatalogArray
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
CCatalog* pCatalog;
|
|
|
|
LOG_block("CCV3::GetCatalog");
|
|
try
|
|
{
|
|
pCatalog = ProcessCatalog(puidCatalog, bstrServerUrl, platformId, bstrBrowserLanguage, lFilters, lFlags);
|
|
|
|
//If we are retrieving the sub-catalog list then the return VB script array
|
|
//needs to be different.
|
|
if (pCatalog->GetCatalogPuid() == WU_CATALOG_LIST_PUID)
|
|
{
|
|
hr = MakeReturnCatalogListArray(pCatalog, lFilters, lFlags, pCatalogArray);
|
|
if (FAILED(hr))
|
|
throw hr;
|
|
}
|
|
else
|
|
{
|
|
hr = MakeReturnCatalogArray(pCatalog, lFilters, lFlags, pCatalogArray);
|
|
|
|
if (FAILED(hr))
|
|
throw hr;
|
|
}
|
|
}
|
|
catch(HRESULT hr)
|
|
{
|
|
// create an empty array so that script doesn't throw a UBound error
|
|
LPSAFEARRAY psa;
|
|
SAFEARRAYBOUND rgsabound[2];
|
|
|
|
|
|
VariantInit(pCatalogArray);
|
|
|
|
rgsabound[0].lLbound = 0;
|
|
rgsabound[0].cElements = 0;
|
|
|
|
rgsabound[1].lLbound = 0;
|
|
rgsabound[1].cElements = 8;
|
|
|
|
|
|
psa = SafeArrayCreate(VT_VARIANT, 2, rgsabound);
|
|
|
|
V_VT(pCatalogArray) = VT_ARRAY | VT_VARIANT;
|
|
V_ARRAY(pCatalogArray) = psa;
|
|
|
|
// we can't return a failure code, because that prevents script from
|
|
// honoring the return value.
|
|
return S_FALSE;
|
|
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP CCV3::GetCatalogHTML(
|
|
IN long puidCatalog,
|
|
IN BSTR bstrServerUrl,
|
|
IN long platformId,
|
|
IN BSTR bstrBrowserLanguage,
|
|
IN long lFilters,
|
|
IN long lFlags,
|
|
OUT RETVAL VARIANT *pCatalogHTML
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
CCatalog* pCatalog;
|
|
|
|
// we are currently not using the templates feature
|
|
// the code for this templates are wrapped in HTML_TEMPLATE define
|
|
|
|
#ifdef HTML_TEMPLATE
|
|
|
|
|
|
TRACE("GetCatalogHTML: %d", puidCatalog);
|
|
try
|
|
{
|
|
pCatalog = ProcessCatalog(puidCatalog, bstrServerUrl, platformId, bstrBrowserLanguage, lFilters, lFlags);
|
|
|
|
hr = MakeCatalogHTML(pCatalog, lFilters, pCatalogHTML);
|
|
if (FAILED(hr))
|
|
throw hr;
|
|
}
|
|
catch(HRESULT hr)
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
return S_OK;
|
|
|
|
#endif
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
/*
|
|
* The process catalog function constructs or retireves the requested
|
|
* V3 catalog. A pointer to the retrieved or constructed catalog is
|
|
* returned to the caller if successfull or an HRESULT error code is
|
|
* thrown if not.
|
|
*/
|
|
CCatalog *CCV3::ProcessCatalog(
|
|
IN PUID puidCatalog,
|
|
IN BSTR bstrServerUrl,
|
|
IN long platformId,
|
|
IN BSTR bstrBrowserLanguage,
|
|
IN long lFilters,
|
|
IN long lFlags
|
|
)
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
CCdm cdm;
|
|
BOOL bCheckInNeeded;
|
|
CDiamond dm;
|
|
CBitmask bmAS;
|
|
CBitmask bmCDM;
|
|
CCatalog* pCatalog;
|
|
TCHAR szIdentServer[MAX_PATH];
|
|
char szBuffer[1024];
|
|
PINVENTORY_ITEM pCatalogItem;
|
|
int i;
|
|
HRESULT hr;
|
|
|
|
LOG_block("CCV3::ProcessCatalog");
|
|
LOG_out("puidCatalog = %d, bstrServerUrl = %s", puidCatalog, OLE2T(bstrServerUrl));
|
|
LOG_out("tick count is %d", GetTickCount());
|
|
// assume that the catalog is in state storage.
|
|
bCheckInNeeded = FALSE;
|
|
|
|
//
|
|
// if catalog is not already in state storage go retrieve and prune it.
|
|
//
|
|
if ( !(pCatalog = g_v3state.Get(puidCatalog)) )
|
|
{
|
|
if (NULL == bstrBrowserLanguage) // AutoUpdate
|
|
{
|
|
// We'll use this flag to determine whether to show UI when we call VerifyFile
|
|
g_v3state.m_nClient = WUV3_CLIENT_AUTOUPDATE;
|
|
LOG_out("Client Set to AutoUpdate");
|
|
LOG_out("Client is currently %s", (CWUDownload::s_fOffline) ? "Offline" : "Online");
|
|
}
|
|
|
|
//
|
|
// check if the ident server is trusted as well as read the valid server
|
|
// list from ident.cab into memory
|
|
//
|
|
lstrcpy(szIdentServer, OLE2T(bstrServerUrl));
|
|
RemoveLastSlash(szIdentServer);
|
|
g_v3state.CheckTrustedServer(szIdentServer, &dm);
|
|
|
|
//
|
|
// check the server the control was launch from. At this point the
|
|
// ident cab is already loaded
|
|
// this function will do the work only once and it will throw if
|
|
// the server is not valid
|
|
//
|
|
CheckLaunchServer();
|
|
|
|
//
|
|
// create a new catalog object
|
|
//
|
|
pCatalog = new CCatalog(g_v3state.GetContentServer(), puidCatalog);
|
|
|
|
if (NULL == pCatalog)
|
|
{
|
|
LOG_out("ProcessCatalog: Creation of new Catalog failed. GetLastError() = %d", GetLastError());
|
|
throw HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
if (NULL != bstrBrowserLanguage) // AutoUpdate whould set it to NULL
|
|
{
|
|
pCatalog->SetBrowserLocale(OLE2T(bstrBrowserLanguage));
|
|
}
|
|
|
|
//
|
|
// Create a download object for the content
|
|
//
|
|
CWUDownload dl(pCatalog->GetCatalogServer(), 8192);
|
|
|
|
//
|
|
// read the catalog
|
|
//
|
|
pCatalog->Read(&dl, &dm);
|
|
|
|
//
|
|
// read the bitmask file
|
|
//
|
|
bmAS.Read(&dl, &dm, puidCatalog, BITMASK_ACTIVESETUP_TYPE, pCatalog->GetBitmaskName());
|
|
|
|
if (!g_v3state.m_pdwPlatformList)
|
|
{
|
|
TCHAR szOsdetServer[MAX_PATH];
|
|
lstrcpy(szOsdetServer, _T("OSDET."));
|
|
AppendExtForOS(szOsdetServer);
|
|
|
|
DownloadFileVerInfo(&dl);
|
|
DownloadDLL(&dm, &dl, szOsdetServer, NULL);
|
|
DetectPlatAndLang();
|
|
LOG_out("Platform %d", g_v3state.m_pdwPlatformList[0]);
|
|
LOG_out("Machine Language 0x%8.8x", GetMachineLangDW());
|
|
LOG_out("User UI Language 0x%8.8x", GetUserLangDW());
|
|
}
|
|
|
|
if (NULL == bstrBrowserLanguage) // AutoUpdate
|
|
{
|
|
// For Windows XP, AU can't use the system language - we need to use the MUI user language
|
|
//pCatalog->SetBrowserLocale(pCatalog->GetMachineLocaleSZ());
|
|
pCatalog->SetBrowserLocale(pCatalog->GetUserLocaleSZ());
|
|
|
|
}
|
|
|
|
if (puidCatalog == WU_CATALOG_LIST_PUID)
|
|
{
|
|
// inventory.plt is platform 0 by convention
|
|
pCatalog->SetPlatform(g_v3state.m_pdwPlatformList[0]);
|
|
}
|
|
else
|
|
{
|
|
// normal inventory item sub catalog.
|
|
if (!platformId)
|
|
{
|
|
// if the caller did not pass in a platformId then we
|
|
// need to detect the current platform and use that value
|
|
// in the catalog. In this case we luck out since the first
|
|
// platform id returned from osdet.dll is the client machines platform.
|
|
pCatalog->SetPlatform(g_v3state.m_pdwPlatformList[0]);
|
|
}
|
|
else
|
|
{
|
|
pCatalog->SetPlatform(platformId);
|
|
}
|
|
}
|
|
|
|
// inventory.plt catalog only does bitmask pruning
|
|
if ( puidCatalog == WU_CATALOG_LIST_PUID )
|
|
{
|
|
// if puidCatalog is 0 then we are retrieving the inventory.plt list of
|
|
// catalogs. We need to handle this catalog differently.
|
|
pCatalog->BitmaskPruning(&bmAS, g_v3state.m_pdwPlatformList, g_v3state.m_iTotalPlatforms);
|
|
}
|
|
else
|
|
{
|
|
// we are retrieving a normal inventory catalog.
|
|
|
|
// since we need the OEM table to perform bitmask detection on an inventory catalog
|
|
// we first check to see if the oem table has already been read. If it has not we
|
|
// download. Note: We use full local caching on the OEM table. This is important for
|
|
// maximum performance.
|
|
if ( !g_v3state.m_pOemInfoTable ) //Pointer OEM info table that OEM detection needs.
|
|
{
|
|
byte_buffer bufOemInfo;
|
|
if (! DownloadToBuffer( _T("oeminfo.bin"), &dl, &dm, bufOemInfo))
|
|
throw HRESULT_FROM_WIN32(GetLastError());
|
|
g_v3state.m_pOemInfoTable = bufOemInfo.detach();
|
|
}
|
|
|
|
// perform active setup record bitmask pruning.
|
|
pCatalog->BitmaskPruning(&bmAS, g_v3state.m_pOemInfoTable);
|
|
|
|
|
|
// since windows 95 & NT4.0 do not support PnP drivers we cannot add any device driver records.
|
|
//
|
|
// NOTE: The catalog.Prune() method hides the device driver insertion record. So we do not need
|
|
// to do anything if the OS does not allow device driver installations.
|
|
if (DoesClientPlatformSupportDrivers())
|
|
{
|
|
LOG_out("Support drivers on this platform");
|
|
|
|
// if there are device driver records to be added.
|
|
if ( pCatalog->GetRecordIndex(WU_TYPE_CDM_RECORD_PLACE_HOLDER) != -1 )
|
|
{
|
|
LOG_out("Have drivers on this platform");
|
|
|
|
// if there are no drivers for this platform
|
|
// the bitmask will be missing and the Read()
|
|
// method will throw. In this case, we do nothing.
|
|
try
|
|
{
|
|
bmCDM.Read(&dl, &dm, puidCatalog, BITMASK_CDM_TYPE, NULL);
|
|
cdm.CreateInventoryList(&bmCDM, &dl, &dm, puidCatalog, g_v3state.m_pOemInfoTable);
|
|
pCatalog->AddCDMRecords(&cdm);
|
|
}
|
|
catch(HRESULT hr)
|
|
{
|
|
LOG_out("ProcessCatalog: bitmask.cdm or inventory.cdm missing.");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG_out("Don't have drivers on this platform");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG_out("Don't support drivers on this platform");
|
|
}
|
|
|
|
pCatalog->ProcessExclusions(&dl);
|
|
|
|
//
|
|
// do active setup detection
|
|
//
|
|
DetectActiveSetupWU(&dl, &dm, pCatalog);
|
|
|
|
// perform catalog inventory link processing and use selected registry item hiding.
|
|
pCatalog->Prune();
|
|
}
|
|
|
|
|
|
if (puidCatalog == WU_CATALOG_LIST_PUID)
|
|
{
|
|
|
|
// description files for catalog list
|
|
//
|
|
// NOTE: With 3.1 we dropped the description fields for catalog list
|
|
// we fill the structure with blank descriptions
|
|
for (i = 0; i < pCatalog->GetHeader()->totalItems; i++)
|
|
{
|
|
if (NULL == (pCatalogItem = pCatalog->GetItem(i)))
|
|
{
|
|
continue;
|
|
}
|
|
if (pCatalogItem->ps->state != WU_ITEM_STATE_PRUNED)
|
|
{
|
|
(void)pCatalog->BlankDescription(pCatalogItem);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// read descriptions for the catalog items
|
|
// 1. read gang description file
|
|
// 2. (pass 1) read individual descriptions for items that did not get descriptions by
|
|
// by using browser language
|
|
// 3. (pass 2) read individual descriptions for itmes that still did not get descriptions
|
|
// by using machine language
|
|
//
|
|
|
|
hr = pCatalog->ReadDescriptionGang(&dl, &dm);
|
|
|
|
// check to see if we need to download any individual descriptions
|
|
BOOL bNeedIndividual = FALSE;
|
|
if (FAILED(hr))
|
|
{
|
|
bNeedIndividual = TRUE;
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < pCatalog->GetHeader()->totalItems; i++)
|
|
{
|
|
if (NULL == (pCatalogItem = pCatalog->GetItem(i)))
|
|
{
|
|
continue;
|
|
}
|
|
if ((pCatalogItem->ps->state != WU_ITEM_STATE_PRUNED) && (pCatalogItem->pd == NULL))
|
|
{
|
|
// need to download it
|
|
bNeedIndividual = TRUE;
|
|
break;
|
|
}
|
|
} // for
|
|
}
|
|
|
|
for (int iPass = 1; iPass <= 2; iPass++)
|
|
{
|
|
if (!bNeedIndividual)
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = S_OK;
|
|
bNeedIndividual = FALSE;
|
|
|
|
//
|
|
// we need to download individual description files
|
|
//
|
|
TCHAR szMapFile[MAX_PATH];
|
|
TCHAR szMapFileLocal[MAX_PATH];
|
|
BYTE* pMapMem;
|
|
DWORD dwMapLen;
|
|
|
|
// build path for crc map file
|
|
wsprintf(szMapFile, _T("%d_%s.des"),
|
|
pCatalog->GetPlatform(),
|
|
(iPass == 1) ? pCatalog->GetBrowserLocaleSZ() : pCatalog->GetMachineLocaleSZ());
|
|
|
|
//because the map file name isn't a CRC, we can't guarantee that it is the right one - always delete and re-download
|
|
GetWindowsUpdateDirectory(szMapFileLocal);
|
|
lstrcat(szMapFileLocal, szMapFile);
|
|
|
|
if(!CWUDownload::s_fOffline)
|
|
{
|
|
DeleteFile(szMapFileLocal);
|
|
}
|
|
|
|
hr = DownloadFileToMem(&dl, szMapFile, &dm, &pMapMem, &dwMapLen);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// create a crc map object with the memory image of the file
|
|
CCRCMapFile DescMap(pMapMem, dwMapLen);
|
|
CWUDownload dlRoot(g_v3state.GetRootServer(), 8192);
|
|
|
|
for (i = 0; i < pCatalog->GetHeader()->totalItems; i++)
|
|
{
|
|
if (NULL == (pCatalogItem = pCatalog->GetItem(i)))
|
|
{
|
|
continue;
|
|
}
|
|
if ((pCatalogItem->ps->state != WU_ITEM_STATE_PRUNED) && (pCatalogItem->pd == NULL))
|
|
{
|
|
DWORD dwDisp = 0;
|
|
hr = pCatalog->ReadDescription(&dlRoot, &dm, pCatalogItem, &DescMap, &dwDisp);
|
|
if (FAILED(hr))
|
|
{
|
|
// description not found
|
|
if (iPass == 1)
|
|
{
|
|
bNeedIndividual = TRUE;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
switch(dwDisp)
|
|
{
|
|
case DISP_PUID_NOT_IN_MAP:
|
|
wsprintfA(szBuffer, "Puid #%d not found in map file - removing from catalog", pCatalogItem->GetPuid());
|
|
LOG_out(szBuffer);
|
|
pCatalogItem->ps->state = WU_ITEM_STATE_PRUNED;
|
|
hr = S_OK;
|
|
break;
|
|
case DISP_DESC_NOT_FOUND:
|
|
wsprintfA(szBuffer, "Missing description file for puid #%d - removing from catalog", pCatalogItem->GetPuid());
|
|
LOG_out(szBuffer);
|
|
pCatalogItem->ps->state = WU_ITEM_STATE_PRUNED;
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
} // for i
|
|
|
|
V3_free(pMapMem);
|
|
}
|
|
else
|
|
{
|
|
if (iPass == 1)
|
|
{
|
|
bNeedIndividual = TRUE;
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
|
|
} // for iPass
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
throw hr;
|
|
}
|
|
|
|
#ifdef _WUV3TEST
|
|
// validate descriptions
|
|
CheckDescDiagInfo(pCatalog);
|
|
#endif // _WUV3TEST
|
|
|
|
}
|
|
|
|
bCheckInNeeded = TRUE;
|
|
LOG_out("catalog for %d NOT found in state cache, create it", puidCatalog);//added by wei
|
|
}
|
|
else //added by wei
|
|
{
|
|
LOG_out("catalog for %d found in state cache, return it", puidCatalog);
|
|
}
|
|
|
|
// in the case where the catalog is already in state storage we simply make
|
|
// the catalog array and return.
|
|
|
|
// NOTE: if this catalog came from state storage then there is no reason to check it in.
|
|
if (bCheckInNeeded)
|
|
{
|
|
// finally check the pruned catalog into state storage.
|
|
g_v3state.Add(puidCatalog, pCatalog);
|
|
}
|
|
|
|
|
|
// if we got here that also means that CheckLaunchServer has validated us
|
|
// mark this instance as a valid one
|
|
m_bValidInstance = TRUE;
|
|
|
|
return pCatalog;
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP CCV3::ChangeItemState(
|
|
IN long puid,
|
|
IN long lNewItemState
|
|
)
|
|
{
|
|
HRESULT hrRet = S_OK;
|
|
|
|
try
|
|
{
|
|
hrRet = ProcessChangeItemState(puid, lNewItemState);
|
|
}
|
|
catch (HRESULT hr)
|
|
{
|
|
hrRet = hr;
|
|
}
|
|
|
|
return hrRet;
|
|
}
|
|
|
|
|
|
|
|
HRESULT CCV3::ProcessChangeItemState(
|
|
IN long puid,
|
|
IN long lNewItemState
|
|
)
|
|
{
|
|
HKEY hKey;
|
|
BOOL bChanged;
|
|
int i;
|
|
int iTotalItems;
|
|
|
|
Varray<PINVENTORY_ITEM> pItemList;
|
|
|
|
if (!m_bValidInstance)
|
|
{
|
|
return E_ACCESSDENIED;
|
|
}
|
|
|
|
//if the requested state is SELECT by itself, we default to SELECT | INSTAL
|
|
if (lNewItemState == ITEM_STATE_SELECT_ITEM)
|
|
{
|
|
lNewItemState = ITEM_STATE_SELECT_ITEM | ITEM_STATE_INSTALL_ITEM;
|
|
}
|
|
|
|
if (!(iTotalItems = g_v3state.GetItemList(puid, pItemList)))
|
|
{
|
|
TRACE("ChangeItemState called with invalid PUID %d", puid);
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
bChanged = FALSE;
|
|
|
|
for (i = 0; i < iTotalItems; i++)
|
|
{
|
|
|
|
//
|
|
// show/hide
|
|
//
|
|
if (lNewItemState & ITEM_STATE_HIDE_ITEM)
|
|
{
|
|
if ( !pItemList[i]->ps->bHidden )
|
|
{
|
|
pItemList[i]->ps->bHidden = TRUE;
|
|
bChanged = TRUE;
|
|
}
|
|
}
|
|
else if (lNewItemState & ITEM_STATE_SHOW_ITEM)
|
|
{
|
|
if ( pItemList[i]->ps->bHidden )
|
|
{
|
|
pItemList[i]->ps->bHidden = FALSE;
|
|
bChanged = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// select
|
|
//
|
|
if (lNewItemState & ITEM_STATE_SELECT_ITEM)
|
|
{
|
|
// default is to select the item for install.
|
|
if (lNewItemState & ITEM_STATE_INSTALL_ITEM)
|
|
{
|
|
|
|
if (!pItemList[i]->ps->bChecked )
|
|
{
|
|
pItemList[i]->ps->bChecked = TRUE;
|
|
bChanged = TRUE;
|
|
|
|
g_v3state.m_selectedItems.Select(puid, TRUE);
|
|
}
|
|
}
|
|
if ( lNewItemState & ITEM_STATE_REMOVE_ITEM )
|
|
{
|
|
// remove only removes requested item.
|
|
g_v3state.m_selectedItems.Select(puid, FALSE);
|
|
bChanged = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// unselect
|
|
//
|
|
if ( lNewItemState & ITEM_STATE_UNSELECT_ITEM )
|
|
{
|
|
if ( pItemList[i]->ps->bChecked )
|
|
{
|
|
|
|
if (pItemList[i]->ps->bChecked )
|
|
{
|
|
pItemList[i]->ps->bChecked = FALSE;
|
|
bChanged = TRUE;
|
|
}
|
|
|
|
// add item and all of its dependent items to selected items array.
|
|
|
|
// remove item from selected items array.
|
|
//
|
|
// NOTE: This will only remove the item
|
|
// if the select count is 0.
|
|
g_v3state.m_selectedItems.Unselect(puid);
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// personalize hide/unhide
|
|
//
|
|
if (lNewItemState & ITEM_STATE_PERSONALIZE_HIDE)
|
|
{
|
|
pItemList[i]->ps->bHidden = TRUE;
|
|
pItemList[i]->ps->dwReason = WU_STATE_REASON_PERSONALIZE;
|
|
bChanged = RegistryHidingUpdate(puid, TRUE);
|
|
|
|
}
|
|
if (lNewItemState & ITEM_STATE_PERSONALIZE_UNHIDE)
|
|
{
|
|
bChanged = RegistryHidingUpdate(puid, FALSE);
|
|
if (bChanged)
|
|
{
|
|
pItemList[i]->ps->bHidden = FALSE;
|
|
pItemList[i]->ps->dwReason = WU_STATE_REASON_NONE;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (!bChanged)
|
|
return E_INVALIDARG;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
int __cdecl SortComparePriority(const void* p1, const void* p2)
|
|
{
|
|
|
|
DWORD d1 = ((DEPENDPUID*)p1)->dwPriority;
|
|
DWORD d2 = ((DEPENDPUID*)p2)->dwPriority;
|
|
|
|
// reverse order compare
|
|
if (d1 > d2)
|
|
return -1;
|
|
else if (d1 < d2)
|
|
return +1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
// Now sort if by priority
|
|
|
|
static Varray<PUID> g_vPuidParent;
|
|
static int g_cPuidParent;
|
|
|
|
void ListDependenciesFirst(Varray<DEPENDPUID>& vInitList, int cInitList, Varray<DEPENDPUID>& vFinalList, int& cFinalList)
|
|
{
|
|
// sort initial list
|
|
if (cInitList > 1)
|
|
qsort((void *)(&vInitList[0]), cInitList, sizeof(DEPENDPUID), SortComparePriority);
|
|
|
|
for (int iInitList = 0; iInitList < cInitList; iInitList++)
|
|
{
|
|
// check if we are in the parent array;
|
|
for (int i = 0; i < g_cPuidParent; i++)
|
|
{
|
|
if (g_vPuidParent[i] == vInitList[iInitList].puid)
|
|
break;
|
|
}
|
|
if (i != g_cPuidParent)
|
|
continue; // didn't get to the very end
|
|
|
|
// Get catalog
|
|
CCatalog* pCatalog;
|
|
PINVENTORY_ITEM pItem;
|
|
if (!g_v3state.GetCatalogAndItem(vInitList[iInitList].puid, &pItem, &pCatalog))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Get dependancies
|
|
Varray<DEPENDPUID> vTmpList;
|
|
int cTmpList = 0;
|
|
pCatalog->GetItemDirectDependencies(pItem, vTmpList, cTmpList);
|
|
|
|
if (cTmpList)
|
|
{
|
|
// set vInitList[iList].pTopLevelItem, for them
|
|
for(int j = 0; j < cTmpList; j ++)
|
|
{
|
|
if (NULL == vInitList[iInitList].pTopLevelItem)
|
|
vTmpList[j].pTopLevelItem = pItem;
|
|
else
|
|
vTmpList[j].pTopLevelItem = vInitList[iInitList].pTopLevelItem;
|
|
}
|
|
|
|
// prepend them
|
|
g_vPuidParent[g_cPuidParent ++] = vInitList[iInitList].puid;
|
|
ListDependenciesFirst(vTmpList, cTmpList, vFinalList, cFinalList);
|
|
g_cPuidParent --;
|
|
}
|
|
|
|
// Check if it's included by now
|
|
for (i = 0; i < cFinalList; i++)
|
|
{
|
|
if (vFinalList[i].puid == vInitList[iInitList].puid)
|
|
break;
|
|
}
|
|
if (i != cFinalList)
|
|
continue; // didn't get to the very end
|
|
|
|
// now add the item if it's not there
|
|
vFinalList[cFinalList ++] = vInitList[iInitList];
|
|
}
|
|
}
|
|
|
|
void ProcessInstallList(Varray<DEPENDPUID>& vFinalList, int& cFinalList)
|
|
{
|
|
LOG_block("CCV3::ProcessInstallList");
|
|
|
|
PSELECTITEMINFO pSel = g_v3state.m_selectedItems.GetItems();
|
|
int cSel = g_v3state.m_selectedItems.GetTotal();
|
|
if (cSel == 0)
|
|
return;
|
|
|
|
// Build selection list
|
|
Varray<DEPENDPUID> vSelectList;
|
|
int cSelectList = 0;
|
|
DEPENDPUID d = {0};
|
|
for (int iSel = 0; iSel < cSel; iSel++)
|
|
{
|
|
|
|
d.puid = pSel[iSel].puid;
|
|
d.puidParent = 0;
|
|
d.pTopLevelItem = NULL; // that is important
|
|
|
|
CCatalog* pCatalog;
|
|
PINVENTORY_ITEM pItem;
|
|
if (!g_v3state.GetCatalogAndItem(d.puid, &pItem, &pCatalog))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// look up priority and add the item to the array
|
|
d.dwPriority = 0;
|
|
PWU_VARIABLE_FIELD pvPri = pItem->pd->pv->Find(WU_DESC_INSTALL_PRIORITY);
|
|
if (pvPri != NULL)
|
|
d.dwPriority = *((DWORD*)(pvPri->pData));
|
|
|
|
vSelectList[cSelectList ++] = d;
|
|
}
|
|
|
|
// build final list
|
|
g_cPuidParent = 0;
|
|
ListDependenciesFirst(vSelectList, cSelectList, vFinalList, cFinalList);
|
|
|
|
//
|
|
// now, clear the select and reselect the items (including dependendcies) for installation in order
|
|
//
|
|
g_v3state.m_selectedItems.Clear();
|
|
|
|
for (int i = 0; i < cFinalList; i++)
|
|
{
|
|
LOG_out("Final List %d Pri=%d Parent=%d TopLevel=%s",
|
|
vFinalList[i].puid, vFinalList[i].dwPriority,
|
|
vFinalList[i].puidParent, (vFinalList[i].pTopLevelItem == NULL ? "NULL" : "Not NULL"));
|
|
|
|
// select the item
|
|
g_v3state.m_selectedItems.Select(vFinalList[i].puid, TRUE);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
STDMETHODIMP CCV3::InstallSelectedItems(
|
|
IN BSTR bstrUnused, //Server Directory if blank then the server used with the catalog was retrieved is used.
|
|
IN long lFlags, //Flags currently only WU_NOSPECIAL_FLAGS and WU_COPYONLY_NO_INSTALL supported.
|
|
IN BSTR bstrNotUsed,
|
|
OUT RETVAL VARIANT *pResultsArray
|
|
)
|
|
{
|
|
LOG_block("CCV3::InstallSelectedItems");
|
|
|
|
int iTotalItems = 0;
|
|
TCHAR szTempDir[MAX_PATH];
|
|
TCHAR szLocalDir[MAX_PATH];
|
|
CDiamond dm;
|
|
PSELECTITEMINFO pInfo = NULL;
|
|
Varray<INSTALLINFOSTRUCT> InstallArr;
|
|
int InstallCnt = 0;
|
|
DWORD dwTotalBytes = 0;
|
|
Varray<DEPENDPUID> vFinalList;
|
|
int cFinalList = 0;
|
|
int i;
|
|
int t;
|
|
|
|
if (!m_bValidInstance)
|
|
{
|
|
return E_ACCESSDENIED;
|
|
}
|
|
|
|
try
|
|
{
|
|
CWUProgress Progress(_Module.GetModuleInstance());
|
|
CServPauser ServPauser;
|
|
CDescriptionMerger DescMerger;
|
|
|
|
g_v3state.m_bRebootNeeded = FALSE;
|
|
|
|
//
|
|
// process depenendencies and priorities of the item
|
|
//
|
|
ProcessInstallList(vFinalList, cFinalList);
|
|
|
|
iTotalItems = g_v3state.m_selectedItems.GetTotal();
|
|
pInfo = g_v3state.m_selectedItems.GetItems();
|
|
|
|
|
|
// pause task scheduler
|
|
ServPauser.PauseTaskScheduler();
|
|
|
|
//
|
|
// go thru all the selected items and build the InstallArr
|
|
//
|
|
for (i = 0, InstallCnt = 0; i < iTotalItems; i++)
|
|
{
|
|
if (pInfo[i].bInstall)
|
|
{
|
|
if (!g_v3state.GetCatalogAndItem(pInfo[i].puid, &InstallArr[InstallCnt].pItem, &InstallArr[InstallCnt].pCatalog))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// remove the "Checked" status from the item
|
|
//
|
|
InstallArr[InstallCnt].pItem->ps->bChecked = FALSE;
|
|
|
|
dwTotalBytes += (InstallArr[InstallCnt].pItem->pd->size * 1024);
|
|
|
|
|
|
//
|
|
// create download objects. We use the GetCabPoolServer to get the server.for cabpool
|
|
// we create objects for only one element and assign pdl of each install element to this object
|
|
//
|
|
// NOTE: We are ignoring the szServer passed in and we will eventually remove it from interface
|
|
//
|
|
if (InstallCnt == 0)
|
|
{
|
|
TRACE("InstallSelectedItems: creating download objects for %s and %s", g_v3state.GetCabPoolServer(), InstallArr[0].pCatalog->GetCatalogServer());
|
|
InstallArr[0].bServerNew = TRUE;
|
|
InstallArr[0].pdl = new CWUDownload(g_v3state.GetCabPoolServer(), 8192);
|
|
InstallArr[0].pdlRoot = new CWUDownload(g_v3state.GetRootServer(), 8192);
|
|
}
|
|
else
|
|
{
|
|
InstallArr[InstallCnt].pdl = InstallArr[0].pdl;
|
|
InstallArr[InstallCnt].pdlRoot = InstallArr[0].pdlRoot;
|
|
InstallArr[InstallCnt].bServerNew = FALSE;
|
|
}
|
|
|
|
InstallArr[InstallCnt].pInfo = &pInfo[i];
|
|
|
|
GetCurTime(&(InstallArr[InstallCnt].pInfo->stDateTime));
|
|
InstallArr[InstallCnt].pInfo->iStatus = ITEM_STATUS_FAILED; //init to failed
|
|
|
|
InstallArr[InstallCnt].iSelIndex = i;
|
|
InstallArr[InstallCnt].dwLocaleID = 0; // use systems
|
|
InstallArr[InstallCnt].dwPlatform = 0; // use systems
|
|
|
|
InstallArr[InstallCnt].bHistoryWritten = FALSE;
|
|
InstallArr[InstallCnt].bDownloaded = FALSE;
|
|
InstallArr[InstallCnt].pTopLevelItem = NULL;
|
|
|
|
//
|
|
// if the item is hidden, set the pTopLevelItem using the dependcies list
|
|
//
|
|
if (InstallArr[InstallCnt].pItem->ps->bHidden)
|
|
{
|
|
PUID puidDep = InstallArr[InstallCnt].pItem->GetPuid();
|
|
//
|
|
//check puidDep, i.e. check if the function GetPuid() is returning valid info
|
|
//
|
|
if (puidDep > 0)
|
|
{
|
|
for (int iDep = 0; iDep < cFinalList; iDep++)
|
|
{
|
|
if (vFinalList[iDep].puid == puidDep)
|
|
{
|
|
InstallArr[InstallCnt].pTopLevelItem = vFinalList[iDep].pTopLevelItem;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// merge the descriptions if machine/browser languages are different so that
|
|
// we have the correct installation information based on machine language
|
|
//
|
|
BLOCK
|
|
{
|
|
HRESULT hr = DescMerger.CheckMerge(&InstallArr[InstallCnt]);
|
|
if (FAILED(hr))
|
|
{
|
|
TRACE("Failed while mergeing description");
|
|
throw hr;
|
|
}
|
|
}
|
|
|
|
InstallCnt++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// get the directory where we will download CABs
|
|
//
|
|
GetWindowsUpdateDirectory(szTempDir);
|
|
AddBackSlash(szTempDir);
|
|
|
|
Progress.StartDisplay();
|
|
Progress.SetDownloadTotal(dwTotalBytes);
|
|
Progress.SetInstallTotal(InstallCnt * 3);
|
|
|
|
//
|
|
// Create directory for and download each item
|
|
//
|
|
for (i = 0; i < InstallCnt; i++)
|
|
{
|
|
wsprintf(szLocalDir, _T("%sCabs\\%d"), szTempDir, InstallArr[i].pInfo->puid);
|
|
|
|
V3_CreateDirectory(szLocalDir);
|
|
|
|
InstallArr[i].bDownloaded = TRUE; //assume the download will be fine
|
|
try
|
|
{
|
|
DownloadItem(szLocalDir, InstallArr[i].pdl, &dm, &InstallArr[i], &Progress);
|
|
}
|
|
catch (HRESULT hr)
|
|
{
|
|
InstallArr[i].pInfo->iStatus = ITEM_STATUS_FAILED;
|
|
InstallArr[i].bDownloaded = FALSE;
|
|
|
|
//
|
|
// check to see if the Cancel button was pressed
|
|
//
|
|
if (WaitForSingleObject(Progress.GetCancelEvent(), 0) == WAIT_OBJECT_0)
|
|
throw hr;
|
|
}
|
|
|
|
// even though the download for this item has succeeded, we reset the status to 'failed'
|
|
// until we actually finish the installation
|
|
InstallArr[i].pInfo->iStatus = ITEM_STATUS_FAILED;
|
|
|
|
}
|
|
Progress.SetDownload(); //100%
|
|
|
|
|
|
//
|
|
// Install each package and delete its directory
|
|
//
|
|
for (i = 0; i < InstallCnt; i++)
|
|
{
|
|
wsprintf(szLocalDir, _T("%sCabs\\%d"), szTempDir, InstallArr[i].pInfo->puid);
|
|
|
|
Progress.SetInstallAdd(1);
|
|
|
|
if (InstallArr[i].bDownloaded)
|
|
{
|
|
// if this is an exclusive component, hide the install progress. We assume
|
|
// that, apart from dependencies, this will be the only component being installed
|
|
// since it is by definition exclusive. In that case, this will be the last component
|
|
// and install progress will be hidden so that the component's own setup UI is displayed
|
|
if (InstallArr[i].pItem->pd->flags & DESCRIPTION_EXCLUSIVE)
|
|
{
|
|
Progress.SetStyle(CWUProgress::ProgStyle::OFF);
|
|
}
|
|
|
|
// install the item
|
|
InstallItem(szLocalDir, InstallArr[i].pInfo, &InstallArr[i], &Progress);
|
|
}
|
|
|
|
Progress.SetInstallAdd(1);
|
|
|
|
DeleteNode(szLocalDir);
|
|
|
|
UpdateInstallHistory(InstallArr[i].pInfo, 1);
|
|
InstallArr[i].bHistoryWritten = TRUE;
|
|
|
|
if ((InstallArr[i].pInfo->iStatus == ITEM_STATUS_SUCCESS) || (InstallArr[i].pInfo->iStatus == ITEM_STATUS_SUCCESS_REBOOT_REQUIRED))
|
|
{
|
|
//update item status to CURRENT
|
|
InstallArr[i].pItem->ps->state = WU_ITEM_STATE_CURRENT;
|
|
|
|
//indicate that a reboot is required
|
|
if (InstallArr[i].pInfo->iStatus == ITEM_STATUS_SUCCESS_REBOOT_REQUIRED)
|
|
g_v3state.m_bRebootNeeded = TRUE;
|
|
}
|
|
|
|
//remove the selected flag from the item
|
|
InstallArr[i].pItem->ps->bChecked = FALSE;
|
|
|
|
Progress.SetInstallAdd(1);
|
|
}
|
|
Progress.SetInstall(); //100%
|
|
|
|
// create return array
|
|
MakeInstallStatusArray(pInfo, iTotalItems, pResultsArray);
|
|
|
|
// remove all items from selected items
|
|
g_v3state.m_selectedItems.Clear();
|
|
|
|
|
|
// close server connections
|
|
for (i = 0; i < iTotalItems; i++)
|
|
{
|
|
if (InstallArr[i].bServerNew)
|
|
{
|
|
delete InstallArr[i].pdl;
|
|
delete InstallArr[i].pdlRoot;
|
|
}
|
|
}
|
|
|
|
Progress.EndDisplay();
|
|
|
|
// delete any cached readthisfirst pages
|
|
CleanupReadThis();
|
|
|
|
}
|
|
catch (HRESULT hr)
|
|
{
|
|
if (pInfo != NULL)
|
|
{
|
|
// create return array
|
|
MakeInstallStatusArray(pInfo, iTotalItems, pResultsArray);
|
|
}
|
|
else
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// if there was an error, we don't want to reboot
|
|
//
|
|
g_v3state.m_bRebootNeeded = FALSE;
|
|
|
|
//
|
|
// remove all items from m_selected
|
|
//
|
|
g_v3state.m_selectedItems.Clear();
|
|
|
|
|
|
// close server connections
|
|
for (i = 0; i < iTotalItems; i++)
|
|
{
|
|
if (InstallArr[i].bServerNew)
|
|
{
|
|
delete InstallArr[i].pdl;
|
|
delete InstallArr[i].pdlRoot;
|
|
}
|
|
|
|
if (!InstallArr[i].bHistoryWritten)
|
|
UpdateInstallHistory(InstallArr[i].pInfo, 1);
|
|
}
|
|
|
|
// delete any cached readthisfirst pages
|
|
CleanupReadThis();
|
|
|
|
return S_FALSE;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
STDMETHODIMP CCV3::GetInstallMetrics(
|
|
OUT RETVAL VARIANT *pMetricsArray
|
|
)
|
|
{
|
|
PSELECTITEMINFO pInfo;
|
|
PINVENTORY_ITEM pItem;
|
|
int iTotalSelectedItems;
|
|
|
|
try
|
|
{
|
|
iTotalSelectedItems = g_v3state.m_selectedItems.GetTotal();
|
|
pInfo = g_v3state.m_selectedItems.GetItems();
|
|
|
|
MakeInstallMetricArray(pInfo, iTotalSelectedItems, pMetricsArray);
|
|
}
|
|
catch(HRESULT hr)
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
STDMETHODIMP CCV3::GetEula(
|
|
OUT RETVAL VARIANT *pEulaArray
|
|
)
|
|
{
|
|
PSELECTITEMINFO pInfo;
|
|
PINVENTORY_ITEM pItem;
|
|
int i;
|
|
int iTotalSelectedItems;
|
|
|
|
if (!m_bValidInstance)
|
|
{
|
|
return E_ACCESSDENIED;
|
|
}
|
|
|
|
try
|
|
{
|
|
//
|
|
// Walk though item array for each install item. Each item selected for installation
|
|
// is store in a puid array that is part of the g_v3state class. This is a performance
|
|
// enhancement. If we did not do this then we would need to walk through the entire
|
|
// state structure for each catalog for every item and check it's installation state.
|
|
//
|
|
iTotalSelectedItems = g_v3state.m_selectedItems.GetTotal();
|
|
pInfo = g_v3state.m_selectedItems.GetItems();
|
|
|
|
MakeEulaArray(pInfo, iTotalSelectedItems, pEulaArray);
|
|
}
|
|
catch(HRESULT hr)
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP CCV3::GetInstallHistory(
|
|
OUT RETVAL VARIANT *pHistoryArray
|
|
)
|
|
{
|
|
int i;
|
|
int iTotalItems;
|
|
Varray<HISTORYSTRUCT> History;
|
|
|
|
if (!m_bValidInstance)
|
|
{
|
|
return E_ACCESSDENIED;
|
|
}
|
|
|
|
try
|
|
{
|
|
//
|
|
// Walk though item array for each install item. Each item selected for installation
|
|
// is store in a puid array that is part of the g_v3state class. This is a performance
|
|
// enhancement. If we did not do this then we would need to walk through the entire
|
|
// state structure for each catalog for every item and check it's installation state.
|
|
//
|
|
ReadHistory(History, iTotalItems);
|
|
|
|
MakeInstallHistoryArray(History, iTotalItems, pHistoryArray);
|
|
}
|
|
catch(HRESULT hr)
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CCV3::GetDependencyList(
|
|
IN long puid,
|
|
OUT RETVAL VARIANT *pDependentItemsArray
|
|
)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP CCV3::GetCatalogItem(
|
|
IN long puid,
|
|
OUT RETVAL VARIANT *pCatalogItem
|
|
)
|
|
{
|
|
PINVENTORY_ITEM pItem;
|
|
HRESULT hrRet = S_OK;
|
|
|
|
if (!m_bValidInstance)
|
|
{
|
|
return E_ACCESSDENIED;
|
|
}
|
|
|
|
try
|
|
{
|
|
if (!g_v3state.GetCatalogAndItem(puid, &pItem, NULL))
|
|
hrRet = E_INVALIDARG;
|
|
else
|
|
hrRet = MakeReturnItemArray(pItem, pCatalogItem);
|
|
}
|
|
catch (HRESULT hr)
|
|
{
|
|
hrRet = hr;
|
|
}
|
|
|
|
return hrRet;
|
|
}
|
|
|
|
BOOL ConfirmUninstall(PINVENTORY_ITEM pItem)
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
PWU_VARIABLE_FIELD pvTitle = pItem->pd->pv->Find(WU_DESCRIPTION_TITLE);
|
|
|
|
//
|
|
// Prefix bug NTBUG9#114181 warning (25): bounds violation (overflow) using buffer 'szResStr'
|
|
//
|
|
TCHAR szResStr[512];
|
|
if (LoadString(_Module.GetModuleInstance(), IDS_UNINSTALLCHECK, szResStr, sizeof(szResStr)/sizeof(TCHAR)) == 0)
|
|
{
|
|
TRACE("Unable to get resource string for uninstall check");
|
|
return FALSE;
|
|
}
|
|
|
|
TCHAR szMsg[1024];
|
|
wsprintf(szMsg, szResStr, pvTitle ? W2T((LPWSTR)(pvTitle->pData)) : _T(""));
|
|
|
|
DWORD dwMBFlags = MB_OKCANCEL | MB_ICONEXCLAMATION | MB_DEFBUTTON2 | MB_SETFOREGROUND;
|
|
|
|
if (IsArabicOrHebrew())
|
|
dwMBFlags |= MB_RTLREADING | MB_RIGHT;
|
|
|
|
if (MessageBox(GetActiveWindow(), szMsg, GetLocStr(IDS_APP_TITLE), dwMBFlags) != IDOK)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// confirms reboot, cleans up and reboot using a thread
|
|
BOOL CleanupAndReboot()
|
|
{
|
|
TCHAR szBuf[512];
|
|
// NOTE: we assume that GetLocStr will not return strings that will overflow this buffer
|
|
wsprintf(szBuf, _T("%s\n\n%s"), GetLocStr(IDS_REBOOT1), GetLocStr(IDS_REBOOT2));
|
|
|
|
// adadi Oct 6, 1999: arabic + hebrew hack
|
|
// we need to add the RTL flag if we're running on loc, but not enabled BIDI systems
|
|
// we don't want the dialog flipped with EN text on an enabled system
|
|
DWORD dwMBFlags = MB_ICONQUESTION | MB_YESNO | MB_TASKMODAL | MB_SETFOREGROUND;
|
|
|
|
if (IsArabicOrHebrew())
|
|
dwMBFlags |= MB_RTLREADING | MB_RIGHT;
|
|
|
|
UINT id = MessageBox(GetActiveWindow(), szBuf, GetLocStr(IDS_APP_TITLE), dwMBFlags);
|
|
|
|
if (id == IDNO)
|
|
return FALSE;
|
|
|
|
// cleanup
|
|
g_v3state.Reset();
|
|
CConnSpeed::WriteToRegistry();
|
|
|
|
|
|
// create a thread to reboot
|
|
DWORD dwThreadID;
|
|
HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)RebootThreadProc, NULL, 0, &dwThreadID);
|
|
if (hThread != NULL)
|
|
{
|
|
// we close the handle because we don't need it. The thread will be destroyed after threadproc terminates
|
|
CloseHandle(hThread);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
DWORD WINAPI RebootThreadProc(LPVOID pv)
|
|
{
|
|
// Wait for 1 second and reboot the system
|
|
Sleep(1000);
|
|
|
|
(void)V3_RebootSystem();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP CCV3::RemoveSelectedItems()
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
PSELECTITEMINFO pInfo;
|
|
PINVENTORY_ITEM pItem;
|
|
PWU_VARIABLE_FIELD pvUninstall;
|
|
|
|
if (!m_bValidInstance)
|
|
{
|
|
return E_ACCESSDENIED;
|
|
}
|
|
|
|
try
|
|
{
|
|
//we need the os type for any device driver installs.
|
|
int iTotalItems = g_v3state.m_selectedItems.GetTotal();
|
|
HRESULT hr = NOERROR;
|
|
|
|
g_v3state.m_bRebootNeeded = FALSE;
|
|
|
|
pInfo = g_v3state.m_selectedItems.GetItems();
|
|
|
|
for (int i=0; i<iTotalItems; i++)
|
|
{
|
|
if (!pInfo[i].bInstall)
|
|
{
|
|
//
|
|
// if item has been selected for removal...
|
|
// confirm that the user wants to install the item. The user will be prompted
|
|
// for each uninstall
|
|
//
|
|
if (!g_v3state.GetCatalogAndItem(pInfo[i].puid, &pItem, NULL))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!ConfirmUninstall(pItem))
|
|
{
|
|
// do not uninstall this item
|
|
continue;
|
|
}
|
|
|
|
GetCurTime(&(pInfo[i].stDateTime));
|
|
|
|
if (pItem->recordType == WU_TYPE_CDM_RECORD)
|
|
{
|
|
hr = UninstallDriverItem(pItem, &pInfo[i]);
|
|
}
|
|
else
|
|
{
|
|
pvUninstall = pItem->pd->pv->Find(WU_DESCRIPTION_UNINSTALL_KEY);
|
|
if (pvUninstall != NULL)
|
|
{
|
|
//
|
|
// its possible that uninstall will reboot. We have no way of knowing that
|
|
// so we set the status initially to ITEM_STATUS_UNINSTALL_STARTED which
|
|
// gets translated to "Started" entry in the log
|
|
//
|
|
pInfo[i].iStatus = ITEM_STATUS_UNINSTALL_STARTED;
|
|
UpdateRemoveHistory(&pInfo[i], 1);
|
|
|
|
hr = UninstallActiveSetupItem(A2T((LPSTR)pvUninstall->pData));
|
|
}
|
|
else
|
|
{
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pInfo[i].iStatus = ITEM_STATUS_SUCCESS;
|
|
pInfo[i].hrError = NOERROR;
|
|
pItem->ps->state = WU_ITEM_STATE_INSTALL;
|
|
}
|
|
else
|
|
{
|
|
pInfo[i].iStatus = ITEM_STATUS_INSTALLED_ERROR;
|
|
pInfo[i].hrError = hr;
|
|
|
|
//Fix #736: we leave the item state unchanged if the uninstall fails
|
|
}
|
|
|
|
pItem->ps->bChecked = FALSE;
|
|
|
|
//
|
|
// write history log for final status. We will write only the error case for active setup
|
|
// items which have already written "Started". For drivers we do not write the "Started"
|
|
// entry so we will write the history now for both success and failure
|
|
//
|
|
if (pItem->recordType == WU_TYPE_CDM_RECORD || pInfo[i].iStatus == ITEM_STATUS_INSTALLED_ERROR)
|
|
{
|
|
UpdateRemoveHistory(&pInfo[i], 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// remove all items from m_selected
|
|
//
|
|
g_v3state.m_selectedItems.Clear();
|
|
|
|
//
|
|
// reboot if an uninstall has indicated that we have to reboot
|
|
//
|
|
if (g_v3state.m_bRebootNeeded)
|
|
{
|
|
(void)CleanupAndReboot();
|
|
}
|
|
g_v3state.m_bRebootNeeded = FALSE;
|
|
|
|
}
|
|
catch(HRESULT _hr)
|
|
{
|
|
g_v3state.m_bRebootNeeded = FALSE;
|
|
return _hr;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//
|
|
// NOTE: This method is no longer supported. We simply return S_OK for compatibility. We will remove
|
|
// this method
|
|
//
|
|
STDMETHODIMP CCV3::IsCatalogAvailable(
|
|
IN long puidCatalog, //Name of catalog to be read from the server.
|
|
IN BSTR bstrServerUrl //The http://servername/share location for the catalog to be retrieved.
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CCV3::FinalizeInstall(IN long lFlags)
|
|
{
|
|
if (!m_bValidInstance)
|
|
{
|
|
return E_ACCESSDENIED;
|
|
}
|
|
|
|
if (lFlags & FINALIZE_DOREBOOT)
|
|
{
|
|
if (g_v3state.m_bRebootNeeded)
|
|
{
|
|
(void)CleanupAndReboot();
|
|
}
|
|
}
|
|
g_v3state.m_bRebootNeeded = FALSE;
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CCV3::SetStrings(IN VARIANT* vStringsArr, IN long lType)
|
|
{
|
|
if (!m_bValidInstance)
|
|
{
|
|
return E_ACCESSDENIED;
|
|
}
|
|
|
|
return SetStringsFromSafeArray(vStringsArr, lType);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CCV3::FixCompatRollbackKey(VARIANT_BOOL *pbRegModified)
|
|
{
|
|
const TCHAR* pszIEVersionRegKey = _T("Software\\Microsoft\\Internet Explorer");
|
|
const TCHAR* pszIEUserAgentCompatKey = _T("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\User Agent\\Post Platform");
|
|
const TCHAR* pszIEVersionRegValueName = _T("Version");
|
|
const TCHAR* pszIEUserAgentCompatValueName = _T("compat");
|
|
TCHAR szIEVersionValue[40];
|
|
DWORD dwDataType = 0;
|
|
DWORD dwDataLength = sizeof(szIEVersionValue);
|
|
|
|
HKEY IEVerKey = NULL;
|
|
HKEY IEUserAgentKey = NULL;
|
|
|
|
*pbRegModified = FALSE;
|
|
|
|
if (!m_bValidInstance)
|
|
{
|
|
return E_ACCESSDENIED;
|
|
}
|
|
|
|
|
|
//check the version key first - we don't care what the UA string says unless we have rolled back to IE4
|
|
if(ERROR_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE, pszIEVersionRegKey, 0, KEY_QUERY_VALUE, &IEVerKey))
|
|
{
|
|
if(ERROR_SUCCESS == RegQueryValueEx(IEVerKey,pszIEVersionRegValueName,0,&dwDataType, (LPBYTE)szIEVersionValue,&dwDataLength))
|
|
{
|
|
if (szIEVersionValue[0] == '4') //check whether the first character is "4"
|
|
{
|
|
if(ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER,pszIEUserAgentCompatKey,0,KEY_SET_VALUE, &IEUserAgentKey))
|
|
{
|
|
if(ERROR_SUCCESS == RegDeleteValue(IEUserAgentKey,pszIEUserAgentCompatValueName))
|
|
{
|
|
*pbRegModified = TRUE;
|
|
}
|
|
RegCloseKey(IEUserAgentKey);
|
|
}
|
|
}
|
|
else // otherwise, this is IE5 - we'll piggyback another reg change onto this function
|
|
{
|
|
UpdateToolsURL();
|
|
}
|
|
}
|
|
RegCloseKey(IEVerKey);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
void CCV3::UpdateToolsURL()
|
|
{
|
|
LOG_block("UpdateToolsURL");
|
|
|
|
const TCHAR DEFAULT_WU_URL[] = _T("http://windowsupdate.microsoft.com/");
|
|
const TCHAR WU_TOOLMENU_PARAM[] = _T("IE");
|
|
const TCHAR REGKEY_IE_URLS[] = _T("Software\\Microsoft\\Internet Explorer\\Help_Menu_Urls");
|
|
const TCHAR REGVALUENAME_WU_URL[] = _T("3");
|
|
const TCHAR REGKEY_WU_URL[] = _T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate");
|
|
const TCHAR REGVALUENAME_WU_Start_URL[] = _T("URL");
|
|
const TCHAR WU_STARTMENU_PARAM[] = _T("Start");
|
|
|
|
|
|
HKEY hkIEURLKey = 0;
|
|
HKEY hkWUURLKey = 0;
|
|
|
|
DWORD dwDisposition = REG_OPENED_EXISTING_KEY;
|
|
|
|
TCHAR tszUpdateURL[INTERNET_MAX_URL_LENGTH] = _T("\0");
|
|
TCHAR tszStartMenuURL[INTERNET_MAX_URL_LENGTH] = _T("\0");
|
|
TCHAR tszNewStartMenuURL[INTERNET_MAX_URL_LENGTH] = _T("\0");
|
|
|
|
wsprintf( tszUpdateURL, _T("%s?%s"), DEFAULT_WU_URL, WU_TOOLMENU_PARAM );
|
|
|
|
if( ERROR_SUCCESS == RegCreateKeyEx( HKEY_LOCAL_MACHINE, REGKEY_IE_URLS, 0, (LPTSTR)NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hkIEURLKey, &dwDisposition ) )
|
|
{
|
|
|
|
if( ERROR_SUCCESS == RegSetValueEx( hkIEURLKey, REGVALUENAME_WU_URL, 0, REG_SZ, (LPBYTE) tszUpdateURL, lstrlen(tszUpdateURL) * sizeof(TCHAR) ) )
|
|
{
|
|
LOG_out("Updated Tools URL to %s", tszUpdateURL);
|
|
}
|
|
|
|
RegCloseKey( hkIEURLKey );
|
|
}
|
|
|
|
//take the existing WU regkey and make sure it has "?Start" appended to it
|
|
// this will misbehave in the following two cases:
|
|
// 1) if the URL key exists but is invalid (such as spaces) - can only hapen if a user manually edits the key
|
|
// 2) if the URL contains a fragment (# + parameter)
|
|
// both of these will result in a start menu URL that is invalid. (the Start Menu link would be broken already if either of these cases existed)
|
|
if (NO_ERROR == RegCreateKeyEx( HKEY_LOCAL_MACHINE, REGKEY_WU_URL, 0, (LPTSTR)NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE | KEY_READ, NULL, &hkWUURLKey, &dwDisposition ))
|
|
{
|
|
DWORD dwSize = sizeof(tszStartMenuURL);
|
|
(void) RegQueryValueEx(hkWUURLKey, REGVALUENAME_WU_Start_URL, 0, 0, (LPBYTE)&tszStartMenuURL, &dwSize);
|
|
|
|
|
|
if(0 != lstrcmp(tszStartMenuURL, _T(""))) // if the reg value isn't blank
|
|
{
|
|
LOG_out("Current Start Menu URL: %s", tszStartMenuURL);
|
|
if( NULL == _tcsstr(tszStartMenuURL, _T("?"))) // if there isn't already a querystring on the URL, make the url currenturl?param
|
|
{
|
|
wsprintf(tszNewStartMenuURL, _T("%s?%s"), tszStartMenuURL, WU_STARTMENU_PARAM);
|
|
RegSetValueEx(hkWUURLKey, REGVALUENAME_WU_Start_URL, 0, REG_SZ, (LPBYTE)tszNewStartMenuURL, lstrlen(tszNewStartMenuURL) * sizeof(TCHAR));
|
|
LOG_out("Updating Start Menu URL To %s", tszNewStartMenuURL);
|
|
}
|
|
else // if there's already a parameter
|
|
{
|
|
if(NULL != _tcsstr(tszStartMenuURL, WU_STARTMENU_PARAM))//if it already has "Start", bail
|
|
{
|
|
// do nothing
|
|
}
|
|
else//otherwise make the url currenturl?param&newparam
|
|
{
|
|
wsprintf(tszNewStartMenuURL, _T("%s&%s"), tszStartMenuURL, WU_STARTMENU_PARAM);
|
|
RegSetValueEx(hkWUURLKey, REGVALUENAME_WU_Start_URL, 0, REG_SZ, (LPBYTE)tszNewStartMenuURL, lstrlen(tszNewStartMenuURL) * sizeof(TCHAR));
|
|
LOG_out("Updating Start Menu URL To %s", tszNewStartMenuURL);
|
|
}
|
|
}
|
|
}
|
|
else // if the reg value is blank, use the default WU URL + "?Start"
|
|
{
|
|
wsprintf(tszNewStartMenuURL, _T("%s?%s"), DEFAULT_WU_URL, WU_STARTMENU_PARAM);
|
|
RegSetValueEx(hkWUURLKey, REGVALUENAME_WU_Start_URL, 0, REG_SZ, (LPBYTE)tszNewStartMenuURL, lstrlen(tszNewStartMenuURL) * sizeof(TCHAR));
|
|
LOG_out("Updated Start Menu URL to %s", tszNewStartMenuURL);
|
|
}
|
|
|
|
RegCloseKey(hkWUURLKey);
|
|
}
|
|
}
|
|
|
|
|
|
// This function converts our internal V3 catalog structure into a safearray of variants
|
|
// that the VBScript web page uses. The format of the safe array will be:
|
|
// array(i,0) = NUMBER puid
|
|
// array(i,1) = STRING TITLE
|
|
// array(i,2) = STRING Description
|
|
// array(i,3) = NUMBER Item Status
|
|
// array(i,4) = NUMBER Download Size in Bytes
|
|
// array(i,5) = NUMBER Download Time in seconds
|
|
// array(i,6) = STRING Uninstall Key
|
|
// array(i,7) = STRING Read This Url
|
|
HRESULT CCV3::MakeReturnCatalogArray(
|
|
CCatalog *pCatalog, //Pointer to catalog structure to be converted.
|
|
long lFilters, //Filters to apply, see GetCatalog for the actual descriptions.
|
|
long lFlags, //Flags that control the amount of information returned in each array record.
|
|
VARIANT *pvaVariant //pointer to returned safearray.
|
|
)
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
PUID puid;
|
|
PWSTR pTitle;
|
|
PWSTR pDescription;
|
|
PSTR pUninstall;
|
|
PWSTR pReadThisUrl;
|
|
HRESULT hr;
|
|
LPVARIANT rgElems;
|
|
LPSAFEARRAY psa;
|
|
SAFEARRAYBOUND rgsabound[2];
|
|
PINVENTORY_ITEM pItem;
|
|
PWU_VARIABLE_FIELD pv;
|
|
PWU_VARIABLE_FIELD pvTitle;
|
|
PWU_VARIABLE_FIELD pvDescription;
|
|
PWU_VARIABLE_FIELD pvUninstall;
|
|
PWU_VARIABLE_FIELD pvRTF;
|
|
PWU_VARIABLE_FIELD pvAltName;
|
|
int i;
|
|
int t;
|
|
int size;
|
|
int downloadTime;
|
|
TCHAR szRTF[MAX_PATH];
|
|
|
|
if ( !pvaVariant )
|
|
return E_INVALIDARG;
|
|
|
|
VariantInit(pvaVariant);
|
|
|
|
hr = NOERROR;
|
|
|
|
rgsabound[0].lLbound = 0;
|
|
rgsabound[0].cElements = 0;
|
|
|
|
rgsabound[1].lLbound = 0;
|
|
rgsabound[1].cElements = 8;
|
|
|
|
// count number records to return
|
|
for(i=0; i<pCatalog->GetHeader()->totalItems; i++)
|
|
{
|
|
pItem = pCatalog->GetItem(i);
|
|
if (NULL == pItem)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( !FilterCatalogItem(pItem, lFilters) )
|
|
continue;
|
|
rgsabound[0].cElements++;
|
|
}
|
|
|
|
psa = SafeArrayCreate(VT_VARIANT, 2, rgsabound);
|
|
if (!psa)
|
|
return E_OUTOFMEMORY;
|
|
|
|
// plug references to the data into the SAFEARRAY
|
|
if (FAILED(hr = SafeArrayAccessData(psa,(LPVOID*)&rgElems)))
|
|
return hr;
|
|
|
|
for(i = 0, t = 0; i < pCatalog->GetHeader()->totalItems; i++)
|
|
{
|
|
pItem = pCatalog->GetItem(i);
|
|
if (NULL == pItem)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!FilterCatalogItem(pItem, lFilters))
|
|
{
|
|
TRACE("Filtering (%d) skipped PUID %d while building SaveArray", lFilters, pItem->GetPuid());
|
|
continue;
|
|
}
|
|
|
|
if (!pItem->pd)
|
|
{
|
|
TRACE("PUID %d skipped while building SaveArray - no description", pItem->GetPuid());
|
|
continue;
|
|
}
|
|
|
|
size = pItem->pd->size;
|
|
downloadTime = CalcDownloadTime(size, pItem->pd->downloadTime);
|
|
|
|
|
|
pItem->GetFixedFieldInfo(WU_ITEM_PUID, (PVOID)&puid);
|
|
|
|
if ( pvTitle = pItem->pd->pv->Find(WU_DESCRIPTION_TITLE) )
|
|
pTitle = (PWSTR)pvTitle->pData;
|
|
else
|
|
pTitle = _T(""); // SafeArray should return empty string, not NULL BSTR
|
|
|
|
if (pvDescription = pItem->pd->pv->Find(WU_DESCRIPTION_DESCRIPTION))
|
|
pDescription = (PWSTR)pvDescription->pData;
|
|
else
|
|
pDescription = _T(""); // SafeArray should return empty string, not NULL BSTR
|
|
|
|
// uninstall/altname field
|
|
pUninstall = ""; // SafeArray should return empty string, not NULL BSTR
|
|
if (pItem->recordType == WU_TYPE_CDM_RECORD)
|
|
{
|
|
//NOTE: we are simply returning a key that says script should put the uninstall button
|
|
// currently we will always display this button but we will change this to only
|
|
// display it when the driver can be uninstalled
|
|
pUninstall = "DriverUninstall";
|
|
}
|
|
else if (pItem->recordType == WU_TYPE_ACTIVE_SETUP_RECORD)
|
|
{
|
|
if ((pvUninstall = pItem->pd->pv->Find(WU_DESCRIPTION_UNINSTALL_KEY)))
|
|
{
|
|
pUninstall = (char *)pvUninstall->pData;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// for everything else, we pass the AltName field if provided
|
|
if ((pvAltName = pItem->pd->pv->Find(WU_DESC_ALTNAME)))
|
|
{
|
|
pUninstall = (char *)pvAltName->pData;
|
|
}
|
|
}
|
|
|
|
// read this first page
|
|
if ((pvRTF = pItem->pd->pv->Find(WU_DESC_RTF_CRC_ARRAY)))
|
|
{
|
|
TCHAR szTemp[MAX_PATH];
|
|
|
|
GetWindowsUpdateDirectory(szTemp);
|
|
|
|
wsprintf(szRTF, _T("file://%sRTF\\%d\\%d.htm"), szTemp, pItem->GetPuid(), pItem->GetPuid());
|
|
pReadThisUrl = szRTF;
|
|
}
|
|
else
|
|
{
|
|
pReadThisUrl = _T(""); // SafeArray should return empty string, not NULL BSTR
|
|
}
|
|
|
|
try
|
|
{
|
|
TRACE("PUID %d added to SaveArray with ReturnStatus %d", pItem->GetPuid(), GetItemReturnStatus(pItem));
|
|
/*
|
|
// Noisy Debug Only
|
|
TRACE("\t%S",pTitle);
|
|
TRACE("\t%S", pDescription);
|
|
TRACE("\t%d", GetItemReturnStatus(pItem));
|
|
TRACE("\t%d", size);
|
|
TRACE("\t%d", downloadTime);
|
|
TRACE("\t%s", pUninstall);
|
|
TRACE("\t%s", pReadThisUrl);
|
|
*/
|
|
AddSafeArrayRecord(rgElems, t, (int)rgsabound[0].cElements,
|
|
"%d%s%s%d%d%d%s%s",
|
|
puid,
|
|
pTitle,
|
|
pDescription,
|
|
GetItemReturnStatus(pItem),
|
|
size,
|
|
downloadTime,
|
|
A2W(pUninstall),
|
|
pReadThisUrl);
|
|
t++;
|
|
}
|
|
catch(HRESULT hr)
|
|
{
|
|
TRACE("Exception thrown calling AddSafeArrayRecord");
|
|
for (; t; t--)
|
|
DeleteSafeArrayRecord(rgElems, t, (int)rgsabound[0].cElements, "%d%s%s%d%d%d%s%s");
|
|
|
|
SafeArrayUnaccessData(psa);
|
|
|
|
VariantInit(pvaVariant);
|
|
|
|
throw hr;
|
|
}
|
|
}
|
|
|
|
SafeArrayUnaccessData(psa);
|
|
|
|
V_VT(pvaVariant) = VT_ARRAY | VT_VARIANT;
|
|
V_ARRAY(pvaVariant) = psa;
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
// This function converts our internal V3 catalog structure for inventory.plt catalogs into a
|
|
// safearray of variants that the VBScript web page uses. The format of the safe array will be:
|
|
// array(i,0) = NUMBER puid
|
|
// array(i,1) = STRING TITLE
|
|
// array(i,2) = STRING Description
|
|
// array(i,3) = NUMBER Item Status
|
|
HRESULT CCV3::MakeReturnCatalogListArray(
|
|
CCatalog *pCatalog, //Pointer to catalog structure to be converted.
|
|
long lFilters, //Filters to apply, see GetCatalog for the actual descriptions.
|
|
long lFlags, //Flags that control the amount of information returned in each array record.
|
|
VARIANT *pvaVariant //pointer to returned safearray.
|
|
)
|
|
{
|
|
PUID puid;
|
|
PWSTR pTitle;
|
|
PWSTR pDescription;
|
|
HRESULT hr;
|
|
LPVARIANT rgElems;
|
|
LPSAFEARRAY psa;
|
|
SAFEARRAYBOUND rgsabound[2];
|
|
PINVENTORY_ITEM pItem;
|
|
PWU_VARIABLE_FIELD pv;
|
|
PWU_VARIABLE_FIELD pvTitle;
|
|
PWU_VARIABLE_FIELD pvDescription;
|
|
PWU_VARIABLE_FIELD pvUninstall;
|
|
int i;
|
|
int t;
|
|
int size;
|
|
int downloadTime;
|
|
|
|
|
|
if ( !pvaVariant )
|
|
return E_INVALIDARG;
|
|
|
|
VariantInit(pvaVariant);
|
|
|
|
hr = NOERROR;
|
|
|
|
rgsabound[0].lLbound = 0;
|
|
rgsabound[0].cElements = 0;
|
|
|
|
rgsabound[1].lLbound = 0;
|
|
rgsabound[1].cElements = 4;
|
|
|
|
// count number records to return
|
|
for(i=0; i<pCatalog->GetHeader()->totalItems; i++)
|
|
{
|
|
if (NULL == (pItem = pCatalog->GetItem(i)))
|
|
{
|
|
continue;
|
|
}
|
|
if ( !FilterCatalogItem(pItem, lFilters) )
|
|
continue;
|
|
rgsabound[0].cElements++;
|
|
}
|
|
|
|
psa = SafeArrayCreate(VT_VARIANT, 2, rgsabound);
|
|
if ( !psa )
|
|
return E_OUTOFMEMORY;
|
|
|
|
// plug references to the data into the SAFEARRAY
|
|
if (FAILED(hr = SafeArrayAccessData(psa,(LPVOID*)&rgElems)))
|
|
return hr;
|
|
|
|
for(i=0,t=0; i<pCatalog->GetHeader()->totalItems; i++)
|
|
{
|
|
if (NULL == (pItem = pCatalog->GetItem(i)))
|
|
{
|
|
continue;
|
|
}
|
|
if ( !FilterCatalogItem(pItem, lFilters) )
|
|
continue;
|
|
|
|
//array(i,0) = NUMBER puid
|
|
//array(i,1) = STRING TITLE
|
|
//array(i,2) = STRING Description
|
|
//array(i,3) = NUMBER Item Status
|
|
|
|
pItem->GetFixedFieldInfo(WU_ITEM_PUID, (PVOID)&puid);
|
|
|
|
if ( pvTitle = pItem->pd->pv->Find(WU_DESCRIPTION_TITLE) )
|
|
pTitle = (PWSTR)pvTitle->pData;
|
|
else
|
|
pTitle = _T(""); // SafeArray should return empty string, not NULL BSTR
|
|
|
|
if ( pvDescription = pItem->pd->pv->Find(WU_DESCRIPTION_DESCRIPTION) )
|
|
pDescription = (PWSTR)pvDescription->pData;
|
|
else
|
|
pDescription = _T(""); // SafeArray should return empty string, not NULL BSTR
|
|
|
|
try
|
|
{
|
|
AddSafeArrayRecord(rgElems, t, (int)rgsabound[0].cElements, "%d%s%s%d",
|
|
puid, pTitle, pDescription, GetItemReturnStatus(pItem));
|
|
t++;
|
|
}
|
|
catch(HRESULT hr)
|
|
{
|
|
for (; t; t--)
|
|
DeleteSafeArrayRecord(rgElems, t, (int)rgsabound[0].cElements, "%d%s%s%d");
|
|
|
|
SafeArrayUnaccessData(psa);
|
|
|
|
VariantInit(pvaVariant);
|
|
|
|
throw hr;
|
|
}
|
|
}
|
|
|
|
SafeArrayUnaccessData(psa);
|
|
|
|
V_VT(pvaVariant) = VT_ARRAY | VT_VARIANT;
|
|
V_ARRAY(pvaVariant) = psa;
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
|
|
void DetectActiveSetupWU(
|
|
CWUDownload* pDownload, // CWUDLOAD download class to be used to download detection DLLs.
|
|
CDiamond* pDiamond, // Diamond Expansion class.
|
|
CCatalog* pCatalog // catalog that detection CIF is to be created from.
|
|
)
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
int cItemsDetected = 0;
|
|
PWU_VARIABLE_FIELD pVar;
|
|
DWORD dwDetectStatus;
|
|
CComponentDetection CompDet;
|
|
|
|
LOG_block("DetectActiveSetupWU");
|
|
|
|
//check input argument if valid
|
|
if (NULL == pCatalog)
|
|
{
|
|
return ;
|
|
}
|
|
|
|
for (int i = 0; i < pCatalog->GetHeader()->totalItems; i++)
|
|
{
|
|
PINVENTORY_ITEM pItem = pCatalog->GetItem(i);
|
|
//check if the function GetItem has returned valid value
|
|
if (NULL == pItem)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (pItem->recordType != WU_TYPE_ACTIVE_SETUP_RECORD || (pItem->ps->state == WU_ITEM_STATE_PRUNED))
|
|
continue;
|
|
|
|
if (!IsValidGuid(&pItem->pf->a.g))
|
|
continue;
|
|
|
|
WCHAR wszGuid[64];
|
|
if (StringFromGUID2(pItem->pf->a.g, wszGuid, sizeof(wszGuid)/sizeof(wszGuid[0])) == 0)
|
|
continue;
|
|
|
|
PUID puid = pItem->GetPuid();
|
|
LOG_out("puid %d", puid);
|
|
if (puid <= 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// we have a valid component to detect
|
|
//
|
|
cItemsDetected++;
|
|
|
|
// guid
|
|
CompDet.SetValue(CComponentDetection::ccGUID, W2A(wszGuid));
|
|
|
|
// version
|
|
char szVersion[64];
|
|
VersionToString(&pItem->pf->a.version, szVersion);
|
|
CompDet.SetValue(CComponentDetection::ccVersion, szVersion);
|
|
|
|
//
|
|
// detection dll variable fields
|
|
//
|
|
char szDllName[128];
|
|
szDllName[0] = '\0';
|
|
|
|
if (pItem->pv->Find(WU_DETECT_DLL_REG_KEY_EXISTS))
|
|
{
|
|
// WUDETECT.DLL,RegKeyExists special case
|
|
CompDet.SetValue(CComponentDetection::ccDetectVersion, "wudetect.dll,RegKeyExists");
|
|
|
|
strcpy(szDllName, "wudetect.bin");
|
|
}
|
|
|
|
if ((pVar = pItem->pv->Find(WU_DETECT_DLL_GENERIC)))
|
|
{
|
|
LPSTR ptr;
|
|
|
|
// generic detection DLL including WUDETECT.DLL,RegKeyVersion case
|
|
CompDet.SetValue(CComponentDetection::ccDetectVersion, (LPCSTR)pVar->pData);
|
|
|
|
// parse out detection dll name from DetectVersion= value
|
|
if ((ptr = (PSTR)_memccpy(szDllName, (char *)pVar->pData, '.', sizeof(szDllName))))
|
|
{
|
|
*(ptr - 1) = 0;
|
|
}
|
|
strcat(szDllName, ".bin");
|
|
|
|
}
|
|
|
|
//
|
|
// download the dll. Download DLL takes care of caching
|
|
//
|
|
if (szDllName[0] != '\0')
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
|
|
// construct full path for the detection dll
|
|
wsprintf(szPath, _T("Detect/%d/%s"), pCatalog->GetPlatform(), A2T(szDllName));
|
|
|
|
DownloadDLL(pDiamond, pDownload, A2T(szDllName), szPath);
|
|
}
|
|
|
|
|
|
//
|
|
// other variable length fields
|
|
//
|
|
|
|
if ((pVar = pItem->pv->Find(WU_KEY_CUSTOMDETECT)))
|
|
{
|
|
// custom data
|
|
CompDet.SetValue(CComponentDetection::ccCustomData, (LPCSTR)pVar->pData);
|
|
}
|
|
|
|
|
|
|
|
if ((pVar = pItem->pv->Find(WU_KEY_UNINSTALLKEY)))
|
|
{
|
|
// UninstallKey, remove quotes first
|
|
char szKeyVal[MAX_PATH];
|
|
LPCSTR pszKeyDat = (LPCSTR)pVar->pData;
|
|
|
|
if (pszKeyDat[0] == '"')
|
|
{
|
|
strcpy(szKeyVal, (pszKeyDat + 1));
|
|
int iKenLen = strlen(szKeyVal);
|
|
if (iKenLen > 0)
|
|
szKeyVal[iKenLen - 1] = '\0';
|
|
}
|
|
else
|
|
{
|
|
strcpy(szKeyVal, pszKeyDat);
|
|
}
|
|
|
|
CompDet.SetValue(CComponentDetection::ccUninstallKey, szKeyVal);
|
|
}
|
|
|
|
// locale
|
|
if (!(pVar = pItem->pv->Find(WU_DET_CIF_LOCALE)))
|
|
{
|
|
CompDet.SetValue(CComponentDetection::ccLocale, "*");
|
|
}
|
|
else
|
|
{
|
|
CompDet.SetValue(CComponentDetection::ccLocale, (LPCSTR)pVar->pData);
|
|
}
|
|
|
|
|
|
/*
|
|
DEBUG CODE ONLY
|
|
|
|
LOG_out("[%d]", pItem->GetPuid());
|
|
char buf[512];
|
|
if (CompDet.GetValue(CComponentDetection::ccGUID, buf, sizeof(buf))) LOG_out("%s=%s", "GUID", buf);
|
|
if (CompDet.GetValue(CComponentDetection::ccVersion, buf, sizeof(buf))) LOG_out("%s=%s", "Version", buf);
|
|
if (CompDet.GetValue(CComponentDetection::ccUninstallKey , buf, sizeof(buf))) LOG_out("%s=%s", "UninstallKey", buf);
|
|
if (CompDet.GetValue(CComponentDetection::ccDetectVersion, buf, sizeof(buf))) LOG_out("%s=%s", "DetectVersion", buf);
|
|
if (CompDet.GetValue(CComponentDetection::ccRegKeyVersion , buf, sizeof(buf))) LOG_out("%s=%s", "RegKeyVersion", buf);
|
|
if (CompDet.GetValue(CComponentDetection::ccLocale , buf, sizeof(buf))) LOG_out("%s=%s", "Locale", buf);
|
|
if (CompDet.GetValue(CComponentDetection::ccQFEVersion , buf, sizeof(buf))) LOG_out("%s=%s", "QFEVersion", buf);
|
|
if (CompDet.GetValue(CComponentDetection::ccCustomData , buf, sizeof(buf))) LOG_out("%s=%s", "CustomData", buf);
|
|
*/
|
|
|
|
//
|
|
// now detect the item
|
|
//
|
|
dwDetectStatus = CompDet.Detect();
|
|
switch (dwDetectStatus)
|
|
{
|
|
case ICI_NOTINSTALLED: // 0 = item not installed(INSTALL)
|
|
pItem->ps->state = WU_ITEM_STATE_INSTALL;
|
|
LOG_out("puid %d not installed", pItem->GetPuid());
|
|
break;
|
|
case ICI_INSTALLED: // 1 = this item is curretly installed
|
|
pItem->ps->state = WU_ITEM_STATE_CURRENT;
|
|
LOG_out("puid %d installed current version", pItem->GetPuid());
|
|
break;
|
|
case ICI_NEWVERSIONAVAILABLE: // 2 Items is installed but newer available
|
|
pItem->ps->state = WU_ITEM_STATE_UPDATE;
|
|
{ // we want to save currently installed version
|
|
DWORD dwInstalledVer;
|
|
DWORD dwInstalledBuild;
|
|
CompDet.GetInstalledVersion(&dwInstalledVer, &dwInstalledBuild);
|
|
pItem->ps->verInstalled.major = HIWORD(dwInstalledVer);
|
|
pItem->ps->verInstalled.minor = LOWORD(dwInstalledVer);
|
|
pItem->ps->verInstalled.build = HIWORD(dwInstalledBuild);
|
|
pItem->ps->verInstalled.ext = LOWORD(dwInstalledBuild);
|
|
LOG_out("puid %d installed version %d.%d.%d.%d and new version available ", pItem->GetPuid(),
|
|
pItem->ps->verInstalled.major, pItem->ps->verInstalled.minor, pItem->ps->verInstalled.build, pItem->ps->verInstalled.ext);
|
|
}
|
|
break;
|
|
case ICI_UNKNOWN: // 3 cannot be determined
|
|
case ICI_OLDVERSIONAVAILABLE: // 4 Why would anyone want to install the older version?
|
|
case ICI_NOTINITIALIZED: // 0xffffffff
|
|
default:
|
|
pItem->ps->bHidden = TRUE;
|
|
pItem->ps->state = WU_ITEM_STATE_PRUNED;
|
|
LOG_out("puid %d should be ignored and hidden", pItem->GetPuid());
|
|
break;
|
|
}
|
|
|
|
|
|
} // each item
|
|
|
|
LOG_out("%d items were detected", cItemsDetected);
|
|
}
|
|
|
|
|
|
|
|
void DownloadItem(
|
|
LPCTSTR pszLocalDir, //directory to download to. Must be created by caller
|
|
CWUDownload* pDownload, //Pointer to internet download class
|
|
CDiamond* pDiamond, //Pointer to compression class
|
|
PINSTALLINFOSTRUCT pInstallInfo, //Pointer to installation information structure.
|
|
IWUProgress* pProgress
|
|
)
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
BYTE itemFlags = 0;
|
|
BOOL bCloseConnection = FALSE;
|
|
DWORD platformId;
|
|
HRESULT hrError;
|
|
PWU_VARIABLE_FIELD pvCabs = NULL;
|
|
PWU_VARIABLE_FIELD pvCRCs = NULL;
|
|
PWU_VARIABLE_FIELD pvServer;
|
|
PWU_VARIABLE_FIELD pvTmp;
|
|
TCHAR szLocale[64];
|
|
BOOL b128Bit;
|
|
|
|
//Check if the input argument is not NULL
|
|
if ( (NULL == pInstallInfo) || (NULL == pInstallInfo->pItem) )
|
|
{
|
|
return ;
|
|
}
|
|
|
|
//See if the package has a server override defined.
|
|
if ((pvServer = pInstallInfo->pItem->pd->pv->Find(WU_DESCRIPTION_SERVERROOT)))
|
|
{
|
|
pDownload = new CWUDownload((LPCTSTR)pvServer->pData);
|
|
bCloseConnection = TRUE;
|
|
}
|
|
|
|
try
|
|
{
|
|
|
|
if (pvTmp = pInstallInfo->pItem->pd->pv->Find(WU_DESCRIPTION_TITLE))
|
|
{
|
|
// if the item is hidden, look for the title in the TopLevel item
|
|
if (pInstallInfo->pItem->ps->bHidden && (pInstallInfo->pTopLevelItem != NULL) && (pInstallInfo->pTopLevelItem->pd != NULL))
|
|
{
|
|
PWU_VARIABLE_FIELD pvTopLvl = pInstallInfo->pTopLevelItem->pd->pv->Find(WU_DESCRIPTION_TITLE);
|
|
if (pvTopLvl)
|
|
{
|
|
pvTmp = pvTopLvl;
|
|
}
|
|
}
|
|
pProgress->SetStatusText(W2T((LPWSTR)(pvTmp->pData)));
|
|
|
|
}
|
|
|
|
//
|
|
// Calculate CIF path
|
|
//
|
|
pInstallInfo->pItem->GetFixedFieldInfo(WU_ITEM_FLAGS, &itemFlags);
|
|
|
|
// locale
|
|
if (pInstallInfo->dwLocaleID == 0)
|
|
{
|
|
if (itemFlags & WU_BROWSER_LANGAUGE_FLAG)
|
|
{
|
|
lstrcpy(szLocale, pInstallInfo->pCatalog->GetBrowserLocaleSZ());
|
|
}
|
|
else
|
|
{
|
|
wsprintf(szLocale, _T("0x%8.8x"), GetMachineLangDW());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
wsprintf(szLocale, _T("0x%8.8x"), pInstallInfo->dwLocaleID);
|
|
}
|
|
|
|
// platform
|
|
if (pInstallInfo->dwPlatform == 0)
|
|
platformId = pInstallInfo->pCatalog->GetPlatform();
|
|
else
|
|
platformId = pInstallInfo->dwPlatform;
|
|
|
|
|
|
pInstallInfo->pInfo->iStatus = ITEM_STATUS_DOWNLOAD_COMPLETE;
|
|
pInstallInfo->pInfo->hrError = NOERROR;
|
|
|
|
//
|
|
// download files
|
|
//
|
|
if (pInstallInfo->pItem->recordType == WU_TYPE_ACTIVE_SETUP_RECORD ||
|
|
pInstallInfo->pItem->recordType == WU_TYPE_CDM_RECORD ||
|
|
pInstallInfo->pItem->recordType == WU_TYPE_RECORD_TYPE_PRINTER)
|
|
{
|
|
b128Bit = FALSE;
|
|
|
|
if (pInstallInfo->pItem->recordType == WU_TYPE_ACTIVE_SETUP_RECORD)
|
|
{
|
|
//download CIF
|
|
DownloadCif(pszLocalDir, pDiamond, pInstallInfo);
|
|
|
|
// check for 128 bit
|
|
if ((pvTmp = pInstallInfo->pItem->pd->pv->Find(WU_DESC_128BIT_PRODID)))
|
|
{
|
|
b128Bit = TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
if (b128Bit)
|
|
{
|
|
// download 128 bit components
|
|
Download128Bit(pszLocalDir, pInstallInfo->pItem, pProgress);
|
|
}
|
|
else
|
|
{
|
|
// download install CABs
|
|
pvCabs = pInstallInfo->pItem->pd->pv->Find(WU_DESCRIPTION_CABFILENAME);
|
|
pvCRCs = pInstallInfo->pItem->pd->pv->Find(WU_DESC_CRC_ARRAY);
|
|
|
|
if (NULL == pvCabs || NULL == pvCRCs)
|
|
{
|
|
// Active setup items can have no cabs
|
|
if( pInstallInfo->pItem->recordType != WU_TYPE_ACTIVE_SETUP_RECORD)
|
|
throw HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
|
|
}
|
|
else
|
|
{
|
|
|
|
DownloadCabs(pszLocalDir,
|
|
pDownload,
|
|
pDiamond,
|
|
(PBYTE)pvCabs->pData,
|
|
(PBYTE)pvCRCs->pData,
|
|
pProgress,
|
|
pInstallInfo->pItem->recordType != WU_TYPE_ACTIVE_SETUP_RECORD);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw E_UNEXPECTED;
|
|
}
|
|
|
|
}
|
|
catch(HRESULT hr)
|
|
{
|
|
//
|
|
// download failed
|
|
//
|
|
pInstallInfo->pInfo->hrError = hr;
|
|
pInstallInfo->pInfo->iStatus = ITEM_STATUS_FAILED;
|
|
|
|
if (bCloseConnection)
|
|
{
|
|
delete pDownload;
|
|
}
|
|
|
|
|
|
//
|
|
// ping URL to report failure
|
|
//
|
|
if (pInstallInfo->pTopLevelItem == NULL)
|
|
{
|
|
// for top level components only
|
|
URLPingReport(pInstallInfo->pItem, pInstallInfo->pCatalog, pInstallInfo->pInfo, (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED)) ? URLPING_CANCELED : URLPING_FAILED);
|
|
}
|
|
|
|
throw hr;
|
|
}
|
|
|
|
if (bCloseConnection)
|
|
{
|
|
delete pDownload;
|
|
}
|
|
|
|
//
|
|
// ping URL to report success
|
|
//
|
|
if (pInstallInfo->pTopLevelItem == NULL)
|
|
{
|
|
URLPingReport(pInstallInfo->pItem, pInstallInfo->pCatalog, pInstallInfo->pInfo, URLPING_SUCCESS);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// this function perform and installation of a catalog item. This function takes care
|
|
// of ensuring that the correct installer is called based on the package type.
|
|
void InstallItem(
|
|
LPCTSTR pszLocalDir, //Local client machine root directory the temp storage
|
|
PSELECTITEMINFO pStatusInfo, //Pointer to installation status array
|
|
PINSTALLINFOSTRUCT pInstallInfo, //Pointer to installation information structure.
|
|
IWUProgress* pProgress
|
|
)
|
|
{
|
|
HRESULT hrError;
|
|
PWU_VARIABLE_FIELD pvTitle;
|
|
PWU_VARIABLE_FIELD pvTmp;
|
|
TCHAR szCIFFile[MAX_PATH];
|
|
BOOL bWindowsNT;
|
|
|
|
//check the input arguments
|
|
if (NULL == pszLocalDir || NULL == pStatusInfo || NULL == pInstallInfo)
|
|
{
|
|
return;
|
|
}
|
|
|
|
bWindowsNT = IsWindowsNT();
|
|
try
|
|
{
|
|
if (pvTitle = pInstallInfo->pItem->pd->pv->Find(WU_DESCRIPTION_TITLE))
|
|
{
|
|
// if the item is hidden, look for the title in the TopLevel item
|
|
if (pInstallInfo->pItem->ps->bHidden && (pInstallInfo->pTopLevelItem != NULL) && (pInstallInfo->pTopLevelItem->pd != NULL))
|
|
{
|
|
PWU_VARIABLE_FIELD pvTopLvl = pInstallInfo->pTopLevelItem->pd->pv->Find(WU_DESCRIPTION_TITLE);
|
|
if (pvTopLvl)
|
|
{
|
|
pvTitle = pvTopLvl;
|
|
}
|
|
}
|
|
pProgress->SetStatusText(W2T((LPWSTR)(pvTitle->pData)));
|
|
}
|
|
|
|
|
|
GetCurTime(&(pStatusInfo->stDateTime));
|
|
pStatusInfo->iStatus = ITEM_STATUS_SUCCESS;
|
|
pStatusInfo->hrError = NOERROR;
|
|
|
|
TRACE("InstallItem, puid=%d, recordtype: %d", pInstallInfo->pItem->GetPuid(), (DWORD)pInstallInfo->pItem->recordType);
|
|
|
|
switch (pInstallInfo->pItem->recordType)
|
|
{
|
|
case WU_TYPE_ACTIVE_SETUP_RECORD:
|
|
BLOCK
|
|
{
|
|
TCHAR szCifBaseName[16];
|
|
|
|
wsprintf(szCifBaseName, _T("%d.cif"), pInstallInfo->pItem->GetPuid());
|
|
GetWindowsUpdateDirectory(szCIFFile);
|
|
lstrcat(szCIFFile, szCifBaseName);
|
|
}
|
|
|
|
//
|
|
// check to see if inseng is up to date (only done once)
|
|
//
|
|
CheckDllsToJit(pInstallInfo->pCatalog->GetCatalogServer());
|
|
|
|
//
|
|
// install active setup item
|
|
//
|
|
if ((pvTmp = pInstallInfo->pItem->pd->pv->Find(WU_DESC_128BIT_PRODID)))
|
|
{
|
|
// 128 bit
|
|
TCHAR szCmd[MAX_PATH];
|
|
long lRet;
|
|
|
|
lstrcpy(szCmd, pszLocalDir);
|
|
AddBackSlash(szCmd);
|
|
lstrcat(szCmd, EXENAME_128BIT);
|
|
|
|
lRet = LaunchProcess(szCmd, NULL, SW_NORMAL, TRUE);
|
|
}
|
|
else
|
|
{
|
|
InstallActiveSetupItem(pszLocalDir, szCIFFile, pStatusInfo, pProgress);
|
|
}
|
|
|
|
// delete CIF file
|
|
DeleteFile(szCIFFile);
|
|
break;
|
|
|
|
case WU_TYPE_CDM_RECORD:
|
|
|
|
InstallDriverItem(pszLocalDir, bWindowsNT, pvTitle ? W2T((LPWSTR)(pvTitle->pData)) : _T(""), pInstallInfo->pItem, pStatusInfo);
|
|
break;
|
|
|
|
case WU_TYPE_RECORD_TYPE_PRINTER:
|
|
{
|
|
PWU_VARIABLE_FIELD pvDriverName = pInstallInfo->pItem->pv->Find(WU_CDM_DRIVER_NAME);
|
|
PWU_VARIABLE_FIELD pvArchitecture = pInstallInfo->pItem->pv->Find(WU_CDM_PRINTER_DRIVER_ARCH);
|
|
if (NULL == pvDriverName)
|
|
throw E_UNEXPECTED; // should never happen
|
|
InstallPrinterItem((LPCTSTR)pvDriverName->pData, pszLocalDir,
|
|
NULL == pvArchitecture ? NULL : (LPCTSTR)pvArchitecture->pData);
|
|
}
|
|
break;
|
|
|
|
case WU_TYPE_CDM_RECORD_PLACE_HOLDER:
|
|
case WU_TYPE_SECTION_RECORD:
|
|
case WU_TYPE_SUBSECTION_RECORD:
|
|
case WU_TYPE_SUBSUBSECTION_RECORD:
|
|
default:
|
|
throw E_UNEXPECTED;
|
|
}
|
|
if(ITEM_STATUS_FAILED == pStatusInfo->iStatus)
|
|
{
|
|
URLPingReport(pInstallInfo->pItem, pInstallInfo->pCatalog, pStatusInfo, URLPING_INSTALL_FAILED);
|
|
}
|
|
else
|
|
{
|
|
URLPingReport(pInstallInfo->pItem, pInstallInfo->pCatalog, pStatusInfo, URLPING_INSTALL_SUCCESS);
|
|
}
|
|
}
|
|
catch(HRESULT hr)
|
|
{
|
|
pStatusInfo->hrError = hr;
|
|
pStatusInfo->iStatus = ITEM_STATUS_FAILED;
|
|
URLPingReport(pInstallInfo->pItem, pInstallInfo->pCatalog, pStatusInfo, URLPING_INSTALL_FAILED);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Download128Bit
|
|
// this shouldn't get called anymore since the US lifted export restrictions on 128 bit items:
|
|
// we don't create items with the 128 bit flag anymore
|
|
//
|
|
void Download128Bit(LPCTSTR pszLocalDir, PINVENTORY_ITEM pItem, IWUProgress* pProgress)
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
static const TCHAR FAILUREURL[] = _T("about:blank");
|
|
static const TCHAR FORMDATA[] = _T("selProdID=%s&Next=Download+Now%%21&failureURL=%s");
|
|
|
|
CWUDownload* pDL = NULL;
|
|
TCHAR szFormData[MAX_PATH];
|
|
TCHAR szLocalFile[MAX_PATH];
|
|
PWU_VARIABLE_FIELD pvTmp;
|
|
DWORD dwErr = ERROR_SUCCESS;
|
|
|
|
// get product id
|
|
if (!(pvTmp = pItem->pd->pv->Find(WU_DESC_128BIT_PRODID)))
|
|
throw HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
|
|
// build form data
|
|
wsprintf(szFormData, FORMDATA, A2T((LPSTR)pvTmp->pData), FAILUREURL);
|
|
|
|
// get URL
|
|
if (!(pvTmp = pItem->pd->pv->Find(WU_DESC_128BIT_URL)))
|
|
throw HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
|
|
// build local file name
|
|
lstrcpy(szLocalFile, pszLocalDir);
|
|
AddBackSlash(szLocalFile);
|
|
lstrcat(szLocalFile, EXENAME_128BIT);
|
|
|
|
// create download class
|
|
pDL = new CWUDownload(A2T((LPSTR)pvTmp->pData), 8192);
|
|
if (!pDL)
|
|
{
|
|
dwErr = GetLastError();
|
|
throw HRESULT_FROM_WIN32(dwErr);
|
|
}
|
|
|
|
if (!pDL->PostCopy(szFormData, szLocalFile, pProgress))
|
|
{
|
|
dwErr = GetLastError();
|
|
}
|
|
delete pDL;
|
|
|
|
if (dwErr != ERROR_SUCCESS)
|
|
throw HRESULT_FROM_WIN32(dwErr);
|
|
}
|
|
|
|
|
|
|
|
void DownloadCabs(
|
|
LPCTSTR szLocalDir, //Local client machine directory to use for temp storage
|
|
CWUDownload *pDownload, //Pointer to internet download class
|
|
CDiamond *pDiamond, //Pointer to compression class
|
|
PBYTE pCabList, //Multi SZ list of cabs to be downloaded.
|
|
PBYTE pCRCList, //array to CRC hash structures
|
|
IWUProgress* pProgress,
|
|
BOOL bUnCab
|
|
)
|
|
{
|
|
|
|
TCHAR szLocalFile[MAX_PATH];
|
|
TCHAR szServerFile[MAX_PATH];
|
|
TCHAR szLocalCab[128];
|
|
TCHAR szServerCab[128];
|
|
int iCabNo = 0;
|
|
|
|
|
|
V3_CreateDirectory(szLocalDir);
|
|
|
|
for (;;)
|
|
{
|
|
|
|
if (FAILED(GetCRCNameFromList(iCabNo, pCabList, pCRCList, szServerCab, sizeof(szServerCab), szLocalCab)))
|
|
break;
|
|
|
|
// build full paths
|
|
lstrcpy(szLocalFile, szLocalDir);
|
|
AddBackSlash(szLocalFile);
|
|
lstrcat(szLocalFile, szLocalCab);
|
|
|
|
lstrcpy(szServerFile, _T("CabPool/"));
|
|
lstrcat(szServerFile, szServerCab);
|
|
|
|
TRACE("Downloading %s", szServerFile);
|
|
|
|
if (!pDownload->Copy(szServerFile, szLocalFile, NULL, NULL, 0, pProgress))
|
|
{
|
|
TRACE("Download of cab %s failed", szServerFile);
|
|
throw HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
//
|
|
// check signature of the download CAB file
|
|
// Don't show an MS cert
|
|
// use VerifyFile (see WU bug # 12251)
|
|
//
|
|
HRESULT hr = VerifyFile(szLocalFile, TRUE);
|
|
TRACE("VerifyFile(%s) Result: %x (%s)", szLocalFile, hr, SUCCEEDED(hr) ? "SUCCESS" : "FAILURE");
|
|
if (FAILED(hr))
|
|
throw hr;
|
|
|
|
if (bUnCab)
|
|
{
|
|
if (pDiamond->IsValidCAB(szLocalFile))
|
|
{
|
|
TCHAR szUnCompFn[MAX_PATH];
|
|
|
|
lstrcpy(szUnCompFn, szLocalDir);
|
|
AddBackSlash(szUnCompFn);
|
|
lstrcat(szUnCompFn, _T("*"));
|
|
|
|
pDiamond->Decompress(szLocalFile, szUnCompFn);
|
|
|
|
//
|
|
// delete the CAB file after uncompressing
|
|
//
|
|
DeleteFile(szLocalFile);
|
|
}
|
|
}
|
|
|
|
iCabNo++;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
void DownloadCif(
|
|
LPCTSTR szLocalDir, // Local client machine directory to use for temp storage
|
|
CDiamond *pDiamond, // Pointer to compression class
|
|
PINSTALLINFOSTRUCT pInstallInfo
|
|
)
|
|
{
|
|
V3_CreateDirectory(szLocalDir);
|
|
|
|
PWU_VARIABLE_FIELD pvCif = pInstallInfo->pItem->pd->pv->Find(WU_DESC_CIF_CRC);
|
|
if (!pvCif)
|
|
{
|
|
TRACE("CIF CRC field missing");
|
|
throw HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
}
|
|
TCHAR szCifBaseName[16];
|
|
wsprintf(szCifBaseName, _T("%d.cif"), pInstallInfo->pItem->GetPuid());
|
|
TCHAR szCifCRCName[64];
|
|
HRESULT hr = MakeCRCName(szCifBaseName, (WUCRC_HASH*)pvCif->pData, szCifCRCName, sizeof(szCifCRCName));
|
|
if (FAILED(hr))
|
|
throw hr;
|
|
|
|
// create a temp file name for the .CIF file with .CI$
|
|
TCHAR szLocalFile[MAX_PATH];
|
|
TCHAR szServerFile[MAX_PATH];
|
|
lstrcpy(szLocalFile, szLocalDir);
|
|
AddBackSlash(szLocalFile);
|
|
lstrcat(szLocalFile, szCifBaseName);
|
|
LPTSTR pszDot = _tcschr(szLocalFile, _T('.'));
|
|
if (pszDot)
|
|
*pszDot = _T('\0');
|
|
lstrcat(szLocalFile, _T(".CI$"));
|
|
|
|
// server file name
|
|
wsprintf(szServerFile, _T("CRCCif/%s"), szCifCRCName);
|
|
|
|
if (!pInstallInfo->pdlRoot->Copy(szServerFile, szLocalFile, NULL, NULL, 0, NULL))
|
|
{
|
|
TRACE("Download of CIF cab %s failed", szServerFile);
|
|
throw HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
// copy or uncompress to a .CIF file name
|
|
TCHAR szTemp[MAX_PATH];
|
|
GetWindowsUpdateDirectory(szTemp);
|
|
lstrcat(szTemp, szCifBaseName);
|
|
if (pDiamond->IsValidCAB(szLocalFile))
|
|
{
|
|
pDiamond->Decompress(szLocalFile, szTemp);
|
|
}
|
|
else
|
|
{
|
|
CopyFile(szLocalFile, szTemp, FALSE);
|
|
}
|
|
|
|
// NOTE:we don't delete the .CI$ file because all the files in the directory get deleted later
|
|
}
|
|
|
|
|
|
// download and save the FileVer.ini file in the cache
|
|
//
|
|
// NOTE: For 3.1 filever.ini file contains image download information as well
|
|
void DownloadFileVerInfo(CWUDownload* pDownload)
|
|
{
|
|
|
|
// if the file is missing, we don't have version information but we do not abort
|
|
(void)pDownload->Copy(FILEVERINI_FN, NULL, NULL, NULL, DOWNLOAD_NEWER | CACHE_FILE_LOCALLY, NULL);
|
|
}
|
|
|
|
|
|
|
|
void DownloadDLL(CDiamond *pDiamond, CWUDownload *pDownload, LPCTSTR pszDLLName, LPCTSTR pszPath)
|
|
{
|
|
//
|
|
// check our state detection dlls array to see if we downloaded
|
|
// this dll in this session already
|
|
//
|
|
// NOTE: We store the file name of the DLL as it is on the server OSDET.W98, wudetect.bin etc
|
|
//
|
|
|
|
TCHAR szDLLIn[MAX_PATH];
|
|
GetWindowsUpdateDirectory(szDLLIn);
|
|
lstrcat(szDLLIn, pszDLLName);
|
|
|
|
TCHAR szDLLOut[MAX_PATH];
|
|
lstrcpy(szDLLOut, szDLLIn);
|
|
|
|
TCHAR* ptr = _tcschr(szDLLOut, _T('.'));
|
|
if (ptr)
|
|
*ptr = 0;
|
|
|
|
lstrcat(szDLLOut, _T(".dll"));
|
|
|
|
if (g_v3state.CacheDLLName(pszDLLName))
|
|
{
|
|
//added by wei for test
|
|
TRACE("%s found in state cache", pszDLLName);
|
|
|
|
// doublecheck to make sure the DLL hasn't been deleted before we bail
|
|
if (FileExists(szDLLOut))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// download the file
|
|
//
|
|
if (pszPath != NULL)
|
|
{
|
|
if (!pDownload->Copy(pszPath, NULL, NULL, NULL, DOWNLOAD_NEWER | CACHE_FILE_LOCALLY | EXACT_FILENAME, NULL))
|
|
throw HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
else
|
|
{
|
|
if (!pDownload->Copy(pszDLLName, NULL, NULL, NULL, DOWNLOAD_NEWER | CACHE_FILE_LOCALLY, NULL))
|
|
throw HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
|
|
CConnSpeed::Learn(pDownload->GetCopySize(), pDownload->GetCopyTime());
|
|
|
|
|
|
|
|
//
|
|
// check to see if we download the file or the cached copy was used. If cache was used
|
|
// we just check to see if the DLL itself also exists, if yes then we return otherwise we
|
|
// will copy the or decompress the compressed file into the DLL
|
|
//
|
|
if (pDownload->CacheUsed())
|
|
{
|
|
if (FileExists(szDLLOut))
|
|
return;
|
|
}
|
|
|
|
if (pDiamond->IsValidCAB(szDLLIn))
|
|
pDiamond->Decompress(szDLLIn, szDLLOut);
|
|
else
|
|
CopyFile(szDLLIn, szDLLOut, FALSE);
|
|
}
|
|
|
|
|
|
void DetectPlatAndLang()
|
|
{
|
|
TCHAR szOSDETDLL[MAX_PATH];
|
|
|
|
GetWindowsUpdateDirectory(szOSDETDLL);
|
|
lstrcat(szOSDETDLL, _T("osdet.dll"));
|
|
|
|
//
|
|
// do platform and langauge detection using Osdet
|
|
//
|
|
CallOsDet(szOSDETDLL, &g_v3state.m_pdwPlatformList, &g_v3state.m_iTotalPlatforms);
|
|
|
|
//If we fail for any reason then use the default platform def_plat.
|
|
if ( !g_v3state.m_pdwPlatformList )
|
|
{
|
|
g_v3state.m_pdwPlatformList = (PULONG)&g_v3state.m_DefPlat;
|
|
g_v3state.m_iTotalPlatforms = 1;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// IObjectSafety
|
|
//
|
|
STDMETHODIMP CCV3::GetInterfaceSafetyOptions(REFIID riid, DWORD *pdwSupportedOptions, DWORD *pdwEnabledOptions)
|
|
{
|
|
if (pdwSupportedOptions == NULL || pdwEnabledOptions == NULL)
|
|
return E_POINTER;
|
|
HRESULT hr = S_OK;
|
|
if (riid == IID_IDispatch)
|
|
{
|
|
*pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER;
|
|
*pdwEnabledOptions = m_dwSafety & INTERFACESAFE_FOR_UNTRUSTED_CALLER;
|
|
}
|
|
else
|
|
{
|
|
*pdwSupportedOptions = 0;
|
|
*pdwEnabledOptions = 0;
|
|
hr = E_NOINTERFACE;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CCV3::SetInterfaceSafetyOptions(REFIID riid, DWORD dwOptionSetMask, DWORD dwEnabledOptions)
|
|
{
|
|
// If we're being asked to set our safe for scripting option then oblige
|
|
if (riid == IID_IDispatch)
|
|
{
|
|
// Store our current safety level to return in GetInterfaceSafetyOptions
|
|
m_dwSafety = dwEnabledOptions & dwOptionSetMask;
|
|
return S_OK;
|
|
}
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
//
|
|
// IWUpdateCatalog interface
|
|
//
|
|
|
|
STDMETHODIMP CCV3::WUIsCatalogAvailable(long puidCatalog,BSTR bstrServerUrl)
|
|
{
|
|
return IsCatalogAvailable(puidCatalog, bstrServerUrl);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CCV3::WUGetCatalog(long puidCatalog, BSTR bstrServerUrl, long platformId,
|
|
BSTR bstrBrowserLanguage, CCatalog** ppCatalogArray)
|
|
{
|
|
// we don't want to check the launch server from this interface
|
|
m_bLaunchServChecked = TRUE;
|
|
|
|
*ppCatalogArray = ProcessCatalog(puidCatalog, bstrServerUrl, platformId, bstrBrowserLanguage, WU_ALL_ITEMS, WU_NO_PRUNING);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP CCV3::WUDownloadItems(CSelections* pSelections, BSTR bstrServer, BSTR bstrTempDir)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CCV3::WUInstallItems(CSelections* pSelections, BSTR bstrServer, BSTR bstrTempDir)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CCV3::WURemoveItems(CSelections* pSelections)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP CCV3::WUCopyInstallHistory(HISTORYARRAY** ppHistoryArray)
|
|
{
|
|
int iTotalItems;
|
|
Varray<HISTORYSTRUCT> History;
|
|
PHISTORYARRAY pRetArray;
|
|
|
|
//Walk though item array for each install item. Each item selected for installation
|
|
//is store in a puid array that is part of the g_v3state class. This is a performance
|
|
//enhancement. If we did not do this then we would need to walk through the entire
|
|
//state structure for each catalog for every item and check it's installation state.
|
|
|
|
ReadHistory(History, iTotalItems);
|
|
|
|
//Since we must use the OLE allocator we now need to allocate the correct
|
|
//type of return memory and copy the existing history structure to it.
|
|
|
|
pRetArray = (PHISTORYARRAY)CoTaskMemAlloc(sizeof(HISTORYARRAY)+(iTotalItems * sizeof(HISTORYSTRUCT)));
|
|
if (NULL == pRetArray)
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
pRetArray->iTotalItems = iTotalItems;
|
|
|
|
memcpy((char *)pRetArray->HistoryItems, &History[0], (iTotalItems * sizeof(HISTORYSTRUCT)));
|
|
|
|
*ppHistoryArray = pRetArray;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP CCV3::WUCopyDependencyList(long puidItem, long** ppDepPui)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP CCV3::WUProgressDlg(BOOL bOn)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CCV3::IsWinUpdDisabled(VARIANT_BOOL * pfDisabled)
|
|
{
|
|
|
|
*pfDisabled = FALSE;
|
|
|
|
if (!m_bValidInstance)
|
|
{
|
|
return E_ACCESSDENIED;
|
|
}
|
|
|
|
bool fDisabled = false;
|
|
HKEY hKey;
|
|
DWORD dwDisabled;
|
|
DWORD dwSize = sizeof(dwDisabled);
|
|
DWORD dwType;
|
|
HKEY hkeyRoot = IsWindowsNT() ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
|
|
|
|
if ( RegOpenKeyEx( hkeyRoot,
|
|
REGPATH_EXPLORER,
|
|
NULL,
|
|
KEY_QUERY_VALUE,
|
|
&hKey) == ERROR_SUCCESS )
|
|
{
|
|
if ( RegQueryValueEx(hKey,
|
|
REGKEY_WINUPD_DISABLED,
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)&dwDisabled,
|
|
&dwSize) == ERROR_SUCCESS )
|
|
{
|
|
if ( (dwType == REG_DWORD) && (dwDisabled != 0) )
|
|
{
|
|
*pfDisabled = TRUE;
|
|
}
|
|
}
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP CCV3::IsReady(VARIANT_BOOL* pbYes)
|
|
{
|
|
*pbYes = TRUE;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CCV3::GetContentURL(OUT RETVAL VARIANT* pURL)
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
VariantInit(pURL);
|
|
|
|
V_VT(pURL) = VT_BSTR;
|
|
pURL->bstrVal = SysAllocString(T2OLE((LPTSTR)g_v3state.GetContentServer()));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// downloads the readthis first page and images for the puid locally
|
|
// script can then navigate to this page
|
|
STDMETHODIMP CCV3::GetReadThisPage(IN long puid)
|
|
{
|
|
CCatalog* pCatalog;
|
|
PINVENTORY_ITEM pItem;
|
|
|
|
if (!m_bValidInstance)
|
|
{
|
|
// the control is not launched from a trusted location
|
|
return E_ACCESSDENIED;
|
|
}
|
|
|
|
if (!g_v3state.GetCatalogAndItem(puid, &pItem, &pCatalog))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
return DownloadReadThis(pItem);
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP CCV3::GetPrintAllPage(OUT RETVAL VARIANT* pURL)
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
const char READTHISLIST[] = "%READTHISLIST%"; // a semi-colon separated list of read this pages. For instance "readthis1.htm;readthis2.htm;readthis3.htm"
|
|
const char READTHISPATH[] = "%READTHISPATH%"; // the URL to the read this pages. For instance "file://d:\windowsupdate\"
|
|
|
|
TCHAR szSourceFile[MAX_PATH];
|
|
HRESULT hr;
|
|
DWORD dwFileSize = 0;
|
|
PBYTE pFileBuf = NULL;
|
|
HANDLE hFile;
|
|
DWORD dwBytes;
|
|
LPTSTR pszReadThisList = NULL;
|
|
|
|
if (!m_bValidInstance)
|
|
{
|
|
// the control is not launched from a trusted location
|
|
return E_ACCESSDENIED;
|
|
}
|
|
|
|
// download the printall page
|
|
GetWindowsUpdateDirectory(szSourceFile);
|
|
lstrcat(szSourceFile, _T("RTF"));
|
|
V3_CreateDirectory(szSourceFile);
|
|
hr = DownloadCommonRTFFiles(TRUE, szSourceFile);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
// build a list of readthis first page names for all selected items
|
|
PSELECTITEMINFO pSel = g_v3state.m_selectedItems.GetItems();
|
|
int cSel = g_v3state.m_selectedItems.GetTotal();
|
|
if (cSel == 0)
|
|
{
|
|
// no items are selected
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// allocate enough memory for names of format puid\puid.htm;
|
|
pszReadThisList = (LPTSTR)malloc(cSel * 80);
|
|
|
|
if(!pszReadThisList)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
pszReadThisList[0] = _T('\0');
|
|
for (int iSel = 0; iSel < cSel; iSel++)
|
|
{
|
|
CCatalog* pCatalog;
|
|
PINVENTORY_ITEM pItem;
|
|
|
|
if (g_v3state.GetCatalogAndItem(pSel[iSel].puid, &pItem, &pCatalog))
|
|
{
|
|
|
|
if SUCCEEDED(DownloadReadThis(pItem))
|
|
{
|
|
TCHAR szTemp[24];
|
|
|
|
wsprintf(szTemp, _T("%d\\%d.htm"), pItem->GetPuid(), pItem->GetPuid());
|
|
|
|
if (pszReadThisList[0] != _T('\0'))
|
|
{
|
|
lstrcat(pszReadThisList, _T(";"));
|
|
}
|
|
lstrcat(pszReadThisList, szTemp);
|
|
}
|
|
}
|
|
} //for
|
|
|
|
if (pszReadThisList[0] == _T('\0'))
|
|
{
|
|
// no items selected with readthisfirst pages
|
|
free(pszReadThisList);
|
|
return S_OK;
|
|
}
|
|
|
|
// read the file in memory with a null appended at the end
|
|
hFile = CreateFile(szSourceFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
dwFileSize = GetFileSize(hFile, NULL);
|
|
if (dwFileSize > 0)
|
|
{
|
|
pFileBuf = (PBYTE)malloc(dwFileSize + 1);
|
|
if (pFileBuf != NULL)
|
|
{
|
|
if (!ReadFile(hFile, pFileBuf, dwFileSize, &dwBytes, NULL))
|
|
{
|
|
free(pFileBuf);
|
|
pFileBuf = NULL;
|
|
dwFileSize = 0;
|
|
}
|
|
pFileBuf[dwFileSize] = 0;
|
|
}
|
|
}
|
|
CloseHandle(hFile);
|
|
}
|
|
|
|
if (pFileBuf != NULL)
|
|
{
|
|
LPSTR pNewBuf;
|
|
TCHAR szDest[MAX_PATH];
|
|
|
|
GetWindowsUpdateDirectory(szDest);
|
|
lstrcat(szDest, _T("RTF\\"));
|
|
|
|
// replace tokens
|
|
if (ReplaceSingleToken(&pNewBuf, (LPSTR)pFileBuf, READTHISLIST, T2A(pszReadThisList)))
|
|
{
|
|
free(pFileBuf);
|
|
pFileBuf = (PBYTE)pNewBuf;
|
|
}
|
|
if (ReplaceSingleToken(&pNewBuf, (LPSTR)pFileBuf, READTHISPATH, T2A(szDest)))
|
|
{
|
|
free(pFileBuf);
|
|
pFileBuf = (PBYTE)pNewBuf;
|
|
}
|
|
|
|
// create the directory
|
|
lstrcat(szDest, _T("0\\"));
|
|
V3_CreateDirectory(szDest);
|
|
|
|
// write out the file
|
|
lstrcat(szDest, _T("printall.htm"));
|
|
hFile = CreateFile(szDest, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_ARCHIVE, NULL);
|
|
if (hFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
if (!WriteFile(hFile, pFileBuf, strlen((LPSTR)pFileBuf), &dwBytes, NULL))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
CloseHandle(hFile);
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
free(pFileBuf);
|
|
|
|
// return the path
|
|
TCHAR szRTF[MAX_PATH];
|
|
VariantInit(pURL);
|
|
wsprintf(szRTF, _T("file://%s"), szDest);
|
|
V_VT(pURL) = VT_BSTR;
|
|
pURL->bstrVal = SysAllocString(T2OLE(szRTF));
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
HRESULT DownloadReadThis(PINVENTORY_ITEM pItem)
|
|
{
|
|
LOG_block("DownloadReadThis");
|
|
|
|
BYTE mszFileNames[512];
|
|
TCHAR szBaseName[64];
|
|
TCHAR szLocalName[64];
|
|
TCHAR szServerName[64];
|
|
TCHAR szServerFile[MAX_PATH];
|
|
TCHAR szLocalFile[MAX_PATH];
|
|
TCHAR szLocalDir[MAX_PATH];
|
|
|
|
if (NULL == pItem->pd)
|
|
{
|
|
LOG_error("NULL == pItem->pd");
|
|
return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
}
|
|
|
|
PWU_VARIABLE_FIELD pvRTFCRC = pItem->pd->pv->Find(WU_DESC_RTF_CRC_ARRAY);
|
|
PWU_VARIABLE_FIELD pvRTFImages = pItem->pd->pv->Find(WU_DESC_RTF_IMAGES);
|
|
if (pvRTFCRC == NULL)
|
|
{
|
|
LOG_error("pvRTFCRC == NULL");
|
|
return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
}
|
|
|
|
// build a multisz string of file names
|
|
int iLen = sprintf((char*)mszFileNames, "%d.htm", pItem->GetPuid());
|
|
mszFileNames[++iLen] = '\0';
|
|
if (pvRTFImages != NULL)
|
|
{
|
|
// we have images
|
|
memcpy(mszFileNames + iLen, pvRTFImages->pData, pvRTFImages->len - 4);
|
|
}
|
|
|
|
// build local directory
|
|
GetWindowsUpdateDirectory(szLocalDir);
|
|
wsprintf(szBaseName, _T("RTF\\%d"), pItem->GetPuid());
|
|
lstrcat(szLocalDir, szBaseName);
|
|
V3_CreateDirectory(szLocalDir);
|
|
|
|
// download common images and other files
|
|
(void)DownloadCommonRTFFiles(FALSE, NULL);
|
|
|
|
// create a download object
|
|
CWUDownload dlRoot(g_v3state.GetRootServer(), 8192);
|
|
|
|
int iFileNo = 0;
|
|
for (;;)
|
|
{
|
|
if (FAILED(GetCRCNameFromList(iFileNo, mszFileNames, pvRTFCRC->pData, szServerName, sizeof(szServerName), szLocalName)))
|
|
{
|
|
// end of the list
|
|
break;
|
|
}
|
|
|
|
// build full paths
|
|
lstrcpy(szLocalFile, szLocalDir);
|
|
AddBackSlash(szLocalFile);
|
|
lstrcat(szLocalFile, szLocalName);
|
|
|
|
lstrcpy(szServerFile, _T("CRCRtf/"));
|
|
lstrcat(szServerFile, szServerName);
|
|
|
|
TRACE("Downloading RTF %s", szServerFile);
|
|
|
|
if (!dlRoot.Copy(szServerFile, szLocalFile, NULL, NULL, 0, NULL))
|
|
{
|
|
LOG_error("Download of RTF %s failed", szServerFile);
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
iFileNo++;
|
|
|
|
} // for
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// deletes any cached readthisfirst pages
|
|
bool CleanupReadThis()
|
|
{
|
|
LOG_block("CleanupReadThis");
|
|
|
|
// build local directory
|
|
TCHAR szRtfPath[MAX_PATH];
|
|
GetWindowsUpdateDirectory(szRtfPath);
|
|
PathAppend(szRtfPath, _T("RTF"));
|
|
|
|
return_if_false(DeleteNode(szRtfPath));
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
// download common images for read-this-first pages and the printall page
|
|
//
|
|
// if bPrintAll=TRUE, only printall.htm page is downloaded, pszPrintAllFN returns locale filename
|
|
// if bPrintAll=FALSE, mages are downloaded, pszPrintAllFN is not used and can be null
|
|
//
|
|
// NOTE: The function downloads images in WinUpdDir/RTF folder and expects it to exist
|
|
HRESULT DownloadCommonRTFFiles(BOOL bPrintAll, LPTSTR pszPrintAllFN)
|
|
{
|
|
|
|
|
|
const TCHAR SECNAME[] = _T("RTF");
|
|
const TCHAR COUNT[] = _T("Count");
|
|
const TCHAR IMGENTRY[] = _T("Img%d");
|
|
const TCHAR PRINTALL[] = _T("PrintAll");
|
|
|
|
TCHAR szLocalDir[MAX_PATH];
|
|
TCHAR szIniFile[MAX_PATH];
|
|
int iCount;
|
|
int iNo;
|
|
CWUDownload* pDownload = NULL;
|
|
TCHAR szBaseName[64];
|
|
TCHAR szServerFile[MAX_PATH];
|
|
TCHAR szLocalFile[MAX_PATH];
|
|
|
|
static BOOL bDoneImages = FALSE;
|
|
static BOOL bDonePrintAll = FALSE;
|
|
|
|
// build paths
|
|
GetWindowsUpdateDirectory(szLocalDir);
|
|
lstrcpy(szIniFile, szLocalDir);
|
|
lstrcat(szLocalDir, _T("RTF\\"));
|
|
lstrcat(szIniFile, FILEVERINI_FN);
|
|
|
|
if (GetPrivateProfileString(SECNAME, PRINTALL, _T(""), szBaseName, sizeof(szBaseName) / sizeof(TCHAR), szIniFile) != 0)
|
|
{
|
|
lstrcpy(szLocalFile, szLocalDir);
|
|
lstrcat(szLocalFile, szBaseName);
|
|
}
|
|
else
|
|
{
|
|
szLocalFile[0] = _T('\0');
|
|
}
|
|
|
|
if (bPrintAll && pszPrintAllFN != NULL)
|
|
{
|
|
lstrcpy(pszPrintAllFN, szLocalFile);
|
|
}
|
|
|
|
// if we are asked to downloaded the printall page and we have not done it in this sesssion
|
|
if (bPrintAll && !bDonePrintAll)
|
|
{
|
|
bDonePrintAll = TRUE;
|
|
|
|
//
|
|
// get the printall page
|
|
//
|
|
lstrcpy(szServerFile, g_v3state.GetSiteURL());
|
|
|
|
// find the last slash
|
|
int l = lstrlen(szServerFile);
|
|
while (l > 0 && szServerFile[l - 1] != _T('\\') && szServerFile[l - 1] != _T('/'))
|
|
l--;
|
|
if (l == 0)
|
|
return E_FAIL;
|
|
|
|
// put a null following the last slash
|
|
szServerFile[l] = _T('\0');
|
|
|
|
// create the download object using the site URL without the file name
|
|
try
|
|
{
|
|
pDownload = new CWUDownload(szServerFile, 8192);
|
|
}
|
|
catch(HRESULT hr)
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
// download the file
|
|
(void)pDownload->Copy(szBaseName, szLocalFile, NULL, NULL, DOWNLOAD_NEWER | CACHE_FILE_LOCALLY | EXACT_FILENAME, NULL);
|
|
|
|
delete pDownload;
|
|
}
|
|
|
|
// if we are asked to download images and we have not yet done them in this session
|
|
if (!bPrintAll && !bDoneImages)
|
|
{
|
|
bDoneImages = FALSE;
|
|
|
|
//
|
|
// download the images
|
|
//
|
|
iCount = (int)GetPrivateProfileInt(SECNAME, COUNT, 0, szIniFile);
|
|
if (iCount == 0)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
for (iNo = 1; iNo <= iCount; iNo++)
|
|
{
|
|
TCHAR szKey[32];
|
|
|
|
wsprintf(szKey, IMGENTRY, iNo);
|
|
if (GetPrivateProfileString(SECNAME, szKey, _T(""), szBaseName, sizeof(szBaseName) / sizeof(TCHAR), szIniFile) == 0)
|
|
{
|
|
// try next one
|
|
continue;
|
|
}
|
|
|
|
if (pDownload == NULL)
|
|
{
|
|
// we don't have a download object, create one
|
|
try
|
|
{
|
|
pDownload = new CWUDownload(g_v3state.GetContentServer(), 8192);
|
|
}
|
|
catch(HRESULT hr)
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
// build file names
|
|
wsprintf(szServerFile, _T("images/%s"), szBaseName);
|
|
lstrcpy(szLocalFile, szLocalDir);
|
|
lstrcat(szLocalFile, szBaseName);
|
|
|
|
// download the file
|
|
(void)pDownload->Copy(szServerFile, szLocalFile, NULL, NULL, DOWNLOAD_NEWER | CACHE_FILE_LOCALLY | EXACT_FILENAME, NULL);
|
|
}
|
|
if (pDownload != NULL)
|
|
{
|
|
delete pDownload;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// this function validates the server the control was instantiated from
|
|
void CCV3::CheckLaunchServer()
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
BOOL bValid;
|
|
HRESULT hr;
|
|
BSTR bstrURL;
|
|
TCHAR szURL[MAX_PATH];
|
|
|
|
if (m_bLaunchServChecked)
|
|
{
|
|
// we only want to do this check once
|
|
return;
|
|
}
|
|
|
|
m_bLaunchServChecked = TRUE;
|
|
|
|
bValid = FALSE;
|
|
|
|
// check to see we have a client site. This will be NULL if the contrainer has not
|
|
// called our IObjectWithSimpleSite interface to set the site.
|
|
if (m_spUnkSite != NULL)
|
|
{
|
|
// NOTE: We are using ATL smart pointers here which call release upon destruction
|
|
|
|
// QI for IServiceProvider from IUnknown of client site
|
|
CComQIPtr<IServiceProvider, &IID_IServiceProvider> spServProv(m_spUnkSite);
|
|
|
|
if (spServProv)
|
|
{
|
|
CComPtr<IWebBrowser2> spBrowser;
|
|
|
|
// QueryService for the IWebBrowser2
|
|
hr = spServProv->QueryService(SID_SInternetExplorer, IID_IWebBrowser2, (LPVOID*)&spBrowser);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
|
|
// get the location URL of the page that instantiated us
|
|
hr = spBrowser->get_LocationURL(&bstrURL);
|
|
if (SUCCEEDED(hr) && bstrURL)
|
|
{
|
|
lstrcpy(szURL, OLE2T(bstrURL));
|
|
|
|
SysFreeString(bstrURL);
|
|
|
|
// copy the entire site URL into the state structure
|
|
g_v3state.SetSiteURL(szURL);
|
|
|
|
// check to see if the host server matches
|
|
// any of the site urls specified in ident.cab
|
|
// this function will crack the site url and all
|
|
// the site server urls to compare the hosts
|
|
bValid = g_v3state.ValidateSiteURL();
|
|
|
|
}
|
|
} // queryservice
|
|
|
|
} // spServProv
|
|
|
|
} // m_spUnkSite
|
|
|
|
|
|
if (!bValid)
|
|
{
|
|
TRACE("CheckLauncServer: The control is launced from an untrusted server--aborting");
|
|
throw E_ACCESSDENIED;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// CDescriptionmMerger class
|
|
//
|
|
|
|
HRESULT CDescriptionMerger::CheckMerge(PINSTALLINFOSTRUCT pInstallInfo)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (!pInstallInfo->pCatalog->LocalesDifferent())
|
|
{
|
|
// if the browser/machine locales are not different then we don't need to do anything
|
|
return hr;
|
|
}
|
|
|
|
if (pInstallInfo->pItem->pd->pv->Find(WU_VARIABLE_MERGEINACTIVE) != NULL)
|
|
{
|
|
// there is a WU_VARIABLE_MERGEINACTIVE which means we have already merged
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// we need merge the descriptions
|
|
//
|
|
|
|
if (m_pMap == NULL || m_pDiamond == NULL)
|
|
{
|
|
// create CCRCMapFile and CDiamond objects for this and future use for this object
|
|
// this method also sets m_pDiamond
|
|
hr = ReadMapFile(pInstallInfo->pCatalog);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pInstallInfo->pCatalog->MergeDescription(pInstallInfo->pdlRoot, m_pDiamond, pInstallInfo->pItem, m_pMap);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CDescriptionMerger::ReadMapFile(CCatalog* pCatalog)
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
TCHAR szMapFile[MAX_PATH];
|
|
BYTE* pMapMem;
|
|
DWORD dwMapLen;
|
|
|
|
// create download object for content server and a diamond object
|
|
CWUDownload dl(g_v3state.GetContentServer(), 8192);
|
|
m_pDiamond = new CDiamond;
|
|
|
|
// build path for crc map file for machine language
|
|
wsprintf(szMapFile, _T("%d_%s.des"),
|
|
pCatalog->GetPlatform(),
|
|
pCatalog->GetMachineLocaleSZ());
|
|
|
|
hr = DownloadFileToMem(&dl, szMapFile, m_pDiamond, &pMapMem, &dwMapLen);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// create a crc map object with the memory image of the file
|
|
m_pMap = new CCRCMapFile(pMapMem, dwMapLen);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
#ifdef _WUV3TEST
|
|
// validates the description using diagnosis variable length fields
|
|
void CheckDescDiagInfo(CCatalog* pCatalog)
|
|
{
|
|
|
|
PINVENTORY_ITEM pItem;
|
|
PWU_VARIABLE_FIELD pvDiag;
|
|
DESCDIAGINFO* pDiagInfo;
|
|
int cGood = 0;
|
|
int cItems = 0;
|
|
|
|
for (int i = 0; i < pCatalog->GetHeader()->totalItems; i++)
|
|
{
|
|
if (NULL == (pItem = pCatalog->GetItem(i)))
|
|
{
|
|
continue;
|
|
}
|
|
if ((pItem->ps->state != WU_ITEM_STATE_PRUNED))
|
|
{
|
|
pvDiag = pItem->pd->pv->Find(WU_DESC_DIAGINFO);
|
|
if (pvDiag != NULL)
|
|
{
|
|
pDiagInfo = (DESCDIAGINFO*)pvDiag->pData;
|
|
|
|
if (pDiagInfo->puid != pItem->GetPuid() ||
|
|
pDiagInfo->dwPlat != pCatalog->GetPlatform() ||
|
|
pDiagInfo->dwLocale != pCatalog->GetBrowserLocaleDW())
|
|
{
|
|
TRACE("CheckDescDiagInfo: puid=%d has invalid description", pItem->GetPuid());
|
|
}
|
|
else
|
|
{
|
|
cGood++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRACE("CheckDescDiagInfo: diagnosis information not found in descriptions");
|
|
break;
|
|
}
|
|
}
|
|
cItems++;
|
|
}
|
|
|
|
TRACE("CheckDescDiagInfo: %d good found for %d items compared", cGood, cItems);
|
|
}
|
|
#endif
|