为什么c ++将WM_DRAWITEM发送到父进程?

时间:2016-05-10 21:05:44

标签: c++ winapi codeblocks

我最近开始学习C ++和WinAPI,我希望能够创建自己的程序

我是一个按钮的子类(进入单独的文件,因为我喜欢清洁和有组织的东西 - 这是一个坏主意吗?),因为我想要几个具有相同参数的类型。我也想绘制它,而我脑海中浮现的问题是:将它全部放在类文件中会不会更好?含义所有参数包括自定义绘制。去主文件更改外观并设置类文件中的所有其他参数很烦人。我使用的是代码块。

编辑(解释):

#include <Windows.h>
#include <Winuser.h>
#include "CustomButton.h"

/*global vars*/
WNDPROC CustomButton::CustomButtonLongPtr;

/*functions*/
LRESULT CALLBACK CustomButtonProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

CustomButton * CustomButton::CreateCustomButton(HINSTANCE hInstance, HWND hwnd, int pos_x, int pos_y, int width, int height)
{
    CustomButton * p_CustomButton = new CustomButton;
    HWND customButton = CreateWindowEx(0, "BUTTON", "OK", WS_VISIBLE | WS_CHILD | BS_OWNERDRAW, pos_x, pos_y, width, height, hwnd, (HMENU)200, (HINSTANCE)GetWindowLong(hwnd, GWLP_HINSTANCE), p_CustomButton);
    if(customButton == NULL)
    {
        delete p_CustomButton;
        MessageBox(NULL, "Problem creating the Search box.", "Error", 0);
        return 0;
    }
    CustomButton::CustomButtonLongPtr = (WNDPROC)SetWindowLongPtr(customButton, GWLP_WNDPROC, (LONG_PTR)&CustomButton::CustomButtonProc);
    return p_CustomButton;
}

我想在WM_DRAWITEM中使用CustomButtonProc这个按钮,我想知道为什么开发人员认为只允许在父WinProc中使用它是明智的。< / p>

2 个答案:

答案 0 :(得分:2)

解释起来有点复杂。

您可能来自将功能插入插座以处理事件的背景,例如

extern void onClicked(void);
button->OnClicked = onClicked;

尽管Windows从一开始就完全可以完成此操作,但您必须记住,Windows最初设计为在内存严重受限的系统上运行,因此控制不浪费内存非常重要。您可以通过按钮获得许多活动:

void (*OnClicked)(void);
void (*OnDoubleClicked)(void);
void (*OnDisabled)(void);
void (*OnHighlight)(void);
void (*OnKillFocus)(void);
void (*OnPaint)(void);
void (*OnSetFocus)(void);
void (*OnUnhighlight)(void);
void (*OnUnpushed)(void);
HBRUSH (*OnCtlColorButton)(void);

对于程序中的每个按钮(即按钮,复选框,单选按钮和分组框)都有这些按钮,其中大部分可能未被使用,这只会浪费大量内存。

因为Windows需要一种在系统和窗口之间以及窗口之间进行通信的方法,所以Microsoft决定创建一个消息传递接口,其中每条消息都有一个16位代码和两个指针大小(最初是一个32位和一个16位)参数和一个指针大小的返回值。并且Windows不需要很多消息,这为窗口类和应用程序提供了很多用于使用消息进行通信的空间。那么为什么不用信息来表示事件?

使用消息可以避免浪费内存,同时仍允许按钮将数据传递到目标窗口并从其返回数据。所以逻辑遵循所有按钮需要做的是

/* this is not the correct syntax but let's use it for expository purposes */
#define BN_CLICKED someNumberHere
case WM_LBUTTONUP:
    SendMessage(GetParent(hwnd), BN_CLICKED, hwnd);
    break;

并且父母会处理:

case BN_CLICKED:
    if (whichButton == button1)
        doButton1Stuff();
    break;

没有浪费内存,但仍然灵活且可扩展。更重要的是,也是二进制兼容的:如果稍后添加更多事件,则需要更改函数指针表的大小,并且试图在较旧系统上使用较新事件的较新程序会破坏随机存储器。有了消息,这些程序就会有死代码。

现在为什么要将消息发送给父母?如果我们将窗口视为通信端点,那么这是显而易见的:您希望按钮告诉其父级已单击它,因为您正在通知该按钮被单击!

但更重要的是,你没有写出按钮的窗口程序。微软做了,他们为每个程序提供相同的一个。如果你可以在按钮程序中处理消息,你会把它放在哪里?毕竟,你无法改变按钮程序。

(现在我们有一些名为&#34;子类化&#34;它允许你覆盖单个窗口的窗口程序来进行自定义处理。它不用于事件处理,因为它&#39更多的工作不仅仅是发送给父母。)

所有这一切都延伸到定制抽奖;只需替换&#34;定制抽奖&#34;为&#34;点击&#34;它应该仍然有意义。希望这个解释很清楚,即使是那种心理替代。

如果需要,可以编写自己的工具来处理函数指针方式中的事件。保留事件函数的窗口句柄映射,并在所有窗口过程中调用全局调度函数来处理事件消息WM_COMMANDWM_NOTIFY和(对于跟踪栏)WM_HSCROLL和{{ 1}}。你是如何做到这一点取决于你,但想想你是否真的想这样做;有时它是必要的,但有时它不是。如果这样做,请记住提供一种方法将任意数据传递给在事件连接时决定的事件函数,因此事件处理程序可以在不依赖全局状态的情况下做一些合理的事情。

答案 1 :(得分:0)

感谢RemyLebeau和IInspectable的评论,我也能找到解决方案来解决我的挫败感,我将在这里向其他人解释这个问题。 此解决方案不需要VCL,也不需要Visual Studio中的任何组件等。

首先定义您自己的自定义消息,方式是您可以在WndProc中找到它:

#define MY_DRAWITEM (WM_APP+1)
UINT uDrawButtonMsg = RegisterWindowMessage(_T("MY_DRAWITEM"));

然后找出分配的号码:

std::cout << uDrawButtonMsg; //for example 49648

并从您希望的任何消息将此消息从WndProc发送到您的子类控件,例如WM_DRAWITEM:

case WM_DRAWITEM:
{
    ::SendMessage(p_CustomButton->customButton, uDrawButtonMsg, wParam, lParam);
    break;
}

然后在您的子类中,只需按照您之前查找的5位数字来捕获消息:

if(49648 == uMsg)
{
    //do your DRAWITEM stuff here
}

感谢所有为本文做出贡献的人们,他们帮助提供了出路,提示以及历史背景!