|
|
//
// update.cpp - assembly update
//
#include <windows.h>
#include <objbase.h>
#include <fusenetincludes.h>
//#include "Iface.h"
#include "server.h"
#include "CUnknown.h" // Base class for IUnknown
#include "update.h"
#include "cfactory.h"
#include "list.h"
#include "version.h"
// used in OnProgress(), copied from guids.c
DEFINE_GUID( IID_IAssemblyManifestImport, 0x696fb37f,0xda64,0x4175,0x94,0xe7,0xfd,0xc8,0x23,0x45,0x39,0xc4);
#define WZ_URL L"Url"
#define WZ_SYNC_INTERVAL L"SyncInterval"
#define WZ_SYNC_EVENT L"SyncEvent"
#define WZ_EVENT_DEMAND_CONNECTION L"EventDemandConnection"
#define SUBSCRIPTION_REG_KEY L"1.0.0.0\\Subscription\\"
#define UPDATE_REG_KEY L"CurrentService"
extern HWND g_hwndUpdateServer; extern CRITICAL_SECTION g_csServer;
List <CDownloadInstance*> g_ActiveDownloadList; HANDLE g_hAbortTimeout = INVALID_HANDLE_VALUE; BOOL g_fSignalUpdate = FALSE;
// ---------------------------------------------------------------------------
// Main timer callback for servicing subscriptions.
// ---------------------------------------------------------------------------
VOID CALLBACK SubscriptionTimerProc( HWND hwnd, // handle to window
UINT uMsg, // WM_TIMER message
UINT_PTR idEvent, // timer identifier
DWORD dwTime // current system time
) { HRESULT hr = S_OK; MAKE_ERROR_MACROS_STATIC(hr);
DWORD dwHash = 0, nMilliseconds = 0, i= 0; BOOL bIsDuplicate = FALSE; CString sUrl; CRegImport *pRegImport = NULL; CRegImport *pSubRegImport = NULL;
IAssemblyDownload *pAssemblyDownload = NULL; CAssemblyBindSink *pCBindSink = NULL; CDownloadInstance *pCDownloadInstance = NULL;
// If update detected stop processing
// subscriptions and kick off new server.
hr = CAssemblyUpdate::CheckForUpdate(); IF_FAILED_EXIT(hr); if (hr == S_OK) goto exit; IF_FAILED_EXIT(CRegImport::Create(&pRegImport, SUBSCRIPTION_REG_KEY)); if (hr == S_FALSE) goto exit; // Enum over subscription keys.
while ((hr = pRegImport->EnumKeys(i++, &pSubRegImport)) == S_OK) { // Get url and polling inteval.
IF_FAILED_EXIT(pSubRegImport->ReadString(WZ_URL, sUrl)); IF_FAILED_EXIT(pSubRegImport->ReadDword(WZ_SYNC_INTERVAL, &nMilliseconds)); // Get url hash
IF_FAILED_EXIT(sUrl.Get65599Hash(&dwHash, CString::CaseInsensitive));
// Check the hash.
if ((dwHash == idEvent)) { // hash checks, now check for dupe and skip if found.
IF_FAILED_EXIT(CAssemblyUpdate::IsDuplicate(sUrl._pwz, &bIsDuplicate)); if (bIsDuplicate) { SAFEDELETE(pSubRegImport); continue; } // Create the download object.
IF_FAILED_EXIT(CreateAssemblyDownload(&pAssemblyDownload, NULL, 0));
// Create bind sink object with download pointer
IF_ALLOC_FAILED_EXIT(pCBindSink = new CAssemblyBindSink(pAssemblyDownload));
// Create the download instance object.
IF_ALLOC_FAILED_EXIT(pCDownloadInstance = new CDownloadInstance);
// Download instance references pAssemblyDownload.
pCDownloadInstance->_pAssemblyDownload = pAssemblyDownload; IF_FAILED_EXIT(pCDownloadInstance->_sUrl.Assign(sUrl));
// Push download object onto list and fire off download; bind sink will remove and release on completion.
EnterCriticalSection(&g_csServer); g_ActiveDownloadList.AddHead(pCDownloadInstance); LeaveCriticalSection(&g_csServer);
// Invoke the download.
hr = pAssemblyDownload->DownloadManifestAndDependencies(sUrl._pwz, (IAssemblyBindSink*) pCBindSink, DOWNLOAD_FLAGS_NOTIFY_BINDSINK);
if(hr == STG_E_TERMINATED) { hr = S_FALSE; // there was an error in download. Log it but don't break into debugger/assert.
}
IF_FAILED_EXIT(hr); }
SAFEDELETE(pSubRegImport); }
exit:
// The active download list looks like:
// (circ. refcount)
// pCDownloadInstance ---> pAssemblyDownload <=========> pCBindSink
// |
// v
// ...
//
// pAssemblyDownload, pCBindSink each have refcount of 1 and will be released
// on successful completion.
// DON'T RELEASE THEM HERE UNLESS A FAILURE OCCURED.
if (FAILED(hr)) { SAFERELEASE(pAssemblyDownload); SAFERELEASE(pCBindSink); SAFEDELETE(pCDownloadInstance); }
SAFEDELETE(pRegImport); SAFEDELETE(pSubRegImport);
return; }
///////////////////////////////////////////////////////////
//
// Interface IAssemblyBindSink
//
// ---------------------------------------------------------------------------
// ctor
// ---------------------------------------------------------------------------
CAssemblyBindSink::CAssemblyBindSink(IAssemblyDownload *pAssemblyDownload) { _cRef = 1; _pAssemblyDownload = pAssemblyDownload; }
// ---------------------------------------------------------------------------
// dtor
// ---------------------------------------------------------------------------
CAssemblyBindSink::~CAssemblyBindSink() {}
// ---------------------------------------------------------------------------
// OnProgress
// ---------------------------------------------------------------------------
HRESULT CAssemblyBindSink::OnProgress( DWORD dwNotification, HRESULT hrNotification, LPCWSTR szNotification, DWORD dwProgress, DWORD dwProgressMax, IUnknown *pUnk) { HRESULT hr = S_OK; MAKE_ERROR_MACROS_STATIC(hr);
LPASSEMBLY_MANIFEST_IMPORT pManifestImport = NULL; LPASSEMBLY_IDENTITY pAppId = NULL;
CAssemblyUpdate *pAssemblyUpdate = NULL; LPMANIFEST_INFO pAppAssemblyInfo = NULL; LPMANIFEST_INFO pSubsInfo = NULL;
if (dwNotification == ASM_NOTIFICATION_SUBSCRIPTION_MANIFEST) { LPWSTR pwz = NULL; DWORD cb = 0, cc = 0, dwFlag = 0; CString sAppName; // szNotification == URL to manifest
IF_NULL_EXIT(szNotification, E_INVALIDARG); // pUnk == manifest import of manifest
IF_FAILED_EXIT(pUnk->QueryInterface(IID_IAssemblyManifestImport, (LPVOID*) &pManifestImport));
// Get the dependent (application) assembly info (0th index)
IF_FAILED_EXIT(pManifestImport->GetNextAssembly(0, &pAppAssemblyInfo)); IF_NULL_EXIT(pAppAssemblyInfo, E_INVALIDARG);
// Get dependent (application) assembly identity
IF_FAILED_EXIT(pAppAssemblyInfo->Get(MAN_INFO_DEPENDENT_ASM_ID, (LPVOID *)&pAppId, &cb, &dwFlag)); IF_NULL_EXIT(pAppId, E_INVALIDARG); // Get application text name.
IF_FAILED_EXIT(hr = pAppId->GetAttribute(SXS_ASSEMBLY_IDENTITY_STD_ATTRIBUTE_NAME_NAME, &pwz, &cc)); IF_FAILED_EXIT(sAppName.TakeOwnership(pwz, cc));
pAssemblyUpdate = new CAssemblyUpdate(); IF_ALLOC_FAILED_EXIT(pAssemblyUpdate); // Get subscription info from manifest
IF_FAILED_EXIT(pManifestImport->GetSubscriptionInfo(&pSubsInfo));
// Register the subscription.
IF_FAILED_EXIT(pAssemblyUpdate->RegisterAssemblySubscriptionFromInfo(sAppName._pwz, (LPWSTR) szNotification, pSubsInfo)); } else if ((dwNotification == ASM_NOTIFICATION_DONE) || (dwNotification == ASM_NOTIFICATION_ABORT) || (dwNotification == ASM_NOTIFICATION_ERROR))
{ // Synchronize with SubscriptionTimerProc.
EnterCriticalSection(&g_csServer); LISTNODE pos = NULL; LISTNODE posRemove = NULL; CDownloadInstance *pDownloadInstance = NULL; // Walk the global download instance list.
pos = g_ActiveDownloadList.GetHeadPosition(); while ((posRemove = pos) && (pDownloadInstance = g_ActiveDownloadList.GetNext(pos))) { // Check for match against callback's interface pointer value.
if (pDownloadInstance->_pAssemblyDownload == _pAssemblyDownload) { // If match found, remove from list and release.
g_ActiveDownloadList.RemoveAt(posRemove);
// If an update has been signalled the client thread will wait until
// the active download list has been drained via abort handling on
// each download object. Signal this when the list is empty.
if (g_fSignalUpdate && (g_ActiveDownloadList.GetCount() == 0)) SetEvent(g_hAbortTimeout); _pAssemblyDownload->Release(); SAFEDELETE(pDownloadInstance);
break; } }
LeaveCriticalSection(&g_csServer);
// Because of the circular refcount between CAssemblyDownload and CAssemblyBindSink
// we don't addreff this instance and it is responsible for deleting itself.
delete this; }
exit: SAFERELEASE(pManifestImport); SAFERELEASE(pAppId); SAFEDELETE(pAssemblyUpdate); SAFERELEASE(pAppAssemblyInfo); SAFERELEASE(pSubsInfo); return hr; }
// ---------------------------------------------------------------------------
// CAssemblyBindSink::QI
// ---------------------------------------------------------------------------
STDMETHODIMP CAssemblyBindSink::QueryInterface(REFIID riid, void** ppvObj) { if ( IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IAssemblyBindSink) ) { *ppvObj = static_cast<IAssemblyBindSink*> (this); AddRef(); return S_OK; } else { *ppvObj = NULL; return E_NOINTERFACE; } }
// ---------------------------------------------------------------------------
// CAssemblyBindSink::AddRef
// ---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CAssemblyBindSink::AddRef() { return InterlockedIncrement ((LONG*) &_cRef); }
// ---------------------------------------------------------------------------
// CAssemblyBindSink::Release
// ---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CAssemblyBindSink::Release() { ULONG lRet = InterlockedDecrement ((LONG*) &_cRef); if (!lRet) delete this; return lRet; }
///////////////////////////////////////////////////////////
//
// Interface IAssemblyUpdate
//
HRESULT __stdcall CAssemblyUpdate::RegisterAssemblySubscription(LPWSTR pwzDisplayName, LPWSTR pwzUrl, DWORD dwInterval) { // ISSUE-2002/04/19-felixybc dummy method to keep interface unchanged.
// This method should not be called.
return E_NOTIMPL; }
// ---------------------------------------------------------------------------
// RegisterAssemblySubscriptionEx
// ---------------------------------------------------------------------------
HRESULT __stdcall CAssemblyUpdate::RegisterAssemblySubscriptionEx(LPWSTR pwzDisplayName, LPWSTR pwzUrl, DWORD dwInterval, DWORD dwIntervalUnit, DWORD dwEvent, BOOL bEventDemandConnection) { DWORD dwMilliseconds = 0, dwDemandConnection = 0, dwHash = 0, dwFactor = 1;
CString sUrl; CString sSubscription;
CRegEmit *pRegEmit = NULL; dwDemandConnection = bEventDemandConnection; // BOOL -> DWORD
switch(dwIntervalUnit) { case SUBSCRIPTION_INTERVAL_UNIT_DAYS: dwFactor *= 24; // falls thru, 1 hr*24 = 1 day
case SUBSCRIPTION_INTERVAL_UNIT_HOURS: default: dwFactor *= 60; // falls thru, 1 min*60 = 1 hr
case SUBSCRIPTION_INTERVAL_UNIT_MINUTES: dwFactor *= 60000; // 1ms*60000 = 1 min
break; }
// BUGBUG: check overflow
dwMilliseconds = dwInterval * dwFactor;
#ifdef DBG
#define REG_KEY_FUSION_SETTINGS TEXT("Software\\Microsoft\\Fusion\\Installer\\1.0.0.0\\Subscription")
// BUGBUG: code to facilitate testing ONLY - shorten minutes to seconds
{ // read subkey, default is false
if (SHRegGetBoolUSValue(REG_KEY_FUSION_SETTINGS, L"ShortenMinToSec", FALSE, FALSE)) { dwMilliseconds /= 60; // at this point, dwMilliseconds >= 60000
} } #endif
// Get hash of url
// BUGBUG - this could just be a global counter, right?
IF_FAILED_EXIT(sUrl.Assign(pwzUrl)); IF_FAILED_EXIT(sUrl.Get65599Hash(&dwHash, CString::CaseInsensitive));
// Form subscription regstring.
IF_FAILED_EXIT(sSubscription.Assign(SUBSCRIPTION_REG_KEY)); IF_FAILED_EXIT(sSubscription.Append(pwzDisplayName));
// Set the subscription regkeys.
IF_FAILED_EXIT(CRegEmit::Create(&pRegEmit, sSubscription._pwz)); IF_FAILED_EXIT(pRegEmit->WriteDword(WZ_SYNC_INTERVAL, dwMilliseconds)); IF_FAILED_EXIT(pRegEmit->WriteDword(WZ_SYNC_EVENT, dwEvent)); IF_FAILED_EXIT(pRegEmit->WriteDword(WZ_EVENT_DEMAND_CONNECTION, dwDemandConnection)); IF_FAILED_EXIT(pRegEmit->WriteString(WZ_URL, sUrl));
// Fire off the timer.
IF_WIN32_FALSE_EXIT(SetTimer((HWND) g_hwndUpdateServer, dwHash, dwMilliseconds, SubscriptionTimerProc));
IF_FAILED_EXIT(CheckForUpdate());
_hr = S_OK;
exit:
SAFEDELETE(pRegEmit); return _hr; }
// ---------------------------------------------------------------------------
// UnRegisterAssemblySubscription
// ---------------------------------------------------------------------------
HRESULT __stdcall CAssemblyUpdate::UnRegisterAssemblySubscription(LPWSTR pwzDisplayName) { CRegEmit *pRegEmit = NULL; // Form full regkey path.
IF_FAILED_EXIT(CRegEmit::Create(&pRegEmit, SUBSCRIPTION_REG_KEY)); IF_FAILED_EXIT(pRegEmit->DeleteKey(pwzDisplayName));
IF_FAILED_EXIT(CheckForUpdate());
_hr = S_OK;
exit:
SAFEDELETE(pRegEmit); return _hr; }
// ---------------------------------------------------------------------------
// Initialize servicing subscriptions.
// ---------------------------------------------------------------------------
HRESULT CAssemblyUpdate::InitializeSubscriptions() { HRESULT hr = S_OK; MAKE_ERROR_MACROS_STATIC(hr);
DWORD dwHash = 0, nMilliseconds = 0, i=0; CString sUrl; CRegImport *pRegImport = NULL; CRegImport *pSubRegImport = NULL;
IF_FAILED_EXIT(CRegImport::Create(&pRegImport, SUBSCRIPTION_REG_KEY)); if (hr == S_FALSE) goto exit;
// Enum over subscription keys.
while ((hr = pRegImport->EnumKeys(i++, &pSubRegImport)) == S_OK) { // Get url and polling inteval.
IF_FAILED_EXIT(pSubRegImport->ReadString(WZ_URL, sUrl)); IF_FAILED_EXIT(pSubRegImport->ReadDword(WZ_SYNC_INTERVAL, &nMilliseconds)); // Get url hash
IF_FAILED_EXIT(sUrl.Get65599Hash(&dwHash, CString::CaseInsensitive));
// Set the subscription timer event.
IF_WIN32_FALSE_EXIT(SetTimer((HWND) g_hwndUpdateServer, dwHash, nMilliseconds, SubscriptionTimerProc));
SAFEDELETE(pSubRegImport); } g_hAbortTimeout = CreateEvent(NULL, TRUE, FALSE, NULL); IF_WIN32_FALSE_EXIT(g_hAbortTimeout != NULL);
exit:
SAFEDELETE(pRegImport); SAFEDELETE(pSubRegImport);
return hr; }
// ---------------------------------------------------------------------------
// CheckForUpdate
// ---------------------------------------------------------------------------
HRESULT CAssemblyUpdate::CheckForUpdate() { HRESULT hr = S_OK; MAKE_ERROR_MACROS_STATIC(hr); ULONGLONG ullUpdateVersion = 0, ullCurrentVersion = 0;
CString sUpdatePath; BOOL bUpdate = FALSE, bDoRelease = TRUE; DWORD dwWaitState = 0; STARTUPINFO si = {0}; PROCESS_INFORMATION pi = {0};
EnterCriticalSection(&g_csServer); if (g_fSignalUpdate == TRUE) goto exit; // Check the registry update location. The service will terminate
// if no key is found (uninstall) or if update with higher version is found.
// ISSUE-2002/03/19-adriaanc
// A race condition exists when Darwin upgrades v1->v2 of ClickOnce -
// Major upgrade first uninstalls v1, then installs v2 so the registry key
// may not be present when checking and we will incorrectly shutdown.
// Mitigating factor is that this requires a major upgrade - so reboot implicit?
// One possible solution is to zomby the process for a time and reverify
// the uninstall by rechecking the regkey.
IF_FAILED_EXIT(ReadUpdateRegistryEntry(&ullUpdateVersion, sUpdatePath)); if (hr == S_OK) { GetCurrentVersion(&ullCurrentVersion); if (ullUpdateVersion <= ullCurrentVersion) { hr = S_FALSE; goto exit; } bUpdate = TRUE; } else hr = S_OK;
// Revoke the class factories.
CFactory::StopFactories();
// Nuke outstanding jobs.
if (g_ActiveDownloadList.GetCount()) { LISTNODE pos = NULL; CDownloadInstance *pDownloadInstance = NULL; // Walk the global download instance list and cancel
// any outstanding jobs.
pos = g_ActiveDownloadList.GetHeadPosition();
// Walk the list and cancel the downloads.
// DO NOT remove them from the list or release
// them - this will be taken care of by the bindsink.
while (pos && (pDownloadInstance = g_ActiveDownloadList.GetNext(pos))) IF_FAILED_EXIT(pDownloadInstance->_pAssemblyDownload->CancelDownload());
}
// CreateProcess on updated server.
if (bUpdate) { si.cb = sizeof(si); IF_WIN32_FALSE_EXIT(CreateProcess(sUpdatePath._pwz, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)); }
// Flag that an update has been signalled. We are now entering process termination phase.
g_fSignalUpdate = TRUE;
// The process must stay resident until any outstanding async callback threads have had a chance to
// complete their aborts. An efficient check is to first check if the active download queue has any
// entries. We can do this here because we are under the global crit sect and BITS callbacks can
// only be extant when one or more downloads are in the queue and no additional downloads will
// be submitted because g_fSignalUpdate before we leave the critsect.
if (g_ActiveDownloadList.GetCount()) { // Downloads are currently in progress. Wait for the aborts to complete.
// We synchronize on the abort event with a 1 minute timeout in the
// case that one or more aborts failed to complete. This is technically
// an error condition but we still MUST exit the process.
// It is necessary to release the global critsect so that the
// downloaders may update the active download list.
bDoRelease = FALSE; ::LeaveCriticalSection(&g_csServer);
// Sync on the abort timeout.
dwWaitState = WaitForSingleObject(g_hAbortTimeout, 60000); IF_WIN32_FALSE_EXIT((dwWaitState != WAIT_FAILED));
// In retail builds we would ignore the timeout. In debug catch the assert.
if (dwWaitState != WAIT_OBJECT_0) { ASSERT(FALSE); } } // decrement artificial ref count; ensures server
// exits on last interface released.
::InterlockedDecrement(&CFactory::s_cServerLocks) ;
// And attempt to terminate the process.
CFactory::CloseExe(); exit: if (bDoRelease) ::LeaveCriticalSection(&g_csServer);
return hr; }
// ---------------------------------------------------------------------------
// RegisterAssemblySubscriptionFromInfo
// NOTE - this is NOT a public method on IAssemblyUpdate
// NOTE - what types of validation should be done here if any?
// ---------------------------------------------------------------------------
HRESULT CAssemblyUpdate::RegisterAssemblySubscriptionFromInfo(LPWSTR pwzDisplayName, LPWSTR pwzUrl, IManifestInfo *pSubscriptionInfo) { DWORD *pdw = NULL; BOOL *pb = NULL; DWORD dwInterval = 0, dwUnit = SUBSCRIPTION_INTERVAL_UNIT_MAX; DWORD dwEvent = 0; BOOL bDemandConnection = FALSE; DWORD dwCB = 0, dwFlag = 0;
IF_FAILED_EXIT(pSubscriptionInfo->Get(MAN_INFO_SUBSCRIPTION_SYNCHRONIZE_INTERVAL, (LPVOID *)&pdw, &dwCB, &dwFlag)); if (pdw != NULL) { dwInterval = *pdw; SAFEDELETEARRAY(pdw); }
IF_FAILED_EXIT(pSubscriptionInfo->Get(MAN_INFO_SUBSCRIPTION_INTERVAL_UNIT, (LPVOID *)&pdw, &dwCB, &dwFlag)); if (pdw != NULL) { dwUnit = *pdw; SAFEDELETEARRAY(pdw); }
IF_FAILED_EXIT(pSubscriptionInfo->Get(MAN_INFO_SUBSCRIPTION_SYNCHRONIZE_EVENT, (LPVOID *)&pdw, &dwCB, &dwFlag)); if (pdw != NULL) { dwEvent = *pdw; SAFEDELETEARRAY(pdw); }
IF_FAILED_EXIT(pSubscriptionInfo->Get(MAN_INFO_SUBSCRIPTION_EVENT_DEMAND_CONNECTION, (LPVOID *)&pb, &dwCB, &dwFlag)); if (pb != NULL) { bDemandConnection = *pb; SAFEDELETEARRAY(pb); }
IF_FAILED_EXIT(RegisterAssemblySubscriptionEx(pwzDisplayName, pwzUrl, dwInterval, dwUnit, dwEvent, bDemandConnection)); exit:
return _hr; }
// ---------------------------------------------------------------------------
// ctor
// ---------------------------------------------------------------------------
CAssemblyUpdate::CAssemblyUpdate() : CUnknown(), _hr(S_OK) { // Empty
}
// ---------------------------------------------------------------------------
// dtor
// ---------------------------------------------------------------------------
CAssemblyUpdate::~CAssemblyUpdate() { }
// ---------------------------------------------------------------------------
// QueryInterface
// ---------------------------------------------------------------------------
HRESULT __stdcall CAssemblyUpdate::QueryInterface(const IID& iid, void** ppv) { if ( IsEqualIID(iid, IID_IUnknown) || IsEqualIID(iid, IID_IAssemblyUpdate) ) { return CUnknown::FinishQI((IAssemblyUpdate*)this, ppv) ; } else { *ppv = NULL; return E_NOINTERFACE; } }
// ---------------------------------------------------------------------------
// CAssemblyDownload::AddRef
// ---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CAssemblyUpdate::AddRef() { return CUnknown::AddRef(); }
// ---------------------------------------------------------------------------
// CAssemblyDownload::Release
// ---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CAssemblyUpdate::Release() { return CUnknown::Release(); }
// ---------------------------------------------------------------------------
// Creation function used by CFactory
// ---------------------------------------------------------------------------
HRESULT CAssemblyUpdate::CreateInstance(IUnknown* pUnknownOuter, CUnknown** ppNewComponent) { if (pUnknownOuter != NULL) { return CLASS_E_NOAGGREGATION ; }
*ppNewComponent = new CAssemblyUpdate() ; return S_OK ; }
// ---------------------------------------------------------------------------
// Init function used by CFactory
// ---------------------------------------------------------------------------
HRESULT CAssemblyUpdate::Init() { return S_OK; }
// ---------------------------------------------------------------------------
// GetCurrentVersion
// ---------------------------------------------------------------------------
HRESULT CAssemblyUpdate::GetCurrentVersion(ULONGLONG *pullCurrentVersion) { ULONGLONG ullVer = 0;
WORD wVer[4] = { FUS_VER_MAJORVERSION , FUS_VER_MINORVERSION, FUS_VER_PRODUCTBUILD, FUS_VER_PRODUCTBUILD_QFE }; for (int i = 0; i < 4; i++) ullVer |= ((ULONGLONG) wVer[i]) << (sizeof(WORD) * 8 * (3-i));
*pullCurrentVersion = ullVer;
return S_OK; }
// ---------------------------------------------------------------------------
// RemoveUpdateRegEntry
// ---------------------------------------------------------------------------
HRESULT CAssemblyUpdate::RemoveUpdateRegistryEntry() { HRESULT hr = S_OK; MAKE_ERROR_MACROS_STATIC(hr);
CRegEmit *pEmit = NULL; IF_FAILED_EXIT(CRegEmit::Create(&pEmit, NULL)); IF_FAILED_EXIT(pEmit->DeleteKey(UPDATE_REG_KEY));
exit:
return hr; }
// ---------------------------------------------------------------------------
// ReadUpdateRegistryEntry
// ---------------------------------------------------------------------------
HRESULT CAssemblyUpdate::ReadUpdateRegistryEntry(ULONGLONG *pullUpdateVersion, CString &sUpdatePath) { HRESULT hr = S_OK; MAKE_ERROR_MACROS_STATIC(hr); LPWSTR pwz = NULL; WORD wVer[4] = {0,0,0,0}; ULONGLONG ullVer = 0; INT i= 0, iVersion = 0; BOOL fDot = TRUE; CString sVersion; CRegImport *pRegImport = NULL;
hr = CRegImport::Create(&pRegImport, UPDATE_REG_KEY); if (hr == S_FALSE) goto exit;
IF_FAILED_EXIT(hr); IF_FAILED_EXIT(pRegImport->ReadString(L"Version", sVersion)); IF_FAILED_EXIT(pRegImport->ReadString(L"Path", sUpdatePath)); // Parse the version to ulonglong
pwz = sVersion._pwz; while (*pwz) { if (fDot) { iVersion=StrToInt(pwz); wVer[i++] = (WORD) iVersion; fDot = FALSE; }
if (*pwz == L'.') fDot = TRUE;
pwz++; if (i > 3) break; }
for (i = 0; i < 4; i++) ullVer |= ((ULONGLONG) wVer[i]) << (sizeof(WORD) * 8 * (3-i));
*pullUpdateVersion = ullVer;
exit:
SAFEDELETE(pRegImport); return hr; }
// ---------------------------------------------------------------------------
// Helper function for determining dupes in active subscription list.
// ---------------------------------------------------------------------------
HRESULT CAssemblyUpdate::IsDuplicate(LPWSTR pwzURL, BOOL *pbIsDuplicate) { HRESULT hr = S_OK; MAKE_ERROR_MACROS_STATIC(hr); BOOL bDuplicate = FALSE; INT iCompare = 0; LISTNODE pos = NULL; CDownloadInstance *pDownloadInstance = NULL;
EnterCriticalSection(&g_csServer);
// Walk the global download instance list.
pos = g_ActiveDownloadList.GetHeadPosition();
while ( (pos) && (pDownloadInstance = g_ActiveDownloadList.GetNext(pos))) { iCompare = CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, pDownloadInstance->_sUrl._pwz, -1, pwzURL, -1); IF_WIN32_FALSE_EXIT(iCompare); if (iCompare == CSTR_EQUAL) { bDuplicate = TRUE; break; } }
*pbIsDuplicate = bDuplicate;
exit:
LeaveCriticalSection(&g_csServer);
return hr; }
|