包装Windows的Window API时的设计问题

时间:2011-02-02 22:43:20

标签: winapi wrapper

我正在为D编写一个面向对象的窗口API包装器,我有一个(非语言特定的)设计问题。

Windows要求所有窗口都先前已注册RegisterClass;扩展现有类需要替换窗口过程。此外,似乎有两种窗口句柄:HWND s需要处理(通过DestroyWindow),而HWND s则不处理(例如来自其他应用程序)。

我创建了一个Window类,只要我只是包装ShowWindowUpdateWindowFindWindow和其他类似的方法,一切都很好和花花公子。但是当我尝试向我的调用className的类添加一个带有CreateWindow参数的构造函数时,我遇到了一个问题:

  1. className必须已经注册了给定的RegisterClass
  2. 为了注册一个新的窗口类,在尝试直接或间接创建一个新窗口之前,我需要让Window的子类以某种方式调用RegisterClass
  3. 为了使我的设计(和继承)有意义,我需要确保对于任何给定的Window子类,所有实例实际上都是同一窗口类的实例;即,来自特定子类的所有className都是相同的。 (这是因为特定窗口类的所有实例的窗口过程必须相同。)
  4. 问题是,没有办法让abstract static方法(为了让Window能够向子类询问他们的类信息,并将它们注册一次),所以我不得不说像CreateWindow(this.className, ...)这样的东西,以便创建一个新的窗口,如果我的子类不遵守这个规则,这很容易成为问题,并为每个窗口实例提供一个不同的类名。

    此外,我需要WNDCLASS.lpfnWndProc字段和我的Window子类(重写)WndProc方法之间的一对一映射。但是,如果我被迫在每个实例的基础上获取方法指针,那么这并不完全有效,因为它打破了整个OOP设计并使一切都搞砸了。

    虽然我可以在运行时强制执行这种一致性,但它有点难看,因此它不是一个很好的解决方案。

    所以,长话短说,有没有人知道创建abstract static方法问题的优雅解决方案?我正在考虑一些像Factory和whatnot这样的设计模式,但我不确定它们是否适合这里......如果有人认为它们可能,我会非常感谢它对它如何适合设计的一点解释。

    谢谢!

3 个答案:

答案 0 :(得分:2)

此标准解决方案是为基类提供两个窗口过程a static one and a virtual one

基类使用静态窗口过程注册其类。然后,静态窗口过程调用虚拟窗口过程。许多人省略了虚拟版本中的HWND参数,因为它可以从this指针获取,但我会将其保留以简化故事。

class Window
{
public:
    virtual LRESULT WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    { return DefWindowProc(hwnd, uMsg, wParam, lParam); }
private:
    static LRESULT CALLBACK StaticWndProc(
        HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        if (uMsg == WM_NCCCREATE) {
            SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LPARAM>(
                 reinterpret_cast<LPCREATESTRUCT>(lParam)->lpCreateParams);
        }
        Window *pThis = reinterpret_cast<Window*>(
                                     GetWindowLongPtr(hwnd, GWLP_USERDATA));
        LRESULT lres = pThis ? pThis->WndProc(hwnd, uMsg, wParam, lParam)
                             : DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
};

派生类覆盖Window::WndProc

答案 1 :(得分:0)

对于特定类名的所有实例,窗口proc不必相同。

通常,您使用RegisterClass函数为该类的窗口设置默认窗口proc。当您创建一个使用该类但又希望它覆盖默认处理的新窗口时,您创建窗口然后调用SetWindowLongPtr(hwnd, GWL_WNDPROC, newWndProc),其中newWndProc是指向新窗口窗口proc的指针。< / p>

新窗口的窗口proc然后可以处理它想要覆盖的消息,并为其余部分调用DefWindowProc

要构造具有新类名的窗口,请让构造函数复制继承类的类信息,并创建一个与该名称相同的新类,但名称除外。然后公开一个允许客户端更改特定于类的事物的方法。您感兴趣的API函数包括GetClassInfoExSetClassLongSetClassWordSetClassLongPtr

继承在这个世界中仍然有意义,因为默认处理对于特定类的每个窗口都是相同的,并且继承的类继承了父项的默认处理。

为此,您不需要abstract static方法。您只需要窗户把手。您可以通过使用窗口句柄调用GetClassName来获取类名,然后使用返回的类名称调用GetClassInfoEx

答案 2 :(得分:0)

哇,我当然没想到这个......

我在Google上搜索了x86 thunks这个词组。而且,事实证明,我不是第一个遇到这个问题的人(出乎意料!)。

关于Window Procs的第一个问题就是这个问题的exact problem and the solution,尽管它与我的查询没有任何关系(或者至少,很少直接做)...希望其他人发现它有用太