|
|
//=======================================================================
//
// Copyright (c) 1998-2000 Microsoft Corporation. All Rights Reserved.
//
// File: engmain.cpp
//
// Description:
//
// DllMain and globals for the IUEngine DLL
//
//=======================================================================
#include "iuengine.h"
#include "iucommon.h"
#include "download.h"
#include <limits.h>
//***********************************************************************
//
// The following definitions are copied from IUCtl.IDL.
// If IUCtl.IDL is changed, these constants need to be
// changed accordingly
//
//***********************************************************************
/**
* the following two groups of constants can be used to construct * lMode parameter of the following APIs: * Download() * DownloadAsync() * Install() * InstallAsync() * * Obviousely, you can only pick one from each group to make up * lMode parameter. * */ const LONG UPDATE_NOTIFICATION_DEFAULT = 0x00000000; const LONG UPDATE_NOTIFICATION_ANYPROGRESS = 0x00000000; const LONG UPDATE_NOTIFICATION_COMPLETEONLY = 0x00010000; const LONG UPDATE_NOTIFICATION_1PCT = 0x00020000; const LONG UPDATE_NOTIFICATION_5PCT = 0x00040000; const LONG UPDATE_NOTIFICATION_10PCT = 0x00080000;
/**
* constant can also be used for SetOperationMode() and GetOperationMode() */ const LONG UPDATE_MODE_THROTTLE = 0x00000100;
/**
* constant can be used by Download() and DownloadAsync(), which will * tell these API's to use Corporate directory structure for destination folder. */ const LONG UPDATE_CORPORATE_MODE = 0x00000200;
/**
* constant can be used by Install() and InstallAsync(). Will disable all * internet related features */ const LONG UPDATE_OFFLINE_MODE = 0x00000400;
/**
* constants for SetOperationMode() API */ const LONG UPDATE_COMMAND_PAUSE = 0x00000001; const LONG UPDATE_COMMAND_RESUME = 0x00000002; const LONG UPDATE_COMMAND_CANCEL = 0x00000004;
/**
* constants for GetOperationMode() API */ const LONG UPDATE_MODE_PAUSED = 0x00000001; const LONG UPDATE_MODE_RUNNING = 0x00000002; const LONG UPDATE_MODE_NOTEXISTS = 0x00000004;
/**
* constants for SetProperty() and GetProperty() API */ const LONG UPDATE_PROP_USECOMPRESSION = 0x00000020; const LONG UPDATE_PROP_OFFLINEMODE = 0x00000080;
/**
* constants for BrowseForFolder() API * IUBROWSE_WRITE_ACCESS - validate write access on selected folder * IUBROWSE_AFFECT_UI - write-access validation affect OK button enable/disable * IUBROWSE_NOBROWSE - do not show browse folder dialog box. validate path passed-in only * * default: * pop up browse folder dialog box, not doing any write-access validation * */ const LONG IUBROWSE_WRITE_ACCESS = 1; const LONG IUBROWSE_AFFECT_UI = 2; const LONG IUBROWSE_NOBROWSE = 4;
CEngUpdate* g_pCDMEngUpdate; // single global instance used by CDM within the process
CRITICAL_SECTION g_csCDM; // used to serialize access to g_pCDMEngUpdate
CRITICAL_SECTION g_csGlobalClasses; // used to serialize access to CSchemaKeys::Initialize() and
BOOL gfInit_csCDM, gfInit_csGC; // CSchemaKeys::Uninitialize()
ULONG g_ulGlobalClassRefCount; // Reference count to track how many CEngUpdate instances are
// using the g_pGlobalSchemaKeys object
LONG g_lDoOnceOnLoadGuard; // Used to prevent AsyncExtraWorkUponEngineLoad() from doing
// any work after the first time it is called.
//
// Used to control shutdown of global threads
//
LONG g_lThreadCounter; HANDLE g_evtNeedToQuit; CUrlAgent *g_pUrlAgent = NULL;
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpvReserved) { if (dwReason == DLL_PROCESS_ATTACH) { //
// create a global CUrlAgent object
//
if (NULL == (g_pUrlAgent = new CUrlAgent) || FAILED(g_pUrlAgent->PopulateData())) { return FALSE; } DisableThreadLibraryCalls(hInstance); g_hinst = hInstance;
gfInit_csCDM = SafeInitializeCriticalSection(&g_csCDM); gfInit_csGC = SafeInitializeCriticalSection(&g_csGlobalClasses);
//
// each global thread when started, should increase this counter
// before exit, should decrease this counter,
// such that ShutdownGlobalThreads() knows when it can return
//
g_lThreadCounter = 0;
//
// create a manual-reset event with init state non-signaled.
// each global thread will check this event, when signalled, it means
// the thread should exit ASAP.
//
g_evtNeedToQuit = CreateEvent(NULL, TRUE, FALSE, NULL);
//
// Initialize free logging
//
InitFreeLogging(_T("IUENGINE")); LogMessage("Starting");
if (!gfInit_csCDM ||!gfInit_csGC) { LogError(E_FAIL, "InitializeCriticalSection"); return FALSE; } } else if (dwReason == DLL_PROCESS_DETACH) { if (NULL != g_evtNeedToQuit) { CloseHandle(g_evtNeedToQuit); }
if (NULL != g_pUrlAgent) { delete g_pUrlAgent; }
if (gfInit_csCDM) { DeleteCriticalSection(&g_csCDM); } if (gfInit_csGC) { DeleteCriticalSection(&g_csGlobalClasses); }
//
// Shutdown free logging
//
LogMessage("Shutting down"); TermFreeLogging(); } return TRUE; }
// ----------------------------------------------------------------------
//
// DLL API: CompleteSelfUpdateProcess()
//
// call by IUCtl.dll after downloading the new IUEngine.dll to complete
// any selfupdate steps beyond update the engine itself.
//
// ----------------------------------------------------------------------
HRESULT WINAPI CompleteSelfUpdateProcess() { LOG_Block("CompleteSelfUpdateProcess()"); HRESULT hr = S_OK;
// Nothing to do yet, just return S_OK.
LogMessage("IUEngine update completed"); return hr; }
// ----------------------------------------------------------------------
//
// DLL API: PingIUEngineUpdateStatus
//
// Used by iuctl.dll to ping status of iuengine.dll supdate
//
// ----------------------------------------------------------------------
HRESULT WINAPI PingIUEngineUpdateStatus( PHANDLE phQuitEvents, // ptr to handles for cancelling the operation
UINT nQuitEventCount, // number of handles
LPCTSTR ptszLiveServerUrl, LPCTSTR ptszCorpServerUrl, DWORD dwError, // error code
LPCTSTR ptszClientName // client name string
) { HRESULT hr;
if (NULL == phQuitEvents || 1 > nQuitEventCount) { return E_INVALIDARG; }
CUrlLog pingSvr( NULL == ptszClientName ? _T("iu") : ptszClientName, ptszLiveServerUrl, ptszCorpServerUrl);
hr = pingSvr.Ping( TRUE, // force online
URLLOGDESTINATION_DEFAULT, // going to live or corp WU server
phQuitEvents, // pt to cancel events
nQuitEventCount, // number of events
URLLOGACTIVITY_Initialization, // activity
SUCCEEDED(dwError) ? URLLOGSTATUS_Success : URLLOGSTATUS_Failed, // status code
dwError // error code
);
return hr; }
// ----------------------------------------------------------------------
//
// DLL API: CreateEngUpdateInstance()
//
// Returns a CEngUpdate instance pointer cast to HIUENGINE, or NULL if it fails.
//
// ----------------------------------------------------------------------
HIUENGINE WINAPI CreateEngUpdateInstance() { LOG_Block("CreateEngUpdateInstance");
return reinterpret_cast<HIUENGINE>(new CEngUpdate); }
// ----------------------------------------------------------------------
//
// DLL API: DeleteEngUpdateInstance()
//
// Returns a CEngUpdate instance pointer, or NULL if it fails.
//
// ----------------------------------------------------------------------
void WINAPI DeleteEngUpdateInstance(HIUENGINE hIUEngine) { LOG_Block("DeleteEngUpdateInstance");
if (NULL != hIUEngine) { delete (reinterpret_cast<CEngUpdate*>(hIUEngine)); } }
// ----------------------------------------------------------------------
//
// DLL API: Stubs to export CEngUpdate functionality across DLL boundry
//
// ----------------------------------------------------------------------
HRESULT EngGetSystemSpec(HIUENGINE hIUEngine, BSTR bstrXmlClasses, DWORD dwFlags, BSTR *pbstrXmlDetectionResult) { if (NULL == hIUEngine) { return E_INVALIDARG; }
return (reinterpret_cast<CEngUpdate*>(hIUEngine))->GetSystemSpec(bstrXmlClasses, dwFlags, pbstrXmlDetectionResult); }
HRESULT EngGetManifest(HIUENGINE hIUEngine, BSTR bstrXmlClientInfo, BSTR bstrXmlSystemSpec, BSTR bstrXmlQuery, DWORD dwFlags, BSTR *pbstrXmlCatalog) { if (NULL == hIUEngine) { return E_INVALIDARG; }
return (reinterpret_cast<CEngUpdate*>(hIUEngine))->GetManifest(bstrXmlClientInfo, bstrXmlSystemSpec, bstrXmlQuery, dwFlags, pbstrXmlCatalog); }
HRESULT EngDetect(HIUENGINE hIUEngine, BSTR bstrXmlCatalog, DWORD dwFlags, BSTR *pbstrXmlItems) { if (NULL == hIUEngine) { return E_INVALIDARG; }
return (reinterpret_cast<CEngUpdate*>(hIUEngine))->Detect(bstrXmlCatalog, dwFlags, pbstrXmlItems); }
HRESULT EngDownload(HIUENGINE hIUEngine,BSTR bstrXmlClientInfo, BSTR bstrXmlCatalog, BSTR bstrDestinationFolder, LONG lMode, IUnknown *punkProgressListener, HWND hWnd, BSTR *pbstrXmlItems) { if (NULL == hIUEngine) { return E_INVALIDARG; }
return (reinterpret_cast<CEngUpdate*>(hIUEngine))->Download(bstrXmlClientInfo, bstrXmlCatalog, bstrDestinationFolder, lMode, punkProgressListener, hWnd, pbstrXmlItems); }
HRESULT EngDownloadAsync(HIUENGINE hIUEngine,BSTR bstrXmlClientInfo, BSTR bstrXmlCatalog, BSTR bstrDestinationFolder, LONG lMode, IUnknown *punkProgressListener, HWND hWnd, BSTR bstrUuidOperation, BSTR *pbstrUuidOperation) { if (NULL == hIUEngine) { return E_INVALIDARG; }
return (reinterpret_cast<CEngUpdate*>(hIUEngine))->DownloadAsync(bstrXmlClientInfo, bstrXmlCatalog, bstrDestinationFolder, lMode, punkProgressListener, hWnd, bstrUuidOperation, pbstrUuidOperation); }
HRESULT EngInstall(HIUENGINE hIUEngine, BSTR bstrXmlClientInfo, BSTR bstrXmlCatalog, BSTR bstrXmlDownloadedItems, LONG lMode, IUnknown *punkProgressListener, HWND hWnd, BSTR *pbstrXmlItems) { if (NULL == hIUEngine) { return E_INVALIDARG; }
return (reinterpret_cast<CEngUpdate*>(hIUEngine))->Install(bstrXmlClientInfo, bstrXmlCatalog, bstrXmlDownloadedItems, lMode, punkProgressListener, hWnd, pbstrXmlItems); }
HRESULT EngInstallAsync(HIUENGINE hIUEngine, BSTR bstrXmlClientInfo, BSTR bstrXmlCatalog, BSTR bstrXmlDownloadedItems, LONG lMode, IUnknown *punkProgressListener, HWND hWnd, BSTR bstrUuidOperation, BSTR *pbstrUuidOperation) { if (NULL == hIUEngine) { return E_INVALIDARG; }
return (reinterpret_cast<CEngUpdate*>(hIUEngine))->InstallAsync(bstrXmlClientInfo, bstrXmlCatalog, bstrXmlDownloadedItems, lMode, punkProgressListener, hWnd, bstrUuidOperation, pbstrUuidOperation); }
HRESULT EngSetOperationMode(HIUENGINE hIUEngine, BSTR bstrUuidOperation, LONG lMode) { //
// 502965 Windows Error Reporting bucket 2096553: Hang following NEWDEV.DLL!CancelDriverSearch
//
// Special-case this function for NULL == hIUEngine to allow access
// by CDM to g_pCDMEngUpdate for CDM.DLL's in .NET Server / SP1 and later
//
if (NULL == hIUEngine) { if (NULL == g_pCDMEngUpdate) { return E_INVALIDARG; } else { return g_pCDMEngUpdate->SetOperationMode(bstrUuidOperation, lMode); } } else { //
// Normal case (instance handle passed in)
//
return (reinterpret_cast<CEngUpdate*>(hIUEngine))->SetOperationMode(bstrUuidOperation, lMode); } }
HRESULT EngGetOperationMode(HIUENGINE hIUEngine, BSTR bstrUuidOperation, LONG* plMode) { if (NULL == hIUEngine) { return E_INVALIDARG; }
return (reinterpret_cast<CEngUpdate*>(hIUEngine))->GetOperationMode(bstrUuidOperation, plMode); }
HRESULT EngGetHistory(HIUENGINE hIUEngine, BSTR bstrDateTimeFrom, BSTR bstrDateTimeTo, BSTR bstrClient, BSTR bstrPath, BSTR* pbstrLog) { if (NULL == hIUEngine) { return E_INVALIDARG; }
return (reinterpret_cast<CEngUpdate*>(hIUEngine))->GetHistory(bstrDateTimeFrom, bstrDateTimeTo, bstrClient, bstrPath, pbstrLog); }
HRESULT EngBrowseForFolder(HIUENGINE hIUEngine, BSTR bstrStartFolder, LONG flag, BSTR* pbstrFolder) { if (NULL == hIUEngine) { return E_INVALIDARG; }
return (reinterpret_cast<CEngUpdate*>(hIUEngine))->BrowseForFolder(bstrStartFolder, flag, pbstrFolder); }
HRESULT EngRebootMachine(HIUENGINE hIUEngine) { if (NULL == hIUEngine) { return E_INVALIDARG; }
return (reinterpret_cast<CEngUpdate*>(hIUEngine))->RebootMachine(); }
// ----------------------------------------------------------------------
//
// DLL API: CreateGlobalCDMEngUpdateInstance()
//
// Initializes the single (global) CEngUpdate instance to be used by CDM
//
// ----------------------------------------------------------------------
HRESULT WINAPI CreateGlobalCDMEngUpdateInstance() { LOG_Block("CreateGlobalCDMEngUpdateInstance");
HRESULT hr = S_OK;
EnterCriticalSection(&g_csCDM);
if (NULL != g_pCDMEngUpdate) { LOG_Error(_T("Another thread in process is already using CDM functionality")); hr = E_ACCESSDENIED; goto CleanUp; }
CleanUpFailedAllocSetHrMsg(g_pCDMEngUpdate = new CEngUpdate);
CleanUp:
LeaveCriticalSection(&g_csCDM);
return hr; }
// ----------------------------------------------------------------------
//
// DLL API: DeleteGlobalCDMEngUpdateInstance()
//
// Deletes the single (global) CEngUpdate instance used by CDM.
//
// ----------------------------------------------------------------------
HRESULT WINAPI DeleteGlobalCDMEngUpdateInstance() { LOG_Block("DeleteGlobalCDMEngUpdateInstance");
HRESULT hr = S_OK;
EnterCriticalSection(&g_csCDM);
//
// Unfortunately (due to backwards compatibility with XPClient V4 CDM)
// we can't tell if this was reached via CDM calling UnloadIUEngine
// or some other client (e.g. AU) within the scope of a CDM instance.
//
// As a result, CDM's instance could get deleted at the wrong time
// causing further calls to CDM to fail with E_INVALIDARG. Nothing
// we can do about it because we reach hear after the other client's
// instance is already deleted, so we can't use g_ulGlobalClassRefCount
// as a guard against this. However AU and CDM should never be in the
// same process, so we should be OK. CDM will coexist with instances
// created via the iuctl COM object since they never call then old
// ShutdownThreads export.
//
if (NULL != g_pCDMEngUpdate) { delete g_pCDMEngUpdate; g_pCDMEngUpdate = NULL; LOG_Driver(_T("CDM's global instance of CEngUpdate was deleted")); } //
// ELSE this would be the case when iuctl!UnLoadIUEngine is
// called from a client other than CDM, such as AU
//
LeaveCriticalSection(&g_csCDM);
return hr; }
CEngUpdate::CEngUpdate() { LOG_Block("CEngUpdate::CEngUpdate");
HRESULT hr; //
// each thread when start, should increase this counter
// before exit, should decrease this counter,
// such that ShutdownInstanceThreads() knows when it can return
//
m_lThreadCounter = 0;
//
// create a manual-reset event with init state non-signaled.
// each thread will check this event, when signalled, it means
// the thread should exit ASAP.
//
m_evtNeedToQuit = CreateEvent(NULL, TRUE, FALSE, NULL);
//
// If needed, create a global CSchemaKeys object, but always
// keep global ref count so we know when to delete
//
EnterCriticalSection(&g_csGlobalClasses);
//
// Construct the global object
//
if (NULL == g_pGlobalSchemaKeys) { g_pGlobalSchemaKeys = new CSchemaKeys; }
#if defined(DBG)
//
// We don't worry about this for practical purposes (will fail to construct
// CEngUpdate before we reach this limit), but maybe on ia64 with huge
// amounts of memory in a test scenario?
//
if (ULONG_MAX == g_ulGlobalClassRefCount) { LOG_Error(_T("g_ulGlobalClassRefCount is already ULONG_MAX and we are trying to add another")); } #endif
g_ulGlobalClassRefCount++; LOG_Out(_T("g_ulGlobalClassRefCount is now %d"), g_ulGlobalClassRefCount);
LeaveCriticalSection(&g_csGlobalClasses); }
CEngUpdate::~CEngUpdate() { LOG_Block("CEngUpdate::~CEngUpdate");
HRESULT hr; //
// First shut down any outstanding threads
//
this->ShutdownInstanceThreads();
if (NULL != m_evtNeedToQuit) { CloseHandle(m_evtNeedToQuit); } //
// Always Uninitialize global CSchemaKeys object
//
EnterCriticalSection(&g_csGlobalClasses);
#if defined(DBG)
//
// Paranoid check for coding error
//
if (0 == g_ulGlobalClassRefCount) { LOG_Error(_T("Unbalanced calls to CEngUpdate ctor and dtor")); } #endif
g_ulGlobalClassRefCount--; LOG_Out(_T("g_ulGlobalClassRefCount is now %d"), g_ulGlobalClassRefCount);
if (0 == g_ulGlobalClassRefCount) { //
// The last CEngUpdate instance is going away, delete the
// global CSchemaKeys object
//
if (NULL != g_pGlobalSchemaKeys) { delete g_pGlobalSchemaKeys; g_pGlobalSchemaKeys = NULL; } else { LOG_Error(_T("Unexpected NULL == g_pGlobalSchemaKeys")); }
CleanupDownloadLib(); }
LeaveCriticalSection(&g_csGlobalClasses); }
// ----------------------------------------------------------------------
//
// ShutdownInstanceThreads()
//
// called by CEngUpdate::~CEngUpdate to shut down any outstanding
// threads before the control can end
//
// ----------------------------------------------------------------------
void WINAPI CEngUpdate::ShutdownInstanceThreads() { LOG_Block("ShutdownInstanceThreads");
if (NULL != m_evtNeedToQuit) { //
// notify all threads go away
//
SetEvent(m_evtNeedToQuit); LOG_Out(_T("Shutdown event has been signalled"));
//
// wait all threads to quit
// I don't think we should have a time limit here
// since if we quit before all threads quit,
// it's almost sure that AV will happen.
//
MSG msg; while (m_lThreadCounter > 0) { while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } }
LOG_Out(_T("All threads appeared gone."));
//
// reset the signal
//
ResetEvent(m_evtNeedToQuit); } }
HRESULT CEngUpdate::RebootMachine() { LOG_Block("RebootMachine()");
HRESULT hr = S_OK; DWORD dwRet; OSVERSIONINFO osvi; osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osvi);
// Check if we're running on NT, if we are, we need to see if we have Privileges to Reboot
if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) { HANDLE hToken; TOKEN_PRIVILEGES tkp; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { dwRet = GetLastError(); LOG_ErrorMsg(dwRet); hr = HRESULT_FROM_WIN32(dwRet); return hr; }
LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid); tkp.PrivilegeCount = 1; tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0)) { dwRet = GetLastError(); LOG_ErrorMsg(dwRet); hr = HRESULT_FROM_WIN32(dwRet); return hr; } }
//
// shutdown the system and force all applications to close
//
ExitWindowsEx(EWX_REBOOT, 0); return hr; }
// ----------------------------------------------------------------------
//
// DLL Public API: ShutdownThreads()
//
// called by unlockengine form control to shut down any outstanding
// threads before the control can end
//
// ----------------------------------------------------------------------
void WINAPI ShutdownThreads() { LOG_Block("ShutdownThreads");
//
// To maintain XPClient V4 CDM compatibility with the iuengine.dll, we
// use the following hack to create and delete the global instance
// of CEngUpdate:
//
// After CDM calls LoadIUEngine, it calls SetGlobalOfflineFlag,
// which we hook and call CreateGlkobalCDMEngUpdateInstance.
//
// When CDM calls UnLoadIUEngine, the function calls the old
// single-instance ShutdownThreads export, which we now use
// to call DeleteGlobalCDMEngUpdateInstance. CEngUpdate does
// its own ShutdownThreads call in it's destructor.
//
DeleteGlobalCDMEngUpdateInstance();
//
// If we are the last client, shutdown the global threads
//
ShutdownGlobalThreads(); }
void WINAPI ShutdownGlobalThreads() { LOG_Block("ShutdownGlobalThreads"); //
// Now shut down any global (not CEngUpdate instance) threads
// if there are no CEngUpdate instances left (last client is exiting)
//
if (NULL != g_evtNeedToQuit && 0 == g_ulGlobalClassRefCount) { //
// notify all threads go away
//
SetEvent(g_evtNeedToQuit); LOG_Out(_T("Shutdown event has been signalled"));
//
// wait all threads to quit
// I don't think we should have a time limit here
// since if we quit before all threads quit,
// it's almost sure that AV will happen.
//
MSG msg; while (g_lThreadCounter > 0) { while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } }
LOG_Out(_T("All global threads appear gone."));
//
// reset the signal
//
ResetEvent(g_evtNeedToQuit); } }
COperationMgr::COperationMgr() : m_pOperationInfoList(NULL) { }
COperationMgr::~COperationMgr() { PIUOPERATIONINFO pCurrent = m_pOperationInfoList; PIUOPERATIONINFO pNext; while (pCurrent) { pNext = pCurrent->pNext; if (NULL != pCurrent->bstrOperationResult) { SafeSysFreeString(pCurrent->bstrOperationResult); } HeapFree(GetProcessHeap(), 0, pCurrent); pCurrent = pNext; } }
BOOL COperationMgr::AddOperation(LPCTSTR pszOperationID, LONG lUpdateMask) { PIUOPERATIONINFO pCurrent = m_pOperationInfoList; PIUOPERATIONINFO pLastOperation = NULL; PIUOPERATIONINFO pNewOperation = NULL;
if (NULL == pszOperationID) { return FALSE; }
// try to find the operation if its already here
while (pCurrent) { pLastOperation = pCurrent; if (0 == StrCmpI(pszOperationID, pCurrent->szOperationUUID)) { // match
break; } pCurrent = pCurrent->pNext; }
if (NULL == pCurrent) { // not found, or no operations in list yet
pNewOperation = (IUOPERATIONINFO *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IUOPERATIONINFO)); if (NULL == pNewOperation) { // out of memory, can't persist operation info..
return FALSE; }
lstrcpyn(pNewOperation->szOperationUUID, pszOperationID, ARRAYSIZE(pNewOperation->szOperationUUID)); pNewOperation->lUpdateMask = lUpdateMask;
if (NULL == m_pOperationInfoList) { m_pOperationInfoList = pNewOperation; } else { if (NULL != pLastOperation) { pLastOperation->pNext = pNewOperation; } } } else { // found the existing operation.. update it.
pCurrent->lUpdateMask = lUpdateMask; SafeSysFreeString(pCurrent->bstrOperationResult); // reset result, new download request
} return TRUE; }
BOOL COperationMgr::FindOperation(LPCTSTR pszOperationID, PLONG plUpdateMask, BSTR *pbstrOperationResult) { BOOL fFound = FALSE; PIUOPERATIONINFO pCurrent = m_pOperationInfoList; if (NULL == pszOperationID) { return FALSE; }
while (pCurrent) { if (0 == StrCmpI(pszOperationID, pCurrent->szOperationUUID)) { fFound = TRUE; break; } pCurrent = pCurrent->pNext; }
if (pCurrent) { if (plUpdateMask) *plUpdateMask = pCurrent->lUpdateMask;
if (pbstrOperationResult) { if (NULL != pCurrent->bstrOperationResult) { *pbstrOperationResult = SysAllocString(pCurrent->bstrOperationResult); } else { *pbstrOperationResult = NULL; } } } return fFound; }
BOOL COperationMgr::RemoveOperation(LPCTSTR pszOperationID) { PIUOPERATIONINFO pCurrent = m_pOperationInfoList; PIUOPERATIONINFO pLastOperation = NULL;
if (NULL == pszOperationID) { return FALSE; }
while (pCurrent) { if (0 == StrCmpI(pszOperationID, pCurrent->szOperationUUID)) { break; } pLastOperation = pCurrent; pCurrent = pCurrent->pNext; }
if (NULL == pCurrent) { return FALSE; // not found
} else { if (pCurrent == m_pOperationInfoList) // only operation in list
{ m_pOperationInfoList = NULL; } else { pLastOperation->pNext = pCurrent->pNext; } }
SafeSysFreeString(pCurrent->bstrOperationResult); HeapFree(GetProcessHeap(), 0, pCurrent);
return TRUE; }
BOOL COperationMgr::UpdateOperation(LPCTSTR pszOperationID, LONG lUpdateMask, BSTR bstrOperationResult) { PIUOPERATIONINFO pCurrent = m_pOperationInfoList;
if (NULL == pszOperationID) { return FALSE; }
while (pCurrent) { if (0 == StrCmpI(pszOperationID, pCurrent->szOperationUUID)) { break; } pCurrent = pCurrent->pNext; }
if (NULL == pCurrent) { return FALSE; // not found
}
pCurrent->lUpdateMask = lUpdateMask; SafeSysFreeString(pCurrent->bstrOperationResult); if (NULL != bstrOperationResult) { pCurrent->bstrOperationResult = SysAllocString(bstrOperationResult); } return TRUE; }
/////////////////////////////////////////////////////////////////////////////
// WUPostMessageAndBlock()
//
// Since COM doesn't like to have COM calls made while processing a SendMessage
// message, we need to use PostMessage instead. However, using PostMessage
// isn't synchronous, so what we need to do is create an event, do the post,
// and wait on the event. When the WndProc at the other end of the post is
// done, it will signal the event and we can unblock.
// Input:
// hwnd: hwnd to post to
// Msg: message value
// pevtData: pointer to an EventData structure to send as the LPARAM. We fill
// in the hevDoneWithMessage field with the event we allocate.
// Return:
// TRUE if we successfully waited for the message processing to complete
// and FALSE otherwise
//
/////////////////////////////////////////////////////////////////////////////
BOOL WUPostEventAndBlock(HWND hwnd, UINT Msg, EventData *pevtData) { BOOL fRet = TRUE;
// ok, so this is funky: if we are in the thread that owns the HWND, then
// just maintain previous semantics and call SendMessage (yes, this means
// effectively not fixing the bug for this case, but nobody should be
// using the synchronus download or install functions.)
if (GetWindowThreadProcessId(hwnd, NULL) == GetCurrentThreadId()) { SendMessage(hwnd, Msg, 0, (LPARAM)pevtData); } else { DWORD dw; // alloc the event we're going to wait on & fill in the field of the
// EventData structure. If this fails, we can't really go on, so
// bail
pevtData->hevDoneWithMessage = CreateEvent(NULL, FALSE, FALSE, NULL); if (pevtData->hevDoneWithMessage == NULL) return TRUE;
// do the post
PostMessage(hwnd, Msg, 0, (LPARAM)pevtData);
// wait for the WndProc to signal that it's done.
dw = WaitForSingleObject(pevtData->hevDoneWithMessage, INFINITE);
// cleanup & return
CloseHandle(pevtData->hevDoneWithMessage); pevtData->hevDoneWithMessage = NULL; fRet = (dw == WAIT_OBJECT_0); } return fRet; }
|