发布《.NET Windows Form 改变窗体类名(Class Name)有多难?》转眼大半年过去了,要不是在前几天有园友对这篇文章进行评论,基本上已经很少关注它了,毕竟那只是一个解惑的研究,在开发中没什么实际的用处。但是由于Squares园友的评论,结合最近自己相关的工作,灵感一现,却真的找到了解决之法,不得不感慨一下,“问题总是会有解决办法的,只是自己能力不够或一时没想到而已”。好了,前奏写完,进入正题。

最近相关工作

  最近一段时间,重新拾起以前比较熟悉的界面UI开发,由于需要,了解了一些 HOOK API 的知识。HOOK API C++ 已经有比较好的开源资源,MHookMinHook。而 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 进行处理的。

最终实现

  不多说,非常简单,一切以代码说话。  

 #include "ClassNameManager.h"
#include <Windows.h>
#include <tchar.h>
#include <assert.h>
#include "../MinHook/include/MinHook.h" #ifdef _M_X64
#pragma comment(lib, "../lib/MinHook/MinHook.x64.lib")
#else
#pragma comment(lib,"../lib/MinHook/MinHook.x86.lib")
#endif namespace Starts2000 {
namespace Window {
namespace Forms { #define FORM_CLASS_NAME L"WindowsForms10.Window.8.app"
#define FORM_CUSTOM_CLASS_NAME L"Starts2000.Window" typedef ATOM (WINAPI * TrueRegisterClassW)(_In_ CONST WNDCLASSW *);
typedef BOOL (WINAPI * TrueUnregisterClassW)(_In_ LPCWSTR, _In_opt_ HINSTANCE);
typedef BOOL (WINAPI * TrueGetClassInfoW)(
_In_opt_ HINSTANCE,
_In_ LPCWSTR,
_Out_ LPWNDCLASSW);
typedef HWND (WINAPI * TrueCreateWindowExW)(
_In_ DWORD,
_In_opt_ LPCWSTR,
_In_opt_ LPCWSTR,
_In_ DWORD,
_In_ int,
_In_ int,
_In_ int,
_In_ int,
_In_opt_ HWND,
_In_opt_ HMENU,
_In_opt_ HINSTANCE,
_In_opt_ LPVOID); TrueRegisterClassW _registerClassW = NULL;
TrueUnregisterClassW _unregisterClassW = NULL;
TrueGetClassInfoW _getClassInfoW = NULL;
TrueCreateWindowExW _createWindowExW = NULL; ATOM WINAPI RegisterClassWD(_In_ CONST WNDCLASSW *lpWndClass) {
if (_tcsstr(lpWndClass->lpszClassName, FORM_CLASS_NAME)) {
WNDCLASSW wndClass;
memcpy(&wndClass, lpWndClass, sizeof(WNDCLASSW));
wndClass.lpszClassName = FORM_CUSTOM_CLASS_NAME;
auto ret = _registerClassW(&wndClass);
return ret;
} return _registerClassW(lpWndClass);
} BOOL WINAPI UnregisterClassWD(_In_ LPCWSTR lpClassName, _In_opt_ HINSTANCE hInstance) {
if (_tcsstr(lpClassName, FORM_CLASS_NAME)) {
return _unregisterClassW(FORM_CUSTOM_CLASS_NAME, hInstance);
} return _unregisterClassW(lpClassName, hInstance);
} BOOL WINAPI GetClassInfoWD(_In_opt_ HINSTANCE hInstance,
_In_ LPCWSTR lpClassName,
_Out_ LPWNDCLASSW lpWndClass) {
if (_tcsstr(lpClassName, FORM_CLASS_NAME)) {
return _getClassInfoW(hInstance, FORM_CUSTOM_CLASS_NAME, lpWndClass);
} return _getClassInfoW(hInstance, lpClassName, lpWndClass);
} HWND WINAPI CreateWindowExWD(
_In_ DWORD dwExStyle,
_In_opt_ LPCWSTR lpClassName,
_In_opt_ LPCWSTR lpWindowName,
_In_ DWORD dwStyle,
_In_ int X,
_In_ int Y,
_In_ int nWidth,
_In_ int nHeight,
_In_opt_ HWND hWndParent,
_In_opt_ HMENU hMenu,
_In_opt_ HINSTANCE hInstance,
_In_opt_ LPVOID lpParam) {
if (_tcsstr(lpClassName, FORM_CLASS_NAME)) {
auto hwnd = _createWindowExW(dwExStyle, FORM_CUSTOM_CLASS_NAME, lpWindowName, dwStyle,
X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);
assert(hwnd);
return hwnd;
} return _createWindowExW(dwExStyle, lpClassName, lpWindowName, dwStyle,
X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);
} ClassNameManager::ClassNameManager() {
auto ret = MH_Initialize();
assert(ret == MH_STATUS::MH_OK); ret = MH_CreateHookApi(L"User32.dll",
"RegisterClassW", &RegisterClassWD, reinterpret_cast<LPVOID*>(&_registerClassW));
assert(ret == MH_STATUS::MH_OK); ret = MH_CreateHookApi(L"User32.dll",
"UnregisterClassW", &UnregisterClassWD, reinterpret_cast<LPVOID*>(&_unregisterClassW));
assert(ret == MH_STATUS::MH_OK); ret = MH_CreateHookApi(L"User32.dll",
"GetClassInfoW", &GetClassInfoWD, reinterpret_cast<LPVOID*>(&_getClassInfoW));
assert(ret == MH_STATUS::MH_OK); ret = MH_CreateHookApi(L"User32.dll",
"CreateWindowExW", &CreateWindowExWD, reinterpret_cast<LPVOID*>(&_createWindowExW));
assert(ret == MH_STATUS::MH_OK); ret = MH_EnableHook(MH_ALL_HOOKS);
assert(ret == MH_STATUS::MH_OK);
} ClassNameManager::~ClassNameManager() {
} ClassNameManager::!ClassNameManager() {
auto ret = MH_Uninitialize();
assert(ret == MH_STATUS::MH_OK);
}
}
}
}

最终效果

最后的最后

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

变不可能为可能 - .NET Windows Form 改变窗体类名(Class Name)有多难?续篇的更多相关文章

  1. .NET Windows Form 改变窗体类名(Class Name)有多难?

    研究WinForm的东西,是我的一个个人兴趣和爱好,以前做的项目,多与WinForm相关,然而这几年,项目都与WinForm没什么关系了,都转为ASP.NET MVC与WPF了.关于今天讨论的这个问题 ...

  2. Ninject之旅之十二:Ninject在Windows Form程序上的应用(附程序下载)

    摘要: 下面的几篇文章介绍如何使用Ninject创建不同类型的应用系统.包括: Windows Form应用系统 ASP.NET MVC应用系统 ASP.NET Web Form应用系统 尽管对于不同 ...

  3. 如何用Web技术开发Windows Form应用

    现在H5很热,很多互联网公司的产品都采用混合编程,其中各个平台客户端的“壳”为原生控件,但是内容很多都是Web网页,因此可以做出很多炫酷的效果.随着Node.js和Ionic等框架的出现,现在感觉Ja ...

  4. Windows Form 中快捷键设置

    在Windows Form程序中使用带下划线的快捷键只需要进行设置: 就能够工作.

  5. VS2008 Windows Form项目安装包生成详解

    2008 Windows Form项目的发布对有经验的程序员来说,可能不值一提,但对很多新手来说却不知道如何操作,因为在很多关于Visual Studio的书籍中也没有相关介绍,权威如<C# 2 ...

  6. VISUAL STUDIO 2008 WINDOWS FORM项目发布生成安装包详解(转)

    转自:http://www.cnblogs.com/killerofyang/archive/2012/05/31/2529193.html Visual Studio 2008 Windows Fo ...

  7. C# Adding Hyperlink to Windows Form z

    When creating a Windows form in C#, we would like to create a hyperlink so that when the user click ...

  8. windows form (窗体) 之间传值小结

    windows form (窗体) 之间传值小结   windows form (窗体) 之间传值小结 在windows form之间传值,我总结了有四个方法:全局变量.属性.窗体构造函数和deleg ...

  9. Windows Form线程同步

    .Net多线程开发中,经常需要启动工作线程Worker thread处理某些事情,而工作线程中又需要更新主线程UI thread的界面状态.我们只能在主线程中操作界面控件,否则.Net会抛出异常. 那 ...

随机推荐

  1. PAT 1034 有理数四则运算(20)(代码框架+思路+测试点错误分析)

    1034 有理数四则运算(20)(20 分)提问 本题要求编写程序,计算2个有理数的和.差.积.商. 输入格式: 输入在一行中按照"a1/b1 a2/b2"的格式给出两个分数形式的 ...

  2. PAT 1003 我要通过!(20)(代码+思路)

    1003 我要通过!(20)(20 分)提问 "答案正确"是自动判题系统给出的最令人欢喜的回复.本题属于PAT的"答案正确"大派送 -- 只要读入的字符串满足下 ...

  3. js点击添加

    1.点击变色 <div id="dd" style="width:100px;height: 100px;background-color: #ccc"& ...

  4. [ Laravel 5.5 文档 ] 底层原理 —— 一次 Laravel 请求的生命周期

     Posted on 2018年3月5日 by  学院君 简介 当我们使用现实世界中的任何工具时,如果理解了该工具的工作原理,那么用起来就会得心应手,应用开发也是如此.当你理解了开发工具如何工作,用起 ...

  5. Android界面设计

    从继承关系来看,所有组件继承自View.容器也是继承自View,它能容纳别的View. 所有容器继承自ViewGroup.包括 FrameLayout, LinearLayout, RelativeL ...

  6. abp 的坑

    多数据库连接问题 viewmodel的验证问题 发布后,一段查找sln查找代码无法适用生产环境问题 多语言问题,默认中文设置与模板配置文件不统一

  7. 【JDBC&Dbutils】JDBC&JDBC连接池&DBUtils使用方法(重要)

    -----------------------JDBC---------- 0.      db.properties文件 driver=com.mysql.jdbc.Driver url=jdbc: ...

  8. eclipse代码自动提示,eclipse设置代码自动提示

    eclipse代码自动提示,eclipse设置代码自动提示 eclipse是很多JAVA开发者基本上都用的工具,用它可以很方便的开发JAVA代码,当编写JAVA代码时,大部分人都是按组合键[Alt+/ ...

  9. 使用Java实现网络爬虫

    网络爬虫 网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本. 另外一些不常使用的名字还有蚂蚁.自动索引.模 ...

  10. 2018.09.26 bzoj4326: NOIP2015 运输计划(二分+树上差分)

    传送门 简单树上操作. 先转边权为点权. 显然所有的询问操作对应的路径会有一些交点,那么我们可以直接二分答案,对于所有大于二分值的询问用树上差分维护,最后dfs一遍每个点被覆盖了几次,当前情况合法当且 ...