Я пишу 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 это делает.
Заранее спасибо.
Ваш 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!
Других решений пока нет …