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

问题描述 投票:0回答:3

我正在用 D 语言为 Windows 编写一个面向对象的窗口 API 包装器,并且我遇到了(非特定于语言的)设计问题。

Windows 要求所有窗口都事先用

RegisterClass
注册;扩展现有类需要替换窗口过程。此外,似乎有两种窗口句柄:需要处理的
HWND
(通过
DestroyWindow
)和不需要的
HWND
(例如来自其他应用程序)。

我创建了一个

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

  1. 给定的
    className
    必须已由
    RegisterClass
    注册。
  2. 为了注册一个新的窗口类,我需要使
    Window
    的子类在尝试直接或间接创建新窗口之前以某种方式调用
    RegisterClass
  3. 为了使我的设计(和继承)有意义,我需要确保对于任何给定的
    Window
    子类,所有实例实际上都是同一窗口类的实例;也就是说,来自特定子类的所有
    className
    都是相同的。 (这是因为特定窗口类的所有实例的窗口过程需要相同。)

问题是,没有办法有

abstract static
方法(以便 Window 能够向子类询问其类信息,并注册一次),所以我被迫说类似
CreateWindow(this.className, ...) 之类的话
为了创建一个新窗口,如果我的子类不遵守此规则,这很容易出现问题,并为每个窗口实例提供不同的类名。

此外,我需要

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

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

那么,长话短说,有谁知道有什么优雅的解决方案来解决创建

abstract static
方法的问题吗?我正在考虑一些设计模式,例如工厂之类的,但我不确定它们是否适合这里......如果有人认为它们可能,我真的很感激关于它如何适合设计的一些解释。

谢谢!

winapi wrapper
3个回答
2
投票

对此的标准解决方案是为基类提供两个窗口过程,一个静态的和一个虚拟的

基类用静态窗口过程注册它的类。然后静态窗口过程调用虚拟窗口过程。许多人在虚拟版本中省略了

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
投票

哇,我当然没想到会是这样......

我在谷歌上搜索了短语

x86 thunks
。而且,事实证明,我并不是第一个遇到这个问题的人(惊讶!)。

第一个命中是关于 Window Procs 的问题的确切问题和解决方案,即使它与我的查询无关(或者至少,直接做的很少)...希望其他人找到也很有用。


0
投票

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

通常,您可以使用

RegisterClass
函数为该类窗口设置 default 窗口过程。 当您创建一个使用该类的新窗口但希望它覆盖默认处理时,您可以创建该窗口,然后调用
SetWindowLongPtr(hwnd, GWL_WNDPROC, newWndProc)
,其中
newWndProc
是指向新窗口的窗口过程的指针。

新窗口的窗口过程可以处理它想要覆盖的消息,并调用

DefWindowProc
来处理其余的事情。

要使用新类名构造一个窗口,请让构造函数复制继承类的类信息,并创建一个与该类相同(名称除外)的新类。 然后公开一个方法,允许客户端更改特定于类的内容。 您感兴趣的 API 函数是

GetClassInfoEx
SetClassLong
SetClassWord
SetClassLongPtr

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

您不需要像

abstract static
之类的方法来实现此目的。您所需要的只是窗把手。 您可以通过使用窗口句柄调用
GetClassName
来获取类名,然后使用返回的类名调用
GetClassInfoEx

© www.soinside.com 2019 - 2024. All rights reserved.