2017-08-29 137 views
0

我正在编写一个VCL组件TGIcon来模仿Windows桌面上的图标,直到我决定将MouseEnter和MouseLeave事件添加到该组件时,它一直工作正常。我跟着从导游:Embarcadero CommunityBEGIN_MESSAGE_MAP导致C++ Builder 10.1崩溃到桌面

,这里是我的代码(头):

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的初学者)会崩溃到桌面。我追溯到问题的根源是“BEGIN_MESSAGE_MAP ... END_MESSAGE_MAP”部分。如果我注释掉那部分,组件工作正常。

我曾经有过使用C++ Builder XE5(Professional)工作的相同组件,但由于这是我不再使用的公司所拥有的,所以我没有组件的二进制文件,所以必须重新编译 - 在这里写。据我记忆,我所做的与我在XE5中编写的完全一样,只有一个可以工作,但是这样会使IDE崩溃,没有错误信息,没有访问冲突,只是简单的CTD。

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

在此先感谢。

+0

您所提供的链接是真的,真的老的文章(近20岁)。我关心的是使用'__fastcall'调用约定。我一直没有使用这些消息处理程序,所以下面的语句可能不正确,但也许这些函数应该使用'__stdcall'约定(或者根本不使用)。至少,你可以试试看。有关这些调用约定的更多上下文,请参阅[本](https://stackoverflow.com/questions/15047758/cdecl-stdcall-and-fastcall-are-all-called-the-exact-same-way)。 –

+0

尝试了__stdcall,仍然是CTD。我知道这篇文章很老,从C++ Builder 6.0开始我一直在使用这种类型的事件处理。我知道在XE5投诉BEGIN_MESSAGE_MAP的内联代码时,它会过时,但Embarcadero根本没有更新任何信息,至少我无法通过Google找到任何信息。不管怎么说,还是要谢谢你。 –

回答

1

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

一个MESSAGE_MAP仅仅是重写虚Dispatch()方法,其中一个奇特的方式:

  • BEGIN_MESSAGE_MAP声明并打开覆盖的方法,并打开一个switch声明
  • MESSAGE_HANDLER(而不是如果你的项目中使用VCL_MESSAGE_HANDLER使用ATL)声明case语句为switch
  • 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! 
+0

的确的确......我忘了这件事真是愚蠢...... Case解决了,非常感谢,Remy Lebeau。 –