Delphi OOP와 C++과의 차이 [4]
Message Hook
============
델파이에서의 메시지 후킹을 알아보기전에 C++과 비교하는 의미로써 MFC를
이용한 간단한 예제를 올린다. 다음 소스는 메시지 맵핑테이블을 이용해
서 마우스이벤트를 후킹한다.
//---- MainWindow Frame Class
class CTestWnd : public CFrameWnd
{
public:
CTestWnd();
virtual ~CTestWnd();
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(CTestWnd, CFrameWnd)
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()
void CTestWnd::OnLButtonDown(UINT nFlags, CPoint point)
{
CClientDC(this).TextOut(point.x, point.y, "Test",4);
CFrameWnd::OnLButtonDown(nFlags, point);
}
//---- Application Class
class CTestApp : public CWinApp
{
public:
CTestApp() {};
virtual BOOL InitInstance();
};
CTestApp theApp;
BOOL CTestApp::InitInstance()
{
CFrameWnd *p;
(p = new CTestWnd())->Create(0,"Test"), m_pMainWnd = p;
m_pMainWnd-> ShowWindow(m_nCmdShow);
return TRUE;
}
크래스 CTestWnd에서 afx_msg void OnLButtonDown 메소드는 버추얼 함수가
아니다. "afx_msg" 는 다음과 같이...
#ifndef afx_msg
#define afx_msg
단지 메시지핸들러 메소드임을 나타내는 placeholder의 의미밖에 갖지않는
다. DECLARE_MESSAGE_MAP(), BEGIN_MESSAGE_MAP(CTestWnd, CFrameWnd)
ON_WM_LBUTTONDOWN(), END_MESSAGE_MAP() 는 아래와같이 헤더화일에 정의
되어있는 매크로 구문이다.
// DECLARE_MESSAGE_MAP의 매크로
#define DECLARE_MESSAGE_MAP() \
private: \
static const AFX_MSGMAP_ENTRY _messageEntries[]; \
protected: \
static AFX_DATA const AFX_MSGMAP messageMap; \
virtual const AFX_MSGMAP* GetMessageMap() const; \
// BEGIN_MESSAGE_MAP의 매크로
#define BEGIN_MESSAGE_MAP(theClass, baseClass) \
const AFX_MSGMAP* theClass::GetMessageMap() const \
{ return &theClass::messageMap; } \
AFX_DATADEF const AFX_MSGMAP theClass::messageMap = \
{ &baseClass::messageMap, &theClass::_messageEntries[0] }; \
const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \
{ \
// END_MESSAGE_MAP의 매크로
#define END_MESSAGE_MAP() \
{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \
}; \
// ON_WM_LBUTTONDOWN의 매크로
#define ON_WM_LBUTTONDOWN() \
{ WM_LBUTTONDOWN, 0, 0, 0, AfxSig_vwp, \
(AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(UINT, CPoint))
OnLButtonDown },
struct AFX_MSGMAP
{
const AFX_MSGMAP* pBaseMap;
const AFX_MSGMAP_ENTRY* lpEntries;
};
위와같이 윈도우프로시져를 후킹한 MFC 크래스라이브러리는 리스트 구조로
체인된 메시지매팽 테이블을 이용해서 적절한 객체에 메시지를 디스페칭하
게된다. 보충 설명은 하이텔 두루물동호회(go mul) 16번 게시판(언어/툴)
4470 번을 참고하기바란다.
윈도우즈시스템은 캡션바(또는 타이틀바)를 갖는 프레임에 한해서 윈도우
프레임을 마우스로 이동 할 수 있게해준다. 그러나 한글윈도우즈에서의
한글입력기는 캡션바가 없어도 마우스로 이동이 가능하다.
Non-Client Area에서 일어나는 이벤트는 DefWindowPro에 의해서 디폴트 처
리가된다.
long FAR PASCAL WndProc(HWND hwnd, WORD msg, WORD w, LONG l)
{
...
switch(message) {
case ...
case ...
...
defalut:
return DefWindowProc(hwnd, message, w, l);
}
return 0L;
}
윈도우 프레임을 이동하게 될 경우 WM_NCHITTEST 메시지가 발생하고,
DefWindowProc는 타당한 Non-Client Area 일때 헤더화일상에 다음과 같이
정의된..
#define HTCAPTION 2
2란 값을 리턴하게된다.
델파이에서는 2가지 방법을 이용해서 메시지후킹을 통해 윈도우즈시스템을
속일수 있다. 캡션바가 없는 윈도우즈 프레임을 이동하는 방법을 델파이
에서 알아보자. 우선 Object Inspector에서 Form의 Border Style을 none
으로 설정한다.
첫번째 방법
-----------
아래와 같이 코드를 수정해준다.
type
TForm1 = class(TForm)
private
{ Private declarations }
// 메시지핸들러 정의
procedure WMNcHitTest(var M:TMessage); message wm_NcHitTest;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.WMNcHitTest(var M: TMessage);
begin
// 선조의 메소드를 호출해서 디폴트 핸들러 수행
inherited;
// 강제적으로 윈도우즈시스템에 htCaption을 리턴
M.Result := htCaption;
end;
end.
델파이에서 윈도우프로시져는 VCL 라이브러리에 의해서 인터셉트가되고
VCL 라이브러리는 메시지 디스페칭을 통해 적절한 객체의 핸들러를 호
출한다.
두번째 방법
-----------
아래와 같이 코드를 수정해준다.
type
TForm1 = class(TForm)
private
{ Private declarations }
// 버추얼함수 WndProc를 오버라이딩
procedure WndProc(var M:TMessage); override;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.WndProc(var M:TMessage);
begin
// 선조의 디폴트핸들러 수행
inherited WndProc(M);
// WM_HCHITTEST 메시지가 발생했을때, 강제로 HTCAPTION을 리턴
if M.Msg = wm_NCHitTest then
M.Result := htCaption;
end;
end.
델파이에서 WndProc는 C 와는 달리 리턴 값을 취하지않는 프로시져 타입이
다. 레퍼런스타입으로 정의된 TMessage의 Result 필드를 통해서 값을 리턴
하게된다.
댓글