95 lines
4.1 KiB
C
95 lines
4.1 KiB
C
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
#include "menu_bridge.h"
|
|
#include "bootstrapper_hook.h"
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
static DWORD foreground_thread32(void) {
|
|
DWORD pid = 0;
|
|
DWORD thread = GetWindowThreadProcessId(GetForegroundWindow(), &pid);
|
|
HANDLE process = pid ? OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid) : NULL;
|
|
BOOL wow64 = FALSE;
|
|
BOOL known = process && IsWow64Process(process, &wow64);
|
|
if (process) CloseHandle(process);
|
|
return known && wow64 ? thread : 0;
|
|
}
|
|
|
|
// Hook DLLs must match the application's architecture. This small PE host
|
|
// supplies 32-bit menu/editor hooks without requiring 32-bit Linux GLib/Winelib.
|
|
int WINAPI WinMain(HINSTANCE instance, HINSTANCE previous, LPSTR args, int show) {
|
|
(void)instance; (void)previous; (void)show;
|
|
char *end;
|
|
DWORD parentId = strtoul(args, &end, 10);
|
|
HWND bridge = (HWND)(UINT_PTR)strtoul(end, &end, 10);
|
|
DWORD bridgeProcess = 0;
|
|
GetWindowThreadProcessId(bridge, &bridgeProcess);
|
|
if (!parentId || bridgeProcess != parentId) return 1;
|
|
HANDLE parent = OpenProcess(SYNCHRONIZE, FALSE, parentId);
|
|
if (!parent) return 1;
|
|
|
|
WCHAR path[4096] = {0};
|
|
DWORD length = GetModuleFileNameW(NULL, path, 4096);
|
|
WCHAR *name = NULL;
|
|
for (DWORD i = 0; i < length && i < 4096; ++i)
|
|
if (path[i] == L'\\' || path[i] == L'/') name = path + i + 1;
|
|
if (!length || length >= 4096 || !name ||
|
|
name - path + lstrlenW(L"wine-access-menu32.dll") >= 4096) {
|
|
CloseHandle(parent);
|
|
return 1;
|
|
}
|
|
lstrcpyW(name, L"wine-access-menu32.dll");
|
|
HMODULE module = LoadLibraryW(path);
|
|
HOOKPROC callback = module ? (HOOKPROC)GetProcAddress(module, "WineAccessMenuHook") : NULL;
|
|
HOOKPROC inputCallback = module ? (HOOKPROC)GetProcAddress(module, "WineAccessInputHook") : NULL;
|
|
HHOOK hook = NULL;
|
|
HHOOK inputHook = NULL;
|
|
DWORD hookedThread = 0;
|
|
if (!callback || !inputCallback)
|
|
fprintf(stderr, "wine-access: 32-bit menu/editor hooks unavailable (error %lu)\n", GetLastError());
|
|
if (callback && inputCallback) {
|
|
while (IsWindow(bridge)) {
|
|
// Wine's mixed-architecture global hook chain can suppress the 64-bit
|
|
// hook. Attach only to the foreground 32-bit UI thread, and release it
|
|
// when focus moves elsewhere. The 64-bit helper keeps its global hook.
|
|
DWORD thread = foreground_thread32();
|
|
if (thread != hookedThread) {
|
|
if (hook) UnhookWindowsHookEx(hook);
|
|
if (inputHook) UnhookWindowsHookEx(inputHook);
|
|
hook = thread ? SetWindowsHookExW(WH_CALLWNDPROC, callback, module, thread) : NULL;
|
|
inputHook = thread ? SetWindowsHookExW(WH_GETMESSAGE, inputCallback, module, thread) : NULL;
|
|
hookedThread = hook && inputHook ? thread : 0;
|
|
if (!hookedThread) {
|
|
if (hook) UnhookWindowsHookEx(hook);
|
|
if (inputHook) UnhookWindowsHookEx(inputHook);
|
|
hook = inputHook = NULL;
|
|
} else {
|
|
HWND foreground = GetForegroundWindow();
|
|
WCHAR className[128];
|
|
if (GetWindowThreadProcessId(foreground, NULL) == hookedThread &&
|
|
GetClassNameW(foreground, className, ARRAYSIZE(className)) &&
|
|
!lstrcmpW(className, L"Blizzard Bootstrapper")) {
|
|
// A query can have passed through while this thread's hook was
|
|
// detached. Reestablish readiness on every attachment, including
|
|
// return from another workspace/application.
|
|
RemovePropW(foreground, BOOTSTRAPPER_STATE_READY);
|
|
RemovePropW(foreground, BOOTSTRAPPER_STATE_PENDING);
|
|
PostMessageW(foreground, WM_NULL, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
DWORD result = MsgWaitForMultipleObjects(1, &parent, FALSE, 100, QS_ALLINPUT);
|
|
if (result == WAIT_OBJECT_0 || result == WAIT_FAILED) break;
|
|
MSG message;
|
|
while (PeekMessageW(&message, NULL, 0, 0, PM_REMOVE)) {
|
|
TranslateMessage(&message);
|
|
DispatchMessageW(&message);
|
|
}
|
|
}
|
|
if (hook) UnhookWindowsHookEx(hook);
|
|
if (inputHook) UnhookWindowsHookEx(inputHook);
|
|
}
|
|
if (module) FreeLibrary(module);
|
|
CloseHandle(parent);
|
|
return callback && inputCallback ? 0 : 1;
|
|
}
|