发布《 .NET Windows Form 改变窗体类名(Class Name)有多难?》转眼大半年过去了,要不是在前几天有园友对这篇文章进行评论,基本上已经很少关注它了,毕竟那只是一个解惑的研究,在开发中没什么实际的用处。但是由于Squares园友的评论,结合最近自己相关的工作,灵感一现,却真的找到了解决之法,不得不感慨一下,“问题总是会有解决办法的,只是自己能力不够或一时没想到而已”。好了,前奏写完,进入正题。
最近相关工作
最近一段时间,重新拾起以前比较熟悉的界面UI开发,由于需要,了解了一些 HOOK API 的知识。HOOK API C++ 已经有比较好的开源资源,MHook 和 MinHook。而 HOOK API 就是解决 “Windows Form 改变窗体类名(Class Name)”的关键。
灵感及思路
还记得上一篇文章里提到为什么不能改变Windows Form窗体类名的原因吗?就是微软的代码里只认系统注册的 ClassName,只要我们在 CreateParams 属性里设置的 ClassName 不是系统注册的 ClassName,就会报错。所以,设置自己喜欢的 ClassName,只能按照窗口创建的过程,自己实现一个窗口。而实现一个窗口的过程也很简单:
- 使用 API 函数 RegisterClass 注册窗口;
- 使用 API 函数 CreateWindowEx 创建窗口;
- 使用 API 函数 ShowWindow 显示窗口;
- 最后退出时使用 API 函数 DestroyWindow 销毁窗口。
过程非常简单,Winform 的窗口也脱离不了这个过程。那这样, HOOK API 不就有机可乘了吗?只要我们 HOOK RegisterClass 和 CreateWindowEx,在 Winform 注册窗口时,把它使用的类名改为我们需要的类名;创建窗口的时候,也同样。当然,在实际处理过程中,UnregisterClass,GetClassInfo 也是需要 HOOK 进行处理的。
最终实现
不多说,非常简单,一切以代码说话。
1 #include "ClassNameManager.h"
2 #include <Windows.h>
3 #include <tchar.h>
4 #include <assert.h>
5 #include "../MinHook/include/MinHook.h"
6
7 #ifdef _M_X64
8 #pragma comment(lib, "../lib/MinHook/MinHook.x64.lib")
9 #else
10 #pragma comment(lib,"../lib/MinHook/MinHook.x86.lib")
11 #endif
12
13 namespace Starts2000 {
14 namespace Window {
15 namespace Forms {
16
17 #define FORM_CLASS_NAME L"WindowsForms10.Window.8.app"
18 #define FORM_CUSTOM_CLASS_NAME L"Starts2000.Window"
19
20 typedef ATOM (WINAPI * TrueRegisterClassW)(_In_ CONST WNDCLASSW *);
21 typedef BOOL (WINAPI * TrueUnregisterClassW)(_In_ LPCWSTR, _In_opt_ HINSTANCE);
22 typedef BOOL (WINAPI * TrueGetClassInfoW)(
23 _In_opt_ HINSTANCE,
24 _In_ LPCWSTR,
25 _Out_ LPWNDCLASSW);
26 typedef HWND (WINAPI * TrueCreateWindowExW)(
27 _In_ DWORD,
28 _In_opt_ LPCWSTR,
29 _In_opt_ LPCWSTR,
30 _In_ DWORD,
31 _In_ int,
32 _In_ int,
33 _In_ int,
34 _In_ int,
35 _In_opt_ HWND,
36 _In_opt_ HMENU,
37 _In_opt_ HINSTANCE,
38 _In_opt_ LPVOID);
39
40 TrueRegisterClassW _registerClassW = NULL;
41 TrueUnregisterClassW _unregisterClassW = NULL;
42 TrueGetClassInfoW _getClassInfoW = NULL;
43 TrueCreateWindowExW _createWindowExW = NULL;
44
45 ATOM WINAPI RegisterClassWD(_In_ CONST WNDCLASSW *lpWndClass) {
46 if (_tcsstr(lpWndClass->lpszClassName, FORM_CLASS_NAME)) {
47 WNDCLASSW wndClass;
48 memcpy(&wndClass, lpWndClass, sizeof(WNDCLASSW));
49 wndClass.lpszClassName = FORM_CUSTOM_CLASS_NAME;
50 auto ret = _registerClassW(&wndClass);
51 return ret;
52 }
53
54 return _registerClassW(lpWndClass);
55 }
56
57 BOOL WINAPI UnregisterClassWD(_In_ LPCWSTR lpClassName, _In_opt_ HINSTANCE hInstance) {
58 if (_tcsstr(lpClassName, FORM_CLASS_NAME)) {
59 return _unregisterClassW(FORM_CUSTOM_CLASS_NAME, hInstance);
60 }
61
62 return _unregisterClassW(lpClassName, hInstance);
63 }
64
65 BOOL WINAPI GetClassInfoWD(_In_opt_ HINSTANCE hInstance,
66 _In_ LPCWSTR lpClassName,
67 _Out_ LPWNDCLASSW lpWndClass) {
68 if (_tcsstr(lpClassName, FORM_CLASS_NAME)) {
69 return _getClassInfoW(hInstance, FORM_CUSTOM_CLASS_NAME, lpWndClass);
70 }
71
72 return _getClassInfoW(hInstance, lpClassName, lpWndClass);
73 }
74
75 HWND WINAPI CreateWindowExWD(
76 _In_ DWORD dwExStyle,
77 _In_opt_ LPCWSTR lpClassName,
78 _In_opt_ LPCWSTR lpWindowName,
79 _In_ DWORD dwStyle,
80 _In_ int X,
81 _In_ int Y,
82 _In_ int nWidth,
83 _In_ int nHeight,
84 _In_opt_ HWND hWndParent,
85 _In_opt_ HMENU hMenu,
86 _In_opt_ HINSTANCE hInstance,
87 _In_opt_ LPVOID lpParam) {
88 if (_tcsstr(lpClassName, FORM_CLASS_NAME)) {
89 auto hwnd = _createWindowExW(dwExStyle, FORM_CUSTOM_CLASS_NAME, lpWindowName, dwStyle,
90 X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);
91 assert(hwnd);
92 return hwnd;
93 }
94
95 return _createWindowExW(dwExStyle, lpClassName, lpWindowName, dwStyle,
96 X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);
97 }
98
99 ClassNameManager::ClassNameManager() {
100 auto ret = MH_Initialize();
101 assert(ret == MH_STATUS::MH_OK);
102
103 ret = MH_CreateHookApi(L"User32.dll",
104 "RegisterClassW", &RegisterClassWD, reinterpret_cast<LPVOID*>(&_registerClassW));
105 assert(ret == MH_STATUS::MH_OK);
106
107 ret = MH_CreateHookApi(L"User32.dll",
108 "UnregisterClassW", &UnregisterClassWD, reinterpret_cast<LPVOID*>(&_unregisterClassW));
109 assert(ret == MH_STATUS::MH_OK);
110
111 ret = MH_CreateHookApi(L"User32.dll",
112 "GetClassInfoW", &GetClassInfoWD, reinterpret_cast<LPVOID*>(&_getClassInfoW));
113 assert(ret == MH_STATUS::MH_OK);
114
115 ret = MH_CreateHookApi(L"User32.dll",
116 "CreateWindowExW", &CreateWindowExWD, reinterpret_cast<LPVOID*>(&_createWindowExW));
117 assert(ret == MH_STATUS::MH_OK);
118
119 ret = MH_EnableHook(MH_ALL_HOOKS);
120 assert(ret == MH_STATUS::MH_OK);
121 }
122
123 ClassNameManager::~ClassNameManager() {
124 }
125
126 ClassNameManager::!ClassNameManager() {
127 auto ret = MH_Uninitialize();
128 assert(ret == MH_STATUS::MH_OK);
129 }
130 }
131 }
132 }
最终效果
最后的最后
源码是要上的,下载项目源代码。