windows-nt/Source/XPSP1/NT/enduser/windows.com/iuctl/selfupd.cpp
2020-09-26 16:20:57 +08:00

1264 lines
38 KiB
C++

#include "stdafx.h"
#include "iudl.h"
#include "selfupd.h"
#include <iucommon.h>
#include <osdet.h>
#include <logging.h>
#include <shlwapi.h>
#include <fileutil.h>
#include <iu.h>
#include "update.h"
#include <WaitUtil.h>
#include <UrlAgent.h>
#include <RedirectUtil.h>
#include "wusafefn.h"
inline DWORD StartSelfUpdateProcess(HANDLE evtQuit, CUpdate* pUpdateComClass, IUnknown* punkUpdateCompleteListener);
DWORD WINAPI MonitorUpdateCompleteProc(LPVOID lpv);
const TCHAR IDENTCAB[] = _T("iuident.cab");
const CHAR SZ_SELF_UPDATE_CHECK[] = "Checking to see if new version of Windows Update software available";
extern HANDLE g_hEngineLoadQuit;
extern CIUUrlAgent *g_pIUUrlAgent;
extern CRITICAL_SECTION g_csUrlAgent;
typedef struct _MONITOR_DATA {
HANDLE hProcess;
HANDLE evtControlQuit;
CUpdate* pUpdateComClass;
IUnknown* punkCallback;
} MONITOR_DATA, *PMONITOR_DATA;
//
// include declaration for interface IUpdateCompleteListener
//
#ifndef __IUpdateCompleteListener_INTERFACE_DEFINED__
#define __IUpdateCompleteListener_INTERFACE_DEFINED__
/* interface IUpdateCompleteListener */
/* [unique][helpstring][uuid][object] */
EXTERN_C const IID IID_IUpdateCompleteListener;
#if defined(__cplusplus) && !defined(CINTERFACE)
MIDL_INTERFACE("1C06B895-E4C8-48eb-9E03-15A53B43B6CA")
IUpdateCompleteListener : public IUnknown
{
public:
virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE OnComplete(
/* [in] */ LONG lErrorCode) = 0;
};
#else /* C style interface */
typedef struct IUpdateCompleteListenerVtbl
{
BEGIN_INTERFACE
HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
IUpdateCompleteListener * This,
/* [in] */ REFIID riid,
/* [iid_is][out] */ void **ppvObject);
ULONG ( STDMETHODCALLTYPE *AddRef )(
IUpdateCompleteListener * This);
ULONG ( STDMETHODCALLTYPE *Release )(
IUpdateCompleteListener * This);
/* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *OnComplete )(
IUpdateCompleteListener * This,
/* [in] */ LONG lErrorCode);
END_INTERFACE
} IUpdateCompleteListenerVtbl;
interface IUpdateCompleteListener
{
CONST_VTBL struct IUpdateCompleteListenerVtbl *lpVtbl;
};
#ifdef COBJMACROS
#define IUpdateCompleteListener_QueryInterface(This,riid,ppvObject) \
(This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
#define IUpdateCompleteListener_AddRef(This) \
(This)->lpVtbl -> AddRef(This)
#define IUpdateCompleteListener_Release(This) \
(This)->lpVtbl -> Release(This)
#define IUpdateCompleteListener_OnComplete(This,lErrorCode) \
(This)->lpVtbl -> OnComplete(This,lErrorCode)
#endif /* COBJMACROS */
#endif /* C style interface */
/* [helpstring][id] */ HRESULT STDMETHODCALLTYPE IUpdateCompleteListener_OnComplete_Proxy(
IUpdateCompleteListener * This,
/* [in] */ LONG lErrorCode);
void __RPC_STUB IUpdateCompleteListener_OnComplete_Stub(
IRpcStubBuffer *This,
IRpcChannelBuffer *_pRpcChannelBuffer,
PRPC_MESSAGE _pRpcMessage,
DWORD *_pdwStubPhase);
#endif /* __IUpdateCompleteListener_INTERFACE_DEFINED__ */
/////////////////////////////////////////////////////////////////////////////
// SelfUpdateCheck()
//
// Determines if a SelfUpdate is needed, or if a SelfUpdate is already in process.
// If one is already in process this will immediately return. If one is needed
// it either perform the selfupdate (synchronous), or launch a rundll32.exe process
// and have it call the BeginSelfUpdate() entrypoint to start the selfupdate (asynchronous)
//
// Return S_FALSE is asked not to update engine but this func found engine
// needs to be updated.
//
/////////////////////////////////////////////////////////////////////////////
HRESULT SelfUpdateCheck(BOOL fSynch, BOOL fStartUpdate, HANDLE evtQuit, CUpdate* pUpdateComClass, IUnknown* punkUpdateCompleteListener)
{
LOG_Block("SelfUpdateCheck()");
HRESULT hr = S_OK;
int iRet = 0;
DWORD dwRet;
DWORD dwWaitResult;
DWORD dwStatus = 0;
DWORD dwSize = 0;
BOOL fSelfUpdateAvailable = FALSE;
BOOL fAsyncSelfUpdateStarted = FALSE;
BOOL fBetaSelfUpdate = FALSE;
TCHAR szEngineClientVersion[64];
TCHAR szEngineServerVersion[64];
char szAnsiEngineServerVersion[64];
TCHAR szIUDir[MAX_PATH];
TCHAR szIdentFile[MAX_PATH];
TCHAR szSystemDir[MAX_PATH+1];
TCHAR szEngineDllPath[MAX_PATH+1];
FILE_VERSION fvClientEngine, fvServerEngine;
HANDLE hDownloadEvent = NULL;
HANDLE hDownloadEventSync = NULL;
HANDLE hMutex = NULL;
HKEY hkey = NULL;
MSG msg;
DWORD dwTickStart, dwTickCurrent, dwTickEnd;
HANDLE aHandles[2];
if (!fSynch && fStartUpdate && NULL == pUpdateComClass)
{
//
// if to do asynchronized update but the COM class pointer not passed in, then
// even we succeed we can not pump up the init state of that class so that COM object
// will still not usable.
//
hr = E_INVALIDARG;
goto CleanUp;
}
// The synchronization between multiple processes running the IU control and doing the selfupdate
// process is reasonably complex. We do this by using two synchronization objects. A named Mutex which protects
// the 'selfupdate checking' process, and a named Event that protects against orphaned selfupdate processes caused
// by reboots during a selfupdate.
hDownloadEvent = CreateEvent(NULL, TRUE, TRUE, IU_EVENT_SELFUPDATE_IN_PROGRESS);
if (NULL == hDownloadEvent)
{
dwRet = GetLastError();
LOG_ErrorMsg(dwRet);
hr = HRESULT_FROM_WIN32(dwRet);
goto CleanUp;
}
hDownloadEventSync = CreateEvent(NULL, TRUE, FALSE, IU_EVENT_SELFUPDATE_EVENT_SYNC);
if (NULL == hDownloadEventSync)
{
dwRet = GetLastError();
LOG_ErrorMsg(dwRet);
hr = HRESULT_FROM_WIN32(hr);
goto CleanUp;
}
// First check to see if a selfupdate is already in process.. This is done by
// checking a regkey for the current selfupdate state. We use a Mutex to synchronize
// reading/writing to the registry key to ensure that only one process is attempting
// the selfupdate. We don't care whether we had to create the mutex, or whether it was
// already created, so as long as it succeeds, we'll use it.
hMutex = CreateMutex(NULL, FALSE, IU_MUTEX_SELFUPDATE_REGCHECK);
if (NULL == hMutex)
{
dwRet = GetLastError();
LOG_ErrorMsg(dwRet);
hr = HRESULT_FROM_WIN32(dwRet);
goto CleanUp;
}
// We're ready to start the selfupdate check process. We'll request the mutex. This helper function
// does a while loop checking every second (1000ms) for a timeout to elapse (calculated with GetTickCount()),
// or for the object to be satisfied. This function should either return a timeout result, a WAIT_OBJECT_0
// for the index 0 object, or else we got the event/mutex we were waiting for.
aHandles[0] = g_hEngineLoadQuit; // index 0
aHandles[1] = hMutex;
dwWaitResult = MyMsgWaitForMultipleObjects(ARRAYSIZE(aHandles), aHandles, FALSE, /*30 seconds*/ 30000, QS_ALLINPUT);
if (WAIT_TIMEOUT == dwWaitResult)
{
LOG_ErrorMsg(IU_SELFUPDATE_TIMEOUT);
hr = IU_SELFUPDATE_TIMEOUT;
goto CleanUp;
}
if (WAIT_OBJECT_0 == dwWaitResult)
{
hr = E_ABORT;
LOG_ErrorMsg(hr);
goto CleanUp;
}
if (ERROR_REQUEST_ABORTED == dwWaitResult) // this indicates we processed a QUIT message while waiting.
{
// not an error
goto CleanUp;
}
dwRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, REGKEY_IUCTL, 0, _T(""), REG_OPTION_NON_VOLATILE,
KEY_READ | KEY_WRITE, NULL, &hkey, &dwStatus);
if (ERROR_SUCCESS != dwRet)
{
LOG_ErrorMsg(dwRet);
hr = HRESULT_FROM_WIN32(dwRet);
goto CleanUp;
}
// if the previous call to RegCreateKeyEx indicated it 'created' the key then we need to set the default
// status to 0.
if (REG_CREATED_NEW_KEY == dwStatus)
{
dwStatus = SELFUPDATE_NONE;
dwRet = RegSetValueEx(hkey, REGVAL_SELFUPDATESTATUS, 0, REG_DWORD, (LPBYTE)&dwStatus, sizeof(dwStatus));
}
else
{
// Check for Beta IU SelfUpdate Handling Requested
dwStatus = 0;
dwSize = sizeof(dwStatus);
dwRet = RegQueryValueEx(hkey, REGVAL_BETASELFUPDATE, NULL, NULL, (LPBYTE)&dwStatus, &dwSize);
if (1 == dwStatus)
{
fBetaSelfUpdate = TRUE;
}
dwStatus = SELFUPDATE_NONE;
dwSize = sizeof(dwStatus);
dwRet = RegQueryValueEx(hkey, REGVAL_SELFUPDATESTATUS, NULL, NULL, (LPBYTE)&dwStatus, &dwSize);
}
// check the result of the QueryValue/SetValue call -
if (ERROR_SUCCESS != dwRet && 2 != dwRet)
{
//
// if dwRet == 2, it's the case that IUControl key exist, but SelfUpdate value not exist,
//
LOG_ErrorMsg(dwRet);
hr = HRESULT_FROM_WIN32(dwRet);
goto CleanUp;
}
if (WAIT_TIMEOUT != WaitForSingleObject(g_hEngineLoadQuit, 0))
{
LOG_ErrorMsg(E_ABORT);
hr = E_ABORT;
goto CleanUp;
}
switch (dwStatus)
{
case SELFUPDATE_NONE:
{
// First find out the version of the Engine on the server.
GetIndustryUpdateDirectory(szIUDir);
hr=PathCchCombine(szIdentFile,ARRAYSIZE(szIdentFile),szIUDir,IDENTTXT);
if(FAILED(hr))
{
LOG_ErrorMsg(hr);
goto CleanUp;
}
GetPrivateProfileString(fBetaSelfUpdate ? IDENT_IUBETASELFUPDATE : IDENT_IUSELFUPDATE,
IDENT_VERSION,
_T(""),
szEngineServerVersion,
ARRAYSIZE(szEngineServerVersion),
szIdentFile);
if ('\0' == szEngineServerVersion[0])
{
// no selfupdate available, no server version information
hr = S_OK;
goto CleanUp;
}
GetSystemDirectory(szSystemDir, ARRAYSIZE(szSystemDir));
hr=PathCchCombine(szEngineDllPath,ARRAYSIZE(szEngineDllPath),szSystemDir, ENGINEDLL);
if(FAILED(hr))
{
LOG_ErrorMsg(hr);
goto CleanUp;
}
if (GetFileVersion(szEngineDllPath, &fvClientEngine))
{
// T2A requires Structured Exception Handling (because it uses alloca which can throw, so we avoid it and
// do it the simple way.
#ifdef UNICODE
WideCharToMultiByte(CP_ACP, 0, szEngineServerVersion, -1, szAnsiEngineServerVersion,
sizeof(szAnsiEngineServerVersion), NULL, NULL);
if (!ConvertStringVerToFileVer(szAnsiEngineServerVersion, &fvServerEngine))
#else
if (!ConvertStringVerToFileVer(szEngineServerVersion, &fvServerEngine))
#endif
{
LOG_ErrorMsg(IU_SELFUPDATE_FAILED);
hr = IU_SELFUPDATE_FAILED;
goto CleanUp;
}
iRet = CompareFileVersion(fvClientEngine, fvServerEngine);
if (iRet == 0)
{
// IUEngine Versions are the same
fSelfUpdateAvailable = FALSE;
}
else if (iRet > 0)
{
LOG_Internet(_T("Version of IUEngine on Client is NEWER than IUEngine on Server"));
fSelfUpdateAvailable = FALSE;
}
else
{
// IUEngine Version on the Server is newer
LOG_Internet(_T("New Version (%s) of IUEngine on Server Found."), szEngineServerVersion);
#if defined(UNICODE) || defined(_UNICODE)
LogMessage("IUEngine on Server is newer version (%ls)", szEngineServerVersion);
#else
LogMessage("IUEngine on Server is newer version (%s)", szEngineServerVersion);
#endif
fSelfUpdateAvailable = TRUE;
}
}
else
{
// no version information found on local file, probably should do a selfupdate anyway.
LOG_Internet(_T("No Version Information On Local IUEngine, SelfUpdating to Server Version"));
fSelfUpdateAvailable = TRUE;
}
if (WAIT_TIMEOUT != WaitForSingleObject(g_hEngineLoadQuit, 0))
{
LOG_ErrorMsg(E_ABORT);
hr = E_ABORT;
goto CleanUp;
}
if (fSelfUpdateAvailable)
{
if (fStartUpdate)
{
dwStatus = SELFUPDATE_IN_PROGRESS;
dwRet = RegSetValueEx(hkey, REGVAL_SELFUPDATESTATUS, 0, REG_DWORD, (LPBYTE)&dwStatus, sizeof(dwStatus));
RegCloseKey(hkey); // done with the reg key now.
hkey = NULL;
// The default state of the DownloadEvent is Signaled (TRUE). If a Process is 'actually' working on the
// SelfUpdate Process this Event Needs to be Reset to FALSE. Any time a client determines that a selfupdate
// 'should' be in progress (from the regkey status) it should check the Event state, if it is signaled (TRUE)
// then there was probably a reboot during the selfupdate, it should restart the selfupdate process itself.
ResetEvent(hDownloadEvent); // mark that this Process will Perform the SelfUpdate by Resetting the Download Event
ReleaseMutex(hMutex); // we are now done with the selfupdate check, both the event and the registry values are set
// properly.
CloseHandle(hMutex);
hMutex = NULL;
if (fSynch)
{
hr = BeginSelfUpdate();
if (FAILED(hr))
{
LOG_Error(_T("BeginSelfUpdate Failed"));
goto CleanUp;
}
}
else
{
fAsyncSelfUpdateStarted = TRUE;
// launch SelfUpdate Asynchronously.
dwRet = StartSelfUpdateProcess(evtQuit, pUpdateComClass, punkUpdateCompleteListener); // inline function
if (ERROR_SUCCESS != dwRet)
{
LOG_ErrorMsg(dwRet);
hr = HRESULT_FROM_WIN32(dwRet);
goto CleanUp;
}
}
}
else
{
//
// in case we are asked to check update info only,
// we signal back the result as S_FALSE for
// engine update avail
//
hr = S_FALSE;
}
}
else
{
//
// somehow, no update needed. must be other process finished it.
//
if (fStartUpdate)
{
hr = IU_SELFUPDATE_USENEWDLL;
goto CleanUp;
}
}
break;
}
case SELFUPDATE_COMPLETE_UPDATE_BINARY_REQUIRED:
{
// As selfupdate has already been completed, but we're waiting to be able to rename the DLL.
// In this case we'll tell the control to load the enginenew.dll.
LOG_Internet(_T("SelfUpdate Already Complete, Updated Binary Available, Waiting for Rename."));
hr = IU_SELFUPDATE_USENEWDLL;
goto CleanUp;
}
case SELFUPDATE_IN_PROGRESS:
default:
{
if (!fStartUpdate)
{
//
// if asked to check update status but not to do actual update,
// then this reg key flag tells the engine not update complete yet.
//
hr = S_FALSE; // signal TRUE for engine need update
goto CleanUp;
}
if (WAIT_TIMEOUT != WaitForSingleObject(g_hEngineLoadQuit, 0))
{
LOG_ErrorMsg(E_ABORT);
hr = E_ABORT;
goto CleanUp;
}
// The RegKey indicates that a SelfUpdate is in Progress Now. We need to make sure this is
// actually true. If a previous attempt at selfupdate was aborted because the machine rebooted
// we could be in a false state. The 'default' state of teh DownloadEvent is Signaled (TRUE).
// If the current State is TRUE then the SelfUpdate is actually 'NOT' in progress.
// Find out the current state of the DownloadEvent
dwWaitResult = WaitForSingleObject(hDownloadEvent, 0);
if (WAIT_OBJECT_0 == dwWaitResult)
{
// The Event State is still Signaled (TRUE), so the selfupdate is not in progress
ResetEvent(hDownloadEvent); // mark that this Process will Perform the SelfUpdate by Resetting the Download Event
ReleaseMutex(hMutex);
CloseHandle(hMutex);
hMutex = NULL;
if (fSynch)
{
hr = BeginSelfUpdate();
if (FAILED(hr))
{
LOG_Error(_T("BeginSelfUpdate Failed"));
goto CleanUp;
}
}
else
{
fAsyncSelfUpdateStarted = TRUE;
// launch SelfUpdate Asynchronously.
dwRet = StartSelfUpdateProcess(evtQuit, pUpdateComClass, punkUpdateCompleteListener); // inline function
if (ERROR_SUCCESS != dwRet)
{
LOG_ErrorMsg(dwRet);
hr = HRESULT_FROM_WIN32(dwRet);
goto CleanUp;
}
}
}
else
{
// The Event State is not Signaled (FALSE), everythings ok with the current selfupdate
// Now we need to either start a thread to wait for the complete and immediately return, 'or'
// wait for the selfupdate to finish (synchronous or asynchronous selfupdate).
if (fSynch)
{
// we need to wait for the event to change back to signaled state. Should indicate the
// selfupdate is finished.
// before we start the wait, we will release our mutex, since we really aren't doing anything
// with the registry anymore
ReleaseMutex(hMutex);
CloseHandle(hMutex);
hMutex = NULL;
aHandles[0] = g_hEngineLoadQuit; // index 0
aHandles[1] = hDownloadEvent;
dwWaitResult = MyMsgWaitForMultipleObjects(ARRAYSIZE(aHandles), aHandles, FALSE, /*120 seconds*/ 120000, QS_ALLINPUT);
if (WAIT_TIMEOUT == dwWaitResult)
{
// Timed Out Waiting for SelfUpdate to Complete. May just be really slow, go ahead
// and use the old DLL for now.
LOG_ErrorMsg(IU_SELFUPDATE_TIMEOUT);
hr = IU_SELFUPDATE_TIMEOUT;
goto CleanUp;
}
if (ERROR_REQUEST_ABORTED == dwWaitResult)
{
goto CleanUp;
}
if (WAIT_OBJECT_0 == dwWaitResult)
{
//
// index 0 (g_hEngineLoadQuit) was signaled
//
hr = E_ABORT;
LOG_ErrorMsg(hr);
goto CleanUp;
}
hr = IU_SELFUPDATE_USENEWDLL;
goto CleanUp;
}
else
{
//
// asked to update in async mode, but found someone else
// already started the update process
//
PMONITOR_DATA pMonitorData = (PMONITOR_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MONITOR_DATA));
if (NULL == pMonitorData)
{
hr = E_OUTOFMEMORY;
LOG_ErrorMsg(hr);
goto CleanUp;
}
else
{
DWORD dwThreadId = 0;
hr = S_OK;
pMonitorData->hProcess = hDownloadEvent;
pMonitorData->evtControlQuit = evtQuit;
pMonitorData->pUpdateComClass = pUpdateComClass;
pMonitorData->punkCallback = punkUpdateCompleteListener;
HANDLE hThread = NULL;
hThread = CreateThread(NULL, 0, MonitorUpdateCompleteProc, pMonitorData, 0, &dwThreadId);
if (NULL == hThread)
{
HeapFree(GetProcessHeap(), 0, pMonitorData);
//
// otherwise, the memory allocated will be released by the thread procedure
//
hr = HRESULT_FROM_WIN32(GetLastError());
LOG_ErrorMsg(hr);
goto CleanUp;
}
else
{
CloseHandle(hThread); // don't leak the thread handle
}
}
goto CleanUp;
}
}
break;
}
}
CleanUp:
// always release the mutex, there are cases during the selfupdate check that can fail, in which
// case they fall through to here. If the mutex is free'd when its not ours the call simply fails.
if (NULL != hMutex)
{
ReleaseMutex(hMutex);
CloseHandle(hMutex);
hMutex = NULL;
}
if (fAsyncSelfUpdateStarted)
{
// if an Asynchronous SelfUpdate has been started we want to wait for it to
// get the DownloadEvent before we close it. There would be a possible Race
// condition where the selfupdate process would 'create' the event, instead
// of 'opening' the event in its 'reset' state. If this happened another
// process could come along, find that the event state is signaled instead of
// reset and assume the selfupdate process had terminated.
// Wait for the DownloadEventSync event to be signaled. We'll put a timeout of
// 30 seconds (should be more than sufficient for the new process to start and
// set the event).
aHandles[0] = g_hEngineLoadQuit; // index 0
aHandles[1] = hDownloadEventSync;
dwWaitResult = MyMsgWaitForMultipleObjects(ARRAYSIZE(aHandles), aHandles, FALSE, /*30 seconds*/ 30000, QS_ALLINPUT);
if (WAIT_TIMEOUT == dwWaitResult)
{
// go ahead and log that we timed out waiting.
LOG_Internet(_T("Timeout Elapsed while waiting for SelfUpdate Process to open the DownloadSync Event"));
}
if (ERROR_REQUEST_ABORTED == dwWaitResult)
{
// go ahead and log that a WM_QUIT, WM_CLOSE, or WM_DESTROY.
LOG_Internet(_T("Received WM_QUIT, WM_CLOSE, or WM_DESTROY while waiting for SelfUpdate Process to open the DownloadSync Event"));
}
if (WAIT_OBJECT_0 == dwWaitResult)
{
LOG_Internet(_T("g_hEngineLoadQuit signaled while waiting for SelfUpdate Process to open the DownloadSync Event"));
hr = E_ABORT;
}
}
if (NULL != hDownloadEvent)
{
CloseHandle(hDownloadEvent);
hDownloadEvent = NULL;
}
if (NULL != hDownloadEventSync)
{
CloseHandle(hDownloadEventSync);
hDownloadEventSync = NULL;
}
if (NULL != hkey)
{
RegCloseKey(hkey);
}
if (SUCCEEDED(hr))
{
LogMessage(SZ_SELF_UPDATE_CHECK);
}
else
{
LogError(hr, SZ_SELF_UPDATE_CHECK);
}
return hr;
}
inline DWORD StartSelfUpdateProcess(HANDLE evtQuit, CUpdate* pUpdateComClass, IUnknown* punkUpdateCompleteListener)
{
TCHAR szRunDll32Path[MAX_PATH+1];
TCHAR szCommandLine[MAX_PATH+1];
TCHAR szDirectory[MAX_PATH+1];
PROCESS_INFORMATION pi;
STARTUPINFO si;
DWORD dwRet = ERROR_SUCCESS;
DWORD dwThreadId;
PMONITOR_DATA pMonitorData;
HRESULT hr=S_OK;
if (0 == GetSystemDirectory(szDirectory, ARRAYSIZE(szDirectory)))
{
return GetLastError();
}
hr=PathCchCombine(szRunDll32Path,ARRAYSIZE(szRunDll32Path),szDirectory, RUNDLL32);
if(FAILED(hr))
return HRESULT_CODE(hr);
if (!FileExists(szRunDll32Path))
{
// probably running on W9x, look in the Windows Folder Instead
if (0 == GetWindowsDirectory(szDirectory, ARRAYSIZE(szDirectory)))
{
return GetLastError();
}
hr=PathCchCombine(szRunDll32Path,ARRAYSIZE(szRunDll32Path),szDirectory, RUNDLL32);
if(FAILED(hr))
return HRESULT_CODE(hr);
if (!FileExists(szRunDll32Path))
{
// we're toast.. can't find rundll32.exe .. bye-bye
return ERROR_FILE_NOT_FOUND;
}
}
// now form the path to the iuctl.dll .. we'll trust nothing and 'get' the module filename
// instead of assuming its in the system folder.
GetModuleFileName(GetModuleHandle(IUCTL), szDirectory, ARRAYSIZE(szDirectory));
hr=StringCchPrintfEx(szCommandLine,ARRAYSIZE(szCommandLine),NULL,NULL,MISTSAFE_STRING_FLAGS,_T("\"%s\" \"%s\"%s"), szRunDll32Path, szDirectory, RUNDLLCOMMANDLINE);
if(FAILED(hr))
return HRESULT_CODE(hr);
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
if (!CreateProcess(NULL, szCommandLine, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi))
{
dwRet = GetLastError();
return dwRet;
}
//
// create a thread that can be used to monitor the completeness of
// this update process
//
pMonitorData = (PMONITOR_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MONITOR_DATA));
if (NULL != pMonitorData)
{
pMonitorData->hProcess = pi.hProcess; // return process handle so we know when it's done
pMonitorData->evtControlQuit = evtQuit;
pMonitorData->pUpdateComClass = pUpdateComClass;
pMonitorData->punkCallback = punkUpdateCompleteListener;
HANDLE hThread = NULL;
hThread = CreateThread(NULL, 0, MonitorUpdateCompleteProc, pMonitorData, 0, &dwThreadId);
if (NULL == hThread)
{
HeapFree(GetProcessHeap(), 0, pMonitorData);
//
// otherwise, the memory allocated will be released by the thread procedure
//
}
else
{
CloseHandle(hThread);
}
}
return dwRet;
}
/////////////////////////////////////////////////////////////////////////////
//
// thread procedure to determine when to signal caller
// the update process has be gone
//
/////////////////////////////////////////////////////////////////////////////
DWORD WINAPI MonitorUpdateCompleteProc(LPVOID lpv)
{
HRESULT hr;
HWND hWnd;
CUpdate* pUpdateClass = NULL;
IUnknown* punkCallback = NULL;
PMONITOR_DATA pData;
HANDLE hEvents[2];
DWORD dwRet, dwErr = 0;
MSG msg;
LOG_Block("MonitorUpdateCompleteProc");
if (NULL == lpv)
{
LOG_ErrorMsg(E_INVALIDARG);
return 0x1; // impossible!
}
pData = (PMONITOR_DATA) lpv;
hEvents[0] = pData->hProcess;
hEvents[1] = pData->evtControlQuit;
punkCallback = pData->punkCallback;
pUpdateClass = pData->pUpdateComClass;
if (pUpdateClass)
{
hWnd = pUpdateClass->GetEventWndClass().GetEvtHWnd();
}
//
// this data allcoated by parent thread, we are responsible to release it
//
HeapFree(GetProcessHeap(), 0, lpv);
if (NULL == pUpdateClass)
{
//
// even we catch the update completeness, without this pointer we can not
// modify the init state so this COM will still not usable. We bail
//
return 0;
}
//
// wait till process gone or quit signal
//
while (TRUE)
{
dwRet = MsgWaitForMultipleObjects(ARRAYSIZE(hEvents), hEvents, FALSE, INFINITE, QS_ALLINPUT);
switch (dwRet)
{
case WAIT_OBJECT_0:
//
// process done, get return code
//
GetExitCodeProcess(hEvents[0], &dwErr);
if (0x0 == dwErr)
{
//
// we are done with no error, then pump the
// init state to ready state
//
dwErr = pUpdateClass->ChangeControlInitState(2);
}
//
// signal event
//
if (NULL != hWnd)
{
PostMessage(hWnd, UM_EVENT_SELFUPDATE_COMPLETE, 0, (LPARAM)dwErr);
LOG_Out(_T("Fired event OnComplete()"));
}
//
// signal callback
//
if (NULL != punkCallback)
{
IUpdateCompleteListener* pCallback = NULL;
if (FAILED(hr = punkCallback->QueryInterface(IID_IUpdateCompleteListener, (void**) &pCallback)))
{
LOG_ErrorMsg(hr);
}
else
{
pCallback->OnComplete(dwErr);
pCallback->Release();
LOG_Out(_T("Returned from callback API OnComplete()"));
}
}
return 0;
break;
case WAIT_OBJECT_0 + 1:
//
// got global Quit event
//
LOG_Out(_T("Found quit event!"));
return 1;
break;
case WAIT_OBJECT_0 + ARRAYSIZE(hEvents):
//
// got message
//
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (WM_QUIT == msg.message)
{
LOG_Out(_T("Found WM_QUIT message. Leaving..."));
return 1;
}
DispatchMessage(&msg);
}
break;
}
}
return 0; // never reach here
}
HRESULT BeginSelfUpdate()
{
LOG_Block("BeginSelfUpdate()");
DWORD dwRet;
DWORD dwStatus;
DWORD dwSize;
HRESULT hr = S_OK;
HKEY hkey = NULL; // PreFast
TCHAR szIUDir[MAX_PATH+1];
TCHAR szLocalPath[MAX_PATH+1];
TCHAR szSystemDir[MAX_PATH+1];
TCHAR szTargetDLLName[MAX_PATH+1];
LPTSTR pszSelfUpdateCabUrl = NULL;
HANDLE hDownloadEventSync = NULL; // PreFast
HANDLE hDownloadEvent = NULL; // PreFast
HANDLE hMutex = NULL; // PreFast
MSG msg;
BOOL fBetaSelfUpdate = FALSE;
HMODULE hNewEngine = NULL;
PFN_CompleteSelfUpdateProcess fpnCompleteSelfUpdateProcess = NULL;
// The SelfUpdate process is done while the SELFUPDATE_IN_PROGRESS event is 'reset'. We
// do everything we can to make sure we 'open' this event in the reset state, but if for
// some reason the event is not there we will create it in the reset state.
hDownloadEvent = CreateEvent(NULL, TRUE, FALSE, IU_EVENT_SELFUPDATE_IN_PROGRESS);
if (NULL == hDownloadEvent)
{
dwRet = GetLastError();
LOG_ErrorMsg(dwRet);
hr = HRESULT_FROM_WIN32(dwRet);
goto CleanUp;
}
// The SELFUPDATE_EVENT_SYNC is the mechanism we use to 'try' to keep the SELFUPDATE_IN_PROGRESS
// event alive and in the 'reset' state until this function can open it and keep it in that state.
// This should prevent a race condition caused when the SelfUpdateCheck function closes the Event
// before this function can open it. If this happens another process could start the selfupdate check
// process and find the SELFUPDATE_IN_PROGRESS event in the wrong state.
hDownloadEventSync = CreateEvent(NULL, TRUE, FALSE, IU_EVENT_SELFUPDATE_EVENT_SYNC);
if (NULL == hDownloadEventSync)
{
dwRet = GetLastError();
LOG_ErrorMsg(dwRet);
hr = HRESULT_FROM_WIN32(hr);
goto CleanUp;
}
// tell the SelfUpdateCheck client that we have the SELFUPDATE_IN_PROGRESS event, so it can
// release its handle to it.
SetEvent(hDownloadEventSync);
// release our handle to the SELFUPDATE_EVENT_SYNC event.
CloseHandle(hDownloadEventSync);
hDownloadEventSync = NULL;
// Get Self-Update Server URL
pszSelfUpdateCabUrl = (LPTSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, INTERNET_MAX_URL_LENGTH * sizeof(TCHAR));
CleanUpFailedAllocSetHrMsg(pszSelfUpdateCabUrl);
EnterCriticalSection(&g_csUrlAgent);
if (FAILED(hr = g_pIUUrlAgent->PopulateData()))
{
LOG_Error(_T("failed to populate data in g_pIUUrlAgent (%lx)"), hr);
}
LeaveCriticalSection(&g_csUrlAgent);
CleanUpIfFailedAndMsg(g_pIUUrlAgent->GetSelfUpdateServer(pszSelfUpdateCabUrl, INTERNET_MAX_URL_LENGTH));
// Download the SelfUpdate CAB
GetIndustryUpdateDirectory(szIUDir);
hr=PathCchCombine(szLocalPath,ARRAYSIZE(szLocalPath),szIUDir, ENGINECAB);
if (FAILED(hr))
{
LOG_ErrorMsg(hr);
goto CleanUp;
}
hr = IUDownloadFile(pszSelfUpdateCabUrl, szLocalPath, TRUE, TRUE);
if (FAILED(hr))
{
LOG_ErrorMsg(hr);
goto CleanUp;
}
DeleteFile(szLocalPath); // clean up the CAB in the IU Folder
// Now we 'should' have IUENGINE.DLL from the SelfUpdate CAB in the IU Folder.
// IUENGINE.DLL is self-signed so we don't need a Catalog File (IUENGINE.CAT)
// NTRAID#NTBUG9-435844-2001/07/16-waltw WU: IU: IUCTL: Remove code to register CAT file when updating iuengine.dll
// Copy the DLL to the new Engine DLL Name
GetSystemDirectory(szSystemDir, ARRAYSIZE(szSystemDir));
hr=PathCchCombine(szTargetDLLName,ARRAYSIZE(szTargetDLLName), szSystemDir, ENGINENEWDLL);
if (FAILED(hr))
{
LOG_ErrorMsg(hr);
goto CleanUp;
}
hr=PathCchCombine(szLocalPath,ARRAYSIZE(szLocalPath),szIUDir, ENGINEDLL);
if (FAILED(hr))
{
LOG_ErrorMsg(hr);
goto CleanUp;
}
CopyFile(szLocalPath, szTargetDLLName, FALSE);
DeleteFile(szLocalPath); // clean up the DLL in the IU Folder now that its been copied to the systemdir.
// Now We've successfully downloaded the new IUEngine - we need to call an entry point in this Engine to
// Chain any SelfUpdate steps the Engine needs to do. It is possible the engine may need to download an
// additional component, or do some registry configuration work. So we'll load the new Engine and call the
// CompleteSelfUpdateProcess entrypoint.
//
// We don't need LoadLibraryFromSystemDir here since we have the full path and
// iuengine isn't a Side-By-Side module.
hNewEngine = LoadLibrary(szTargetDLLName);
if (NULL == hNewEngine)
{
dwRet = GetLastError();
LOG_ErrorMsg(dwRet);
hr = HRESULT_FROM_WIN32(dwRet);
goto CleanUp;
}
fpnCompleteSelfUpdateProcess = (PFN_CompleteSelfUpdateProcess) GetProcAddress(hNewEngine, "CompleteSelfUpdateProcess");
if (NULL == fpnCompleteSelfUpdateProcess)
{
LOG_ErrorMsg(ERROR_INVALID_DLL);
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DLL);
goto CleanUp;
}
// Call the New Engine to let it finish its SelfUpdate Process
hr = fpnCompleteSelfUpdateProcess();
if (FAILED(hr))
{
LOG_ErrorMsg(hr);
goto CleanUp;
}
// Now update the registry information about the SelfUpdate process being Complete
hMutex = CreateMutex(NULL, FALSE, IU_MUTEX_SELFUPDATE_REGCHECK);
if (NULL == hMutex)
{
dwRet = GetLastError();
LOG_ErrorMsg(dwRet);
hr = HRESULT_FROM_WIN32(dwRet);
goto CleanUp;
}
HANDLE aHandles[2];
aHandles[0] = g_hEngineLoadQuit; // index 0
aHandles[1] = hMutex;
// Finish the Registry Settings
dwRet= MyMsgWaitForMultipleObjects(ARRAYSIZE(aHandles), aHandles, FALSE, /*30 seconds*/ 30000, QS_ALLINPUT);
if (WAIT_TIMEOUT == dwRet)
{
LOG_Internet(_T("Timed Out while waiting for IU_MUTEX_SELFUPDATE_REGCHECK Mutex"));
// NOTE: If we failed to RegCheck Mutex after 30 seconds something is probably wrong in another
// process. However, we don't want to leave the registry showing a selfupdate is still in progress
// so we'll just go ahead and update the registry.
}
if (ERROR_REQUEST_ABORTED == dwRet)
{
goto CleanUp;
}
if (WAIT_OBJECT_0 == dwRet)
{
//
// index 0 (g_hEngineLoadQuit) was signaled
//
hr = E_ABORT;
LOG_ErrorMsg(hr);
goto CleanUp;
}
dwRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY_IUCTL, 0, KEY_READ | KEY_WRITE, &hkey);
if (ERROR_SUCCESS != dwRet)
{
LOG_ErrorMsg(dwRet);
hr = HRESULT_FROM_WIN32(dwRet);
goto CleanUp;
}
dwStatus = SELFUPDATE_COMPLETE_UPDATE_BINARY_REQUIRED;
dwRet = RegSetValueEx(hkey, REGVAL_SELFUPDATESTATUS, 0, REG_DWORD, (LPBYTE)&dwStatus, sizeof(dwStatus));
if (ERROR_SUCCESS != dwRet)
{
LOG_ErrorMsg(dwRet);
hr = HRESULT_FROM_WIN32(dwRet);
}
CleanUp:
if (NULL != hNewEngine)
{
FreeLibrary(hNewEngine);
hNewEngine = NULL;
}
if (NULL != hMutex)
{
ReleaseMutex(hMutex); // doesn't matter whether we got the mutex or not, if we didn't this will just fail.
CloseHandle(hMutex);
hMutex = NULL;
}
if (NULL != hDownloadEvent)
{
// Tell any clients that are waiting for the selfupdate process to finish that we're now done.
SetEvent(hDownloadEvent);
CloseHandle(hDownloadEvent);
hDownloadEvent = NULL;
}
if (NULL != hDownloadEventSync)
{
CloseHandle(hDownloadEventSync);
hDownloadEventSync = NULL;
}
if (NULL != hkey)
{
RegCloseKey(hkey);
}
SafeHeapFree(pszSelfUpdateCabUrl);
return hr;
}
HRESULT PingEngineUpdate(
HMODULE hEngineModule,
PHANDLE phQuitEvents,
UINT nQuitEventCount,
LPCTSTR ptszLiveServerUrl,
LPCTSTR ptszCorpServerUrl,
DWORD dwError,
LPCTSTR ptszClientName
)
{
LOG_Block("PingEngineUpdate");
HRESULT hr;
BOOL fFreeEngModule = FALSE;
PFN_PingIUEngineUpdateStatus pfnPingIUEngineUpdateStatus;
if (NULL == hEngineModule)
{
// try loading iuenginenew.dll first
hEngineModule = LoadLibraryFromSystemDir(_T("iuenginenew.dll"));
if (NULL != hEngineModule)
{
LOG_Internet(_T("Loaded IUENGINENEW.DLL"));
}
else
{
LOG_Internet(_T("Loaded IUENGINE.DLL"));
hEngineModule = LoadLibraryFromSystemDir(_T("iuengine.dll"));
}
//
// If load engine succeeded, we'll need to unload it later
//
if (NULL != hEngineModule)
{
fFreeEngModule = TRUE;
}
else
{
hr = HRESULT_FROM_WIN32(GetLastError());
LOG_ErrorMsg(hr);
}
}
//
// If we got an iuengine.dll (either passed in or loaded ourselves), call PingIUEngineUpdateStatus
//
if (NULL != hEngineModule)
{
pfnPingIUEngineUpdateStatus = (PFN_PingIUEngineUpdateStatus) GetProcAddress(hEngineModule, "PingIUEngineUpdateStatus");
if (NULL != pfnPingIUEngineUpdateStatus)
{
hr = pfnPingIUEngineUpdateStatus(
phQuitEvents,
nQuitEventCount,
ptszLiveServerUrl,
ptszCorpServerUrl,
dwError,
ptszClientName
);
}
else
{
hr = HRESULT_FROM_WIN32(GetLastError());
LOG_ErrorMsg(hr);
}
}
if (TRUE == fFreeEngModule)
{
FreeLibrary(hEngineModule);
}
return hr;
}
//
// this function wraps up DownloadIUIdent() and CIUUrlAgent::PopulateData(), since we use it
// in both selfupd.cpp and loadengine.cpp.
//
HRESULT DownloadIUIdent_PopulateData()
{
LOG_Block("DownloadIUIdent_PopulateData");
HRESULT hr = S_OK;
//
// Look for any specified iuident Server Location in the Registry (Overrides Default)
//
LPTSTR pszTempUrlBuffer = (LPTSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, INTERNET_MAX_URL_LENGTH * sizeof(TCHAR));
CleanUpFailedAllocSetHrMsg(pszTempUrlBuffer);
if (FAILED(hr = g_pIUUrlAgent->GetOriginalIdentServer(pszTempUrlBuffer, INTERNET_MAX_URL_LENGTH)))
{
LOG_Error(_T("failed to get original ident server URL (%lx)"), hr);
goto CleanUp;
}
TCHAR szIUDir[MAX_PATH];
//GetIndustryUpdateDirectory(szIUDir);
//
// ensure WU directory exist and correctly ACL'ed
//
CleanUpIfFalseAndSetHrMsg(!GetWUDirectory(szIUDir, ARRAYSIZE(szIUDir), TRUE), HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND));
hr = CreateDirectoryAndSetACLs(szIUDir, TRUE);
CleanUpIfFailedAndMsg(hr);
if (FAILED(hr = DownloadIUIdent(
g_hEngineLoadQuit,
pszTempUrlBuffer,
szIUDir,
0,
(S_OK == g_pIUUrlAgent->IsIdentFromPolicy()))))
{
LOG_Error(_T("iuident download failed (%lx)"), hr);
goto CleanUp;
}
EnterCriticalSection(&g_csUrlAgent);
if (FAILED(hr = g_pIUUrlAgent->PopulateData()))
{
LOG_Error(_T("failed to populate data in g_pIUUrlAgent (%lx)"), hr);
}
LeaveCriticalSection(&g_csUrlAgent);
CleanUp:
SafeHeapFree(pszTempUrlBuffer);
return hr;
}