【问题标题】:BEGIN_MESSAGE_MAP caused C++ Builder 10.1 to crash to desktopBEGIN_MESSAGE_MAP 导致 C++ Builder 10.1 崩溃到桌面
【发布时间】:2018-02-06 11:49:21
【问题描述】:

我正在编写一个 VCL 组件 TGIcon 来模仿 Windows 桌面中的图标,在我决定将 MouseEnter 和 MouseLeave 事件添加到组件之前,它一直运行良好。我遵循以下指南:Embarcadero Community

这是我的代码(标题):

class PACKAGE TGIcon : public TGraphicControl
{
    private:
        AnsiString FCaption;
        TPngImage *FIcon, *FDIcon;
        TFont *FFont;
        TNotifyEvent FOnMouseEnter;
        TNotifyEvent FOnMouseLeave;

        void __fastcall CMMouseEnter(TMessage &Message);
        void __fastcall CMMouseLeave(TMessage &Message);

        BEGIN_MESSAGE_MAP
            MESSAGE_HANDLER(CM_MOUSEENTER, TMessage, CMMouseEnter)
            MESSAGE_HANDLER(CM_MOUSELEAVE, TMessage, CMMouseLeave)
        END_MESSAGE_MAP(TGIcon)

    protected:
        virtual void __fastcall Paint();
        void __fastcall SetCaption(AnsiString value);
        void __fastcall SetIcon(TPngImage *value);
        void __fastcall SetFont(TFont *value);

    public:
        __fastcall TGIcon(TComponent* Owner);
        __fastcall ~TGIcon();
        void __fastcall MakeGray(void);

    __published:
        __property AnsiString Caption = {read=FCaption, write=SetCaption, nodefault};
        __property TPngImage  *Icon   = {read=FIcon, write=SetIcon};
        __property TFont      *Font   = {read=FFont, write=SetFont};
        __property Parent;
        __property Enabled;
        __property OnClick;

        __property TNotifyEvent OnMouseEnter = {read=FOnMouseEnter, write=FOnMouseEnter};
        __property TNotifyEvent OnMouseLeave = {read=FOnMouseLeave, write=FOnMouseLeave};
};

每当我尝试将组件放在表单上时,IDE(C++ Builder Starter)都会崩溃到桌面。我已经追踪到问题的根源是“BEGIN_MESSAGE_MAP ...END_MESSAGE_MAP”部分。如果我注释掉那部分,组件就可以正常工作。

我曾经在 C++Builder XE5 (Professional) 中使用相同的组件,但由于该组件归我不再合作的公司所有,因此我没有组件的二进制文件,因此我必须重新写在这里。据我所知,我所做的与我在 XE5 中编写的完全一样,一个可以工作,但这个会导致 IDE 崩溃,没有错误消息,没有访问冲突,只是简单的 CTD。强>

有人可以帮忙吗,我需要做些什么才能在 C++ Builder 10.1 (Berlin) Starter Edition 中完成这项工作吗?这是 C++Builder 的一个错误,还是在 Starter Edition 中无法完成的,只能在“付费”版本中完成?还是这种方法已经过时了?如果是这样,请告诉我“现代化”的 C++ Builder 是如何做到的。

提前致谢。

【问题讨论】:

  • 您提供的链接是一篇非常非常古老的文章(将近 20 年了)。我担心的是使用__fastcall 调用约定。我已经有一段时间没有使用这些消息处理程序了,因此以下语句可能不正确,但也许这些函数应该使用 __stdcall 约定(或者根本不使用)。至少,你可以试一试。有关这些调用约定的更多上下文,请参见例如this.
  • 尝试了 __stdcall,仍然是 CTD。我知道这篇文章很旧,我从 C++ Builder 6.0 开始就一直在使用这种类型的事件处理。当在 XE5 中抱怨 BEGIN_MESSAGE_MAP 的内联代码时,我知道它已经过时了,但 Embarcadero 根本没有更新任何关于此的信息,至少我无法通过 Google 找到任何信息。还是谢谢。

标签: c++ c++builder-10.1-berlin


【解决方案1】:

您的MESSAGE_MAP 被错误终止。在END_MESSAGE_MAP 宏中,您必须指定组件派生自的基类 (TGraphicControl)。

MESSAGE_MAP 只是覆盖虚拟Dispatch() 方法的一种奇特方式,其中:

  • BEGIN_MESSAGE_MAP 声明并打开被覆盖的方法,并打开 switch 语句
  • MESSAGE_HANDLER(如果您的项目使用 ATL,则使用 VCL_MESSAGE_HANDLER)为 switch 声明 case 语句
  • END_MESSAGE_MAP对未处理的消息调用指定类的Dispatch()方法,关闭switch,关闭被覆盖的方法。

这是来自sysmac.h的声明:

#define BEGIN_MESSAGE_MAP   virtual void __fastcall Dispatch(void *Message) \
        {                                           \
          switch  (((PMessage)Message)->Msg)        \
          {

#define VCL_MESSAGE_HANDLER(msg,type,meth)          \
          case    msg:                              \
            meth(*((type *)Message));               \
            break;

// NOTE: ATL defines a MESSAGE_HANDLER macro which conflicts with VCL's macro. The
//       VCL macro has been renamed to VCL_MESSAGE_HANDLER. If you are not using ATL,
//       MESSAGE_HANDLER is defined as in previous versions of BCB.
//
#if !defined(USING_ATL) && !defined(USING_ATLVCL) && !defined(INC_ATL_HEADERS)
#define MESSAGE_HANDLER  VCL_MESSAGE_HANDLER
#endif // ATL_COMPAT

#define END_MESSAGE_MAP(base)           default:    \
                        base::Dispatch(Message);    \
                        break;                      \
          }                                         \
        }

所以,这段代码:

BEGIN_MESSAGE_MAP
    MESSAGE_HANDLER(CM_MOUSEENTER, TMessage, CMMouseEnter)
    MESSAGE_HANDLER(CM_MOUSELEAVE, TMessage, CMMouseLeave)
END_MESSAGE_MAP(TGIcon) // <-- error!

由预处理器翻译成这段代码,这是编译器看到的:

virtual void __fastcall Dispatch(void *Message)
{
    switch (((PMessage)Message)->Msg)
    {
        case CM_MOUSEENTER:
            CMMouseEnter(*((TMessage *)Message));
            break;

        case CM_MOUSELEAVE:
            CMMouseLeave(*((TMessage *)Message));
            break;

        default:
            TGIcon::Dispatch(Message); // <-- recursive loop!
            break;
    }
}

如您所见,由于您指定了自己的组件类 (TGIcon) 而不是 END_MESSAGE_MAP 中的基类 (TGraphicControl),因此当组件收到未处理的消息时,您正在创建无限递归循环. TGIcon::Dispatch() 再次调用 TGIcon::Dispatch()。它需要改为调用TGraphicControl::Dispatch()(您的CMMouseEnter()CMMouseLeave() 方法也是如此):

BEGIN_MESSAGE_MAP
    MESSAGE_HANDLER(CM_MOUSEENTER, TMessage, CMMouseEnter)
    MESSAGE_HANDLER(CM_MOUSELEAVE, TMessage, CMMouseLeave)
END_MESSAGE_MAP(TGraphicControl) // <-- fixed!

【讨论】:

  • 是的,确实……忘记这件事真是太愚蠢了……案件解决了,非常感谢,Remy Lebeau。
猜你喜欢
  • 2016-06-25
  • 2011-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多