|
|
#include "inspch.h"
#include "insobj.h"
//=--------------------------------------------------------------------------=
// Function name here
//=--------------------------------------------------------------------------=
// Function description
//
// Parameters:
//
// Returns:
//
// Notes:
//
CInstaller::CInstaller(CInstallEngine *p) : CTimeTracker(60000) { _pInsEng = p; _hkProg = NULL; _hMutex = NULL; _hStatus = NULL; _cRef = 1;
DllAddRef(); }
CInstaller::~CInstaller() { DllRelease(); }
//=--------------------------------------------------------------------------=
// Function name here
//=--------------------------------------------------------------------------=
// Function description
//
// Parameters:
//
// Returns:
//
// Notes:
//
STDMETHODIMP_(ULONG) CInstaller::AddRef() { return(++_cRef); }
//=--------------------------------------------------------------------------=
// Function name here
//=--------------------------------------------------------------------------=
// Function description
//
// Parameters:
//
// Returns:
//
// Notes:
//
STDMETHODIMP_(ULONG) CInstaller::Release() { if(!--_cRef) { delete this; return(0); } return( _cRef ); }
HRESULT CInstaller::Abort() { if(_hMutex) { WaitForMutex(_hMutex); if(_hkProg) { DWORD dwCancel = 1; RegSetValueEx(_hkProg, CANCEL_VALUENAME, 0, REG_DWORD, (BYTE *) &dwCancel, sizeof(dwCancel)); } ReleaseMutex(_hMutex); } return NOERROR; }
HRESULT CInstaller::Suspend() { // assume no install going on
HRESULT hr = S_OK; // if we have a mutex grab it and check for safe
if(_hMutex) { // there is an install, assume it isn't safe to cancel it
hr = S_FALSE; WaitForMutex(_hMutex); if(_hkProg) { DWORD dwSafe = 0; DWORD dwSize = sizeof(dwSafe); if(RegQueryValueEx(_hkProg, SAFE_VALUENAME, NULL, NULL, (BYTE *) &dwSafe, &dwSize) == ERROR_SUCCESS) { if(dwSafe == 1) hr = S_OK; } } } return hr; }
HRESULT CInstaller::Resume() { // if we have a mutex release it
if(_hMutex) ReleaseMutex(_hMutex);
return NOERROR; }
HRESULT CInstaller::DoInstall(LPCSTR pszDir, LPSTR pszCmd, LPSTR pszArgs, LPCSTR pszProg, LPCSTR pszCancel, UINT uType, LPDWORD pdwStatus, IMyDownloadCallback *pcb) { char szFileName[MAX_PATH + 128]; char szBuf[MAX_PATH]; HANDLE hProc = NULL; HMODULE hAdvpack; HRESULT hr = S_OK; DWORD dwTemp; INF_ARGUEMENTS InsArgs; _uTotalProgress = 0;
// create progress key if we need to
if(pszProg && pszCancel) { lstrcpy(szBuf, REGSTR_PROGRESS_KEY); lstrcat(szBuf, pszProg); RegCreateKeyEx(HKEY_LOCAL_MACHINE, szBuf, 0, NULL,0, KEY_READ | KEY_WRITE,NULL, &_hkProg, &dwTemp); lstrcpy(szBuf, pszCancel); lstrcat(szBuf, "Mutex"); _hMutex = CreateMutex(NULL, FALSE, szBuf); lstrcpy(szBuf, pszCancel); lstrcat(szBuf, "Event"); _hStatus = CreateEvent(NULL, FALSE, FALSE, szBuf);
}
// copy the advpack we are using into the temp dir of what we want to launch
// this insures that we always get a "good" copy of advpack
hAdvpack = GetModuleHandle("advpack.dll"); if(hAdvpack) { if(GetModuleFileName(hAdvpack, szBuf, sizeof(szBuf))) { lstrcpy(szFileName, pszDir); AddPath(szFileName, "advpack.dll");
CopyFile(szBuf, szFileName, TRUE);
szBuf[0] = 0; szFileName[0] = 0; } }
switch(uType) { case InfCommand: case InfExCommand: GetStringField(pszCmd, 0, szBuf, sizeof(szBuf)); // ParseURLA below is to ensure we only run inf out of our temp dir
lstrcpy(szFileName, pszDir); AddPath(szFileName, ParseURLA(szBuf));
GetStringField(pszCmd, 1, szBuf, sizeof(szBuf)); lstrcpy(InsArgs.szInfname, szFileName); lstrcpy(InsArgs.szSection, szBuf); lstrcpy(InsArgs.szDir, pszDir); InsArgs.dwType = uType; if(uType == InfCommand) { InsArgs.dwFlags = RSC_FLAG_INF | RSC_FLAG_NGCONV; if(_pInsEng->GetCommandMode() == 0) InsArgs.dwFlags |= RSC_FLAG_QUIET; } if(uType == InfExCommand) { // add cab name
lstrcpy(InsArgs.szCab, ""); InsArgs.dwFlags = AtoL(pszArgs); }
if(_pInsEng->GetStatus() & STOPINSTALL_REBOOTNEEDED) InsArgs.dwFlags |= RSC_FLAG_DELAYREGISTEROCX; wsprintf(szLogBuf, "Launching Inf - inf: %s, section: %s\r\n", szFileName, lstrlen(szBuf) ? szBuf : "Default"); _pInsEng->WriteToLog(szLogBuf, TRUE);
hProc = CreateThread(NULL, 0, LaunchInfCommand, &InsArgs, 0, &dwTemp); hr = E_FAIL; if(hProc) { _WaitAndPumpProgress(hProc, pcb); DWORD dwRet; if(GetExitCodeThread(hProc, &dwRet)) { hr = dwRet; if(SUCCEEDED(hr)) { if(hr == ERROR_SUCCESS_REBOOT_REQUIRED) *pdwStatus |= STOPINSTALL_REBOOTNEEDED; } else { wsprintf(szLogBuf, "Inf failed - return code %x\r\n", dwRet); _pInsEng->WriteToLog(szLogBuf, TRUE); } } else _pInsEng->WriteToLog("Failed to get exit code\r\n", TRUE); } else _pInsEng->WriteToLog("Create thread failed\r\n", TRUE); break;
case WExtractExe: case Win32Exe: case HRESULTWin32Exe: // ParseURLA below is to ensure we only run exe out of our temp dir
wsprintf(szFileName, CMDLINE, pszDir, ParseURLA(pszCmd), pszArgs);
wsprintf(szLogBuf, "Launching exe: command: %s\r\n", szFileName); _pInsEng->WriteToLog(szLogBuf, TRUE);
hr = LaunchProcess(szFileName, &hProc, pszDir, SW_SHOWNORMAL); if(SUCCEEDED(hr)) _WaitAndPumpProgress(hProc, pcb); if(SUCCEEDED(hr)) { // BUGBUG: Trace this path with WEXTRACT > 1140
if ( (uType == WExtractExe) || (uType == HRESULTWin32Exe) ) { DWORD dwRet; hr = E_FAIL; if(GetExitCodeProcess(hProc, &dwRet)) { wsprintf(szLogBuf, "Exe return code: %x\r\n", dwRet); _pInsEng->WriteToLog(szLogBuf, TRUE); hr = (HRESULT) dwRet; if (uType == WExtractExe) { if(SUCCEEDED(hr)) { if(hr == ERROR_SUCCESS_REBOOT_REQUIRED) *pdwStatus |= STOPINSTALL_REBOOTNEEDED; } } } else _pInsEng->WriteToLog("Failed to get exit code\r\n", TRUE); } } else _pInsEng->WriteToLog("Create process failed\r\n", TRUE); break;
default: // whatever
hr = E_INVALIDARG; }
if (_hkProg) { RegCloseKey(_hkProg); _hkProg = NULL; }
if (_hMutex) { CloseHandle(_hMutex); _hMutex = NULL; }
if (_hStatus) { CloseHandle(_hStatus); _hStatus = NULL; }
return hr; }
#define PROGRESS_INTERVAL 1000
#define SEARCHFORCONFLICT_INTERVAL 3
void CInstaller::_WaitAndPumpProgress(HANDLE hProc, IMyDownloadCallback *pcb) { HANDLE pHandles[2] = {hProc, _hStatus}; DWORD dwStartTick = GetTickCount(); DWORD dwOldTick = dwStartTick; DWORD dwTickChange = 0; DWORD dwProgress; DWORD dwCurrentTick; BOOL fQuit = FALSE; DWORD dwRet; DWORD dwSearchCount = 0; HWND hLastVersionConflict = NULL; HWND hVersionConflict = NULL;
while(!fQuit) { dwRet = MsgWaitForMultipleObjects(_hStatus ? 2 : 1, pHandles, FALSE, 1000, QS_ALLINPUT); dwCurrentTick = GetTickCount(); dwTickChange = dwCurrentTick - dwOldTick; if(dwOldTick > dwCurrentTick) dwTickChange = ~dwTickChange;
if(dwTickChange > PROGRESS_INTERVAL) { dwSearchCount++; if(dwSearchCount > SEARCHFORCONFLICT_INTERVAL || hLastVersionConflict) { hVersionConflict = GetVersionConflictHWND(); if(hVersionConflict) { if(hVersionConflict != hLastVersionConflict) { if(GetForegroundWindow() == _pInsEng->GetHWND()) SetForegroundWindow(hVersionConflict); BOOL foo = MessageBeep(MB_ICONASTERISK); } } hLastVersionConflict = hVersionConflict; dwSearchCount = 0; }
// if there is a version conflict, we assume you made no progress during the last time period
if(!hLastVersionConflict) { dwProgress = (dwTickChange / 1000) * GetBytesPerSecond(); dwProgress >>= 10; _uTotalProgress += dwProgress;
pcb->OnProgress(_uTotalProgress, NULL); } dwOldTick = dwCurrentTick; }
// Handle Message or done
if(dwRet == WAIT_OBJECT_0) { fQuit = TRUE; } else if ( (dwRet == WAIT_OBJECT_0 + 1) && (_hStatus != 0)) // update jobexec's status msg
{ if (!(fQuit = WaitForMutex(_hMutex))) { if (_hkProg != NULL) { char szBuf[MAX_PATH]; DWORD dwSize = sizeof(szBuf);
if (RegQueryValueEx(_hkProg, PROGRESS_DISPLAY, NULL, NULL, (LPBYTE) szBuf, &dwSize) == ERROR_SUCCESS) pcb->OnProgress(_uTotalProgress, szBuf); }
ReleaseMutex(_hMutex); } } else { MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
// if it's a quit message we're out of here
if (msg.message == WM_QUIT) fQuit = TRUE; else { // otherwise dispatch it
DispatchMessage(&msg); } // end of PeekMessage while loop
} } } }
|