Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

876 lines
28 KiB

//
// 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;
}