Battle.net installer now working. It crashes when the login page loads, but that seems to be fixed in upcoming version and the controls did read.

This commit is contained in:
Storm Dragon committed 2026-10-16 12:26:59 -04:00
1 parent 38358ad27a
commit e0c83859f5
13 files changed
+753 -11

No files matched your search

+118
View File
@@ -0,0 +1,118 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
#define COBJMACROS
#include "bootstrapper_hook.h"
#include "menu_bridge.h"
#include <initguid.h>
#include <uiautomationcore.h>
#include <uiautomationcoreapi.h>
#include <uiautomationclient.h>
#include <string.h>
typedef LRESULT (WINAPI *ReturnProvider)(HWND, WPARAM, LPARAM, IRawElementProviderSimple *);
static ReturnProvider originalReturn;
static HWND queryWindow, queryBridge;
static LONG querying;
static void read_toggle(IRawElementProviderSimple *root) {
IRawElementProviderFragmentRoot *fragmentRoot = NULL;
IRawElementProviderFragment *focus = NULL;
IRawElementProviderSimple *provider = NULL;
IUnknown *pattern = NULL;
IToggleProvider *toggle = NULL;
VARIANT type = {0}, name = {0};
enum ToggleState state;
if (FAILED(IRawElementProviderSimple_QueryInterface(root, &IID_IRawElementProviderFragmentRoot,
(void **)&fragmentRoot)) || !fragmentRoot) goto done;
if (FAILED(IRawElementProviderFragmentRoot_GetFocus(fragmentRoot, &focus)) || !focus) goto done;
if (FAILED(IRawElementProviderFragment_QueryInterface(focus, &IID_IRawElementProviderSimple,
(void **)&provider)) || !provider) goto done;
if (FAILED(IRawElementProviderSimple_GetPropertyValue(provider, UIA_ControlTypePropertyId, &type)) ||
type.vt != VT_I4 || type.lVal != UIA_CheckBoxControlTypeId) goto done;
if (FAILED(IRawElementProviderSimple_GetPatternProvider(provider, UIA_TogglePatternId, &pattern)) ||
!pattern || FAILED(IUnknown_QueryInterface(pattern, &IID_IToggleProvider, (void **)&toggle)) ||
!toggle || FAILED(IToggleProvider_get_ToggleState(toggle, &state)) ||
(state != ToggleState_Off && state != ToggleState_On && state != ToggleState_Indeterminate)) goto done;
if (FAILED(IRawElementProviderSimple_GetPropertyValue(provider, UIA_NamePropertyId, &name)) ||
name.vt != VT_BSTR || !name.bstrVal || GetForegroundWindow() != queryWindow) goto done;
WineAccessBootstrapperState event = {0};
event.owner = (ULONGLONG)(UINT_PTR)queryWindow;
event.tick = GetTickCount64();
event.state = state;
UINT length = SysStringLen(name.bstrVal);
if (length >= ARRAYSIZE(event.name)) goto done;
memcpy(event.name, name.bstrVal, length * sizeof(WCHAR));
COPYDATASTRUCT data = {WINE_ACCESS_BOOTSTRAPPER_TAG, sizeof(event), &event};
DWORD_PTR result;
SendMessageTimeoutW(queryBridge, WM_COPYDATA, 0, (LPARAM)&data,
SMTO_ABORTIFHUNG | SMTO_BLOCK, 50, &result);
done:
VariantClear(&type); VariantClear(&name);
if (toggle) IToggleProvider_Release(toggle);
if (pattern) IUnknown_Release(pattern);
if (provider) IRawElementProviderSimple_Release(provider);
if (focus) IRawElementProviderFragment_Release(focus);
if (fragmentRoot) IRawElementProviderFragmentRoot_Release(fragmentRoot);
}
static LRESULT WINAPI return_provider(HWND window, WPARAM wp, LPARAM lp,
IRawElementProviderSimple *provider) {
// Borrow the provider only during this original call, on its UI thread.
// Never retain it, toggle a control, or change the original UIA result.
if (provider && window == queryWindow && queryBridge && (LONG)lp == UiaRootObjectId)
read_toggle(provider);
return originalReturn(window, wp, lp, provider);
}
static BOOL watch_import(void) {
BYTE *base = (BYTE *)GetModuleHandleW(NULL);
IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)base;
if (!base || dos->e_magic != IMAGE_DOS_SIGNATURE) return FALSE;
IMAGE_NT_HEADERS *header = (IMAGE_NT_HEADERS *)(base + dos->e_lfanew);
if (header->Signature != IMAGE_NT_SIGNATURE) return FALSE;
IMAGE_DATA_DIRECTORY directory = header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
if (!directory.VirtualAddress || !directory.Size) return FALSE;
IMAGE_IMPORT_DESCRIPTOR *imports = (IMAGE_IMPORT_DESCRIPTOR *)(base + directory.VirtualAddress);
for (; imports->Name; ++imports) {
if (lstrcmpiA((char *)(base + imports->Name), "uiautomationcore.dll") ||
!imports->OriginalFirstThunk) continue;
IMAGE_THUNK_DATA *names = (IMAGE_THUNK_DATA *)(base + imports->OriginalFirstThunk);
IMAGE_THUNK_DATA *addresses = (IMAGE_THUNK_DATA *)(base + imports->FirstThunk);
for (; names->u1.AddressOfData; ++names, ++addresses) {
if (IMAGE_SNAP_BY_ORDINAL(names->u1.Ordinal)) continue;
IMAGE_IMPORT_BY_NAME *name = (IMAGE_IMPORT_BY_NAME *)(base + names->u1.AddressOfData);
if (strcmp((char *)name->Name, "UiaReturnRawElementProvider")) continue;
if (addresses->u1.Function == (ULONG_PTR)return_provider) return TRUE;
HMODULE uia = GetModuleHandleW(L"uiautomationcore.dll"), pinned = NULL;
if (!uia) return FALSE;
FARPROC address = GetProcAddress(uia, "UiaReturnRawElementProvider");
if (!address || addresses->u1.Function != (ULONG_PTR)address) return FALSE;
memcpy(&originalReturn, &address, sizeof(originalReturn));
if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN,
(LPCWSTR)(ULONG_PTR)return_provider, &pinned)) return FALSE;
DWORD protection;
if (!VirtualProtect(&addresses->u1.Function, sizeof(addresses->u1.Function), PAGE_READWRITE,
&protection)) return FALSE;
InterlockedExchangePointer((PVOID volatile *)&addresses->u1.Function, (PVOID)(ULONG_PTR)return_provider);
DWORD ignored;
VirtualProtect(&addresses->u1.Function, sizeof(addresses->u1.Function), protection, &ignored);
return TRUE;
}
}
return FALSE;
}
void wine_access_query_bootstrapper_state(HWND window, HWND bridge) {
WCHAR className[128];
DWORD process = 0;
if (!bridge || window != GetForegroundWindow() ||
GetWindowThreadProcessId(window, &process) != GetCurrentThreadId() ||
process != GetCurrentProcessId() || !GetClassNameW(window, className, ARRAYSIZE(className)) ||
lstrcmpW(className, L"Blizzard Bootstrapper")) return;
if (InterlockedCompareExchange(&querying, 1, 0)) return;
if (watch_import()) {
queryWindow = window; queryBridge = bridge;
SendMessageW(window, WM_GETOBJECT, 0, UiaRootObjectId);
queryWindow = queryBridge = NULL;
}
InterlockedExchange(&querying, 0);
}