如何在 Win32 控制台应用程序中注册一个不可见的 window class?
how do I register an invisible window class in a Win32 console application?
我正在尝试在 Win32 控制台应用程序中注册一个不可见的 window。我的目标是在 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 -- window 创建有效,但 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);
}
}
}
}
我想理想地注册 class 并在控制台上显示原始输入(方法 1),但能够执行该修改方法 2 代码也可以。
为什么我在方法 1 中注册失败?为什么我无法在方法 2 中收听 WM_INPUT
消息?
在方法 1 中:
您需要初始化wndclass
:
WNDCLASSEX wndclass = { 0 };
否则,未初始化部分默认为随机值(未定义行为,通常如0xcccccccc
),RegisterClassEx
将失败,错误代码:87。
在方法 2 中:
您初始化了 wndclass
中的所有成员,所以 RegisterClassEx
成功了。
我无法用方法 2 重现问题,问题可能出在 registerTouchpadForInput
.
注意到这个函数没有参数,但是RegisterRawInputDevices
需要设置RAWINPUTDEVICE.hwndTarget
,可能你没有设置目标window,如果你设置为NULL,根据document,您必须将键盘对准 window 才能接收消息。
此外,在方法 2 中,程序将创建一个可见的 window。要生成不可见的 window,请按照方法 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);
}
}
}
}
我正在尝试在 Win32 控制台应用程序中注册一个不可见的 window。我的目标是在 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 -- window 创建有效,但 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);
}
}
}
}
我想理想地注册 class 并在控制台上显示原始输入(方法 1),但能够执行该修改方法 2 代码也可以。
为什么我在方法 1 中注册失败?为什么我无法在方法 2 中收听 WM_INPUT
消息?
在方法 1 中:
您需要初始化wndclass
:
WNDCLASSEX wndclass = { 0 };
否则,未初始化部分默认为随机值(未定义行为,通常如0xcccccccc
),RegisterClassEx
将失败,错误代码:87。
在方法 2 中:
您初始化了 wndclass
中的所有成员,所以 RegisterClassEx
成功了。
我无法用方法 2 重现问题,问题可能出在 registerTouchpadForInput
.
注意到这个函数没有参数,但是RegisterRawInputDevices
需要设置RAWINPUTDEVICE.hwndTarget
,可能你没有设置目标window,如果你设置为NULL,根据document,您必须将键盘对准 window 才能接收消息。
此外,在方法 2 中,程序将创建一个可见的 window。要生成不可见的 window,请按照方法 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);
}
}
}
}