PROWAREtech

articles » current » windows » application-programming-interface » graphical-setup-program

Windows API: Graphical Setup Program (Setup.exe)

A Windows graphical setup program with example executables; written in C/C++.

This setup utility allows one to install an executable and then run executables that will setup the computer, like .NET setup files.

This code reads the install.txt file to know the executable that needs to be installed in the Program Files folder and for the shortcut link installed in the Start Menu. Next, the system reads the execute.txt file to know the files that need to be executed (like .NET setup files) to finish setting up the computer.

This program is written using the Win32 API. Feel free to modify the code to meet your needs.

To download the example executables and source code files, click here: WINAPISETUP.zip.

See this article for an example of this same code running in a console application.

Excuse the spaghetti code.

#include "Setup.h"

#define MAX_LOADSTRING 6 // NOTE: MAKE GREATER TO ACCOMMODATE LONGER STRINGS

#define ALLOC(siz) LocalAlloc(LMEM_FIXED, siz)
#define FREE(mem) LocalFree(mem)

char *Strcpy(char* dest, const char* src)
{
	while (*src)
		*dest++ = *src++;
	*dest = '\0';
	return dest;
}
char* Strstr(register const char* string, const char* substring)
{
	register const char* a, * b;

	b = substring;
	if (*b == 0) {
		return (char*)string;
	}
	for (; *string != 0; string += 1) {
		if (*string != *b)
			continue;
		a = string;
		while (true) {
			if (*b == 0)
				return (char*)string;
			if (*a++ != *b++)
				break;
		}
		b = substring;
	}
	return nullptr;
}

int Strlen(const char* sz)
{
	int i = 0;
	while (sz[i])i++;
	return i;
}

HINSTANCE hInst;
HWND hWnd;
WCHAR szTitle[MAX_LOADSTRING];
WCHAR szWindowClass[MAX_LOADSTRING];
char* szCmdLine, * szInstallProgress;
bool canClose = false;

ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
HRESULT CreateShortcut(char* szPathToObject, char* szPathToShortcut, char* szDescription);
int FailMessage();
DWORD WINAPI PrintDots(void* pv);
void PrintLastError();
BOOL ExecuteProcess(char* szCmdLine);
void WriteToWindow(const char* pszFirst, const char* pszSecond);
HRESULT CreateShortcut(char* szPathToObject, char* szPathToShortcut, char* szDescription);
BOOL Reboot();

DWORD WINAPI setup_thread(void*)
{
	// BEGIN SETUP PROGRAM CODE

	HANDLE hFile;
	char* szInstallTxt;

	char* szProgramFilesFolder = (char*)ALLOC(MAX_PATH);
	SHGetFolderPathA(NULL, CSIDL_PROGRAM_FILES, NULL, SHGFP_TYPE_CURRENT, szProgramFilesFolder);

	char* szProgramsMenuFolder = (char*)ALLOC(MAX_PATH);
	SHGetFolderPathA(NULL, CSIDL_COMMON_PROGRAMS, NULL, SHGFP_TYPE_CURRENT, szProgramsMenuFolder);

	DWORD i;
	for (i = Strlen(szCmdLine); i > 0 && szCmdLine[i] != '\\'; i--);
	if (0 == i)
	{
		WriteToWindow("Error in path\r\n", NULL);
		return FailMessage();
	}
	char ch = szCmdLine[++i];
	szCmdLine[i] = 0;
	WriteToWindow("Changing folder: ", szCmdLine);
	if (!SetCurrentDirectoryA(szCmdLine))
	{
		PrintLastError();
		return FailMessage();
	}
	szCmdLine[i] = ch;
	WriteToWindow("\r\n", NULL);

	WriteToWindow("Opening \"install.txt\"\r\n", NULL);
	hFile = CreateFileA("install.txt", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
	if (hFile == INVALID_HANDLE_VALUE)
	{
		PrintLastError();
		return FailMessage();
	}
	else
	{
		szInstallTxt = (char*)ALLOC(i = GetFileSize(hFile, NULL) + 1);
		WriteToWindow("Reading \"install.txt\"\r\n", NULL);
		if (!ReadFile(hFile, szInstallTxt, i, &i, NULL))
		{
			PrintLastError();
			return FailMessage();
		}
		CloseHandle(hFile);
		szInstallTxt[i] = 0;
		WriteToWindow("Checking \"", szInstallTxt);
		WriteToWindow("\"\r\n", NULL);
		hFile = CreateFileA(szInstallTxt, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
		if (hFile == INVALID_HANDLE_VALUE)
		{
			PrintLastError();
			return FailMessage();
		}
		CloseHandle(hFile);
	}

	char* sz;
	for (sz = szProgramFilesFolder; *sz; sz++);
	*sz++ = '\\';
	for (i = Strlen(szInstallTxt); i && szInstallTxt[i] != '.'; i--);
	if (i == 0)
	{
		WriteToWindow("Invalid file name \"", szInstallTxt);
		WriteToWindow("\"\r\n", NULL);
		return FailMessage();
	}
	szInstallTxt[i] = 0;
	Strcpy(sz, szInstallTxt);
	char* szName = (char*)ALLOC(Strlen(szInstallTxt));
	Strcpy(szName, szInstallTxt);
	szInstallTxt[i] = '.';
	WIN32_FIND_DATAA fd;
	HANDLE hFind = FindFirstFileA(szProgramFilesFolder, &fd);
	if (hFind != INVALID_HANDLE_VALUE)
	{
		FindClose(hFind);
		if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
		{
			if (!CreateDirectoryA(szProgramFilesFolder, NULL))
			{
				PrintLastError();
				return FailMessage();
			}
		}
	}
	else
	{
		if (!CreateDirectoryA(szProgramFilesFolder, NULL))
		{
			PrintLastError();
			return FailMessage();
		}
	}
	for (sz = szProgramFilesFolder; *sz; sz++);
	*sz++ = '\\';
	Strcpy(sz, szInstallTxt);
	if (!CopyFileA(szInstallTxt, szProgramFilesFolder, FALSE))
	{
		PrintLastError();
		return FailMessage();
	}
	sz = &szProgramsMenuFolder[Strlen(szProgramsMenuFolder)];
	*sz++ = '\\';
	Strcpy(sz, szName);
	sz = &sz[Strlen(sz)];
	Strcpy(sz, ".lnk");
	WriteToWindow("Creating \"", szProgramsMenuFolder);
	WriteToWindow("\"\r\n", NULL);
	if (!SUCCEEDED(CreateShortcut(szProgramFilesFolder, szProgramsMenuFolder, szName)))
		WriteToWindow("Error creating shortcut\r\n", NULL);

	WriteToWindow("Opening \"execute.txt\"\r\n", NULL);
	hFile = CreateFileA("execute.txt", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
	if (hFile != INVALID_HANDLE_VALUE)
	{
		char* szExecTxt = (char*)ALLOC(i = GetFileSize(hFile, NULL) + 2);
		WriteToWindow("Reading \"execute.txt\"\r\n", NULL);
		if (!ReadFile(hFile, szExecTxt, i, &i, NULL))
		{
			PrintLastError();
			return FailMessage();
		}
		CloseHandle(hFile);
		if (i > 0)
		{
			*((short*)&szExecTxt[i]) = 0;
			for (i = 0; szExecTxt[i]; i++)
			{
				if (szExecTxt[i] == '\r' && szExecTxt[i + 1] == '\n')
				{
					szExecTxt[i] = 0;
					Strcpy(&szExecTxt[i + 1], &szExecTxt[i + 2]);
				}
			}
			while (*szExecTxt == 0) szExecTxt++;
			while (*szExecTxt)
			{
				if (!ExecuteProcess(szExecTxt))
					return FailMessage();
				while (*szExecTxt) szExecTxt++;
				szExecTxt++;
			}
		}
	}
	canClose = true;
	if (MessageBoxA(hWnd, "The program installed successfully. Restart your computer now to finish the installation.", "Success", MB_OK | MB_ICONINFORMATION | MB_OKCANCEL) == IDOK)
	{
		if (!Reboot())
			MessageBoxA(hWnd, "Please reboot your computer now.", "Reboot Required", MB_OK);
	}
}

int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{

	if (!lpCmdLine[0]) // THEN THERE IS NO COMMAND LINE ARGUMENTS
	{
		szCmdLine = GetCommandLineA();
		szCmdLine++;
		*Strstr(szCmdLine, "\"") = '\0';
		ShellExecuteA(NULL, "runas", szCmdLine, "/admin", NULL, SW_SHOWNORMAL); // this will run the program as administrator to allow installing the app
		return 0;
	}

#ifdef _DEBUG
	Sleep(10000); // wait ten seconds for debugger to attach
#endif

	szCmdLine = GetCommandLineA();
	szCmdLine++;
	*Strstr(szCmdLine, "\"") = '\0';

	szInstallProgress = (char*)ALLOC(1);
	szInstallProgress[0] = '\0';

	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);


	// Initialize global strings
	LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadStringW(hInstance, IDC_SETUP, szWindowClass, MAX_LOADSTRING);
	MyRegisterClass(hInstance);

	// Perform application initialization:
	if (!InitInstance (hInstance, nCmdShow))
		return FALSE;

	// This sets the appropriate flags to prevent the system going into sleep mode
	SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_AWAYMODE_REQUIRED);

	MSG msg;

	// Main message loop:
	while (GetMessage(&msg, nullptr, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	// This allows the system to sleep normally
	SetThreadExecutionState(ES_CONTINUOUS);

	return (int) msg.wParam;
}


ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEXW wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.style = CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc = WndProc;
	wcex.cbClsExtra = 0;
	wcex.cbWndExtra = 0;
	wcex.hInstance = hInstance;
	wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_SETUP));
	wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
	wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_SETUP);
	wcex.lpszClassName = szWindowClass;
	wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SETUP));

	return RegisterClassExW(&wcex);
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
	hInst = hInstance;

	RECT rect;
	GetClientRect(GetDesktopWindow(), &rect);

	hWnd = CreateWindowW(szWindowClass, szTitle, WS_CAPTION | WS_SYSMENU, rect.right / 2 - 300, rect.bottom / 2 - 200, 600, 400, nullptr, nullptr, hInst, nullptr);

	if (!hWnd)
	  return FALSE;

	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);

	CreateThread(NULL, 0, setup_thread, NULL, 0, NULL);

	return TRUE;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_COMMAND:
		break;
	case WM_PAINT:
		{
			PAINTSTRUCT ps;
			HDC hdc = BeginPaint(hWnd, &ps);
			RECT rect, rectC;
			char* sz = szInstallProgress;
			GetClientRect(hWnd, &rect);
			rect.top += 5;
			rect.right -= 5;
			rect.bottom -= 5;
			rect.left += 5;
			while(sz)
			{
				CopyRect(&rectC, &rect);
				DrawTextA(hdc, sz, -1, &rectC, DT_CALCRECT);
				if (rectC.bottom <= rect.bottom)
					break;
				if (sz = Strstr(sz, "\r\n"))
					sz += 2;
			}
			if(sz)
				DrawTextA(hdc, sz, -1, &rect, DT_NOCLIP);
			EndPaint(hWnd, &ps);
		}
		break;
	case WM_CLOSE:
		if(canClose)
			DestroyWindow(hWnd);
		return 0;
	case WM_DESTROY:
		if(canClose)
			PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

HRESULT CreateShortcut(char* szPathToObject, char* szPathToShortcut, char* szDescription)
{
	HRESULT hr;
	IShellLinkA* psl;

	CoInitialize(NULL);
	hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkA, (void**)&psl);
	if (SUCCEEDED(hr))
	{
		IPersistFile* ppf;

		psl->SetPath(szPathToObject);
		psl->SetDescription(szDescription);

		hr = psl->QueryInterface(IID_IPersistFile, (void**)&ppf);
		if (SUCCEEDED(hr))
		{
			WCHAR* wsz = (WCHAR*)ALLOC(MAX_PATH * sizeof(WCHAR));
			int i;

			if (i = MultiByteToWideChar(CP_ACP, 0, szPathToShortcut, -1, wsz, MAX_PATH))
				hr = ppf->Save(wsz, TRUE);
			else
				hr = -1;
			FREE(wsz);
			ppf->Release();
		}
		psl->Release();
	}
	CoUninitialize();
	return hr;
}

void WriteToWindow(const char* pszFirst, const char* pszSecond)
{
	char* sz = (char*)ALLOC(Strlen(szInstallProgress) + Strlen(pszFirst) + Strlen(pszSecond ? pszSecond : "") + 1);
	Strcpy(Strcpy(Strcpy(sz, szInstallProgress), pszFirst), pszSecond ? pszSecond : "");
	FREE(szInstallProgress);
	szInstallProgress = sz;
	InvalidateRect(hWnd, NULL, true);
	UpdateWindow(hWnd);
}

BOOL ExecuteProcess(char* szCmdLine)
{
	STARTUPINFOA* si = (STARTUPINFOA*)ALLOC(sizeof(STARTUPINFOA));
	PROCESS_INFORMATION* pi = (PROCESS_INFORMATION*)ALLOC(sizeof(PROCESS_INFORMATION));

	ZeroMemory(si, sizeof(STARTUPINFOA));
	si->cb = sizeof(STARTUPINFOA);
	ZeroMemory(pi, sizeof(PROCESS_INFORMATION));
	WriteToWindow("Executing \"", szCmdLine);
	if (!CreateProcessA(NULL, szCmdLine, NULL, NULL, FALSE, 0, NULL, NULL, si, pi))
	{
		PrintLastError();
		FREE(si);
		FREE(pi);
		return FALSE;
	}
	HANDLE hThread = CreateThread(NULL, 0, PrintDots, NULL, 0, NULL);
	WaitForSingleObject(pi->hProcess, INFINITE);
	TerminateThread(hThread, 0);
	WriteToWindow("\r\n", NULL);
	CloseHandle(pi->hProcess);
	CloseHandle(pi->hThread);
	FREE(pi);
	FREE(si);
	return TRUE;
}

void PrintLastError()
{
	LPVOID lpMsgBuf;
	FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&lpMsgBuf, 0, NULL);
	WriteToWindow((char*)lpMsgBuf, "\r\n");
	FREE(lpMsgBuf);
}

DWORD WINAPI PrintDots(void* pv)
{
	while (TRUE)
	{
		WriteToWindow(".", NULL);
		Sleep(1000);
	}
	return 0xffffffff;
}

int FailMessage()
{
	canClose = true;
	MessageBoxA(hWnd, "The program failed to install successfully.", "Failed", MB_OK | MB_ICONSTOP);
	return 0;
}

BOOL Reboot()
{
	HANDLE hToken;
	TOKEN_PRIVILEGES tkp;

	if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
	{
		PrintLastError();
		FailMessage();
		return FALSE;
	}

	LookupPrivilegeValueA(NULL, "SeShutdownPrivilege", &tkp.Privileges[0].Luid); // "SeShutdownPrivilege" == SE_SHUTDOWN_NAME

	tkp.PrivilegeCount = 1;
	tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

	if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0))
	{
		PrintLastError();
		FailMessage();
		return FALSE;
	}

	if (!InitiateSystemShutdownA(NULL, NULL, 0, TRUE, TRUE))
	{
		PrintLastError();
		FailMessage();
		return FALSE;
	}

	tkp.Privileges[0].Attributes = 0;

	if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0))
	{
		PrintLastError();
		FailMessage();
	}
	return TRUE;
}

This site uses cookies. Cookies are simple text files stored on the user's computer. They are used for adding features and security to this site. Read the privacy policy.
CLOSE