Adds the PyQt6 Windows client (Homepage, Scan & results, About & updates, Settings), ClamAV scanning and quarantine, browser-download watch with an exclusive scan gate, Bolt loopback/PKCE sign-in with DPAPI key storage, Gitea release feed with checksum verification, opt-in diagnostics, and Windows/portable build tooling. Co-Authored-By: Claude Code <noreply@anthropic.com>
80 lines
2.6 KiB
C
80 lines
2.6 KiB
C
/*
|
|
* BoltClam portable Windows launcher.
|
|
*
|
|
* This is a tiny GUI-subsystem bootstrap for the portable package. It finds
|
|
* the runtime beside itself, starts pythonw.exe with the bundled app, and
|
|
* exits immediately. It contains no product logic; the Qt application lives
|
|
* in app/boltclam.
|
|
*
|
|
* Build (from the repository root):
|
|
* zig cc -target x86_64-windows-gnu -O2 -mwindows packaging/launcher.c \
|
|
* -o build/portable/BoltClam.exe
|
|
*/
|
|
#define UNICODE
|
|
#define _UNICODE
|
|
#include <windows.h>
|
|
#include <wchar.h>
|
|
|
|
static void fail(const wchar_t *message) {
|
|
MessageBoxW(NULL, message, L"BoltClam", MB_OK | MB_ICONERROR);
|
|
}
|
|
|
|
int WINAPI WinMain(HINSTANCE instance, HINSTANCE previous, LPSTR command_line, int show) {
|
|
(void)instance;
|
|
(void)previous;
|
|
(void)command_line;
|
|
(void)show;
|
|
|
|
wchar_t exe[MAX_PATH];
|
|
DWORD length = GetModuleFileNameW(NULL, exe, MAX_PATH);
|
|
if (length == 0 || length >= MAX_PATH) {
|
|
fail(L"BoltClam could not locate its own executable.");
|
|
return 1;
|
|
}
|
|
wchar_t *slash = wcsrchr(exe, L'\\');
|
|
if (slash == NULL) {
|
|
fail(L"BoltClam could not locate its runtime folder.");
|
|
return 1;
|
|
}
|
|
*slash = L'\0';
|
|
|
|
wchar_t pythonw[MAX_PATH * 2];
|
|
_snwprintf(pythonw, MAX_PATH * 2, L"%s\\pythonw.exe", exe);
|
|
if (GetFileAttributesW(pythonw) == INVALID_FILE_ATTRIBUTES) {
|
|
fail(L"BoltClam's runtime is incomplete: pythonw.exe is missing.\n\n"
|
|
L"Extract the whole portable folder, not just BoltClam.exe.");
|
|
return 1;
|
|
}
|
|
|
|
wchar_t appdir[MAX_PATH * 2];
|
|
_snwprintf(appdir, MAX_PATH * 2, L"%s\\app", exe);
|
|
if (GetFileAttributesW(appdir) == INVALID_FILE_ATTRIBUTES) {
|
|
fail(L"BoltClam's runtime is incomplete: the app folder is missing.\n\n"
|
|
L"Extract the whole portable folder, not just BoltClam.exe.");
|
|
return 1;
|
|
}
|
|
SetCurrentDirectoryW(appdir);
|
|
|
|
wchar_t cmdline[MAX_PATH * 4];
|
|
_snwprintf(cmdline, MAX_PATH * 4, L"\"%s\" -m boltclam", pythonw);
|
|
|
|
STARTUPINFOW startup;
|
|
PROCESS_INFORMATION process;
|
|
ZeroMemory(&startup, sizeof(startup));
|
|
ZeroMemory(&process, sizeof(process));
|
|
startup.cb = sizeof(startup);
|
|
if (!CreateProcessW(pythonw, cmdline, NULL, NULL, FALSE,
|
|
CREATE_UNICODE_ENVIRONMENT, NULL, appdir,
|
|
&startup, &process)) {
|
|
wchar_t error[512];
|
|
_snwprintf(error, 512,
|
|
L"BoltClam could not start its runtime (error %lu).",
|
|
(unsigned long)GetLastError());
|
|
fail(error);
|
|
return 1;
|
|
}
|
|
CloseHandle(process.hThread);
|
|
CloseHandle(process.hProcess);
|
|
return 0;
|
|
}
|