This commit is contained in:
Vichingo455
2025-03-23 15:26:15 +01:00
parent cacf40b6cd
commit ad8366918e
72 changed files with 10255 additions and 2 deletions

18
launcher/CplTasks.xml Normal file
View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<applications xmlns="http://schemas.microsoft.com/windows/cpltasks/v1" xmlns:sh="http://schemas.microsoft.com/windows/tasks/v1">
<application id="{FFBE8D44-E9CF-4DD8-9FD6-976802C94D9C}">
<sh:task id="{9943E8C8-748D-47CE-AE10-9FA4A80ED28B}" needsElevation="true">
<sh:name>@&quot;%ProgramFiles%\Legacy Update\LegacyUpdate.exe&quot;,-3</sh:name>
<sh:keywords>legacy update;legacyupdate;update;windows;microsoft;driver;security;software;</sh:keywords>
<sh:command>&quot;%ProgramFiles%\Legacy Update\LegacyUpdate.exe&quot;</sh:command>
</sh:task>
<category id="5">
<sh:task idref="{9943E8C8-748D-47CE-AE10-9FA4A80ED28B}" />
</category>
<category id="10">
<sh:task idref="{9943E8C8-748D-47CE-AE10-9FA4A80ED28B}" />
</category>
</application>
</applications>

298
launcher/InitRunOnce.c Normal file
View File

@@ -0,0 +1,298 @@
#include <windows.h>
#include <commctrl.h>
#include "MsgBox.h"
#include "VersionInfo.h"
#include "LoadImage.h"
#define HK_RUNCMD 1
typedef DWORD (__fastcall *_ThemeWaitForServiceReady)(DWORD timeout);
typedef DWORD (__fastcall *_ThemeWatchForStart)();
static const COLORREF WallpaperColorWin2k = RGB(58, 110, 165); // #3a6ea5
static const COLORREF WallpaperColorWinXP = RGB( 0, 78, 152); // #004e98
static const COLORREF WallpaperColorWin8 = RGB(32, 103, 178); // #2067b2
static const COLORREF WallpaperColorWin10 = RGB(24, 0, 82); // #180052
static const WCHAR RunOnceClassName[] = L"LegacyUpdateRunOnce";
static HANDLE g_cmdHandle;
static void StartThemes() {
// Ask UxInit.dll to ask the Themes service to start a session for this desktop. Themes doesn't automatically start a
// session for the SYSTEM desktop, so we need to ask it to. This matches what msoobe.exe does on first boot.
// Windows 7 moves this to UxInit.dll
HMODULE shsvcs = LoadLibrary(L"UxInit.dll");
if (!shsvcs) {
shsvcs = LoadLibrary(L"shsvcs.dll");
if (!shsvcs) {
return;
}
}
// Get functions by ordinals
_ThemeWaitForServiceReady $ThemeWaitForServiceReady = (_ThemeWaitForServiceReady)GetProcAddress(shsvcs, MAKEINTRESOURCEA(2));
_ThemeWatchForStart $ThemeWatchForStart = (_ThemeWatchForStart)GetProcAddress(shsvcs, MAKEINTRESOURCEA(1));
// 1. Wait up to 1000ms for Themes to start
if ($ThemeWaitForServiceReady) {
$ThemeWaitForServiceReady(1000);
}
// 2. Prompt Themes to start a session for the SYSTEM desktop
if ($ThemeWatchForStart) {
$ThemeWatchForStart();
}
FreeLibrary(shsvcs);
}
static BOOL RunCmd(LPPROCESS_INFORMATION processInfo) {
WCHAR cmd[MAX_PATH];
ExpandEnvironmentStrings(L"%SystemRoot%\\System32\\cmd.exe", cmd, ARRAYSIZE(cmd));
STARTUPINFO startupInfo = {0};
startupInfo.cb = sizeof(startupInfo);
if (!CreateProcess(NULL, cmd, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &startupInfo, processInfo)) {
MsgBox(NULL, L"Launching cmd.exe failed", NULL, MB_OK | MB_ICONERROR);
return FALSE;
}
CloseHandle(processInfo->hThread);
g_cmdHandle = processInfo->hProcess;
return TRUE;
}
static LRESULT CALLBACK RunOnceWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_NCHITTEST:
// Don't accept any mouse input
return HTNOWHERE;
case WM_HOTKEY: {
// Shift-F10 to run cmd
if (wParam == HK_RUNCMD) {
DWORD exitCode;
if (!g_cmdHandle || (GetExitCodeProcess(g_cmdHandle, &exitCode) && exitCode != STILL_ACTIVE)) {
PROCESS_INFORMATION processInfo;
RunCmd(&processInfo);
}
}
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, message, wParam, lParam);;
}
static void CreateRunOnceWindow() {
// Init common controls
INITCOMMONCONTROLSEX initComctl = {0};
initComctl.dwSize = sizeof(initComctl);
initComctl.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&initComctl);
// Create window
WNDCLASS wndClass = {0};
wndClass.style = CS_VREDRAW | CS_HREDRAW | CS_OWNDC | CS_NOCLOSE;
wndClass.lpfnWndProc = RunOnceWndProc;
wndClass.hInstance = GetModuleHandle(NULL);
wndClass.lpszClassName = RunOnceClassName;
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
if (!RegisterClass(&wndClass)) {
TRACE(L"RegisterClass failed: %d", GetLastError());
return;
}
HWND hwnd = CreateWindowEx(
WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE,
wndClass.lpszClassName,
L"Legacy Update",
WS_POPUP,
0, 0, 0, 0,
NULL, NULL,
wndClass.hInstance,
NULL
);
if (!hwnd) {
TRACE(L"CreateWindow failed: %d", GetLastError());
return;
}
// Register hotkey
RegisterHotKey(hwnd, HK_RUNCMD, MOD_SHIFT, VK_F10);
// Check if the display is 8-bit color or lower
HDC dc = GetDC(NULL);
int bpp = GetDeviceCaps(dc, BITSPIXEL);
ReleaseDC(NULL, dc);
if (bpp >= 8) {
// Set the wallpaper color
COLORREF color = GetSysColor(COLOR_DESKTOP);
if (AtLeastWin10()) {
color = WallpaperColorWin10;
} else if (AtLeastWin8()) {
color = WallpaperColorWin8;
} else if ((IsWinXP2002() || IsWinXP2003()) && color == RGB(0, 0, 0)) {
// XP uses a black wallpaper in fast user switching mode. Override to the default blue.
color = WallpaperColorWinXP;
}
SetSysColors(1, (const INT[1]){COLOR_DESKTOP}, (const COLORREF[1]){color});
DWORD width = GetSystemMetrics(SM_CXSCREEN);
DWORD height = GetSystemMetrics(SM_CYSCREEN);
HBITMAP wallpaper;
if (IsWin7()) {
// 7: Bitmap in oobe dir
WCHAR bmpPath[MAX_PATH];
ExpandEnvironmentStrings(L"%SystemRoot%\\System32\\oobe\\background.bmp", bmpPath, ARRAYSIZE(bmpPath));
wallpaper = LoadImage(NULL, bmpPath, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
} else if (IsWinVista()) {
// Vista: Resources in ooberesources.dll
WCHAR ooberesPath[MAX_PATH];
ExpandEnvironmentStrings(L"%SystemRoot%\\System32\\oobe\\ooberesources.dll", ooberesPath, ARRAYSIZE(ooberesPath));
HMODULE ooberes = LoadLibrary(ooberesPath);
if (ooberes) {
// Width logic is the same used by Vista msoobe.dll
LPWSTR resource = GetSystemMetrics(SM_CXSCREEN) < 1200 ? L"OOBE_BACKGROUND_0" : L"OOBE_BACKGROUND_LARGE_0";
wallpaper = LoadPNGResource(ooberes, resource, RT_RCDATA);
}
FreeLibrary(ooberes);
}
if (wallpaper) {
// Write to disk
WCHAR tempPath[MAX_PATH];
ExpandEnvironmentStrings(L"%ProgramData%\\Legacy Update\\background.bmp", tempPath, ARRAYSIZE(tempPath));
if (GetFileAttributes(tempPath) != INVALID_FILE_ATTRIBUTES || ScaleAndWriteToBMP(wallpaper, width, height, tempPath)) {
SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, (PVOID)tempPath, SPIF_SENDWININICHANGE);
}
DeleteObject(wallpaper);
}
}
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
}
static BOOL IsSystemUser() {
BOOL result = FALSE;
PTOKEN_USER tokenInfo;
PSID systemSid;
HANDLE token;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) {
goto end;
}
DWORD tokenInfoLen;
GetTokenInformation(token, TokenUser, NULL, 0, &tokenInfoLen);
tokenInfo = (PTOKEN_USER)LocalAlloc(LPTR, tokenInfoLen);
if (!GetTokenInformation(token, TokenUser, tokenInfo, tokenInfoLen, &tokenInfoLen)) {
goto end;
}
DWORD sidSize = SECURITY_MAX_SID_SIZE;
systemSid = LocalAlloc(LPTR, sidSize);
if (!CreateWellKnownSid(WinLocalSystemSid, NULL, systemSid, &sidSize)) {
goto end;
}
result = EqualSid(tokenInfo->User.Sid, systemSid);
end:
if (tokenInfo) {
LocalFree(tokenInfo);
}
if (systemSid) {
LocalFree(systemSid);
}
if (token) {
CloseHandle(token);
}
return result;
}
void RunOnce() {
#ifndef _DEBUG
// Only relevant if we're SYSTEM
if (!IsSystemUser()) {
PostQuitMessage(1);
return;
}
#endif
// Start Themes on this desktop
StartThemes();
// Find and hide the FirstUxWnd window, if it exists (Windows 7+)
HWND firstUxWnd = FindWindow(L"FirstUxWndClass", NULL);
if (firstUxWnd) {
ShowWindow(firstUxWnd, SW_HIDE);
}
// Set up our window
CreateRunOnceWindow();
// Construct path to LegacyUpdateSetup.exe
WCHAR setupPath[MAX_PATH];
GetModuleFileName(NULL, setupPath, ARRAYSIZE(setupPath));
wcsrchr(setupPath, L'\\')[1] = L'\0';
wcsncat(setupPath, L"LegacyUpdateSetup.exe", ARRAYSIZE(setupPath) - wcslen(setupPath) - 1);
// Execute and wait for completion
STARTUPINFO startupInfo = {0};
startupInfo.cb = sizeof(startupInfo);
PROCESS_INFORMATION processInfo = {0};
LPWSTR cmdLine = (LPWSTR)LocalAlloc(LPTR, 4096 * sizeof(WCHAR));
wsprintf(cmdLine, L"\"%ls\" /runonce", setupPath);
if (!CreateProcess(setupPath, cmdLine, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &startupInfo, &processInfo)) {
#ifdef _DEBUG
// Run cmd.exe instead
if (!RunCmd(&processInfo)) {
PostQuitMessage(0);
return;
}
#else
MsgBox(NULL, L"Continuing Legacy Update setup failed", NULL, MB_OK | MB_ICONERROR);
PostQuitMessage(0);
return;
#endif
}
CloseHandle(processInfo.hThread);
// Wait for it to finish, while running a message loop
MSG msg;
while (WaitForSingleObject(processInfo.hProcess, 100) == WAIT_TIMEOUT) {
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
CloseHandle(processInfo.hProcess);
// Don't let SYSTEM cmd keep running beyond runonce
if (g_cmdHandle) {
TerminateProcess(g_cmdHandle, 0);
CloseHandle(g_cmdHandle);
}
// Show FirstUxWnd again
if (firstUxWnd) {
ShowWindow(firstUxWnd, SW_SHOW);
}
PostQuitMessage(0);
}

205
launcher/LaunchUpdateSite.c Normal file
View File

@@ -0,0 +1,205 @@
#include "stdafx.h"
#include "main.h"
#include "resource.h"
#include <exdisp.h>
#include "Exec.h"
#include "HResult.h"
#include "MsgBox.h"
#include "RegisterServer.h"
#include "Registry.h"
#include "SelfElevate.h"
#include "User.h"
#include "VersionInfo.h"
const LPWSTR UpdateSiteURLHttp = L"http://legacyupdate.net/windowsupdate/v6/";
const LPWSTR UpdateSiteURLHttps = L"https://legacyupdate.net/windowsupdate/v6/";
const LPWSTR UpdateSiteFirstRunFlag = L"?firstrun=true";
DEFINE_GUID(IID_ILegacyUpdateCtrl, 0xC33085BB, 0xC3E1, 0x4D27, 0xA2, 0x14, 0xAF, 0x01, 0x95, 0x3D, 0xF5, 0xE5);
DEFINE_GUID(CLSID_LegacyUpdateCtrl, 0xAD28E0DF, 0x5F5A, 0x40B5, 0x94, 0x32, 0x85, 0xEF, 0xD9, 0x7D, 0x1F, 0x9F);
static const LPWSTR GetUpdateSiteURL() {
// Fallback: Use SSL only on Vista and up
BOOL useHTTPS = AtLeastWinVista();
// Get the Windows Update website URL set by Legacy Update setup
LPWSTR data;
DWORD size;
HRESULT hr = GetRegistryString(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate", L"URL", KEY_WOW64_64KEY, &data, &size);
if (SUCCEEDED(hr)) {
// Return based on the URL value
if (wcscmp(data, UpdateSiteURLHttps) == 0) {
useHTTPS = TRUE;
} else if (wcscmp(data, UpdateSiteURLHttp) == 0) {
useHTTPS = FALSE;
}
LocalFree(data);
}
return useHTTPS ? UpdateSiteURLHttps : UpdateSiteURLHttp;
}
void LaunchUpdateSite(int argc, LPWSTR *argv, int nCmdShow) {
HRESULT hr = S_OK;
IWebBrowser2 *browser;
VARIANT url;
VARIANT flags;
VARIANT nullVariant;
LPTSTR siteURL;
HMONITOR monitor;
// If running on 2k/XP, make sure we're elevated. If not, show Run As prompt.
if (!AtLeastWinVista() && !IsUserAdmin()) {
LPWSTR args = (LPWSTR)LocalAlloc(LPTR, 512 * sizeof(WCHAR));
wsprintf(args, L"/launch %ls", argc > 0 ? argv[0] : L"");
hr = SelfElevate(args, NULL);
// Access denied happens when the user clicks No/Cancel.
if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED)) {
hr = S_OK;
}
goto end;
}
// Can we instantiate our own ActiveX control? If not, try to register it.
hr = CoCreateInstance(&CLSID_LegacyUpdateCtrl, NULL, CLSCTX_LOCAL_SERVER, &IID_ILegacyUpdateCtrl, (void **)&browser);
if (hr == REGDB_E_CLASSNOTREG) {
hr = RegisterServer(0, TRUE, TRUE);
if (!SUCCEEDED(hr)) {
goto end;
}
hr = CoCreateInstance(&CLSID_LegacyUpdateCtrl, NULL, CLSCTX_LOCAL_SERVER, &IID_ILegacyUpdateCtrl, (void **)&browser);
if (!SUCCEEDED(hr)) {
goto end;
}
IUnknown_Release(browser);
} else if (!SUCCEEDED(hr)) {
goto end;
}
// Spawn an IE window via the COM interface. This ensures the page opens in IE (ShellExecute uses
// default browser), and avoids hardcoding a path to iexplore.exe. Also conveniently allows testing
// on Windows 11 (iexplore.exe redirects to Edge, but COM still works). Same strategy as used by
// Wupdmgr.exe and Muweb.dll,LaunchMUSite.
hr = CoCreateInstance(&CLSID_InternetExplorer, NULL, CLSCTX_LOCAL_SERVER, &IID_IWebBrowser2, (void **)&browser);
if (hr == REGDB_E_CLASSNOTREG) {
// Handle case where the user has uninstalled Internet Explorer using Programs and Features.
OSVERSIONINFOEX *versionInfo = GetVersionInfo();
// Windows 8+: Directly prompt to reinstall IE using Fondue.exe.
if (AtLeastWin8()) {
SYSTEM_INFO systemInfo;
GetSystemInfo(&systemInfo);
LPCTSTR archSuffix = systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ? L"amd64" : L"x86";
WCHAR fondue[MAX_PATH];
ExpandEnvironmentStrings(L"%SystemRoot%\\System32\\fondue.exe", fondue, ARRAYSIZE(fondue));
WCHAR fondueArgs[256];
wsprintf(fondueArgs, L"/enable-feature:Internet-Explorer-Optional-%ls", archSuffix);
hr = Exec(NULL, fondue, fondueArgs, NULL, SW_SHOWDEFAULT, FALSE, NULL);
if (SUCCEEDED(hr)) {
goto end;
}
}
// Tell the user what they need to do, then open the Optional Features dialog.
WCHAR message[4096];
LoadString(GetModuleHandle(NULL), IDS_IENOTINSTALLED, message, ARRAYSIZE(message));
MsgBox(NULL, message, NULL, MB_OK | MB_ICONEXCLAMATION);
WCHAR optionalFeatures[MAX_PATH];
ExpandEnvironmentStrings(L"%SystemRoot%\\System32\\OptionalFeatures.exe", optionalFeatures, ARRAYSIZE(optionalFeatures));
Exec(NULL, optionalFeatures, NULL, NULL, SW_SHOWDEFAULT, FALSE, NULL);
hr = S_OK;
goto end;
} else if (!SUCCEEDED(hr)) {
goto end;
}
// Can we connect with https? WinInet will throw an error if not.
siteURL = GetUpdateSiteURL();
// Is this a first run launch? Append first run flag if so.
if (argc > 0 && lstrcmpi(argv[0], L"/firstrun") == 0) {
WCHAR newSiteURL[256];
wsprintf(newSiteURL, L"%s%s", siteURL, UpdateSiteFirstRunFlag);
siteURL = newSiteURL;
}
VariantInit(&url);
url.vt = VT_BSTR;
url.bstrVal = SysAllocString(siteURL);
VariantInit(&flags);
flags.vt = VT_I4;
flags.lVal = 0;
VariantInit(&nullVariant);
hr = IWebBrowser2_Navigate2(browser, &url, &flags, &nullVariant, &nullVariant, &nullVariant);
if (!SUCCEEDED(hr)) {
goto end;
}
HWND ieHwnd;
hr = IWebBrowser2_get_HWND(browser, (SHANDLE_PTR *)&ieHwnd);
if (!SUCCEEDED(hr)) {
goto end;
}
// Are we on a small display? If so, resize and maximise the window.
monitor = MonitorFromWindow(ieHwnd, MONITOR_DEFAULTTONEAREST);
MONITORINFO monitorInfo;
monitorInfo.cbSize = sizeof(MONITORINFO);
if (GetMonitorInfo(monitor, &monitorInfo) > 0) {
LONG workWidth = monitorInfo.rcWork.right - monitorInfo.rcWork.left;
LONG workHeight = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top;
LONG width, height;
IWebBrowser2_get_Width(browser, &width);
IWebBrowser2_get_Height(browser, &height);
if (width < 800) {
width = workWidth < 800 ? workWidth : 800;
IWebBrowser2_put_Width(browser, width);
}
if (height < 600) {
height = workHeight < 600 ? workHeight : 600;
IWebBrowser2_put_Height(browser, height);
}
LONG left, top;
IWebBrowser2_get_Left(browser, &left);
IWebBrowser2_get_Top(browser, &top);
if (left + width > workWidth) {
IWebBrowser2_put_Left(browser, 0);
}
if (top + height > workHeight) {
IWebBrowser2_put_Top(browser, 0);
}
if (workWidth <= 1152) {
ShowWindow(ieHwnd, SW_MAXIMIZE);
}
}
IWebBrowser2_put_Visible(browser, TRUE);
// Focus the window, since it seems to not always get focus as it should.
SetForegroundWindow(ieHwnd);
end:
if (!SUCCEEDED(hr)) {
MsgBox(NULL, GetMessageForHresult(hr), NULL, MB_ICONEXCLAMATION);
}
browser = NULL;
CoUninitialize();
PostQuitMessage(0);
}

11
launcher/Log.c Normal file
View File

@@ -0,0 +1,11 @@
#include "WULog.h"
#include "MsgBox.h"
void LaunchLog(int nCmdShow) {
HRESULT hr = ViewWindowsUpdateLog(nCmdShow);
if (!SUCCEEDED(hr)) {
MsgBox(NULL, GetMessageForHresult(hr), NULL, MB_OK);
}
PostQuitMessage(hr);
}

8
launcher/Makefile Normal file
View File

@@ -0,0 +1,8 @@
all:
+$(MAKE) -f Makefile.actual ARCH=32
+$(MAKE) -f Makefile.actual ARCH=64
clean:
rm -rf obj
.PHONY: all clean

126
launcher/Makefile.actual Normal file
View File

@@ -0,0 +1,126 @@
FILES = \
$(wildcard *.c) \
../shared/Exec.c \
../shared/HResult.c \
../shared/LegacyUpdate.c \
../shared/LoadImage.c \
../shared/Registry.c \
../shared/VersionInfo.c \
../shared/Wow64.c
RCFILES = resource.rc
DEFFILES = ../LegacyUpdate/LegacyUpdate.def
ARCH ?= 32
ifeq ($(ARCH),64)
TAG = x86_64
else
TAG = i686
endif
PREFIX = $(TAG)-w64-mingw32-
BIN = obj/LegacyUpdate$(ARCH).exe
OBJ = $(foreach file,$(FILES),obj/$(notdir $(basename $(file)).$(TAG).o))
RES = $(foreach file,$(RCFILES),obj/$(notdir $(basename $(file)).$(TAG).res))
DEFDLL = $(foreach file,$(DEFFILES),obj/lib$(notdir $(basename $(file)).$(TAG).a))
CC = $(PREFIX)g++
RC = $(PREFIX)windres
DLLTOOL = $(PREFIX)dlltool
override DEBUG := $(or $(DEBUG),1)
CFLAGS = \
-mwindows \
-municode \
-DUNICODE \
-D_UNICODE \
$(if $(filter 1,$(DEBUG)),-D_DEBUG -g,-DNDEBUG -Os) \
-s \
-fPIE \
-ffunction-sections \
-fdata-sections \
-fno-unwind-tables \
-fno-asynchronous-unwind-tables \
-fno-exceptions \
-flto \
-Wno-write-strings \
-I../include \
-I../shared \
-include stdafx.h
CXXFLAGS = \
$(CFLAGS) \
-std=c++11 \
-fno-rtti
LDFLAGS = \
-nodefaultlibs \
-nostartfiles \
-nostdlib \
-Wl,--gc-sections \
-Wl,--no-seh \
-Wl,--nxcompat \
-Wl,--enable-auto-image-base \
-Wl,--enable-stdcall-fixup \
-Wl,--strip-all \
-Lobj \
-lmsvcrt \
-lgcc \
-lpsapi \
-lkernel32 \
-luser32 \
-lole32 \
-loleaut32 \
-ladvapi32 \
-lcomctl32 \
-lshell32 \
-lversion \
-lgdi32 \
-lmsimg32
RCFLAGS = \
-O coff \
-I../shared
ifeq ($(ARCH),64)
LDFLAGS += -Wl,-ewWinMain
RCFLAGS += -F pe-x86-64
else
CFLAGS += -march=i486
LDFLAGS += -Wl,-e_wWinMain
RCFLAGS += -F pe-i386
endif
all: before-all $(DEFDLL) $(BIN)
ifeq ($(SIGN),1)
../build/sign.sh $(BIN)
endif
before-all:
mkdir -p obj
$(BIN): $(OBJ) $(RES)
$(CC) $^ $(CFLAGS) $(LDFLAGS) -o $@
obj/%.$(TAG).o: %.c
$(CC) -x c $< $(CFLAGS) -c -o $@
obj/%.$(TAG).o: ../shared/%.c
$(CC) -x c $< $(CFLAGS) -c -o $@
obj/%.$(TAG).res: %.rc
$(RC) $< $(RCFLAGS) -o $@
obj/lib%.$(TAG).a: ../LegacyUpdate/%.def
$(DLLTOOL) -d $< -l $@ -D $(notdir $(basename $<).dll)
clean:
rm -rf obj
test:
+$(MAKE) DEBUG=$(DEBUG)
./obj/LegacyUpdate.exe
.PHONY: all before-all clean test

82
launcher/MsgBox.c Normal file
View File

@@ -0,0 +1,82 @@
#include "stdafx.h"
#include "main.h"
#include "resource.h"
#undef _WIN32_WINNT
#define _WIN32_WINNT _WIN32_WINNT_VISTA
#include <commctrl.h>
typedef HRESULT (WINAPI *_TaskDialogIndirect)(const TASKDIALOGCONFIG *pTaskConfig, int *pnButton, int *pnRadioButton, BOOL *pfVerificationFlagChecked);
static BOOL _loadedTaskDialog = FALSE;
static _TaskDialogIndirect $TaskDialogIndirect;
int MsgBox(HWND hwnd, LPCTSTR instruction, LPCTSTR body, UINT type) {
if (!_loadedTaskDialog) {
_loadedTaskDialog = TRUE;
$TaskDialogIndirect = (_TaskDialogIndirect)GetProcAddress(LoadLibrary(L"comctl32.dll"), "TaskDialogIndirect");
}
// Play the sound matching the icon, because MB_USERICON doesn't play a sound
MessageBeep(type & 0x000000F0);
type = (type & ~0x000000F0) | MB_USERICON;
if (!$TaskDialogIndirect) {
LPWSTR finalBody = (LPWSTR)instruction;
if (body && lstrlen(body) > 0) {
size_t length = lstrlen(instruction) + lstrlen(body) + 3;
finalBody = (LPWSTR)LocalAlloc(LPTR, length * sizeof(TCHAR));
wsprintf(finalBody, L"%s\n\n%s", instruction, body);
}
MSGBOXPARAMS params = { 0 };
params.cbSize = sizeof(MSGBOXPARAMS);
params.hwndOwner = hwnd;
params.hInstance = GetModuleHandle(NULL);
params.lpszText = finalBody;
params.lpszCaption = L"Legacy Update";
params.dwStyle = type;
params.lpszIcon = MAKEINTRESOURCE(IDI_APPICON);
int result = MessageBoxIndirect(&params);
if (finalBody != body) {
LocalFree(finalBody);
}
return result;
}
TASKDIALOG_COMMON_BUTTON_FLAGS buttons;
DWORD flags = TDF_POSITION_RELATIVE_TO_WINDOW;
switch (type & 0x0000000F) {
case MB_OK:
buttons = TDCBF_OK_BUTTON;
flags |= TDF_ALLOW_DIALOG_CANCELLATION;
break;
case MB_OKCANCEL:
buttons = TDCBF_OK_BUTTON | TDCBF_CANCEL_BUTTON;
flags |= TDF_ALLOW_DIALOG_CANCELLATION;
break;
case MB_YESNO:
buttons = TDCBF_YES_BUTTON | TDCBF_NO_BUTTON;
break;
default:
break;
}
TASKDIALOGCONFIG config = { 0 };
config.cbSize = sizeof(TASKDIALOGCONFIG);
config.hwndParent = hwnd;
config.hInstance = GetModuleHandle(NULL);
config.dwFlags = flags;
config.dwCommonButtons = buttons;
config.pszWindowTitle = L"Legacy Update";
config.pszMainInstruction = instruction;
config.pszContent = body;
config.pszMainIcon = MAKEINTRESOURCE(IDI_APPICON);
int button;
$TaskDialogIndirect(&config, &button, NULL, NULL);
return button;
}

5
launcher/MsgBox.h Normal file
View File

@@ -0,0 +1,5 @@
#pragma once
#include <windows.h>
int MsgBox(HWND hwnd, LPCTSTR instruction, LPCTSTR body, UINT type);

40
launcher/Options.c Normal file
View File

@@ -0,0 +1,40 @@
#include <windows.h>
#include "Exec.h"
#include "VersionInfo.h"
#include "Wow64.h"
void LaunchOptions(int nCmdShow) {
#if !_WIN64
// Some issues arise from the working directory being SysWOW64 rather than System32. Notably,
// Windows Vista - 8.1 don't have wuauclt.exe in SysWOW64. Disable WOW64 redirection temporarily
// to work around this.
PVOID oldValue;
BOOL isRedirected = DisableWow64FsRedirection(&oldValue);
#endif
HRESULT hr;
if (AtLeastWin10()) {
// Windows 10+: Open Settings app
hr = Exec(NULL, L"ms-settings:windowsupdate-options", NULL, NULL, SW_SHOWDEFAULT, FALSE, NULL);
} else if (AtLeastWinVista()) {
// Windows Vista, 7, 8: Open Windows Update control panel
WCHAR wuauclt[MAX_PATH];
ExpandEnvironmentStrings(L"%SystemRoot%\\System32\\wuauclt.exe", wuauclt, ARRAYSIZE(wuauclt));
hr = Exec(NULL, wuauclt, L"/ShowOptions", NULL, SW_SHOWDEFAULT, FALSE, NULL);
} else {
// Windows 2000, XP: Open Automatic Updates control panel
WCHAR wuaucpl[MAX_PATH];
ExpandEnvironmentStrings(L"%SystemRoot%\\System32\\wuaucpl.cpl", wuaucpl, ARRAYSIZE(wuaucpl));
hr = Exec(NULL, wuaucpl, NULL, NULL, SW_SHOWDEFAULT, FALSE, NULL);
}
#if !_WIN64
// Revert WOW64 redirection if we changed it
if (isRedirected) {
RevertWow64FsRedirection(oldValue);
}
#endif
PostQuitMessage(hr);
}

153
launcher/RegisterServer.c Normal file
View File

@@ -0,0 +1,153 @@
#include <windows.h>
#include "Exec.h"
#include "HResult.h"
#include "LegacyUpdate.h"
#include "MsgBox.h"
#include "Registry.h"
#include "SelfElevate.h"
#include "User.h"
#include "VersionInfo.h"
#include "Wow64.h"
static HRESULT RegisterDllInternal(LPWSTR path, BOOL state) {
HMODULE module = LoadLibrary(path);
if (!module) {
return HRESULT_FROM_WIN32(GetLastError());
}
HRESULT hr = S_OK;
FARPROC proc = GetProcAddress(module, state ? "DllRegisterServer" : "DllUnregisterServer");
if (!proc) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto end;
}
hr = ((HRESULT (WINAPI *)())proc)();
end:
if (module) {
FreeLibrary(module);
}
return hr;
}
static HRESULT RegisterDllExternal(LPWSTR path, BOOL state) {
WCHAR regsvr32[MAX_PATH];
ExpandEnvironmentStrings(L"%SystemRoot%\\System32\\regsvr32.exe", regsvr32, ARRAYSIZE(regsvr32));
LPWSTR args = (LPWSTR)LocalAlloc(LPTR, (lstrlen(path) + 6) * sizeof(WCHAR));
wsprintf(args, L"/s %ls\"%ls\"", state ? L"" : L"/u ", path);
DWORD status;
HRESULT hr = Exec(NULL, regsvr32, args, NULL, SW_HIDE, TRUE, &status);
if (!SUCCEEDED(hr)) {
hr = HRESULT_FROM_WIN32(GetLastError());
return hr;
}
if (status != 0) {
// Run again without /s, so the user can see the error
wsprintf(args, L"%ls\"%ls\"", state ? L"" : L"/u ", path);
hr = Exec(NULL, regsvr32, args, NULL, SW_SHOWDEFAULT, TRUE, &status);
}
return status == 0 ? S_OK : E_FAIL;
}
HRESULT RegisterServer(HWND hwnd, BOOL state, BOOL forLaunch) {
// Ensure elevation
HRESULT hr = S_OK;
LPWSTR installPath;
LPWSTR dllPath;
if (!IsUserAdmin()) {
LPWSTR args = (LPWSTR)LocalAlloc(LPTR, 512 * sizeof(WCHAR));
wsprintf(args, L"%ls %i", state ? L"/regserver" : L"/unregserver", hwnd);
DWORD code;
hr = SelfElevate(args, &code);
if (!SUCCEEDED(hr)) {
// Ignore error on cancelling UAC dialog
if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED)) {
hr = S_OK;
}
goto end;
}
hr = HRESULT_FROM_WIN32(code);
goto end;
}
hr = GetInstallPath(&installPath);
if (!SUCCEEDED(hr)) {
goto end;
}
dllPath = (LPWSTR)LocalAlloc(LPTR, MAX_PATH * sizeof(WCHAR));
wsprintf(dllPath, L"%ls\\LegacyUpdate.dll", installPath);
#ifdef _DEBUG
// Warn if registration path differs, to help with debugging
LPWSTR currentPath;
hr = GetRegistryString(HKEY_CLASSES_ROOT, L"CLSID\\{AD28E0DF-5F5A-40B5-9432-85EFD97D1F9F}\\InprocServer32", NULL, KEY_WOW64_64KEY, &currentPath, NULL);
if (SUCCEEDED(hr) && wcscmp(currentPath, dllPath) != 0) {
if (MsgBox(hwnd, L"DEBUG: Native dll currently registered at a different path. Override?", currentPath, MB_YESNO) != IDYES) {
hr = S_OK;
goto end;
}
}
#endif
hr = RegisterDllInternal(dllPath, state);
if (!SUCCEEDED(hr)) {
// Try external registration
if (SUCCEEDED(RegisterDllExternal(dllPath, state))) {
hr = S_OK;
} else {
goto end;
}
}
#if _WIN64
if (!SUCCEEDED(hr)) {
goto end;
}
wsprintf(dllPath, L"%ls\\LegacyUpdate32.dll", installPath);
#ifdef _DEBUG
// Warn if registration path differs, to help with debugging
hr = GetRegistryString(HKEY_CLASSES_ROOT, L"CLSID\\{AD28E0DF-5F5A-40B5-9432-85EFD97D1F9F}\\InprocServer32", NULL, KEY_WOW64_32KEY, &currentPath, NULL);
if (SUCCEEDED(hr) && wcscmp(currentPath, dllPath) != 0) {
if (MsgBox(hwnd, L"DEBUG: 32-bit dll currently registered at a different path. Override?", currentPath, MB_YESNO) != IDYES) {
hr = S_OK;
goto end;
}
}
#endif
hr = RegisterDllExternal(dllPath, state);
#endif
end:
if (installPath) {
LocalFree(installPath);
}
if (dllPath) {
LocalFree(dllPath);
}
if (!SUCCEEDED(hr)) {
LPWSTR title = state
? L"Failed to register Legacy Update ActiveX control"
: L"Failed to unregister Legacy Update ActiveX control";
MsgBox(hwnd, title, GetMessageForHresult(hr), MB_ICONERROR);
}
if (!forLaunch) {
PostQuitMessage(hr);
}
return hr;
}

View File

@@ -0,0 +1,5 @@
#pragma once
#include <windows.h>
HRESULT RegisterServer(HWND hwnd, BOOL state, BOOL forLaunch);

12
launcher/SelfElevate.c Normal file
View File

@@ -0,0 +1,12 @@
#include "SelfElevate.h"
#include <windows.h>
#include "Exec.h"
#include "VersionInfo.h"
HRESULT SelfElevate(LPWSTR args, LPDWORD code) {
LPWSTR fileName;
GetOwnFileName(&fileName);
HRESULT hr = Exec(L"runas", fileName, args, NULL, SW_SHOWDEFAULT, TRUE, code);
LocalFree(fileName);
return hr;
}

5
launcher/SelfElevate.h Normal file
View File

@@ -0,0 +1,5 @@
#pragma once
#include <windows.h>
HRESULT SelfElevate(LPWSTR args, LPDWORD code);

138
launcher/main.c Normal file
View File

@@ -0,0 +1,138 @@
#include "stdafx.h"
#include "main.h"
#include "resource.h"
#include <windows.h>
#include <commctrl.h>
#include "MsgBox.h"
#include "RegisterServer.h"
#include "Startup.h"
HINSTANCE g_hInstance;
extern void LaunchUpdateSite(int argc, LPWSTR *argv, int nCmdShow);
extern void LaunchOptions(int nCmdShow);
extern void LaunchLog(int nCmdShow);
extern void RunOnce();
typedef enum Action {
ActionLaunch,
ActionOptions,
ActionLog,
ActionRunOnce,
ActionRegServer,
ActionUnregServer
} Action;
static const LPWSTR actions[] = {
L"/launch",
L"/options",
L"/log",
L"/runonce",
L"/regserver",
L"/unregserver",
NULL
};
EXTERN_C __declspec(dllexport)
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) {
g_hInstance = hInstance;
Startup();
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (!SUCCEEDED(hr)) {
ExitProcess(hr);
return hr;
}
int argc;
LPWSTR *argv = CommandLineToArgvW(GetCommandLineW(), &argc);
LPWSTR actionFlag = L"/launch";
if (argc > 1) {
actionFlag = argv[1];
}
// All remaining args past the action
LPWSTR *flags = {0};
int flagsCount = 0;
if (argc > 2) {
flags = &argv[2];
flagsCount = argc - 2;
}
Action action = -1;
for (int i = 0; actions[i] != NULL; i++) {
if (wcscmp(actionFlag, actions[i]) == 0) {
action = i;
break;
}
}
switch (action) {
case ActionLaunch:
LaunchUpdateSite(flagsCount, flags, nCmdShow);
break;
case ActionOptions:
LaunchOptions(nCmdShow);
break;
case ActionLog:
LaunchLog(nCmdShow);
break;
case ActionRunOnce:
RunOnce();
break;
case ActionRegServer:
case ActionUnregServer: {
BOOL state = action == ActionRegServer;
HWND hwnd = flagsCount > 0 ? (HWND)(intptr_t)wcstol(flags[0], NULL, 10) : 0;
RegisterServer(hwnd, state, FALSE);
break;
}
default: {
const LPWSTR usage = L""
L"LegacyUpdate.exe [/launch|/regserver|/unregserver]\n"
L"\n"
L"/launch\n"
L" Launch Legacy Update website in Internet Explorer\n"
L"\n"
L"/options\n"
L" Open the Windows Update Options control panel\n"
L"\n"
L"/log\n"
L" Open the Windows Update log file\n"
L"\n"
L"/regserver\n"
L" Register ActiveX control\n"
L"\n"
L"/unregserver\n"
L" Unregister ActiveX control\n"
L"\n"
L"If no parameters are provided, /launch is assumed.";
MsgBox(NULL, L"LegacyUpdate.exe usage", usage, MB_OK);
PostQuitMessage(1);
break;
}
}
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) == 1) {
TranslateMessage(&msg);
DispatchMessage(&msg);
switch (msg.message) {
case WM_QUIT:
case WM_DESTROY:
break;
}
}
CoUninitialize();
ExitProcess(msg.wParam);
return msg.wParam;
}

1
launcher/main.h Normal file
View File

@@ -0,0 +1 @@
EXTERN_C HINSTANCE g_hInstance;

30
launcher/manifest.xml Normal file
View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<dependency>
<dependentAssembly>
<assemblyIdentity type="Win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
</dependentAssembly>
</dependency>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> <!-- Windows Vista -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> <!-- Windows 7 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> <!-- Windows 8 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> <!-- Windows 8.1 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> <!-- Windows 10 -->
</application>
</compatibility>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</windowsSettings>
</application>
</assembly>

11
launcher/resource.h Normal file
View File

@@ -0,0 +1,11 @@
#define IDR_CPLTASKS 202
#define IDI_APPICON 100
#define IDS_LEGACYUPDATEOCX 1
#define IDS_LEGACYUPDATE 2
#define IDS_CHECKFORUPDATES 3
#define IDS_CHECKFORUPDATES_ALT 4
#define IDS_IENOTINSTALLED 5
#define ID_MANIFEST 1

95
launcher/resource.rc Normal file
View File

@@ -0,0 +1,95 @@
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
#include <windows.h>
#include "Version.h"
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(65001)
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_PATCH,VERSION_BUILD
PRODUCTVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_PATCH,VERSION_BUILD
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "Hashbang Productions"
VALUE "FileDescription", "Legacy Update"
VALUE "FileVersion", VERSION_STRING
VALUE "InternalName", "LegacyUpdate.exe"
VALUE "LegalCopyright", "© Hashbang Productions. All rights reserved."
VALUE "OriginalFilename", "LegacyUpdate.exe"
VALUE "ProductName", "Legacy Update"
VALUE "ProductVersion", VERSION_STRING
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_APPICON ICON "../LegacyUpdate/icon.ico"
/////////////////////////////////////////////////////////////////////////////
//
// XML
//
IDR_CPLTASKS XML "CplTasks.xml"
/////////////////////////////////////////////////////////////////////////////
//
// String Table
//
STRINGTABLE
BEGIN
IDS_LEGACYUPDATEOCX "Legacy Update Control"
IDS_LEGACYUPDATE "Legacy Update"
IDS_CHECKFORUPDATES "Check for updates"
IDS_CHECKFORUPDATES_ALT "Check for software and driver updates via Legacy Update."
IDS_IENOTINSTALLED "Internet Explorer is not installed. Use Optional Features in Control Panel to install it before using Legacy Update."
END
/////////////////////////////////////////////////////////////////////////////
//
// Manifest
//
ID_MANIFEST RT_MANIFEST "manifest.xml"
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////

23
launcher/stdafx.h Normal file
View File

@@ -0,0 +1,23 @@
#pragma once
#ifndef STRICT
#define STRICT
#endif
#define WINVER _WIN32_WINNT_WIN2K
#define _WIN32_WINNT _WIN32_WINNT_WIN2K
// Use msvcrt stdio functions
#define __USE_MINGW_ANSI_STDIO 0
// Enable comctl 6.0 (visual styles)
#define ISOLATION_AWARE_ENABLED 1
// Enable COM C interfaces
#define CINTERFACE
#define COBJMACROS
#define INITGUID
#include "resource.h"
#include <windows.h>
#include "Trace.h"