C++Builder Programming Forum
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
C++빌더 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
컴포넌트/라이브러리
메신저 프로젝트
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
볼랜드포럼 광고 모집

C++빌더 강좌/문서
C++Builder Programming Tutorial&Docments
[132] Message Handler를 이용하여 Event 추가하기
이정구 [appleii] 15846 읽음    2007-06-04 21:30
윈도우는 사용자로부터 마우스 , 키보드 등으로부터 입력을 받아 메시지를 발생시키고 이 메시지를 처리하는 함수를
실행시키는 방식으로 작동합니다. 메시지를 처리하는 함수를 메시지 핸들러라고 합니다.
C++ Builder 에서 구현하는 방법은 3가지가 있습니다.

(1) WndProc 함수사용
(2) Dispatch 함수 오버라이딩
(3) (2) 를 MESSAGE_MAP macro 로 구현

(2) 와 (3) 은 같습니다. 모양만 다를 뿐입니다.

이중에서 (2) 번의 예는 김태선님께서 올리신 자료를 참고하세요.
KTS Compo

여기서는 (3) 번의 방법으로 구현하는 방법을 알아보겠습니다.
다음과 같이 구현합니다. OnMouseEnter , OnMouseLeave property  를 __published 섹션에 넣습니다. 이벤트도 property 이니까요

class PACKAGE TMyImage : public TImage
{
private:
  TNotifyEvent FOnMouseEnter;
  TNotifyEvent FOnMouseLeave;
  MESSAGE void __fastcall CMMouseEnter(TMessage &Message);  //MouseEnter 메시지 핸들러
  MESSAGE void __fastcall CMMouseLeave(TMessage &Message); //MouseLeave 메시지 핸들러
 
protected:
   BEGIN_MESSAGE_MAP
    VCL_MESSAGE_HANDLER(CM_MOUSEENTER, TMessage, CMMouseEnter)
    VCL_MESSAGE_HANDLER(CM_MOUSELEAVE, TMessage, CMMouseLeave)
  END_MESSAGE_MAP(TImage)

public:
  __fastcall TMyImage(TComponent* Owner);

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

Dispatch 함수를 직접 사용하지 않고 Macro 를 이용하여 메시지 핸들러와 메시지가 어떻게 연결되어 있는지 알기
쉽도록 한 것입니다. 하지만 이것 또한 내부적으로는 Dispatch 함수와 switch - case , 그리고 마지막은 기반 클래스의
Dispatch 를 호출하는 것으로 되어 있습니다. MESSAGE 는 아무의미없는 Macro 입니다. 그냥 Message Handler 라는 것을 강조하기 위해서 넣은 것입니다. 스타일에 따라서 안 넣는 분들도 많으니..

메시지와 메시지 핸들러를 연결하는 형식은 VCL_MESSAGE_HANDLER(처리할 메시지, 메시지 타입, 메시지 핸들러) 입니다.

VCL_MESSAGE_HANDLER(CM_MOUSEENTER, TMessage, CMMouseEnter)

CM_MOUSEENTER 는 Control Message 입니다. CM_ 으로 시작합니다. VCL 자체 메시지입니다.
CMMouseEnter 는 메시지 핸들러입니다. 밑줄 _ 를 제거하고 대소문자 구분을 해줍니다. 그냥 VCL 관습입니다.

sysmac.h 에 있는 MESSAGE_MAP macro 부분

#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;

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

이벤트를 처리하는 과정은 MainWndProc 이 WndProc 을 호출하고 Dispatch 를 호출해서 만약 사용자가 이벤트 핸들러를
작성했다면 이벤트 핸들러를 호출하고 아니면 기본 핸들러를 호출합니다. 메시지 핸들러가 이벤트 핸들러를 호출하는 방법은 다음과 같습니다.
void __fastcall TMyImage::CMMouseEnter(TMessage &Message)
{
  if(FOnMouseEnter)        //사용자가 작성한 OnMouseEnter Event Handler 가 있다면
    FOnMouseEnter(this); //OnMouseEnter Event Handler 호출
}

FOnMouseEnter 는 TNotifyEvent 형입니다.
TNotifyEvent FOnMouseEnter;

  TNotifyEvent 형은 사용자가 작성한 이벤트 핸들러를 호출하는데 사용되는 함수 포인터형입니다. 여기서 한가지 신기한 점이 생깁니다. C++Builder 를 이용하여 폼에 Button을 올려놓고 TButton 타입의 객체 Button1 의 Event Handler 를 작성하면 Form1의 멤버함수가 됩니다. Button1을 Click 했을때의 작동을 결정하는 Button1Click 함수는 TButton class 의 멤버 함수가 되어야 하는 것이 아닐까요? 객체 지향 프로그래밍에서 객체의 동작은 해당 객체의 class 에서 멤버함수로 등록되어야 합니다. 그런데 엉뚱하게도 Form1의 멤버함수로 등록됩니다. 이것이 C++Builder 의 특징입니다. 기능을 추가했을때 해당 class 를 수정 하는 대신, 추가된 기능을 함수 포인터로 연결해 줍니다. 그리고 Form 위에 올려놓은 컴포넌트들의 동작을 모두 한개의 소스로 작성합니다. 이점이 C++Builder 를 처음 접했을때 당황스러운 점입니다. class 좀 배우고 객체 지향 프로그래밍이 뭔지 알듯말듯한데 배운것과는 약간 다른 방법을 사용하니까요.

Classes.hpp TNotifyEvent 선언 부분
typedef void __fastcall (__closure *TNotifyEvent)(System::TObject* Sender);

특이한 것은 __closure 입니다. 이것은 8바이트 포인터로 앞에는 함수의 주소, 뒤에는 함수가 있는 클래스의 인스턴스라고... 합니다. 자세한 것은 C++Builder 의 C++ 확장 을 참조하세요. Button1 과 이벤트 핸들러인 Button1Click 함수가 어떻게 연결되는지 눈치로 알 수 있습니다. 뒤에는 인자를 어떻게 넘길것인가를 나타냅니다. TNotifyEvent 형은 기본형으로 TObject* Sender 를 인자로 넘겨줍니다.

Delphi 에서는 다음과 같이 선언합니다. of object 를 사용하는 군요.

Classes.pas TNotifyEvent 선언 부분
TNotifyEvent = procedure(Sender: TObject) of object;

이것도 참고하세요.

컴포넌트 만들기.. 속성, 메소드, 이벤트
 
TImage , TMyImage 의 Event 탭을  비교하면 다음과 같습니다.




C++Builder 로 된 메시지 핸들러 부분을  Delphi 로 표현한다면 다음과 같습니다.

interface 부분
procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER;
procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;

implementation 부분
procedure TMyImage.CMMouseEnter(var Message: TMessage);
begin
  if Assigned(FOnMouseEnter) then
    FOnMouseEnter(Self);
end;

procedure TMyImage.CMMouseLeave(var Message: TMessage);
begin
  if Assigned(FOnMouseLeave) then
    FOnMouseLeave(Self);
end;

MESSAGE_MAP Macro 를 사용하는 대신 message 키워드를 사용하는 것을 제외하면 C++Builder 와 유사합니다.


김태선 [cppbuilder]   2007-06-05 10:26 X
멋진 강의에 별볼일 없는 제 작품을 알려주셔서 감사합니다.
그런데 강의 제작(웹과 동영상)은 뭘로 하시나요?
이정구 [appleii]   2007-06-05 11:42 X
화면 캡쳐는 Open Capture 를 사용하고 http://openproject.nazzim.net/  플래시 동영상은 Wink 를 사용합니다.(FreeWare입니다.) http://www.debugmode.com/wink/         나머지는 html 작업입니다.
김태선 [cppbuilder]   2007-06-05 17:56 X
좋은 툴인 모양이군요.
제가 작업할때 참조해 보겠습니다.
^^;
외랑 [jaehuns]   2007-06-16 09:17 X
강의 감사드립니다. 짝짝짝짝 짝짝~~박수입니다..
다음 강의도 기다립니다.
붕붕이 [goodkkj]   2014-11-26 14:22 X
어후 아는 지식은 C언어기초박에 없어 이해하기힘드네요.. 이내용을 이해하려면 무엇을 좀더 공부를해야될련지..ㅠ?

+ -

관련 글 리스트
132 Message Handler를 이용하여 Event 추가하기 이정구 15846 2007/06/04
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.