Implement runonce background wallpaper

This commit is contained in:
Adam Demasi 2024-09-27 17:11:46 +09:30
parent b556ed4a81
commit 3f08c003e6
No known key found for this signature in database
GPG key ID: 5D3B26B3D58C7D91
8 changed files with 254 additions and 40 deletions

View file

@ -1,24 +1,23 @@
#include <windows.h>
#include <commctrl.h>
#include "GetPidForName.h"
#include "MsgBox.h"
#include "VersionInfo.h"
#include "LoadImage.h"
#ifndef WM_DPICHANGED
#define WM_DPICHANGED 0x02E0
#endif
#define HK_RUNCMD 1
typedef DWORD (__fastcall *_ThemeWaitForServiceReady)(DWORD timeout);
typedef DWORD (__fastcall *_ThemeWatchForStart)();
static HBITMAP g_wallpaperBitmap;
static HANDLE g_cmdHandle;
static void StartThemes() {
// Only relevant if we're SYSTEM
DWORD usernameLen = 256;
LPWSTR username = (LPWSTR)LocalAlloc(LPTR, usernameLen * sizeof(WCHAR));
GetUserName(username, &usernameLen);
if (lstrcmpi(username, L"SYSTEM") != 0) {
GlobalFree(username);
return;
}
GlobalFree(username);
// 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, e.g., on first boot.
@ -49,9 +48,212 @@ static void StartThemes() {
FreeLibrary(shsvcs);
}
static BOOL RunCmd(LPPROCESS_INFORMATION processInfo) {
WCHAR cmd[MAX_PATH];
GetSystemDirectory(cmd, ARRAYSIZE(cmd));
wcscat(cmd, L"\\cmd.exe");
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 const COLORREF WallpaperColorWin10 = RGB(24, 0, 82); // #180052
static const COLORREF WallpaperColorWin8 = RGB(32, 103, 178); // #2067b2
static COLORREF g_wallpaperColor;
static LRESULT CALLBACK WallpaperWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_PAINT: {
// Set background image on the window
// 2k/XP: Desktop color
// Vista/7: OOBE wallpaper
// 8/10+: Solid color
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
if (g_wallpaperBitmap) {
HDC memDC = CreateCompatibleDC(hdc);
HBITMAP oldBitmap = SelectObject(memDC, g_wallpaperBitmap);
BITMAP bitmap;
GetObject(g_wallpaperBitmap, sizeof(BITMAP), &bitmap);
// Get the dimensions of the window
RECT rect;
GetClientRect(hwnd, &rect);
// Stretch the bitmap to fit the entire window
SetStretchBltMode(hdc, HALFTONE);
StretchBlt(hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
memDC, 0, 0, bitmap.bmWidth, bitmap.bmHeight, SRCCOPY);
SelectObject(memDC, oldBitmap);
DeleteDC(memDC);
} else {
HBRUSH brush;
if (IsOSVersionOrLater(6, 2)) {
brush = CreateSolidBrush(IsOSVersionOrLater(10, 0) ? WallpaperColorWin10 : WallpaperColorWin8);
} else {
brush = CreateSolidBrush(GetSysColor(COLOR_DESKTOP));
}
FillRect(hdc, &ps.rcPaint, brush);
DeleteObject(brush);
}
break;
}
case WM_DISPLAYCHANGE:
case WM_THEMECHANGED:
case WM_DPICHANGED: {
// Resize to fit the new screen size
DWORD width = GetSystemMetrics(SM_CXSCREEN);
DWORD height = GetSystemMetrics(SM_CYSCREEN);
SetWindowPos(hwnd, NULL, 0, 0, width, height, SWP_NOZORDER | SWP_NOMOVE);
InvalidateRect(hwnd, NULL, TRUE);
break;
}
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);;
}
const WCHAR WallpaperClassName[] = L"LegacyUpdateWallpaper";
static void CreateWallpaperWindow() {
// Init COM
CoInitialize(NULL);
// 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 = WallpaperWndProc;
wndClass.hInstance = GetModuleHandle(NULL);
wndClass.lpszClassName = WallpaperClassName;
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
if (!RegisterClass(&wndClass)) {
TRACE(L"RegisterClass failed: %d", GetLastError());
return;
}
DWORD width = GetSystemMetrics(SM_CXSCREEN);
DWORD height = GetSystemMetrics(SM_CYSCREEN);
HWND hwnd = CreateWindowEx(
WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE,
wndClass.lpszClassName, L"Legacy Update",
WS_POPUP,
0, 0, width, height,
NULL, NULL,
wndClass.hInstance,
NULL
);
if (!hwnd) {
TRACE(L"CreateWindow failed: %d", GetLastError());
return;
}
// Register hotkey
RegisterHotKey(hwnd, HK_RUNCMD, MOD_SHIFT, VK_F10);
WCHAR sysDir[MAX_PATH];
GetSystemDirectory(sysDir, ARRAYSIZE(sysDir));
if (IsOSVersionEqual(6, 1)) {
// 7: Bitmap in oobe dir
WCHAR bmpPath[MAX_PATH];
wsprintf(bmpPath, L"%ls\\oobe\\background.bmp", sysDir);
g_wallpaperBitmap = LoadImage(NULL, bmpPath, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
} else if (IsOSVersionEqual(6, 0)) {
// Vista: Resources in ooberesources.dll
WCHAR ooberesPath[MAX_PATH];
wsprintf(ooberesPath, L"%ls\\oobe\\ooberesources.dll", sysDir);
HMODULE ooberes = LoadLibrary(ooberesPath);
if (ooberes) {
// Width logic is the same used by Vista msoobe.dll
g_wallpaperBitmap = LoadPNGResource(ooberes, width < 1200 ? L"OOBE_BACKGROUND_0" : L"OOBE_BACKGROUND_LARGE_0", RT_RCDATA);
}
}
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
}
void RunOnce() {
#ifndef _DEBUG
// Only relevant if we're SYSTEM
HANDLE token;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) {
return;
}
DWORD tokenInfoLen;
GetTokenInformation(token, TokenUser, NULL, 0, &tokenInfoLen);
PTOKEN_USER tokenInfo = (PTOKEN_USER)LocalAlloc(LPTR, tokenInfoLen);
GetTokenInformation(token, TokenUser, tokenInfo, tokenInfoLen, &tokenInfoLen);
PSID systemSid;
CreateWellKnownSid(WinLocalSystemSid, NULL, NULL, &tokenInfo->User.Sid);
if (!EqualSid(tokenInfo->User.Sid, systemSid)) {
LocalFree(tokenInfo);
return;
}
LocalFree(tokenInfo);
#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);
}
// Show our wallpaper window
CreateWallpaperWindow();
// Construct path to LegacyUpdateSetup.exe
WCHAR setupPath[MAX_PATH];
GetModuleFileName(NULL, setupPath, ARRAYSIZE(setupPath));
@ -68,15 +270,12 @@ void RunOnce() {
if (!CreateProcess(setupPath, cmdLine, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &startupInfo, &processInfo)) {
#ifdef _DEBUG
// Run cmd.exe instead
if (!CreateProcess(L"C:\\Windows\\System32\\cmd.exe", NULL, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &startupInfo, &processInfo)) {
if (!RunCmd(&processInfo)) {
PostQuitMessage(0);
return;
}
#endif
#else
MsgBox(NULL, L"Continuing Legacy Update setup failed", NULL, MB_OK | MB_ICONERROR);
#ifndef _DEBUG
PostQuitMessage(0);
return;
#endif
@ -84,8 +283,22 @@ void RunOnce() {
CloseHandle(processInfo.hThread);
// Wait for it to finish
WaitForSingleObject(processInfo.hProcess, INFINITE);
// 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);
}
PostQuitMessage(0);
}

View file

@ -2,6 +2,7 @@ FILES = \
$(wildcard *.c) \
../shared/Exec.c \
../shared/GetPidForName.c \
../shared/LoadImage.c \
../shared/Registry.c \
../shared/VersionInfo.c \
../shared/Wow64.c
@ -76,6 +77,8 @@ LDFLAGS = \
-lcomctl32 \
-lshell32 \
-lversion \
-lgdi32 \
-lmsimg32 \
-lLegacyUpdate.$(TAG)
RCFLAGS = \

View file

@ -2,6 +2,7 @@ FILES = \
$(wildcard *.c) \
../include/nsis/pluginapi.c \
../Shared/HResult.c \
../Shared/LoadImage.c \
../Shared/GetPidForName.c \
../Shared/WUErrors.c
RCFILES = resource.rc
@ -93,9 +94,8 @@ obj/%.$(TAG).o: %.c
obj/%.$(TAG).o: %.cpp
$(CC) -x c++ $< $(CXXFLAGS) -c -o $@
# TODO: Change to C, WUErrors.c requires C++ for now
obj/%.$(TAG).o: ../shared/%.c
$(CC) -x c++ $< $(CXXFLAGS) -c -o $@
$(CC) -x c $< $(CFLAGS) -c -o $@
obj/%.$(TAG).o: ../shared/%.cpp
$(CC) -x c++ $< $(CXXFLAGS) -c -o $@

View file

@ -51,6 +51,7 @@
; RunOnce
!define REGPATH_SETUP "System\Setup"
!define REGPATH_RUNONCE "Software\Microsoft\Windows\CurrentVersion\RunOnce"
!define REGPATH_POLICIES_SYSTEM "Software\Microsoft\Windows\CurrentVersion\Policies\System"
; Windows Update keys
!define REGPATH_WU "Software\Microsoft\Windows\CurrentVersion\WindowsUpdate"

View file

@ -49,11 +49,11 @@ Function CleanUpRunOnceFinal
; Enable logon animation again if needed
${If} ${AtLeastWin8}
ClearErrors
ReadRegDword $0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" "EnableFirstLogonAnimation_LegacyUpdateTemp"
ReadRegDword $0 HKLM "${REGPATH_POLICIES_SYSTEM}" "EnableFirstLogonAnimation_LegacyUpdateTemp"
${If} ${Errors}
DeleteRegValue HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" "EnableFirstLogonAnimation"
DeleteRegValue HKLM "${REGPATH_POLICIES_SYSTEM}" "EnableFirstLogonAnimation"
${Else}
WriteRegDword HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" "EnableFirstLogonAnimation" $0
WriteRegDword HKLM "${REGPATH_POLICIES_SYSTEM}" "EnableFirstLogonAnimation" $0
${EndIf}
${EndIf}
FunctionEnd
@ -95,11 +95,11 @@ FunctionEnd
; Temporarily disable logon animation if needed
${If} ${AtLeastWin8}
ClearErrors
ReadRegDword $0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" "EnableFirstLogonAnimation"
ReadRegDword $0 HKLM "${REGPATH_POLICIES_SYSTEM}" "EnableFirstLogonAnimation"
${IfNot} ${Errors}
WriteRegDword HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" "EnableFirstLogonAnimation_LegacyUpdateTemp" $0
WriteRegDword HKLM "${REGPATH_POLICIES_SYSTEM}" "EnableFirstLogonAnimation_LegacyUpdateTemp" $0
${EndIf}
WriteRegDword HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" "EnableFirstLogonAnimation" 0
WriteRegDword HKLM "${REGPATH_POLICIES_SYSTEM}" "EnableFirstLogonAnimation" 0
${EndIf}
; Reboot now
@ -123,10 +123,6 @@ Function OnRunOnceLogon
; To be safe in case we crash, immediately restore setup keys. We'll set them again if needed.
Call CleanUpRunOnce
!if ${SIGN} != 1
ExecShell "" "cmd.exe" "" SW_SHOWMINIMIZED
!endif
; If we're in the middle of installing a service pack, let it keep doing its thing. We'll register
; for setup again, and try again on next boot.
ClearErrors
@ -135,12 +131,6 @@ Function OnRunOnceLogon
SetRebootFlag true
Call RebootIfRequired
${EndIf}
; Find and hide the FirstUxWnd window, if it exists (Windows 7+)
FindWindow $0 "FirstUxWndClass"
${If} $0 != 0
ShowWindow $0 ${SW_HIDE}
${EndIf}
FunctionEnd
Function OnRunOnceDone

View file

@ -16,27 +16,32 @@ static IStream *GetResourceStream(HINSTANCE hInstance, LPWSTR name, LPWSTR type)
IStream *stream;
HRSRC resource = FindResource(hInstance, name, type);
if (!resource) {
TRACE(L"FindResource failed: %d", GetLastError());
return NULL;
}
DWORD resourceSize = SizeofResource(hInstance, resource);
HGLOBAL imageHandle = LoadResource(hInstance, resource);
if (!imageHandle) {
TRACE(L"LoadResource failed: %d", GetLastError());
return NULL;
}
LPVOID sourceResourceData = LockResource(imageHandle);
if (!sourceResourceData) {
if (!LockResource) {
TRACE(L"FindResource failed: %d", GetLastError());
return NULL;
}
HGLOBAL resourceDataHandle = GlobalAlloc(GMEM_MOVEABLE, resourceSize);
if (!resourceDataHandle) {
TRACE(L"GlobalAlloc failed: %d", GetLastError());
return NULL;
}
LPVOID resourceData = GlobalLock(resourceDataHandle);
if (!resourceData) {
TRACE(L"GlobalLock failed: %d", GetLastError());
GlobalFree(resourceDataHandle);
return NULL;
}
@ -58,7 +63,7 @@ static IWICBitmapSource *GetWICBitmap(IStream *imageStream) {
UINT frameCount;
IWICBitmapFrameDecode *frame;
if (!SUCCEEDED(CoCreateInstance(&CLSID_WICPngDecoder, NULL, CLSCTX_INPROC_SERVER, &IID_IWICBitmapDecoder, (LPVOID*)&decoder))) {
if (!SUCCEEDED(CoCreateInstance(&CLSID_WICPngDecoder, NULL, CLSCTX_INPROC_SERVER, &IID_IWICBitmapDecoder, (LPVOID *)&decoder))) {
return NULL;
}
@ -126,11 +131,13 @@ HBITMAP LoadPNGResource(HINSTANCE hInstance, LPWSTR resourceName, LPWSTR resourc
IStream *imageStream = GetResourceStream(hInstance, resourceName, resourceType);
if (!imageStream) {
TRACE(L"GetResourceStream failed: %d", GetLastError());
return NULL;
}
IWICBitmapSource *bitmap = GetWICBitmap(imageStream);
if (!bitmap) {
TRACE(L"GetWICBitmap failed: %d", GetLastError());
IStream_Release(imageStream);
return NULL;
}

View file

@ -262,5 +262,5 @@ struct HResultMessage WUErrorMessages[] = {
{ WU_E_EE_INVALID_ATTRIBUTEDATA, L"An expression evaluator operation could not be completed because there was an invalid attribute." },
{ WU_E_EE_CLUSTER_ERROR, L"An expression evaluator operation could not be completed because the cluster state of the computer could not be determined." },
{ WU_E_EE_UNEXPECTED, L"There was an expression evaluator error not covered by another WU_E_EE_* error code." },
NULL
{ 0, NULL }
};