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

Why does c++ send WM_DRAWITEM to the parent process?

本文关键字:进程 DRAWTITEM c++ WM 为什么      更新时间:2023-10-16

我最近开始学习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;
}

我想在CustomButtonProc中使用WM_DRAWITEM作为这个按钮,我想知道为什么开发人员认为只允许在父WinProc中使用它会很聪明。

这解释起来有点复杂。

你可能来自一个后台,在那里你把一个函数插入一个插座来处理事件,比如

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;

没有浪费内存,但仍然具有灵活性和可扩展性。更重要的是,二进制兼容:如果以后添加更多事件,函数指针表的大小将需要更改,而试图在旧系统上使用新事件的新程序将占用随机内存。如果有消息,这些程序就会有死代码。

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

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

(如今,我们有一种名为"子类化"的东西,它允许您覆盖单个窗口的窗口过程来进行自定义处理。它不用于事件处理,因为它比发送到父窗口要多。)

所有这些都延伸到了自定义绘制;只要用"自定义绘制"代替"点击",它应该仍然有意义。希望这个解释是清楚的,即使有心理上的替代。


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

感谢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_DRAWTITEM:

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

然后在您的子类中,只需通过您刚才查找的5位数字来捕获消息:

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

感谢所有为这篇文章做出贡献的人,他们帮助我们进行了详尽的阐述、技巧和历史背景!