1264 lines
38 KiB
C++
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;
|
||
|
}
|