Skip to content

Commit 4d00264

Browse files
asklarCopilot
andcommitted
Add WPF framework detection and provider skeleton (Phase 1)
Detect WPF apps by scanning for PresentationFramework.dll in target process modules, with fallback to HwndWrapper[ window class prefix. Labels HwndWrapper windows as framework=wpf in the element tree. Tree walking (VisualTreeHelper via managed DLL injection) will follow in subsequent phases. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 6b74130 commit 4d00264

6 files changed

Lines changed: 63 additions & 0 deletions

File tree

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ add_executable(lvt
1818
src/providers/comctl_provider.cpp
1919
src/providers/xaml_provider.cpp
2020
src/providers/winui3_provider.cpp
21+
src/providers/wpf_provider.cpp
2122
src/providers/xaml_diag_common.cpp
2223
)
2324

@@ -78,6 +79,7 @@ add_executable(lvt_unit_tests
7879
src/providers/comctl_provider.cpp
7980
src/providers/xaml_provider.cpp
8081
src/providers/winui3_provider.cpp
82+
src/providers/wpf_provider.cpp
8183
src/providers/xaml_diag_common.cpp
8284
)
8385
target_include_directories(lvt_unit_tests PRIVATE src)

src/framework_detector.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ std::string framework_to_string(Framework f) {
1212
case Framework::ComCtl: return "comctl";
1313
case Framework::Xaml: return "xaml";
1414
case Framework::WinUI3: return "winui3";
15+
case Framework::Wpf: return "wpf";
1516
}
1617
return "unknown";
1718
}
@@ -28,6 +29,7 @@ struct DetectData {
2829
bool hasComCtl = false;
2930
bool hasWinUI3 = false;
3031
bool hasXaml = false;
32+
bool hasWpf = false;
3133
};
3234

3335
static BOOL CALLBACK detect_child_proc(HWND hwnd, LPARAM lParam) {
@@ -53,6 +55,10 @@ static BOOL CALLBACK detect_child_proc(HWND hwnd, LPARAM lParam) {
5355
data->hasXaml = true;
5456
}
5557

58+
if (wcsstr(cls, L"HwndWrapper[")) {
59+
data->hasWpf = true;
60+
}
61+
5662
return TRUE;
5763
}
5864

@@ -147,6 +153,7 @@ std::vector<FrameworkInfo> detect_frameworks(HWND hwnd, DWORD pid) {
147153

148154
bool detectedWinUI3 = false;
149155
bool detectedXaml = false;
156+
bool detectedWpf = false;
150157
if (pid) {
151158
auto winui = detect_module(pid, L"Microsoft.UI.Xaml.dll");
152159
if (winui.found) {
@@ -158,13 +165,20 @@ std::vector<FrameworkInfo> detect_frameworks(HWND hwnd, DWORD pid) {
158165
result.push_back({Framework::Xaml, xaml.version});
159166
detectedXaml = true;
160167
}
168+
auto wpf = detect_module(pid, L"PresentationFramework.dll");
169+
if (wpf.found) {
170+
result.push_back({Framework::Wpf, wpf.version});
171+
detectedWpf = true;
172+
}
161173
}
162174

163175
// Class-name fallback (works when module enumeration fails)
164176
if (!detectedWinUI3 && data.hasWinUI3)
165177
result.push_back({Framework::WinUI3, {}});
166178
if (!detectedXaml && data.hasXaml)
167179
result.push_back({Framework::Xaml, {}});
180+
if (!detectedWpf && data.hasWpf)
181+
result.push_back({Framework::Wpf, {}});
168182

169183
return result;
170184
}

src/framework_detector.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ enum class Framework {
1010
ComCtl,
1111
Xaml,
1212
WinUI3,
13+
Wpf,
1314
};
1415

1516
struct FrameworkInfo {

src/providers/wpf_provider.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#include "wpf_provider.h"
2+
#include <cstdio>
3+
#include <functional>
4+
#include <Windows.h>
5+
6+
namespace lvt {
7+
8+
// Label WPF HwndWrapper windows in the element tree
9+
static void label_wpf_windows(Element& el) {
10+
if (el.className.starts_with("HwndWrapper[")) {
11+
el.framework = "wpf";
12+
el.type = "WpfWindow";
13+
}
14+
for (auto& child : el.children) {
15+
label_wpf_windows(child);
16+
}
17+
}
18+
19+
void WpfProvider::enrich(Element& root, HWND /*hwnd*/, DWORD /*pid*/) {
20+
label_wpf_windows(root);
21+
22+
// TODO (Phase 2+): Inject managed TAP DLL to walk WPF visual tree
23+
// via VisualTreeHelper and graft results into the element tree.
24+
}
25+
26+
} // namespace lvt

src/providers/wpf_provider.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#pragma once
2+
#include "provider.h"
3+
4+
namespace lvt {
5+
6+
class WpfProvider : public IProvider {
7+
public:
8+
// Enrich the element tree with WPF visual tree information.
9+
// Labels HwndWrapper windows and (future) injects managed TAP DLL
10+
// to walk the WPF visual tree via VisualTreeHelper.
11+
void enrich(Element& root, HWND hwnd, DWORD pid);
12+
};
13+
14+
} // namespace lvt

src/tree_builder.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "providers/comctl_provider.h"
55
#include "providers/xaml_provider.h"
66
#include "providers/winui3_provider.h"
7+
#include "providers/wpf_provider.h"
78
#include <algorithm>
89
#include <memory>
910

@@ -58,6 +59,11 @@ Element build_tree(HWND hwnd, DWORD pid, const std::vector<FrameworkInfo>& frame
5859
winui3.enrich(root, hwnd, pid);
5960
break;
6061
}
62+
case Framework::Wpf: {
63+
WpfProvider wpf;
64+
wpf.enrich(root, hwnd, pid);
65+
break;
66+
}
6167
default:
6268
break;
6369
}

0 commit comments

Comments
 (0)