【问题标题】:Linking Button and Window Classes链接按钮和窗口类
【发布时间】:2012-02-24 22:26:38
【问题描述】:

我正在开发自己的个性化 winapi 包装器。我想要的语法是这样的:

// #define wndproc(name) void name (Window & hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
// #define buttonproc(name) void name (Button & hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

wndproc (rightClick) { //evaluates to function to handle window message
    ::msg ("You right clicked the window. Closing window...");
    hwnd.close(); //close() is implemented in my Window class
}

buttonproc (buttonClick) { //same thing basically
    ::msg ("You clicked this button. I'm going to hide the other one...");

    //if text on this button is "One button", find the one belonging to parent
    //with the text "Other button" and hide it, or vice-versa
    hwnd.text == "One button"
    ? hwnd.parent().button ("Other button").hide();
    : hwnd.parent().button ("One button").hide();
}

int main() {
    Window win; //create default window
    win.addmsg (WM_LBUTTONDOWN, rightClick); //look for l-click message and call that

    Button b1 (win, "One button", 100, 100, 50, 20, buttonClick); //parent, coords, size, clicked
    Button b2 (win, "Other button", 200, 100, 50, 20, buttonClick);  

    return messageLoop(); //should be self-explanatory
}  

问题是,在 wndproc 中,hwndWindow &,在 buttonproc 中,hwndButton &。我也许可以说:

msgproc (Window, rightClick){...} 
msgproc (Button, buttonClick){...}

问题在于我必须调用这些程序并赋予它们正确的hwnd。我的主窗口过程在我的Window 类中实现。它得到四个普通参数。如果我需要将WM_COMMAND 消息传递给右键过程,我想给它相应的Button 对象。

按照目前的方式,我将一个指针传递给WindowButton 的超类。当然,它会创建复杂的代码,例如:

((Window *)hwnd)->operator()() //get HWND of the Window

无论如何,它似乎并没有那么好用。不幸的是,目前我能想到的唯一方法是保留每个创建的Button 的列表,然后取出正​​确的一个。我什至可以将此扩展到所有可能的收件人。

这样做的好处是我的Button 类有一个静态窗口过程,只要找到WM_COMMAND 消息就会调用它。我没有添加其他控件,但它旨在通过使用现有控件检查 id 并调用您在创建按钮时指定的过程(如果它是匹配的)来工作。问题是,完成后,添加WM_COMMAND 处理程序的任何其他东西(如复选框)也将被调用。

我正在考虑在Window 中保留每个 HWND 子对象及其对应对象的列表。这样我就可以像Button那样对每个类中的额外过程进行核对,这将导致发生大量额外处理,并将proc [i] ((BaseWindow *)hwnd, msg, wParam, lParam)替换为proc [i] (control [loword(wParam)], msg, wParam, lParam)之类的WM_COMMAND,使用lParam看看是不是控件。

我似乎错过了一些重要的东西。我很有可能会开始实现它,然后遇到一个主要问题。有没有更好的方法来完成这一切?

当我这样做时,有没有一种方法可以创建一个 control() 函数来返回正确的对象类型(按钮、复选框...),这取决于它找到对应的 id 而不是一个不同对象的数组(我很确定我已经看到了一种方法)?

【问题讨论】:

  • 你显然在使用 C++,那为什么首先要使用宏呢?
  • @Cody Gray,其背后的原因是为了消除常用的参数列表。每次打出来都挺费劲的。
  • 好吧,我明白了。为什么不使用虚函数?无论如何,您已经定义了 WindowButton 类。
  • 你能解释一下我如何使用一个吗?我没有接受提示。
  • 如果你不知道虚函数是什么,你需要先得到a book that teaches you the C++ language,然后再继续。一旦你学会了 C++,就从 Raymond Chen 的C++ scratch program 开始,并进行相应的定制。

标签: c++ windows winapi inheritance wrapper


【解决方案1】:

问题是如何处理传递给父类而不是按钮类(或其他)的 WM_COMMAND 消息。

最简单的解决方法是向窗口基类添加 WM_COMMAND 处理程序,以将消息转发到生成它的控件。然后消息将在控件的类中得到处理。

WM_COMMAND 处理程序中的代码可能如下所示:

if (lParam != 0)
{
    // lParam non zero so this is a control notification.
    if ((HWND)lParam == hWnd)
    {
        // The message has arrived at its destination
        return OnNotify(HIWORD(wParam), LOWORD(wParam));
    }
    else
    {
        // Reflect the message back to the control.
        return SendMessage((HWND)lParam, WM_COMMAND, wParam, lParam);
    }
}

我最初误解了这个问题。下面描述了几种将消息路由到对象的方法:

MFC 方式

每个窗口都使用相同的窗口过程。您有一个从 HWND 到窗口对象的全局映射。 (地图实际上是每个线程的,但在大多数应用程序中这是不必要的。)当消息到达时,您查找对象并将消息发送给它。如果Button 派生自Window,那么对每个类进行处理非常容易。

一个小麻烦是,如果您想捕获在创建窗口时生成的消息,您需要在全局窗口过程中将窗口添加到您的地图中。

ATL 方式

每个窗口都有自己的窗口过程(可能也有自己的类,以便在初始设置窗口过程;我忘记了)。窗口过程是一个生成的存根,它加载指向对象* 的指针并跳转到基类的窗口过程(这是一个非静态成员函数)。 (为了简单起见,存根应该跳转到基类中的非虚拟 wndproc,它调用“真实的”虚拟 wndproc。)除了改变 HWND 映射到对象的方式之外,在其他方面,这与MFC 模型。

*在 x86 上,存根在跳转到 wndproc 之前将对象指针放入 ECX。这也适用于 x64(虽然我不知道它是否确实像这样工作)但对象指针会覆盖 HWND(因此基类非虚拟 wndproc 不会有 HWND 参数)。

【讨论】:

  • 我对 MFC 的一个问题是它使用托管代码来编写包装器。我认为它有点挫败学习体验,而且我对本机代码更加熟悉。它现在的设置方式是您的 ATL 方式。我的 Window 和 Button 都派生自的超类具有窗口过程,它强制每个继承的类实现自己的,它在传递消息时调用它。但是,问题是,当控制消息通过时,它是对父级的 WM_COMMAND。目的地是我需要的错误程序。
  • 我也想Button可以派生自Window,但它们的功能完全不同。相反,这种关系是父子关系。虽然我的 Window 类可以成为最顶层、全屏、关闭,但按钮将无法使用其中任何一个。
  • MFC 早于 .Net。我已经有一段时间没有使用它了,但是您确定它使用的是托管代码吗?这将扼杀它的主要优势之一(不需要 .Net 运行时)。是的,WM_COMMAND 转到父级,但(对于控件)它包含一个 HWND,因此您只需将其转发给正确的对象。为什么不让 Button 和 TopLevelWindow(全屏、关闭等)都从 Window 派生?
  • 我对托管代码的理解可能有误。我的意思是包装代码,因为它基本上将所有内容放入类等中,但错过了我希望包装器成为的主要内​​容(个人,易于阅读或易于编写,具体取决于情况)。我想我现在明白你在转发的意思了,我以前从来没有这样想过。基本上,Window(您的 TopLevelWindow)中的过程传递 WM_COMMAND 的方式与 BaseWindow(您的 Window)传递所有内容的方式相同?
  • 还不错;托管代码意味着它在 .Net 上运行。即使对于一个小型包装器,我确实认为将所有内容放在类中会更清楚。转发 WM_COMMAND 的最简单方法是再次调用 SendMessage(使用新的 HWND),然后它将自动到达 Button。这段代码应该放在 BaseWindow 而不是 Window 中,因为子窗口本身可以包含更多的子窗口。
猜你喜欢
  • 1970-01-01
  • 2011-11-06
  • 2017-11-11
  • 2021-07-12
  • 1970-01-01
  • 1970-01-01
  • 2019-03-31
  • 2013-05-12
  • 2014-07-16
相关资源
最近更新 更多