📄 classxp.c
字号:
////////////////////////////////////////////////////////////////////////////////////////////////////
// 说明: ClassXP.c 文件
// 更新: 2003-3-10
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// 编译预处理
#if _WIN32_WINNT < 0x0400
#define _WIN32_WINNT 0x0400
#endif
#include <Windows.h>
#include "ClassXP.h"
#pragma warning(disable: 4311)
#pragma warning(disable: 4312)
#pragma comment(lib, "Msimg32.lib")
// 导出函数
#ifdef CXP_DLLMODE
#pragma comment(linker, "/EXPORT:ClassXP=_ClassXP@8")
#endif // CXP_DLLMODE
// 强制使用 C 语言方式编译
#ifdef __cplusplus
extern "C"
{
#endif // __cplusplus
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// 宏定义
// 窗口类型
#define CXPT_UNKNOWN -1 // 不能处理的类型
#define CXPT_PUSHBUTTON 0 // 按钮
#define CXPT_CHECKBOX 1 // 复选框
#define CXPT_RADIOBOX 2 // 单选框
#define CXPT_EDITBOX 3 // 编辑框
#define CXPT_COMBOBOX 4 // 组合框
// 窗口状态
#define CXPS_DISABLED 0x00000001L // 禁用状态
#define CXPS_PRESSED 0x00000002L // 按下状态
#define CXPS_HOTLIGHT 0x00000004L // 高亮状态 (鼠标在该窗口上)
#define CXPS_FOCUS 0x00000008L // 具有键盘输入焦点
#define CXPS_DEFAULT 0x00000010L // 默认状态 (用于按钮)
#define CXPS_CHECKED 0x00000020L // 选中状态 (用于复选框)
#define CXPS_INDETERMINATE 0x00000040L // 未确定状态 (用于复选框)
#define CXPS_READONLY 0x00000080L // 只读状态 (用于编辑框)
// 设置窗口状态
#define CXPM_SETSTATE(Data, Mask, IsSet) ((IsSet) ? (Data |= Mask) : (Data &= ~Mask))
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// CLASSXP 结构,所有的代码都是围绕这个结构而编写的
typedef struct tagCLASSXP
{
HWND hWnd; // 窗口句柄
DWORD dwType; // 窗口的类型
DWORD dwState; // 窗口的状态
WNDPROC wpPrev; // 子类化之前的窗口回调函数地址
struct tagCLASSXP * pNext; // 指向下一个 CLASSXP 结构,这里采用单向链表结构
}
CLASSXP, * PCLASSXP;
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// MEMDCXP 结构,为了方便使用内存兼容设备场景而设计
typedef struct tagMEMDCXP
{
HWND hWnd; // 窗口句柄,输入参数
HDC hDC; // 窗口设备场景,输出参数
HDC hMemDC; // 窗口内存兼容设备场景,输出参数
BOOL bTransfer; // 是否要用在 hDC 和 hMemDC 间传送数据,输入参数
HBITMAP hBitmap; // 位图句柄,输入和输出参数
}
MEMDCXP, * LPMEMDCXP;
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// 函数声明
PCLASSXP WINAPI CreateClassXP(HWND hWnd);
PCLASSXP WINAPI DeleteClassXP(HWND hWnd);
PCLASSXP WINAPI GetClassXP(HWND hWnd);
DWORD WINAPI GetWindowTypeXP(HWND hWnd);
HDC WINAPI GetMemDCXP(LPMEMDCXP pMdcxp);
VOID WINAPI ReleaseMemDCXP(LPMEMDCXP pMdcxp);
VOID WINAPI GradientRectXP(HDC hDC, LPRECT pRect,COLORREF crColor[4]);
VOID WINAPI DrawDropGripXP(HDC hDC, LPRECT pRect);
BOOL CALLBACK EnumWndProcXP(HWND hWnd, LPARAM lParam);
LRESULT CALLBACK HookProcXP(int iCode, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK WindowProcXP(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
VOID WINAPI DrawPushButtonXP(PCLASSXP pCxp);
VOID WINAPI DrawCheckBoxXP(PCLASSXP pCxp);
VOID WINAPI DrawRadioBoxXP(PCLASSXP pCxp);
VOID WINAPI DrawEditBoxXP(PCLASSXP pCxp);
VOID WINAPI DrawComboBoxXP(PCLASSXP pCxp);
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// 全局变量
HHOOK g_hPrevHookXP = NULL; // 窗口消息 HOOK 句柄
PCLASSXP g_pClassXP = NULL; // 窗口的 CLASSXP 结构指针
#ifdef CXP_DLLMODE
HINSTANCE g_hModuleXP = NULL; // 动态连接库模块句柄
#endif // CXP_DLLMODE
////////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef CXP_DLLMODE
////////////////////////////////////////////////////////////////////////////////////////////////////
// 动态连接库主函数
BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, LPVOID pvReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
g_hModuleXP = hModule;
DisableThreadLibraryCalls(hModule);
}
return TRUE;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
#endif // CXP_DLLMODE
////////////////////////////////////////////////////////////////////////////////////////////////////
// 设置或取消窗口的 ClassXP 风格
BOOL WINAPI ClassXP(HWND hWnd, BOOL bEnable)
{
BOOL bReturn;
bReturn = FALSE;
// 如果是影响当前进程中的所有窗口
if (hWnd == NULL)
{
// 如果是取消当前进程中的所有窗口
if ((bEnable == FALSE) && (g_hPrevHookXP != NULL))
{
// 枚举当前线程的窗口并取消 ClassXP 风格
EnumThreadWindows(GetCurrentThreadId(), EnumWndProcXP, FALSE);
// 取消窗口消息 HOOK
bReturn = UnhookWindowsHookEx(g_hPrevHookXP);
g_hPrevHookXP = NULL;
}
// 如果是设置当前进程中的所有窗口
else if ((bEnable == TRUE) && (g_hPrevHookXP == NULL))
{
// 枚举当前线程中已存在的窗口并设置为 ClassXP 风格
EnumThreadWindows(GetCurrentThreadId(), EnumWndProcXP, TRUE);
// 安装窗口消息 HOOK
g_hPrevHookXP = SetWindowsHookEx(WH_CALLWNDPROC, HookProcXP, 0, GetCurrentThreadId());
bReturn = (BOOL) g_hPrevHookXP;
}
}
else
{
// 如果是取消指定窗口的 ClassXP 风格
if (bEnable == FALSE)
bReturn = (BOOL) DeleteClassXP(hWnd);
// 如果是设置指定窗口的 ClassXP 风格
else
bReturn = (BOOL) CreateClassXP(hWnd);
}
return bReturn;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// 创建并初始化 CLASSXP 数据结构;子类化窗口
// 如果返回 NULL,表示没有创建;否则返回新创建节点的指针,同时 g_pClassXP 指向新创建的节点
PCLASSXP WINAPI CreateClassXP(HWND hWnd)
{
LONG lStyle;
DWORD dwType;
PCLASSXP pCxp;
// 是否已经是 ClassXP 风格
if (GetClassXP(hWnd) == NULL)
{
// 获取窗口类型,如果并判断是否能设置为 ClassXP 风格
dwType = GetWindowTypeXP(hWnd);
if ((dwType >= CXPT_PUSHBUTTON) && (dwType <= CXPT_COMBOBOX))
{
lStyle = GetWindowLong(hWnd, GWL_STYLE);
// 分配存储空间,增加一个节点
pCxp = (PCLASSXP) HeapAlloc(GetProcessHeap(), 0, sizeof(CLASSXP));
pCxp->pNext = g_pClassXP;
g_pClassXP = pCxp;
// 子类化窗口并初始化 CLASSXP 数据结构
pCxp->hWnd = hWnd;
pCxp->dwType = dwType;
pCxp->dwState = (lStyle & WS_DISABLED) ? CXPS_DISABLED : 0;
if (hWnd == GetFocus())
pCxp->dwState |= CXPS_FOCUS;
pCxp->wpPrev = (WNDPROC) SetWindowLong(hWnd, GWL_WNDPROC, (LONG) WindowProcXP);
// 按窗口类型分别 CLASSXP 数据结构
switch (dwType)
{
case CXPT_PUSHBUTTON:
case CXPT_CHECKBOX:
case CXPT_RADIOBOX:
if ((lStyle & SS_TYPEMASK) == BS_DEFPUSHBUTTON)
pCxp->dwState |= CXPS_DEFAULT;
lStyle = (LONG) SendMessage(hWnd, BM_GETCHECK, 0, 0);
if (lStyle == BST_CHECKED)
pCxp->dwState |= CXPS_CHECKED;
else if (lStyle == BST_INDETERMINATE)
pCxp->dwState |= CXPS_INDETERMINATE;
break;
case CXPT_EDITBOX:
if (lStyle & ES_READONLY)
pCxp->dwState |= CXPS_READONLY;
break;
}
// 重画窗口
RedrawWindow(hWnd, NULL, NULL,
RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ERASENOW | RDW_UPDATENOW);
return pCxp;
}
}
return NULL;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// 取消窗口子类化;销毁窗口的 CLASSXP 数据结构
// 如果返回值不为 NULL 表示成功删除,返回值为指向上一个节点指针;
// 如果返回 NULL 且 g_pClassXP 为 NULL,表全部节点被删除;
// 否则表示没有找到该节点。
// 致谢: 感谢 dREAMtHEATER 改进此函数!
PCLASSXP WINAPI DeleteClassXP(HWND hWnd)
{
PCLASSXP pDel;
PCLASSXP pCxp;
// 获取待删除的节点指针
pDel = GetClassXP(hWnd);
if (pDel != NULL)
{
// 如果待删除的节点就是 g_pClassXP 节点
if (pDel == g_pClassXP)
pCxp = g_pClassXP = pDel->pNext;
else
{
// 循环查找待删除节点的上一个节点
for (pCxp = g_pClassXP; pCxp != NULL; pCxp = pCxp->pNext)
{
// 如果找到
if (pCxp->pNext == pDel)
{
// 使链表跳过待删除的节点
pCxp->pNext = pDel->pNext;
break;
}
}
}
// 取消窗口子类化并重画窗口
SetWindowLong(hWnd, GWL_WNDPROC, (LONG) pDel->wpPrev);
// 删除堆内存
HeapFree(GetProcessHeap(), 0, pDel);
// 重画窗口
RedrawWindow(hWnd, NULL, NULL,
RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ERASENOW | RDW_UPDATENOW);
return pCxp;
}
return NULL;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// 获取窗口的 CLASSXP 数据结构
// 如果返回 NULL,表示没有找到;否则返回节点的指针
PCLASSXP WINAPI GetClassXP(HWND hWnd)
{
PCLASSXP pCxp;
for (pCxp = g_pClassXP; pCxp != NULL; pCxp = pCxp->pNext)
{
if (pCxp->hWnd == hWnd)
return pCxp;
}
return FALSE;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// 获取窗口类型
DWORD WINAPI GetWindowTypeXP(HWND hWnd)
{
DWORD lReturn;
char szTemp[MAX_PATH];
static char s_szClass[][32] =
{
"Button", // 按钮类
"Edit", // 编辑框类
"ComboBox", // 组合框类
#ifdef CXP_DLLMODE
"TButton", // VCL TButton 类
"ThunderCommandButton", // Visual Basic Command Button 类
"ThunderRT6CommandButton", // Visual Basic Command Button 类
"TCheckBox",
"ThunderCheckBox",
"ThunderRT6CheckBox",
"TEdit",
"TNumberEdit",
"ThunderTextBox",
"ThunderRT6TextBox",
"TComboBox",
"ThunderComboBox",
"ThunderRT6ComboBox"
#endif // CXP_DLLMODE
};
// 查找判断匹配的类名称
GetClassName(hWnd, szTemp, sizeof(szTemp));
for (lReturn = 0; lReturn < (sizeof(s_szClass) / sizeof(s_szClass[0])); lReturn++)
if (lstrcmpi(szTemp, s_szClass[lReturn]) == 0)
break;
switch (lReturn)
{
case 0:
lReturn = GetWindowLong(hWnd, GWL_STYLE);
switch (lReturn & SS_TYPEMASK)
{
case BS_DEFPUSHBUTTON: // 默认按钮
case BS_PUSHBUTTON: // 普通按钮
lReturn = CXPT_PUSHBUTTON;
break;
case BS_CHECKBOX: // 复选框
case BS_AUTOCHECKBOX: // 自动复选框
case BS_3STATE: // 三状态复选框
case BS_AUTO3STATE: // 自动三状态复选框
if (lReturn & BS_PUSHLIKE)
lReturn = CXPT_PUSHBUTTON;
else
lReturn = CXPT_CHECKBOX;
break;
case BS_RADIOBUTTON: // 单选框
case BS_AUTORADIOBUTTON: // 自动单选框
if (lReturn & BS_PUSHLIKE)
lReturn = CXPT_PUSHBUTTON;
else
lReturn = CXPT_RADIOBOX;
break;
default: // 未知类型
lReturn = CXPT_UNKNOWN;
}
break;
case 1: // 编辑框
lReturn = CXPT_EDITBOX;
break;
case 2: // 组合框
if ((GetWindowLong(hWnd, GWL_STYLE) & 0x00000003) == CBS_SIMPLE)
lReturn = CXPT_UNKNOWN;
else
lReturn = CXPT_COMBOBOX;
break;
#ifdef CXP_DLLMODE
// VB 和 VCL 的控件,只有在动态连接库方式下才有可能出现这种情况
case 3:
case 4:
case 5:
lReturn = CXPT_PUSHBUTTON;
break;
case 6:
case 7:
case 8:
lReturn = CXPT_CHECKBOX;
break;
case 9:
case 10:
case 11:
case 12:
lReturn = CXPT_EDITBOX;
break;
case 13:
case 14:
case 15:
lReturn = CXPT_COMBOBOX;
break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -