Win32编程之通过SetWindowsHookEx注入DLL(十六)
一、SetWindowsHookEx函数
SetWindowsHookEx 是用于在Windows操作系统中设置全局或本地的钩子(hook)。钩子是一种用于监视并拦截特定事件或消息的机制,通常用于拦截和处理键盘输入、鼠标操作、窗口消息等。SetWindowsHookEx 允许你安装一个全局或本地的钩子过程,以便在事件发生时执行自定义的代码。
函数的介绍:
HHOOK SetWindowsHookEx( int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId );
参数说明:
-
idHook: 指定要安装的钩子类型。可以是以下之一:WH_KEYBOARD: 用于监视键盘输入。WH_MOUSE: 用于监视鼠标事件。- 其他钩子类型,用于监视不同类型的事件。
-
lpfn: 指定一个回调函数的地址,当特定事件发生时,该函数将被调用。 -
hMod: 指定包含钩子函数的模块句柄。通常情况下,可以将其设置为NULL,表示使用当前进程的模块句柄。 -
dwThreadId: 指定要关联钩子的线程ID。如果是全局钩子,可以将其设置为 0,将钩子应用到所有线程。
SetWindowsHookEx 返回一个非零值,表示成功安装钩子,或者返回 NULL 表示安装失败。安装成功后,钩子函数将开始监视并处理指定类型的事件。
二、UnhookWindowsHookEx函数
UnhookWindowsHookEx 是用于卸载先前通过 SetWindowsHookEx 安装的全局或本地钩子。钩子是用于监视和拦截特定事件或消息的机制。在不再需要一个已安装的钩子时,你应该使用 UnhookWindowsHookEx 来卸载它,以释放相关资源并停止拦截事件。
函数的介绍:
BOOL UnhookWindowsHookEx( HHOOK hhk );
参数说明:
hhk: 要卸载的钩子的句柄,它是通过SetWindowsHookEx安装钩子时返回的句柄。
UnhookWindowsHookEx 返回一个布尔值,如果成功卸载钩子则返回非零值,否则返回零。
SetWindowsHookEx函数和UnhookWindowsHookEx的使用示例:
// HookApp.cpp : 定义应用程序的入口点。
//
#include "framework.h"
#include "HookApp.h"
#define MAX_LOADSTRING 100
typedef struct _MYHOOKDATA {
int nType;
HOOKPROC hkprc;
HHOOK hhok;
}MYHOOKDATA;
MYHOOKDATA myHookData[7];
LRESULT CALLBACK CallWndProc(int code, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK CBTProc(int code, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK DebugProc(int code, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK GetMsgProc(int code, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK MessageProc(int code, WPARAM wParam, LPARAM lParam);
// 全局变量:
HINSTANCE hInst; // 当前实例
WCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名
// 此代码模块中包含的函数的前向声明:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: 在此处放置代码。
// 初始化全局字符串
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_HOOKAPP, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// 执行应用程序初始化:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_HOOKAPP));
MSG msg;
// 主消息循环:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
//
// 函数: MyRegisterClass()
//
// 目标: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_HOOKAPP));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_HOOKAPP);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
//
// 函数: InitInstance(HINSTANCE, int)
//
// 目标: 保存实例句柄并创建主窗口
//
// 注释:
//
// 在此函数中,我们在全局变量中保存实例句柄并
// 创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // 将实例句柄存储在全局变量中
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// 函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 目标: 处理主窗口的消息。
//
// WM_COMMAND - 处理应用程序菜单
// WM_PAINT - 绘制主窗口
// WM_DESTROY - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WM_CREATE: {
OutputDebugString(TEXT("HookApp WM_CREATE"));
myHookData[0].nType = WH_CALLWNDPROC;
myHookData[0].hkprc = CallWndProc;
myHookData[1].nType = WH_CBT;
myHookData[1].hkprc = CBTProc;
myHookData[2].nType = WH_DEBUG;
myHookData[2].hkprc = DebugProc;
myHookData[3].nType = WH_GETMESSAGE;
myHookData[3].hkprc = GetMsgProc;
myHookData[4].nType = WH_KEYBOARD;
myHookData[4].hkprc = KeyboardProc;
myHookData[5].nType = WH_MOUSE;
myHookData[5].hkprc = MouseProc;
myHookData[6].nType = WH_MSGFILTER;
myHookData[6].hkprc = MessageProc;
break;
}
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_CHAR: {
switch (wParam) {
case 'a':
OutputDebugString(TEXT("HookApp HOOK WH_CALLWNDPROC"));
myHookData[0].hhok = SetWindowsHookEx(myHookData[0].nType, myHookData[0].hkprc, (HINSTANCE)NULL, GetCurrentThreadId());
break;
case 'b':
OutputDebugString(TEXT("HookApp UNHOOK WH_CALLWNDPROC"));
UnhookWindowsHookEx(myHookData[0].hhok);
break;
case 'c':
OutputDebugString(TEXT("HookApp HOOK WH_CBT"));
myHookData[1].hhok = SetWindowsHookEx(myHookData[1].nType, myHookData[1].hkprc, (HINSTANCE)NULL, GetCurrentThreadId());
break;
case 'd':
OutputDebugString(TEXT("HookApp UNHOOK WH_CBT"));
UnhookWindowsHookEx(myHookData[1].hhok);
break;
case 'e':
OutputDebugString(TEXT("HookApp HOOK WH_DEBUG"));
myHookData[2].hhok = SetWindowsHookEx(myHookData[2].nType, myHookData[2].hkprc, (HINSTANCE)NULL, GetCurrentThreadId());
break;
case 'f':
OutputDebugString(TEXT("HookApp UNHOOK WH_DEBUG"));
UnhookWindowsHookEx(myHookData[2].hhok);
case 'g':
OutputDebugString(TEXT("HookApp HOOK WH_GETMESSAGE"));
myHookData[3].hhok = SetWindowsHookEx(myHookData[3].nType, myHookData[3].hkprc, (HINSTANCE)NULL, GetCurrentThreadId());
break;
case 'h':
OutputDebugString(TEXT("HookApp UNHOOK WH_GETMESSAGE"));
UnhookWindowsHookEx(myHookData[3].hhok);
break;
case 'i':
OutputDebugString(TEXT("HookApp HOOK WH_KEYBOARD"));
myHookData[4].hhok = SetWindowsHookEx(myHookData[4].nType, myHookData[4].hkprc, (HINSTANCE)NULL, GetCurrentThreadId());
break;
case 'j':
OutputDebugString(TEXT("HookApp UNHOOK WH_KEYBOARD"));
UnhookWindowsHookEx(myHookData[4].hhok);
break;
case 'k':
OutputDebugString(TEXT("HookApp HOOK WH_MOUSE"));
myHookData[5].hhok = SetWindowsHookEx(myHookData[5].nType, myHookData[5].hkprc, (HINSTANCE)NULL, GetCurrentThreadId());
break;
case 'l':
OutputDebugString(TEXT("HookApp UNHOOK WH_MOUSE"));
UnhookWindowsHookEx(myHookData[5].hhok);
break;
case 'm':
OutputDebugString(TEXT("HookApp HOOK WH_MSGFILTER"));
myHookData[6].hhok = SetWindowsHookEx(myHookData[6].nType, myHookData[6].hkprc, (HINSTANCE)NULL, GetCurrentThreadId());
break;
case 'n':
OutputDebugString(TEXT("HookApp UNHOOK WH_MSGFILTER"));
UnhookWindowsHookEx(myHookData[6].hhok);
break;
default:
break;
}
break;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: 在此处添加使用 hdc 的任何绘图代码...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
LRESULT CALLBACK CallWndProc(int code, WPARAM wParam, LPARAM lParam)
{
switch (code) {
case HC_ACTION:
OutputDebugString(TEXT("HookApp CallWndProc HC_ACTION"));
default:
break;
}
return CallNextHookEx(myHookData[0].hhok, code, wParam, lParam);
}
LRESULT CALLBACK CBTProc(int code, WPARAM wParam, LPARAM lParam)
{
switch (code) {
case HCBT_ACTIVATE:
OutputDebugString(TEXT("HookApp CBTProc HCBT_ACTIVATE"));
break;
case HCBT_CLICKSKIPPED:
OutputDebugString(TEXT("HookApp CBTProc HCBT_CLICKSKIPPED"));
break;
case HCBT_MINMAX:
OutputDebugString(TEXT("HookApp CBTProc HCBT_MINMAX"));
break;
default:
break;
}
return CallNextHookEx(myHookData[1].hhok, code, wParam, lParam);
}
LRESULT CALLBACK DebugProc(int code, WPARAM wParam, LPARAM lParam)
{
switch (code) {
case HC_ACTION:
OutputDebugString(TEXT("HookApp DebugProc HC_ACTION"));
break;
default:
break;
}
return CallNextHookEx(myHookData[2].hhok, code, wParam, lParam);
}
LRESULT CALLBACK GetMsgProc(int code, WPARAM wParam, LPARAM lParam)
{
switch (code) {
case HC_ACTION:
OutputDebugString(TEXT("HookApp GetMsgProc HC_ACTION"));
break;
default:
break;
}
return CallNextHookEx(myHookData[3].hhok, code, wParam, lParam);
}
LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam)
{
if (code < 0) {
return CallNextHookEx(myHookData[4].hhok, code, wParam, lParam);
}
switch (code) {
case HC_ACTION: {
OutputDebugString(TEXT("HookApp KeyboardProc HC_ACTION"));
if (lParam & 0x20000000) {
//按下ALT键
OutputDebugString(TEXT("HookApp KeyboardProc HC_ACTION [ALT] KEYDOWN"));
}
if (lParam & 0x80000000) {
//释放按下的键
OutputDebugString(TEXT("HookApp KeyboardProc HC_ACTION [ALT] KEYUP"));
}
break;
}
default:
break;
}
return CallNextHookEx(myHookData[4].hhok, code, wParam, lParam);
}
LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam)
{
if (code < 0) {
return CallNextHookEx(myHookData[5].hhok, code, wParam, lParam);
}
switch (wParam) {
case WM_LBUTTONDOWN: {
OutputDebugString(TEXT("HookApp MouseProc WM_LBUTTONDOWN"));
PMOUSEHOOKSTRUCT pInfo = (PMOUSEHOOKSTRUCT)lParam;
WCHAR buff[30] = { 0 };
wsprintf(buff, TEXT("HookApp (%d, %d)"), pInfo->pt.x, pInfo->pt.y);
OutputDebugString(buff);
}
default:
break;
}
return CallNextHookEx(myHookData[5].hhok, code, wParam, lParam);
}
LRESULT CALLBACK MessageProc(int code, WPARAM wParam, LPARAM lParam)
{
switch (code) {
case MSGF_DIALOGBOX:
OutputDebugString(TEXT("HookApp MessageProc MSGF_DIALOGBOX"));
break;
case MSGF_MENU:
OutputDebugString(TEXT("HookApp MessageProc MSGF_MENU"));
break;
default:
OutputDebugString(TEXT("HookApp MessageProc default"));
break;
}
return CallNextHookEx(myHookData[6].hhok, code, wParam, lParam);
}
三、在DLL文件中注册和解除钩子
DLL头文件:
#pragma once
#include <Windows.h>
extern "C" {
__declspec(dllexport) void __stdcall SetHookById(int id);
__declspec(dllexport) void __stdcall UnHookById(int id);
}
LRESULT CALLBACK CallWndProc(int code, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK CBTProc(int code, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK DebugProc(int code, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK GetMsgProc(int code, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK MessageProc(int code, WPARAM wParam, LPARAM lParam);
DLL源文件:
#include "HookMsg.h"
#include <Windows.h>
typedef struct _MYHOOKDATA {
int nType;
HOOKPROC hkprc;
HHOOK hhok;
}MYHOOKDATA;
//#pragma data_msg("shared")
//MYHOOKDATA myHookData[7];
//#pragma data_msg()
MYHOOKDATA myHookData[7];
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH: {
OutputDebugString(TEXT("HookMsg DLL_PROCESS_ATTACH"));
myHookData[0].nType = WH_CALLWNDPROC;
myHookData[0].hkprc = CallWndProc;
myHookData[1].nType = WH_CBT;
myHookData[1].hkprc = CBTProc;
myHookData[2].nType = WH_DEBUG;
myHookData[2].hkprc = DebugProc;
myHookData[3].nType = WH_GETMESSAGE;
myHookData[3].hkprc = GetMsgProc;
myHookData[4].nType = WH_KEYBOARD;
myHookData[4].hkprc = KeyboardProc;
myHookData[5].nType = WH_MOUSE;
myHookData[5].hkprc = MouseProc;
myHookData[6].nType = WH_MSGFILTER;
myHookData[6].hkprc = MessageProc;
break;
}
case DLL_THREAD_ATTACH:break;
case DLL_THREAD_DETACH:break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
void __stdcall SetHookById(int id) {
OutputDebugString(TEXT("HookMsg SetHookById start"));
myHookData[id].hhok = SetWindowsHookEx(myHookData[id].nType, myHookData[id].hkprc, (HINSTANCE)NULL, GetCurrentThreadId());
OutputDebugString(TEXT("HookMsg SetHookById end"));
}
void __stdcall UnHookById(int id) {
UnhookWindowsHookEx(myHookData[id].hhok);
}
LRESULT CALLBACK CallWndProc(int code, WPARAM wParam, LPARAM lParam)
{
switch (code) {
case HC_ACTION:
OutputDebugString(TEXT("HookMsg CallWndProc HC_ACTION"));
default:
break;
}
return CallNextHookEx(myHookData[0].hhok, code, wParam, lParam);
}
LRESULT CALLBACK CBTProc(int code, WPARAM wParam, LPARAM lParam)
{
switch (code) {
case HCBT_ACTIVATE:
OutputDebugString(TEXT("HookMsg CBTProc HCBT_ACTIVATE"));
break;
case HCBT_CLICKSKIPPED:
OutputDebugString(TEXT("HookMsg CBTProc HCBT_CLICKSKIPPED"));
break;
case HCBT_MINMAX:
OutputDebugString(TEXT("HookMsg CBTProc HCBT_MINMAX"));
break;
default:
break;
}
return CallNextHookEx(myHookData[1].hhok, code, wParam, lParam);
}
LRESULT CALLBACK DebugProc(int code, WPARAM wParam, LPARAM lParam)
{
switch (code) {
case HC_ACTION:
OutputDebugString(TEXT("HookMsg DebugProc HC_ACTION"));
break;
default:
break;
}
return CallNextHookEx(myHookData[2].hhok, code, wParam, lParam);
}
LRESULT CALLBACK GetMsgProc(int code, WPARAM wParam, LPARAM lParam)
{
switch (code) {
case HC_ACTION:
OutputDebugString(TEXT("HookMsg GetMsgProc HC_ACTION"));
break;
default:
break;
}
return CallNextHookEx(myHookData[3].hhok, code, wParam, lParam);
}
LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam)
{
if (code < 0) {
return CallNextHookEx(myHookData[4].hhok, code, wParam, lParam);
}
switch (code) {
case HC_ACTION: {
OutputDebugString(TEXT("HookMsg KeyboardProc HC_ACTION"));
if (lParam & 0x20000000) {
//按下ALT键
OutputDebugString(TEXT("HookMsg KeyboardProc HC_ACTION [ALT] KEYDOWN"));
}
if (lParam & 0x80000000) {
//释放按下的键
OutputDebugString(TEXT("HookMsg KeyboardProc HC_ACTION [ALT] KEYUP"));
}
break;
}
default:
break;
}
return CallNextHookEx(myHookData[4].hhok, code, wParam, lParam);
}
LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam)
{
if (code < 0) {
return CallNextHookEx(myHookData[5].hhok, code, wParam, lParam);
}
switch (wParam) {
case WM_LBUTTONDOWN: {
OutputDebugString(TEXT("HookMsg MouseProc WM_LBUTTONDOWN"));
PMOUSEHOOKSTRUCT pInfo = (PMOUSEHOOKSTRUCT)lParam;
WCHAR buff[30] = { 0 };
wsprintf(buff, TEXT("HookMsg (%d, %d)"), pInfo->pt.x, pInfo->pt.y);
OutputDebugString(buff);
}
default:
break;
}
return CallNextHookEx(myHookData[5].hhok, code, wParam, lParam);
}
LRESULT CALLBACK MessageProc(int code, WPARAM wParam, LPARAM lParam)
{
switch (code) {
case MSGF_DIALOGBOX:
OutputDebugString(TEXT("HookMsg MessageProc MSGF_DIALOGBOX"));
break;
case MSGF_MENU:
OutputDebugString(TEXT("HookMsg MessageProc MSGF_MENU"));
break;
default:
OutputDebugString(TEXT("HookMsg MessageProc default"));
break;
}
return CallNextHookEx(myHookData[6].hhok, code, wParam, lParam);
}
示例代码调用:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_CHAR: {
switch (wParam) {
case 'a':
g_pSetHook(0);
break;
case 'b':
g_pUnHook(0);
break;
case 'c':
g_pSetHook(1);
break;
case 'd':
g_pUnHook(1);
break;
case 'e':
g_pSetHook(2);
break;
case 'f':
g_pUnHook(2);
case 'g':
g_pSetHook(3);
break;
case 'h':
g_pUnHook(3);
break;
case 'i':
g_pSetHook(4);
break;
case 'j':
g_pUnHook(4);
break;
case 'k':
g_pSetHook(5);
break;
case 'l':
g_pUnHook(5);
break;
case 'm':
g_pSetHook(6);
break;
case 'n':
g_pUnHook(6);
break;
case 'x': {
//加载DLL
OutputDebugString(TEXT("hookApp Load Library"));
g_hModule = LoadLibrary(TEXT("HookMsg.dll"));
if (g_hModule != NULL) {
g_pSetHook = (HOOK_FUNC)GetProcAddress(g_hModule, "SetHookById");
g_pUnHook = (HOOK_FUNC)GetProcAddress(g_hModule, "UnHookById");
}
break;
}
case 'y': {
//释放dll
OutputDebugString(TEXT("hookApp Free Library"));
FreeLibrary(g_hModule);
break;
}
default:
break;
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: 在此处添加使用 hdc 的任何绘图代码...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}

浙公网安备 33010602011771号