WindowsXP-SP1/enduser/windows.com/iuengine/engmain.cpp
2020-09-30 16:53:49 +02:00

1064 lines
29 KiB
C++

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