如何在 Win32 控制台应用程序中注册不可见的窗口类?
Posted
技术标签:
【中文标题】如何在 Win32 控制台应用程序中注册不可见的窗口类?【英文标题】:how do I register an invisible window class in a Win32 console application? 【发布时间】:2020-05-29 14:27:55 【问题描述】:我正在尝试在 Win32 控制台应用程序中注册一个不可见的窗口。我的目标是在 WindowProc 中侦听原始输入,以 (1) 在控制台上显示它,以及 (2) 执行额外的计算,例如通过 Web 套接字发送信息。我关注了this CodeProject article,但我的WNDCLASSEX
注册似乎失败了。
这是我的代码:
方法 1 -- 注册似乎不起作用
我的主要功能
WNDCLASSEX wndclass;
wndclass.cbSize = sizeof(WNDCLASSEX);
wndclass.lpfnWndProc = NVTouch_WindowProc;
wndclass.hInstance = GetModuleHandle(NULL);
wndclass.lpszClassName = L"myclass";
bool isClassRegistered = false;
isClassRegistered = RegisterClassEx(&wndclass);
if (isClassRegistered) //1
HWND window = CreateWindow(wndclass.lpszClassName, NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, GetModuleHandle(0), NULL);
if (window)
ShowWindow(window, SW_SHOWDEFAULT);
MSG msg;
while (GetMessage(&msg, 0, 0, 0))
TranslateMessage(&msg);
DispatchMessage(&msg);
我的 WindowProc 函数:
static LRESULT CALLBACK NVTouch_WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
std::cout << "window Proc";
bool registrationStatus = false;
switch (message)
case WM_CREATE:
registrationStatus = registerTouchpadForInput();
break;
case WM_DESTROY:
std::cout << "destroying application.";
PostQuitMessage(0);
break;
case WM_INPUT:
std::cout << "input!";
break;
default:
std::cout << "default.";
return DefWindowProc(hwnd, message, wParam, lParam);
return 0;
当我在 Visual Studio 2019 中设置断点时,我注意到我的代码从 //1
跳转到 //2
,注册状态为 false
。
我有一个基于this article 的类似实现,注册成功,但我无法收听WM_INPUT
消息。
方法 2 -- 可以创建窗口,但不会读取 WM_INPUT 消息
int main()
WNDCLASSEX wndclass =
sizeof(WNDCLASSEX),
CS_DBLCLKS,
NVTouch_WindowProc,
0,
0,
GetModuleHandle(0),
LoadIcon(0,IDI_APPLICATION),
LoadCursor(0,IDC_ARROW),
HBRUSH(COLOR_WINDOW + 1),
0,
L"myclass",
LoadIcon(0,IDI_APPLICATION)
;
bool isClassRegistered = false;
isClassRegistered = RegisterClassEx(&wndclass);
if (isClassRegistered)
std::cout << "window class registered!";
HWND window = CreateWindowEx(0, L"myclass", L"title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, GetModuleHandle(0), 0);
if (window)
ShowWindow(window, SW_SHOWDEFAULT);
MSG msg;
while (GetMessage(&msg, 0, 0, 0))
TranslateMessage(&msg);
DispatchMessage(&msg);
我希望在理想情况下注册类并在控制台上显示原始输入(方法 1),但能够做到这一点,修改方法 2 代码也可以。
为什么我在方法 1 中注册失败?为什么我无法在方法 2 中收听 WM_INPUT
消息?
【问题讨论】:
您需要在使用wndclass
之前将其归零和/或设置其中的所有值而不是使其浮动。
请注意WM_INPUT 文档中的RegisterRawInputDevices 部分。
是的。我有一个 RegisterRawInput 的函数,它可以工作。为简洁起见,我没有包括它。它在 registerTouchpadForInput() 函数中被调用。
可能不是问题,但您正在泄漏资源。来自WM_INPUT:“应用程序必须调用DefWindowProc,这样系统才能执行清理。”
【参考方案1】:
在方法 1 中:
你需要初始化wndclass
:
WNDCLASSEX wndclass = 0 ;
否则,未初始化的部分默认为随机值(未定义的行为,通常像0xcccccccc
),RegisterClassEx
将失败,错误代码为:87。
在方法 2 中:
你初始化了wndclass
中的所有成员,所以RegisterClassEx
成功了。
我无法用方法 2 重现问题,问题可能出在 registerTouchpadForInput
。
注意到这个函数没有参数,但是RegisterRawInputDevices
需要设置RAWINPUTDEVICE.hwndTarget
,可能你没有设置目标窗口,如果你设置为NULL,根据document,你必须把键盘焦点放在你的窗口来接收消息。
另外,在方法2中,程序会创建一个可见窗口。要生成一个不可见的窗口,请按照方法 1 创建一个 Message-Only Windows。
样本:
#include <windows.h>
#include <iostream>
using namespace std;
BOOL registerTouchpadForInput(HWND hWnd)
RAWINPUTDEVICE rid;
rid.dwFlags = RIDEV_NOLEGACY | RIDEV_INPUTSINK;
rid.usUsagePage = 1; // raw keyboard data only
rid.usUsage = 6;
rid.hwndTarget = hWnd;
return RegisterRawInputDevices(&rid, 1, sizeof(rid));
static LRESULT CALLBACK NVTouch_WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
std::cout << "window Proc";
BOOL registrationStatus = false;
switch (message)
case WM_CREATE:
registrationStatus = registerTouchpadForInput(hwnd);
break;
case WM_DESTROY:
std::cout << "destroying application.";
PostQuitMessage(0);
break;
case WM_INPUT:
std::cout << "input!";
return DefWindowProc(hwnd, message, wParam, lParam);
default:
std::cout << "default.";
return DefWindowProc(hwnd, message, wParam, lParam);
return 0;
int main()
WNDCLASSEX wndclass =
sizeof(WNDCLASSEX),
CS_DBLCLKS,
NVTouch_WindowProc,
0,
0,
GetModuleHandle(0),
LoadIcon(0,IDI_APPLICATION),
LoadCursor(0,IDC_ARROW),
HBRUSH(COLOR_WINDOW + 1),
0,
L"myclass",
LoadIcon(0,IDI_APPLICATION)
;
bool isClassRegistered = false;
isClassRegistered = RegisterClassEx(&wndclass);
if (isClassRegistered)
std::cout << "window class registered!";
//HWND window = CreateWindowEx(0, L"myclass", L"title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, GetModuleHandle(0), 0);
HWND window = CreateWindow(wndclass.lpszClassName, NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, GetModuleHandle(0), NULL);
if (window)
ShowWindow(window, SW_SHOWDEFAULT);
MSG msg;
while (GetMessage(&msg, 0, 0, 0))
TranslateMessage(&msg);
DispatchMessage(&msg);
【讨论】:
嗨,@Venkatesh Potluri,这个样本对你有帮助吗?如果确实解决了问题,请随时accept回答。 你是对的。我将 hwndTarget 设置为 NULL。我将文档解释为将其设置为 null 将跟随键盘焦点并从具有键盘焦点的窗口给我输入消息。有趣的是,将 hwndTarget 设置为 NULL 会导致 Win32 参数无效错误。进行更改会导致我的方法一起作用,您的示例显示了如何在方法二中执行此操作。谢谢!以上是关于如何在 Win32 控制台应用程序中注册不可见的窗口类?的主要内容,如果未能解决你的问题,请参考以下文章
在 Python 中通过 win32com 使用 COM 对象
使用AllocConsole在Win32程序中调用控制台调试输出