#include "vcbootstrap.h"
/**
* Note: All Strings are in ANSI. Unicows won't help with a bootstrap.
**/
/**
* Generic Thunk to the Scatchcode.
**/
int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE, LPSTR lpCmdLine, int nCmdShow)
{
int dwErr = 0;
#if defined (_MSC_VER) && defined (_DEBUG) && (_MSC_VER >= 1400)
/** Like every substandard programmer. I am prone to creating programs with Memory leaks. Hopefully, this
* call should help detect those memory leaks before they get released.
**/
::_CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF |
_CRTDBG_ALLOC_MEM_DF | ::_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG));
#endif /* _MSC_VER */
try {
::SetLastError(ERROR_SUCCESS);
::InitCommonControls();
std::vector<TCHAR> curDir(FILENAME_MAX);
_getcwd(&curDir.at(0), FILENAME_MAX);
{/* Change the current directory to the same as the application */
std::vector<TCHAR> appName(FILENAME_MAX);
::GetModuleFileNameA(NULL, &appName.at(0), FILENAME_MAX);
std::basic_string<TCHAR> appDir(&appName.at(0));
appDir.erase(appDir.find_last_of(_T("\\")));
_chdir(appDir.c_str());
}
std::basic_string<TCHAR> sCmdLine(lpCmdLine);
dwErr = ScratchCode(hInst, sCmdLine, nCmdShow);
_chdir(&curDir.at(0));
} catch(std::exception &ex) {
std::basic_string<TCHAR> ErrStr("An error occurred during Setup initialization. The error was: ");
ErrStr += ex.what();
::MessageBoxA(NULL, ErrStr.c_str(), _T("Application Error"), MB_OK | MB_ICONEXCLAMATION);
dwErr = 3;
}
return dwErr;
}
/// <summary>
/// My customised entry point to WinMain
/// </summary>
/// <param name="hInst">Contains the HINSTANCE of this app</param>
/// <param name="sCmdLine">The command line to echo to the user</param>
/// <param name="nCmdShow">whether to show the window minimised, maximised etc.</param>
int ScratchCode(const HINSTANCE hInst, const std::basic_string<TCHAR> &sCmdLine, const int nCmdShow)
{
bool needsMSI3 = true;
int reqdSoftware = 0;
int Result = 0;
#ifdef _DEBUG
reqdSoftware |= WINDOWS_INSTALLER_3_1_INSTALL | INTERNET_EXPLORER_INSTALL;
#endif /* Code to test the dialog box on dev machines (ie. fully patched windows). */
if(CheckIfCanLoadDLL(reqdSoftware) && reqdSoftware == 0)
{/* If this succeeded, then the CRTs are installed and we don't even need this bootstrapper. */
return 0;
}
#ifndef EXCLUDE_IE6
try {/* Internet Explorer is signified by checking for IE5.0 */
std::vector<BYTE> IeVersionNum;
read_registry_key IEReg("SOFTWARE\\Microsoft\\Internet Explorer");
IeVersionNum = IEReg.get_value("Version");
if(IeVersionNum.at(0) < '5')/* The first character should be 5 or greater. */
throw std::runtime_error("Internet Explorer is an old version");
} catch (std::exception &) {/* Internet Explorer is not installed */
reqdSoftware |= INTERNET_EXPLORER_INSTALL;
}
#endif /* EXCLUDE_IE6 */
try {/* Check if we are on the unsupported Windows NT or 95. */
OSVERSIONINFOEXA osVer = {sizeof(OSVERSIONINFO)};
::GetVersionExA(reinterpret_cast<LPOSVERSIONINFOA>(&osVer));
if(osVer.dwMajorVersion < 4)
{/* Very old Windows. Never reaches here */
throw std::runtime_error("This application requires Windows 98 or later");
}
if(osVer.dwMajorVersion == 4)
{
needsMSI3 = false;
if(osVer.dwPlatformId & VER_PLATFORM_WIN32_NT)
{/* Windows NT version 4.0. Not Supported. */
throw std::runtime_error("This application does not run on Windows NT 4.0");
}
/* Win9x */
if(osVer.dwMinorVersion < 10)
{/* Before Windows 98, ie Windows 95. */
throw std::runtime_error("This application requires Windows 98 or later");
/** BUGBUG: The VC2005 compiler produces code inherently incompatible with Windows 95.
* Therefore, this is stale code. No way round except to recompile the CRT library or
* obtain an older compiler.
**/
}
}
if(osVer.dwMajorVersion == 5 && osVer.dwMinorVersion == 0)
{/* Windows 2000 */
needsMSI3 = false;
osVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
::GetVersionExA(reinterpret_cast<LPOSVERSIONINFOA>(&osVer));
if(osVer.wServicePackMajor < 3)
{/* TODO: Service pack is done interactively, and this app needs to be run upon restart. */
if(::MessageBoxA(NULL, "Before continuing, please install Service Pack 4 for Windows 2000, "
"would you like to do that now?", "Operating System Error", MB_YESNO | MB_ICONERROR) != IDYES)
{
return ERROR_INSTALL_PACKAGE_VERSION;
}
return ExtractAndLoadExecutable(NULL, "SP4Express_EN.exe", "", SW_SHOWNORMAL);
}
}
/* Otherwise, everything looks normal. */
} catch (std::runtime_error &ex) {
::MessageBoxA(NULL, ex.what(), "Operating System Error", MB_OK | MB_ICONERROR);
return ERROR_EXE_MARKED_INVALID;
}
try {/* Windows Installer Version Check. MSI2 is required on Win98/Me/2k and MSI31 is required on XP/2k3/Vista */
AutoLibrary hMsi(::LoadLibraryA("MSI"));
HRESULT (CALLBACK *pfnDllGetVersion) (DLLVERSIONINFO *) =
reinterpret_cast<HRESULT (CALLBACK *)(DLLVERSIONINFO *)>(::GetProcAddress(hMsi, "DllGetVersion"));
if(!pfnDllGetVersion)
throw std::runtime_error("Msi.dll is corrupt or missing a required export");
DLLVERSIONINFO msiVer = {sizeof(DLLVERSIONINFO), 0};
if(FAILED(pfnDllGetVersion(&msiVer)))
{
throw std::runtime_error("Error occurred determining version of Windows Installer");
}
if(msiVer.dwMajorVersion < 3)
{
if(needsMSI3)
reqdSoftware |= WINDOWS_INSTALLER_3_1_INSTALL;
}
} catch(std::exception &) {
reqdSoftware |= WINDOWS_INSTALLER_2_0_INSTALL;
}
if(reqdSoftware != 0 && reqdSoftware != VISUAL_C80_INSTALL)
{/* Installation is required */
if(sCmdLine.find("silent") < sCmdLine.size())
{/* Silent installation requested. Just install. */
Result = InstallRequiredApps(NULL, reqdSoftware);
}
else
{/* Show the UI */
Result = static_cast<int>(::DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_DIALOG1), NULL, MsgDlgProc, reqdSoftware));
}
}
if(reqdSoftware & VISUAL_C80_INSTALL && Result != ERROR_CANCELLED)
{/* Everything is okay for deployment.. run Vcredist_x86.exe. */
const std::basic_string<char> appName = VCREDIST_NAME;
Result = ExtractAndLoadExecutable(NULL, appName, sCmdLine, nCmdShow/*IDB_RESOURCE1*/);
}
return Result;
}
BOOL CheckIfCanLoadDLL(int &reqdSoftware)
{/* Reference type is deliberate (out param) */
::SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
::SetLastError(ERROR_SUCCESS);
HMODULE hVC8DLL = ::LoadLibraryA("VC80Test.dll");
if(!hVC8DLL)
{/* If this fails, alter the reqdSoftware. Most other GetLastErrors cannot be handled by us. */
reqdSoftware |= VISUAL_C80_INSTALL;
/* BUGBUG: But what if the DLL Load failed for some other reason, like access denied? */
}
::FreeLibrary(hVC8DLL);
::SetErrorMode(0);
return TRUE;
}
DWORD ExtractAndLoadExecutable(HWND TheirhWnd, const std::basic_string<char> &exeName,
const std::basic_string<char> &sCmdLine, int nCmdShow)
{
/* The IExpress installer has plopped the executable into our directory */
std::basic_string<char> lpCmdString(exeName);
std::basic_string<char> lpCmd2 = lpCmdString;
lpCmd2.append(" ");
lpCmd2.append(sCmdLine);
std::vector<char> lpCmdLine(lpCmd2.size() + 1);
std::copy<const std::basic_string<char>::const_iterator, std::vector<char>::iterator>
(lpCmd2.begin(), lpCmd2.end(), lpCmdLine.begin());
STARTUPINFOA lpStartupInfo = {0};
lpStartupInfo.cb = sizeof(STARTUPINFOA);
lpStartupInfo.dwFlags = STARTF_USESHOWWINDOW;
lpStartupInfo.wShowWindow = static_cast<WORD>(nCmdShow);
PROCESS_INFORMATION lpProcessInformation = {0};
DWORD hr = ::CreateProcessA(lpCmdString.c_str(), &lpCmdLine.at(0), NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS,
NULL, NULL, &lpStartupInfo, &lpProcessInformation);
if(TheirhWnd != NULL)
{/* Since we're in a UI, also process any message. */
WaitWithMessageLoop(lpProcessInformation.hProcess);
}
else
{/* We're not in a UI */
::WaitForSingleObject(lpProcessInformation.hProcess, INFINITE);
}
::GetExitCodeProcess(lpProcessInformation.hProcess, &hr);
::CloseHandle(lpProcessInformation.hThread);
::CloseHandle(lpProcessInformation.hProcess);
return hr;
}
BOOL WaitWithMessageLoop(HANDLE hHandleToWaitOn, DWORD dwIterateTimeOutMilliseconds /*= INFINITE*/)
{/* Bah, must use a Waitwithmessageloop. Because we don't have threads in /ML */
DWORD dwRet=0;
MSG msg={0};
dwRet = ::WaitForSingleObject(hHandleToWaitOn, 0);
if(dwRet == WAIT_OBJECT_0)
{
// The object is already signalled.
return TRUE;
}
for(;;)
{
// There are one or more window message available. Dispatch them.
while(::PeekMessage(&msg,NULL,NULL,NULL,PM_REMOVE) > 0)
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
dwRet = ::WaitForSingleObject(hHandleToWaitOn, 0);
if(dwRet == WAIT_OBJECT_0)
{
// The object is already signalled.
return TRUE;
}
}
/** Now we've dispatched all the messages in the queue. Use MsgWaitForMultipleObjects() to either tell us
* there are more messages to dispatch, or that our object has been signalled.
**/
dwRet = ::MsgWaitForMultipleObjects(1, &hHandleToWaitOn, FALSE,
dwIterateTimeOutMilliseconds, QS_ALLEVENTS);
if(dwRet == WAIT_OBJECT_0)
{/* The event was signaled. */
return TRUE;
}
else if(dwRet == WAIT_OBJECT_0 + 1)
{/* New messages have come that need to be dispatched. */
continue;
}
else if(dwRet == WAIT_TIMEOUT)
{/* We hit our infinite time limit, Exit. */
break;
}
else
{/* Something else happened. */
return FALSE;
}
}
return FALSE;
}
int InstallRequiredApps(HWND TheirhWnd, const int reqdSoftware)
{
std::map<const std::basic_string<char>, std::basic_string<char> > exeCommand;
/* This section is very much WIP. */
#ifndef EXCLUDE_IE6
if(reqdSoftware & INTERNET_EXPLORER_INSTALL)
{
/** I wonder if the /q:a switch would be better? I personally prefer /q:u, since the /q:a switch
* makes it feel like you've got something to hide.
**/
exeCommand["ie6setup.exe"] = " /q:u /r:n";
}
#endif /* EXCLUDE_IE6 */
if(reqdSoftware & WINDOWS_INSTALLER_2_0_INSTALL)
{
exeCommand["InstMSIA.exe"] = " /q:u /r:n";
}
if(reqdSoftware & WINDOWS_INSTALLER_3_1_INSTALL)
{
exeCommand["WindowsInstaller-KB893803-v2-x86.exe"] = " /norestart";
}
DWORD TotalResult = 0;
for(std::map<const std::basic_string<char>, std::basic_string<char> >::const_iterator Iter1 = exeCommand.begin();
Iter1 != exeCommand.end(); ++Iter1)
{
TotalResult = ExtractAndLoadExecutable(TheirhWnd, Iter1->first, Iter1->second, SW_SHOWNORMAL);
if(TotalResult != 0)
{/* Panic at the first sign of error */
return TotalResult;
}
}
return TotalResult;
}
#if 0
const std::basic_string<char> GetTemporaryName()
{
char *tmpDirPtr = NULL;
std::basic_string<char> Result;
try {
tmpDirPtr = _tempnam(NULL, "VCRT");
if(tmpDirPtr == NULL) throw std::runtime_error("Error generating temp name");
Result = tmpDirPtr;
} catch (...) {/* finally */
free(tmpDirPtr);
throw;
}
free(tmpDirPtr);
return Result;
}
#endif /* STALE CODE */
INT_PTR CALLBACK MsgDlgProc(HWND TheirhWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch(Msg)
{
case WM_INITDIALOG:
{
#ifndef EXCLUDE_IE6
::PostMessageA(::GetDlgItem(TheirhWnd, IDC_CHECKIE), BM_SETCHECK, BST_CHECKED, 0);
#endif /* EXCLUDE_IE6 */
::PostMessageA(::GetDlgItem(TheirhWnd, IDC_CHECKMSI2), BM_SETCHECK, BST_CHECKED, 0);
::PostMessageA(::GetDlgItem(TheirhWnd, IDC_CHECKMSI31), BM_SETCHECK, BST_CHECKED, 0);
::PostMessageA(::GetDlgItem(TheirhWnd, IDC_CHECK2K4), BM_SETCHECK, BST_CHECKED, 0);
int lpCmdLine = static_cast<DWORD>(lParam);
#ifndef EXCLUDE_IE6
if(!(lpCmdLine & INTERNET_EXPLORER_INSTALL))
{
::PostMessageA(::GetDlgItem(TheirhWnd, IDC_CHECKIE), BM_SETCHECK, BST_UNCHECKED, 0);
::ShowWindow(::GetDlgItem(TheirhWnd, IDC_CHECKIE), SW_HIDE);
}
#endif /* EXCLUDE_IE6 */
if(!(lpCmdLine & WINDOWS_INSTALLER_2_0_INSTALL))
{
::PostMessageA(::GetDlgItem(TheirhWnd, IDC_CHECKMSI2), BM_SETCHECK, BST_UNCHECKED, 0);
::ShowWindow(::GetDlgItem(TheirhWnd, IDC_CHECKMSI2), SW_HIDE);
}
if(!(lpCmdLine & WINDOWS_SERVICE_PACK4_INSTALL))
{
::PostMessageA(::GetDlgItem(TheirhWnd, IDC_CHECK2K4), BM_SETCHECK, BST_UNCHECKED, 0);
::ShowWindow(::GetDlgItem(TheirhWnd, IDC_CHECK2K4), SW_HIDE);
}
if(!(lpCmdLine & WINDOWS_INSTALLER_3_1_INSTALL))
{
::PostMessageA(::GetDlgItem(TheirhWnd, IDC_CHECKMSI31), BM_SETCHECK, BST_UNCHECKED, 0);
::ShowWindow(::GetDlgItem(TheirhWnd, IDC_CHECKMSI31), SW_HIDE);
}
::SetLastError(ERROR_SUCCESS);
return TRUE;
}
case WM_CLOSE:
{
::EndDialog(TheirhWnd, ERROR_CANCELLED);
return TRUE;
}
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
case IDOK:
{
int reqdSoftware = 0, Result = ERROR_CANCELLED;
::EnableWindow(TheirhWnd, FALSE);
#ifndef EXCLUDE_IE6
if(::IsDlgButtonChecked(TheirhWnd, IDC_CHECKIE))
{
reqdSoftware |= INTERNET_EXPLORER_INSTALL;
}
#endif /* EXCLUDE_IE6 */
if(::IsDlgButtonChecked(TheirhWnd, IDC_CHECKMSI2))
{
reqdSoftware |= WINDOWS_INSTALLER_2_0_INSTALL;
}
if(::IsDlgButtonChecked(TheirhWnd, IDC_CHECKMSI31))
{
reqdSoftware |= WINDOWS_INSTALLER_3_1_INSTALL;
}
Result = InstallRequiredApps(TheirhWnd, reqdSoftware);
::EndDialog(TheirhWnd, Result);
return TRUE;
}
case IDCANCEL:
{
::EndDialog(TheirhWnd, ERROR_CANCELLED);
return TRUE;
}
default:
break;
}
return FALSE;
}
default:
break;
}
return FALSE;
}