如何在 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 控制台应用程序中注册不可见的窗口类?的主要内容,如果未能解决你的问题,请参考以下文章

如何在win32 C++控制台应用程序中调用uwp类库

在 Python 中通过 win32com 使用 COM 对象

使用AllocConsole在Win32程序中调用控制台调试输出

如何在 Win32 控制台中隐藏鼠标光标?

在win32 application下如何修改应用程序图标?

win32如何自己编写控件