Skip to content

Commit 8593f8f

Browse files
asklarCopilot
andcommitted
Add x86 build support for 32-bit WPF targets
- Add x86 CMake preset (build-x86 directory, x86-windows triplet) - Add x86 to release pipeline matrix (msvc_arch: amd64_x86) - Add managed WPF assembly build step to CI and release pipelines - Include WPF TAP DLL and managed assembly in release packages - Show clear error when 64-bit lvt.exe encounters 32-bit WPF target: 'WPF target is 32-bit (WoW64) - run lvt-x86.exe instead' - Remove x86 cross-compile hacks from CMakeLists.txt - each arch is built as a separate CMake configuration instead Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 5f0dff2 commit 8593f8f

5 files changed

Lines changed: 50 additions & 18 deletions

File tree

.github/workflows/ci.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ jobs:
2626
- name: Configure
2727
run: cmake --preset default
2828

29+
- name: Build managed WPF assembly
30+
run: dotnet build src/tap_wpf/LvtWpfTap.csproj -c Release
31+
2932
- name: Build
3033
run: cmake --build build
3134

@@ -50,3 +53,5 @@ jobs:
5053
path: |
5154
build/lvt.exe
5255
build/lvt_tap_x64.dll
56+
build/lvt_wpf_tap_x64.dll
57+
build/LvtWpfTap.dll

.github/workflows/release.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ jobs:
2525
msvc_arch: x64
2626
preset: default
2727
build_dir: build
28+
- arch: x86
29+
msvc_arch: amd64_x86
30+
preset: x86
31+
build_dir: build-x86
2832
- arch: arm64
2933
msvc_arch: amd64_arm64
3034
preset: arm64
@@ -48,6 +52,9 @@ jobs:
4852
- name: Configure
4953
run: cmake --preset ${{ matrix.preset }} -DCMAKE_BUILD_TYPE=Release
5054

55+
- name: Build managed WPF assembly
56+
run: dotnet build src/tap_wpf/LvtWpfTap.csproj -c Release
57+
5158
- name: Build
5259
run: cmake --build ${{ matrix.build_dir }}
5360

@@ -64,6 +71,12 @@ jobs:
6471
New-Item -ItemType Directory -Path release -Force
6572
Copy-Item ${{ matrix.build_dir }}\lvt.exe release\
6673
Copy-Item ${{ matrix.build_dir }}\lvt_tap_${{ matrix.arch }}.dll release\
74+
if (Test-Path ${{ matrix.build_dir }}\lvt_wpf_tap_${{ matrix.arch }}.dll) {
75+
Copy-Item ${{ matrix.build_dir }}\lvt_wpf_tap_${{ matrix.arch }}.dll release\
76+
}
77+
if (Test-Path ${{ matrix.build_dir }}\LvtWpfTap.dll) {
78+
Copy-Item ${{ matrix.build_dir }}\LvtWpfTap.dll release\
79+
}
6780
New-Item -ItemType Directory -Path release\skills\lvt -Force
6881
Copy-Item .github\skills\lvt\SKILL.md release\skills\lvt\
6982
Compress-Archive -Path release\* -DestinationPath lvt-${{ env.RELEASE_TAG }}-${{ matrix.arch }}.zip

CMakeLists.txt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ target_compile_definitions(lvt PRIVATE
4444
# Determine TAP DLL architecture suffix from vcpkg triplet
4545
if(VCPKG_TARGET_TRIPLET MATCHES "arm64")
4646
set(TAP_ARCH_SUFFIX "arm64")
47+
elseif(VCPKG_TARGET_TRIPLET MATCHES "x86")
48+
set(TAP_ARCH_SUFFIX "x86")
4749
else()
4850
set(TAP_ARCH_SUFFIX "x64")
4951
endif()
@@ -82,13 +84,12 @@ set_target_properties(lvt_wpf_tap PROPERTIES
8284
)
8385

8486
# Build managed WPF tree walker assembly and copy to output
85-
# Run dotnet build separately; the managed DLL must be pre-built before running lvt
86-
# with WPF targets. Use: dotnet build src/tap_wpf/LvtWpfTap.csproj -c Release -o build
87+
# Pre-build with: dotnet build src/tap_wpf/LvtWpfTap.csproj -c Release
8788
add_custom_command(TARGET lvt_wpf_tap POST_BUILD
8889
COMMAND ${CMAKE_COMMAND} -E copy_if_different
89-
"${CMAKE_SOURCE_DIR}/src/tap_wpf/bin/x64/Release/LvtWpfTap.dll"
90+
"${CMAKE_SOURCE_DIR}/src/tap_wpf/bin/$<IF:$<STREQUAL:${TAP_ARCH_SUFFIX},x86>,x86,x64>/Release/LvtWpfTap.dll"
9091
"$<TARGET_FILE_DIR:lvt>/LvtWpfTap.dll"
91-
COMMENT "Copying managed WPF tree walker assembly (pre-build with: dotnet build src/tap_wpf/LvtWpfTap.csproj -c Release)"
92+
COMMENT "Copying managed WPF tree walker assembly"
9293
)
9394

9495
# --- Tests ---

CMakePresets.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,22 @@
1717
"VCPKG_TARGET_TRIPLET": "x64-windows"
1818
}
1919
},
20+
{
21+
"name": "x86",
22+
"displayName": "x86",
23+
"generator": "Ninja",
24+
"architecture": {
25+
"value": "x86",
26+
"strategy": "external"
27+
},
28+
"binaryDir": "${sourceDir}/build-x86",
29+
"toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
30+
"cacheVariables": {
31+
"CMAKE_C_COMPILER": "cl.exe",
32+
"CMAKE_CXX_COMPILER": "cl.exe",
33+
"VCPKG_TARGET_TRIPLET": "x86-windows"
34+
}
35+
},
2036
{
2137
"name": "arm64",
2238
"displayName": "ARM64",
@@ -39,6 +55,10 @@
3955
"name": "default",
4056
"configurePreset": "default"
4157
},
58+
{
59+
"name": "x86",
60+
"configurePreset": "x86"
61+
},
4262
{
4363
"name": "arm64",
4464
"configurePreset": "arm64"

src/providers/wpf_inject.cpp

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -158,30 +158,23 @@ static bool inject_dll(DWORD pid, const std::wstring& dllPath) {
158158
}
159159

160160
bool inject_and_collect_wpf_tree(Element& root, HWND /*hwnd*/, DWORD pid) {
161-
// Check target process bitness matches ours — can't inject x64 DLL into x86 process
162-
auto targetArch = detect_process_architecture(pid);
163-
auto hostArch = get_host_architecture();
164-
if (targetArch != hostArch) {
165-
if (g_debug)
166-
fprintf(stderr, "lvt: WPF target is %s but lvt is %s, skipping injection\n",
167-
architecture_name(targetArch), architecture_name(hostArch));
168-
return false;
169-
}
170-
171-
// Also check for WoW64 (32-bit on 64-bit) which detect_process_architecture may miss
161+
// Check target process bitness matches ours
172162
wil::unique_handle proc(OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid));
173163
if (proc) {
174164
BOOL isWow64 = FALSE;
175165
if (IsWow64Process(proc.get(), &isWow64) && isWow64) {
176-
if (g_debug)
177-
fprintf(stderr, "lvt: WPF target is 32-bit (WoW64), skipping injection\n");
166+
#if defined(_M_X64) || defined(_M_ARM64)
167+
fprintf(stderr,
168+
"lvt: WPF target is 32-bit (WoW64) - run lvt-x86.exe instead\n");
178169
return false;
170+
#endif
179171
}
180172
}
181173

182174
std::wstring exeDir = get_exe_dir();
183175
const wchar_t* tapSuffix = (get_host_architecture() == Architecture::arm64)
184-
? L"\\lvt_wpf_tap_arm64.dll" : L"\\lvt_wpf_tap_x64.dll";
176+
? L"\\lvt_wpf_tap_arm64.dll" : (sizeof(void*) == 4)
177+
? L"\\lvt_wpf_tap_x86.dll" : L"\\lvt_wpf_tap_x64.dll";
185178
std::wstring tapDll = exeDir + tapSuffix;
186179

187180
if (GetFileAttributesW(tapDll.c_str()) == INVALID_FILE_ATTRIBUTES) {

0 commit comments

Comments
 (0)