mirror of
https://github.com/LegacyUpdate/LegacyUpdate.git
synced 2025-01-22 14:12:07 -05:00
Implement runonce background wallpaper
This commit is contained in:
parent
b556ed4a81
commit
3f08c003e6
8 changed files with 254 additions and 40 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 = \
|
||||
|
|
|
@ -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 $@
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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 }
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue