본문 바로가기
카테고리 없음

Delphi OOP와 C++과의 차이 [4]

by MonoSoft 2021. 4. 29.
728x90
반응형

Delphi OOP와 C++과의 차이 [4]

Message Hook

============

델파이에서의 메시지 후킹을 알아보기전에 C++과 비교하는 의미로써 MFC를

이용한 간단한 예제를 올린다. 다음 소스는 메시지 맵핑테이블을 이용해

서 마우스이벤트를 후킹한다.

 

#include

#include

 

//---- 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

#endif

 

단지 메시지핸들러 메소드임을 나타내는 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 필드를 통해서 값을 리턴

하게된다.

728x90
반응형

댓글