BEGIN_MESSAGE_MAP вызвал сбой C ++ Builder 10.1 на рабочем столе

Я пишу VCL компонент, TGIcon, чтобы имитировать значки на рабочем столе Windows, он работал нормально, пока я не решил добавить события MouseEnter и MouseLeave к компоненту. Я следовал инструкциям от: Сообщество Embarcadero

и вот мой код (заголовок):

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 или это то, что нельзя сделать в версии для начинающих, что это можно сделать только в «платных» версиях ?? Или этот метод уже устарел? Если это так, пожалуйста, покажите мне, как «модернизированный» C ++ Builder это делает.

Заранее спасибо.

-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) вместо базового класса (TGraphicControl) в END_MESSAGE_MAPвы создаете бесконечный цикл рекурсии, когда компонент получает необработанное сообщение. 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!
1

Другие решения

Других решений пока нет …