【问题标题】:Object oriented c++ win32?面向对象的c++ win32?
【发布时间】:2023-04-05 06:32:01
【问题描述】:

我想创建自己的类来处理创建窗口和窗口过程,但我注意到窗口过程必须是静态的!我现在想知道是否可以使窗口过程面向对象?我已经阅读了一些关于面向对象窗口的教程,但它们总是使过程静态-.- 那有什么用? :/

任何有关如何解决此问题的链接或信息将不胜感激,

谢谢

【问题讨论】:

  • 请参阅[存储此指针以在 WndProc 中使用的最佳方法](stackoverflow.com/questions/117792/…)。
  • 正是出于这个原因,我一直希望WndProc 有一个void* user_data 参数。这将使创建基于对象的包装器变得更加容易。
  • @Evan:是的,但它也需要有人 sane 来负责设计 API...Win32 API 将是一个非常不同的野兽,如果是这样的话。
  • 而且它要求在创建 C++ 之后一次编写 Windows API - Windows API 是在 1983 年左右创建的(并于 1985 年发布),C++ 仅在 1983 年创建并没有直到 1990 年代初才获得主流认可(ARM 直到 1990 年才发布)。
  • @Larry:原则上,当然。但是将一些任意用户数据绑定到上下文回调的概念肯定不是 C++ 独有的。我见过很多具有此功能的“古老”API,而这些 API 恰好与创建 c++ 包装器相得益彰。哦,好吧。

标签: c++ winapi


【解决方案1】:

您可以通过让静态 WndProc 将所有内容委托给成员来解决此问题:

// Forward declarations
class MyWindowClass;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

std::map<HWND, MyWindowClass *> windowMap;

// Your class
class MyWindowClass  {
private:
  HWND m_handle;

  // The member WndProc
  LRESULT MyWndProc(UINT message, WPARAM wParam, LPARAM lParam) { /* ... */ }

public:
  MyWindowClass()
  {
    /* TODO: Create the window here and assign its handle to m_handle */
    /* Pass &WndProc as the pointer to the Window procedure */

    // Register the window
    windowMap[m_handle] = this;
  }
};

// The delegating WndProc
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  std::map<HWND, MyWindowClass *>::iterator it = windowMap.find(hWnd);
  if (it != windowMap.end())
    return it->second->MyWndProc(message, wParam, lParam);
  return 0;
}

【讨论】:

  • +1,但您可能还想在那里检查 WM_DESTROY 并从地图中删除句柄。
  • 不错的例子,看起来很酷。我将尝试自己实现它,很好地使用 std::map 来找到匹配的句柄
  • @SoapBox 在很多方面都不完整,不过感谢您注意到这一点。
  • +1 用于将 HWND 字典转换为 MyWindowClass。我以前见过人们在 win32 结构中存储指针、转换它并直接取消引用它的方法。这是使您的程序容易受到黑客攻击的一种极其简单的方法。
  • 似乎CreateWindowEx 将始终返回NULL,而没有给出来自GetLastError 的详细错误代码(它返回ERROR_SUCCESS)。在委派WndProc 中,您应该将return 0; 替换为return DefWindowProc(hWnd, message, wParam, lParam);,它将解决此问题。
【解决方案2】:

允许将窗口实例表示为类实例的一般技术是利用 SetWindowLongPtr 和 GetWindowLongPtr 将您的类实例指针与窗口句柄相关联。下面是一些示例代码,可帮助您入门。如果没有一些调整,它可能无法编译。仅供参考。

就个人而言,几年前当我发现 ATL 的 CWindow 和 CWindowImpl 模板类时,我已经停止滚动自己的窗口类。他们负责为您完成所有这些平凡的编码,因此可以专注于编写处理窗口消息的方法。请参阅我写的示例代码here

希望这会有所帮助。

class CYourWindowClass
{
private:
    HWND m_hwnd;

public:
    LRESULT WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        switch (uMsg)
        {
            case WM_CREATE: return OnCreate(wParam, lParam);
            case wM_PAINT: return OnPaint(wParam, lParam);
            case WM_DESTROY:
            {
                SetWindowLongPtr(m_hwnd, GWLP_USERDATA, NULL);
                m_hwnd = NULL;
                return 0;
            }
        }
        return DefWindowProc(m_hwnd, uMsg, wParam, lParam);

    }

    CYourWindowClass()
    {
        m_hwnd = NULL;
    }

    ~CYourWindowClass()
    {
        ASSERT(m_hwnd == NULL && "You forgot to destroy your window!");
        if (m_hwnd)
        {
            SetWindowLong(m_hwnd, GWLP_USERDATA, 0);
        }
    }

    bool Create(...) // add whatever parameters you want
    {
        HWND hwnd = CreateWindow("Your Window Class Name", "Your Window title", dwStyle, x, y, width, height, NULL, hMenu, g_hInstance, (LPARAM)this);
        if (hwnd == NULL)
            return false;

        ASSERT(m_hwnd == hwnd);
        return true;
    }


    static LRESULT __stdcall StaticWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        CYourWindowClass* pWindow = (CYourWindowClass*)GetWindowLongPtr(hwnd, GWLP_USERDATA);

        if (uMsg == WM_CREATE)
        {
            pWindow = ((CREATESTRUCT*)lParam)->lpCreateParams;
            SetWindowLongPtr(hwnd, GWLP_USERDATA, (void*)pWindow);
            m_hWnd = hwnd;
        }

        if (pWindow != NULL)
        {
            return pWindow->WndProc(uMsg, wParam, lParam);
        }

        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    };


};

【讨论】:

  • 一直想知道 lpParam 在 CreateWindowEx 函数上的用途,但我支持这可能是使其易受攻击的方式,因为有人可以使用 GetWindowLong 获取您的用户数据:P
【解决方案3】:

如果您正在寻找面向对象的 Win32 API,那么您应该寻找MFC 和/或WTL

【讨论】:

  • 用于多种用途的过度杀伤解决方案。 MFC 可能相对瘦,但它本身仍然是一个大型 API,从一个 API 切换到另一个 API 需要大量工作。如果您只想在为 Win32 编码时使用自己的几个 C++ 类,这也是完全没有必要的。我自己用于 Win32 的旧“对象框架”可能是大约 2 或 3 面代码——只不过是一个基类、一些初始化和一个主要的 GetMessage/etc 循环。
  • MFC 是一个令人讨厌的框架,但它在 Win32 世界中得到了很好的支持并且相对知名。
  • MFC 可能有点矫枉过正,但 WTL 是一种乐趣。一个记录得很差的快乐……但仍然是一种快乐。 (嗯,相对而言与 Win32 和所有)
【解决方案4】:

只是添加到 Brian 的答案,但对于更适合初学者的 win32 框架,请查看Win32++。与 MFC 或 QT 相比,库本身的功能并不全面,但这是设计人员在开始时做出的权衡,以使库易于理解和使用。

如果你仍然对这个主题感兴趣,我强烈建议你看看它,因为它使用了另一种技术,通过利用线程本地存储来保存“this”指针。

【讨论】:

    【解决方案5】:

    您可以使用传递给 WindowProc 的窗口句柄来获取您为该特定窗口创建的对象并将事件处理委托给该对象。

    例如

    IMyWindowInterface* pWnd = getMyWindowObject(hWnd);
    pWnd->ProcessMessage(uMsg, wParam, lParam);
    

    【讨论】:

    • 听起来不错,我注意到该过程中唯一独特的是手柄,但不知道如何通过它找到我的窗口。就像在您的示例中一样,getMyWindowObject(hwnd),该函数是否包括遍历我打开的窗口以查看句柄是否匹配?因为如果是这样,如果我正在处理 WM_MOUSEMOVE 或 WM_TIMER,那在处理器上会不会很乏味?
    • 最好的办法是使用某种形式的哈希表从 HWND 哈希到指向您的窗口对象的指针 - 那是查找很快。除非您打开了大量的窗口,否则我希望循环遍历所有 (HWND,object*) 对会足够快。
    猜你喜欢
    • 2011-04-01
    • 2011-06-18
    • 2014-07-10
    • 2016-12-29
    • 1970-01-01
    • 2012-01-06
    • 1970-01-01
    • 2015-02-12
    • 1970-01-01
    相关资源
    最近更新 更多