闪酷跑路网

一行行枯燥的代码,却描绘出人生的点点滴滴

您现在的位置是:首页>_.NET组件控件

变不可能为或许

发布时间:2018-11-15浏览(2705)

    变不可能为可能

      发布《 .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,只能按照窗口创建的过程,自己实现一个窗口。而实现一个窗口的过程也很简单:

    1. 使用 API 函数 RegisterClass 注册窗口;
    2. 使用 API 函数 CreateWindowEx 创建窗口;
    3. 使用 API 函数 ShowWindow 显示窗口;
    4. 最后退出时使用 API 函数 DestroyWindow 销毁窗口。

      过程非常简单,Winform 的窗口也脱离不了这个过程。那这样, HOOK API 不就有机可乘了吗?只要我们 HOOK RegisterClass 和 CreateWindowEx,在 Winform 注册窗口时,把它使用的类名改为我们需要的类名;创建窗口的时候,也同样。当然,在实际处理过程中,UnregisterClassGetClassInfo 也是需要 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 }

    最终效果

    最后的最后

      源码是要上的,下载项目源代码

    5楼zhong0326
    楼主你好,非常感谢提供的思路及源码, 并且帮我解决了大问题;,分享一下我在使用过程中的心得吧, 因为我们的主窗口是一个mdiparentform, 加载主窗口的过程中, 会new出两个子窗口实例, 第一个子窗口中包含richeditbox控件, 另一个子窗口包含webbrowser控件, 致使在new的过程中报错, 根据报错堆栈信息,发现在CreateWindowExWD中的wcsstr(Char* , Char* )报错, 提示:尝试读取或写入受保护的内存等, 调试后发现, 因为在构造此类控件时, 创建一些子元素时,会出现classname为空的情况, 后来解决办法是:将if (_tcsstr(lpClassName, FORM_CLASS_NAME)) 调整为:if (!IsBadReadPtr(lpClassName, 1) amp;amp; _tcsstr(lpClassName, FORM_CLASS_NAME)) {,,最后问题解决了, 供其他同学参考指正,谢谢!
    4楼上帝之城
    有这个必要吗,改类名做什么
    3楼XiaoFaye
    能不能从内存里找到保存这个类名的空间,然后直接修改?
    2楼InkFx
    大神一出手,就知有没有。
    1楼冲杀
    没看懂