2017-08-29 38 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。 –