【问题标题】:Do CustomDialogProc have to be static. WinAPICustomDialogProc 是否必须是静态的。 WinAPI
【发布时间】:2017-02-10 20:16:53
【问题描述】:

我一直在寻找使用 WinApi 为我的应用程序创建自定义控件,并且我创建了一个包含 CustomDialogProcCreateWindowExRegisterClass() 函数的类。

我可以在CustomDialogProc 中设置一个断点,它会命中,所以类注册正确。

但是,我必须将 CustomDialogProc 函数声明为我的类的静态标题

static LRESULT CALLBACK CustomDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam,LPARAM lParam);

如果我不将它设置为静态,我会得到错误

Error   C3867   'CustomControl::CustomDialogProc': non-standard syntax; use '&' to create a pointer to member   

这是必要的吗,这要求我在此控件中创建的所有控件也都是静态的。如果我想要这个控件的多个实例怎么办? 我怎样才能解决这个问题?主 MsgProc 似乎不是静态函数。下面显示的第一个链接中的UpDownDialogProc 也不是

下面是我的 CustomControl.h 代码,以防有人需要。 将代码放在一起: https://msdn.microsoft.com/en-us/library/windows/desktop/hh298353(v=vs.85).aspx https://www.codeproject.com/Articles/485767/True-Windows-control-subclassing

谢谢,

#pragma once
#include <windows.h>

#include <commctrl.h>

#pragma comment(lib, "comctl32.lib")

class CustomControl
{
public:
    CustomControl();
    ~CustomControl();

    LRESULT CALLBACK CustomDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam,LPARAM lParam)
    {
        switch (message)
        {
            case WM_CREATE:
            //DO STUFF HERE
            break;
        }
    }

    bool CreateControl(HWND hwnd, HINSTANCE* m_hApp_instance)
    {
        g_hInst = m_hApp_instance;

        RegisterSubClass(*g_hInst, WC_LISTBOX, TEXT("CustomControl"), CustomDialogProc);

        HWND hwndCustom = CreateWindow(TEXT("CustomControl"), NULL, WS_CHILD | WS_VISIBLE,
        0, 0, 0, 0, hwnd, (HMENU)100, *g_hInst, NULL);

        return true;
    }

private:

    HINSTANCE* g_hInst;

    WNDPROC RegisterSubClass(HINSTANCE hInstance, LPCTSTR ParentClass, LPCTSTR ChildClassName, WNDPROC ChildProc) {
        WNDCLASSEX  twoWayStruct;
        WNDPROC     parentWndProc;

        if (GetClassInfoEx(NULL, ParentClass, &twoWayStruct)) {
            parentWndProc = twoWayStruct.lpfnWndProc; // save the original message handler 

            twoWayStruct.cbSize = sizeof(WNDCLASSEX); // does not always get filled properly
            twoWayStruct.hInstance = hInstance;
            twoWayStruct.lpszClassName = ChildClassName;
            twoWayStruct.lpfnWndProc = ChildProc;

            /* Register the window class, and if it fails return 0 */
            if (RegisterClassEx(&twoWayStruct))
                return parentWndProc; // returns the parent class WndProc pointer;
                                      // subclass MUST call it instead of DefWindowProc();
                                      // if you do not save it, this function is wasted
        }
        return 0;
    }
};

【问题讨论】:

  • DialogProcs(和类似的回调)不能是非静态成员函数,因为它们需要传递给 C API,而 C API 不理解这些东西。
  • 既然您已经安装了 Visual Studio,您应该阅读 MFC 源代码。它是少数几个能做到这一点的实现之一(公认的答案没有)。
  • 说实话,编写一个好的 Windows API 包装器是一项全职工作。这可不是那么容易被拍打的东西。 API 足够复杂,有各种曲折,如果你对 C++ 的了解不够(至少接近高级水平),这本身就是一个攀登的障碍。
  • Class method for WndProc的可能重复

标签: c++ winapi visual-studio-2015 com


【解决方案1】:

最常见的方法是使用SetWindowLongPtr 来存储指向与窗口句柄关联的对象的指针。

HWND hWnd = CreateWindow(...);
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) this);

然后在您的对话过程中,获取该指针并调用您的类:

// this static method is registered with your window class
static LRESULT CALLBACK CustomDialogProcStatic(HWND hWnd, UINT uMsg, WPARAM wParam,LPARAM lParam)
{
    auto pThis = (CustomControl*) GetWindowLongPtr(hWnd, GWLP_USERDATA);
    if (pThis != NULL)
        return pThis->CustomDialogProcInstance(hWnd, uMsg, wParam, lParam);
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

// this instance method is called by the static method
LRESULT CustomDialogProcInstance(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    ...
}

确保适当地管理窗口和类的生命周期,以防止窗口进程调用已删除的对象实例。在许多情况下,这就像确保在您的类被破坏时调用 DestroyWindow 一样简单。

【讨论】:

  • 谢谢,我会在将其标记为答案之前尝试解决这个问题
  • 我试过了,它编译了,但在行中:auto pThis = (CustomControl*)GetWindowLongPtr(hWnd, GWLP_USERDATA); p这总是NULL
  • @Kenneth - 因为你没有打电话给SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)this);
  • 我确实在 CreateWindow 之后致电 SetWindowLongPtr。奇怪的是,如果我在 return pThis-&gt;CustomDialogProcInstance(hWnd, uMsg, wParam, lParam); 上设置断点,它会触发,但仅在我关闭应用程序时触发,而不是在它运行时触发。但是,我在if (pThis != NULL) 上运行时遇到断点,它只是 pThis 为 NULL,直到程序由于某种原因关闭
  • SetWindowSubclass()much safer option,而不是使用 SetWindowLongPtr()。您可以在回调的dwContext 中传递this,而根本不必将其存储在HWND 本身中。
【解决方案2】:

Windows API 基于C 语言。它对C++、非静态成员函数、对象等一无所知。

所以是的,您所有将与 Windows API直接通信的函数必须static 类成员函数,或非类/全局/“免费“ 职能。

这并不妨碍您为 C API 编写 C++ 包装器。这就是诸如 Microsoft 的 MFC 或旧的 Borland OWL 库之类的库所完成的任务。其他独立小组也为 Windows API 编写了包装器。

请注意,这些不同的库以不同的方式实现了将基于 C 的 API 挂钩到 C++ 的目标。一种是使用@MichaelGunter给出的答案中提到的SetWindowLongPtr方法。另一种方法是使用映射来关联窗口句柄和静态窗口过程。

我建议在您自己尝试(创建包装器)之前,先调查其他人是如何做到这一点的,然后选择最适合的方法。另一个建议是,在您创建包装器之前,您应该了解基于 C 的 API,而不仅仅是粗略的了解。如果您希望包装器在不同场景下相对完美地工作,您需要对任何计划创建 C++ 包装器的 C API 具有高级专业知识。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-31
    • 1970-01-01
    • 2018-11-01
    • 2014-05-27
    • 1970-01-01
    • 2012-04-11
    相关资源
    最近更新 更多