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

C++빌더 Q&A
C++Builder Programming Q&A
[50547] Re:Re:MakeObjectInstance에 대해... 감사합니다.
장성호 [nasilso] 1603 읽음    2007-09-13 08:24
정말 감사합니다.
실력이 부족해 완전히 이해하지는 못했지만
MakeObjectInstance의 흐름에 대해 어느정도는 이해하게 됬습니다.

추가로 올리신 내용도 MakeObjectInstance 를 보면서
VCL말고 다른 MFC나 윈도우 Library에서는 어떻게 처리하는지 궁금해하고 있던 내용입니다.


덕택에 참 많이 배웁니다.
얼마전 MethodAddress 관한 질문에서도 그렇구...

요즘 틈나면 VCL을 조금씩 보는데 실력도 딸리고 Delphi문법도 익숙치 않아 많이 헤맵니다.
앞으로도 잘 부탁드립니다.

그럼

0 님이 쓰신 글 :
: 장성호 님이 쓰신 글 :
: : MakeObjectInstance에 대해 누가좀 가르쳐 주세요
: :
: :
: : C++Builder를 이용해 프로그램하면서 Sub-classing이 필요한경우
: : 폼에다가 TWndMethod 형의 메소드를 만들어서
: : //typedef void __fastcall (__closure *TWndMethod)(Messages::TMessage &Message);
: : WindowProc를 교체하여서 하곤 했었습니다.
: :
: : 그런데 얼마전 MakeObjectInstance 라는 VCL함수가 있는줄 알게되었습니다.
: : 뭐 아래와 같은식으로 사용되더군요
: :
: : void *proc;
: : FARPROC origProc;
: : void TForm1::SubWndProc(TMessage &msg)
: : {
: :    ...
: :   Msg.Result = CallWindowProc(fProc,Button1->Handle,Msg.Msg,Msg.WParam,Msg.LParam);
: : };
: : //----------------------------------------
: : proc=MakeObjectInstance((SubWndProc);
: : fProc=SetWindowLong(Button1->Handle, GWL_WNDPROC, (long)proc));
: : //----------------------------------------
: : SetWindowLong(Button1->Handle, GWL_WNDPROC, (long)fproc));
: : FreeObjectInstance(proc);
: :
: : 사용하는 방법을 몰라서 질문 올리는것이 아니라
: : 원리를 알고 싶습니다.
: :
: : MakeObjectInstance 함수의 소스코드를 보니까
: :
: : VirtualAlloc 이라는 kernel32.dll에 함수를 쓰더군요
: : BlockCode[2]={0x59,0xE9};    //0x59 ={ POP ECX }  , 0xE9 ={ JMP StdWndProc }
: : BlockCode 라는 값을 확보한 메모리로 복사하고
: : StdWndProc와 Offset값을 계산해서 설정하고..
: : 뭐 이것저것 하던데요
: :
: : 실력이 딸려서 정확히 이해하지 못하겠습니다.
: :
: : 누가 MakeObjectInstance 함수에 대해 좀 가르쳐 주세요
: : 왜 그렇게 사용하는지도 함께..
:
: - 윈도우 인스턴스: CreateWindow로 만든 윈도우
: - 클래스 인스턴스: C++ 클래스의 인스턴스
: - VCL의 윈도우 클래스: 윈도우를 다루는 VCL 클래스
: - 윈도우 클래스: WNDCLASS
:
: MS Windows의 WNDCLASS 구조체의 lpfnWndProc 멤버에 지정된 게 해당 윈도우 클래스의 윈도우 프러시저이고
: 이 윈도우 프러시저 함수가 해당 윈도우 클래스로 만들어진 모든 윈도우 인스턴스의 모든 메시지를 처리합니다.
: lpfnWndProc 멤버는 WNDPROC 형인데 이는 함수 포인터형으로서 __closure와는 다릅니다.
: LRESULT CALLBACK WindowProc(
:     HWND hwnd,    // handle of window
:     UINT uMsg,    // message identifier
:     WPARAM wParam,    // first message parameter
:     LPARAM lParam     // second message parameter
:    );
: 이 윈도우 프러시저는 위와 같은 형태로서 C++의 멤버 함수가 아닌 C 언어의 일반 함수입니다.
: 이를 보면 그 어디에도 클래스의 인스턴스 포인터와 그 윈도우 핸들을 연관시켜줄 만한 멤버가 없을뿐만아니라
: 윈도우 인스턴스를 만들기 전에 등록해야 하는 것이므로 더더욱 불가능합니다.
: (운영체제에게 인스턴스를 미리 알려줄 방법도 없습니다)
: CreateWindow 호출로 얻은 윈도우 핸들을 가지고 별도의 특별한 작업을 해주어야
: 비교나 탐색 없이 C++ 클래스의 인스턴스를 대상으로 멤버 함수인 윈도우 프러시저를 운영체제가 호출하게 하는 효과를 낼 수 있습니다.
:
: VCL의 윈도우 클래스들은 C++ 클래스로 되어있고 그 멤버 함수를 통하여 조작합니다.
: 클래스의 멤버 함수를 호출하기 위해서는 클래스의 인스턴스를 지정해주어야 합니다.
: 예를 들어, TForm1 *Form1; Form1->Show();에서 Form1이 클래스의 인스턴스 포인터이고 Show가 멤버 함수입니다.
: 어떤 인스턴스에 대해 클래스의 어떤 멤버 함수를 호출할 것인가를 하나의 변수로 저장하기 위해 만든 것이 __closure인데 상위 4바이트는 클래스의 인스턴스 포인터이고 하위 4바이트는 해당 멤버 함수의 주소입니다.
:
: VCL의 윈도우 클래스에도 윈도우 프러시저가 있어야 윈도우 메시지를 처리할 수 있습니다.
: 그런데, VCL의 윈도우 클래스는 C++ 클래스로서 윈도우 프러시저는 그 멤버 함수로 되어있습니다.
: 이를 운영체제가 요구하는 실질적인 윈도우 프러시저로 등록하기 위해서는 별도의 조치가 있어야 합니다.
: 이 조치가 MakeObjectInstance입니다.
:
: 1. MakeObjectInstance 함수
: TObjectInstance 인스턴스 하나가 VCL 윈도우 클래스 인스턴스 하나를 처리합니다.
: TInstanceBlock 인스턴스 하나에는 TObjectInstance 인스턴스가 314개 포함되어 있습니다.
: ( PageSize-offsetof(TInstanceBlock,Instances)) div sizeof(TObjectInstance)
:   = (4096-10) div 13 = 314 )
: TInstanceBlock 인스턴스가 하나도 없거나 현재 사용 중인 인스턴스(InstBlockList) 안의 TObjectInstance 인스턴스 314 개가 모두 사용되었으면 InstFreeList가 nil이 되며 이 때 또 하나의 TInstanceBlock 인스턴스를 만들고 InstBlockList에 그 번지(포인터)를 저장해둡니다.
: TObjectInstance 인스턴스 314 개는 연결리스트로 연결해두는데 뒤쪽부터 앞쪽 순서로 사용하도록 연결해둡니다.
: TInstanceBlock의 인스턴스들도 연결리스트로 연결해둡니다.
: InstBlockList에 포함된 TObjectInstance 인스턴스 314 개 중 다음 번에 사용할 인스턴스의 번지를 InstFreeList에 저장해둡니다.
:
: TObjectInstance 인스턴스의 Code 멤버는 실행파일에서의 실행코드로서 함수 호출 코드입니다.
: TObjectInstance 인스턴스의 Offset 멤버는 어떤 함수를 호출할 것인지 그 함수의 위치값으로서
: 이 인스턴스를 포함하고 있는 TInstanceBlock 인스턴스의 Code를 호출하도록 설정됩니다.
: TInstanceBlock 인스턴스의 Code는 스택에 저장된 값 하나를 꺼내서 ECX 레지스터에 저장하는 명령과
: StdWndProc 함수로 실행점을 옮기는 JMP 명령으로 구성되어 있습니다.
: 여기서, 스택에 저장된 값은 TObjectInstance 인스턴스의 Code(함수호출) 실행 시 스택에 자동으로 저장되는 값으로서
: 함수 호출 후 되돌아왔을 때 실행될 코드의 위치값입니다.
: 이 위치는 TObjectInstance 인스턴스의 Method 위치입니다.
: TInstanceBlock 인스턴스의 Code에는 스택에 저장된 값 하나를 꺼내는 명령이 있으므로 위 위치로 되돌아갈 수 없게 됩니다.
: 스택에 저장된 값 하나를 꺼내고 나서 StdWndProc 함수로 실행점이 옮겨집니다(JMP).
:
: 2. StdWndProc 함수
: 운영체제가 윈도우 프러시저를 호출할 때는 먼저 그 매개변수들을 스택에 저장하며 호출 시 복귀위치가 자동으로 스택에 저장된 후 호출됩니다.
: StdWndProc 함수의 매개변수들은 윈도우 프러시저로 등록된 TObjectInstance 인스턴스의 Code를 운영체제가 호출하기 직전에 스택에 저장해둔 것들입니다.
: StdWndProc 함수 내의 PUSH 명령 4 개는 TWndMethod의 매개변수인 TMessage 형의 값을 스택에 만드는데 그 원천은 운영체제가 스택을 통하여 넘겨준 것입니다.
: (매개변수 전달 방식 중 값에 의한 전달의 경우 스택에 그 값을 저장하여 전달함)
: PUSH 반대 순서대로 TMessage의 Msg,WParam,LParam,Result가 됩니다.
: MOV     EAX,[ECX].Longint[4]
: CALL    [ECX].Pointer
: StdWndProc 함수에서는 MakeObjectInstance 함수를 통하여 TObjectInstance 인스턴스의 Method에
: 저장해둔 __closure(VCL 윈도우 클래스 인스턴스 포인터와 해당 멤버 함수의 주소가 함께 저장됨) 값을 가지고
: 해당하는 VCL 윈도우 클래스 인스턴스에 대해 윈도우 프러시저를 호출합니다.
: 위 MOVE 명령으로 EAX 레지스터에 저장되는 값이, 해당하는 VCL 윈도우 클래스 인스턴스의 윈도우 프러시저 내에서, this 포인터가 됩니다.
: TObjectInstance 인스턴스의 Method 위치는 위에서 말한대로 ECX 레지스터에 저장되어 있습니다.
: 마지막 명령(POP EAX)으로 StdWndProc 함수의 반환값은 EAX 레지스터에 저장되며
: 마지막 두 명령으로 이 함수에서 사용한 스택 공간도 반환됩니다.
: 함수 실행이 종료되면 TObjectInstance 인스턴스의 Method 위치로 return 하지 않고 바로 운영체제로 return 합니다.
: (위에서 말한대로 스택에 저장된 복귀 위치 하나를 꺼내버렸기 때문에 현재 스택의 top에 저장된 것은 운영체제 쪽 복귀 위치임)
:
:
: 첨기:
: 아래와 같이 윈도우 인스턴스와 클래스 인스턴스를 연관시킬 수 있습니다.
: 이 방식은 모든 메시지 처리시 해당 윈도우 클래스(WNDCLASS)의 전역(static) 윈도우 프러시저가 호출되며 매번
: GetWindowLong 함수를 호출하여 클래스 인스턴스 포인터(this 포인터)를 가져와서 사용합니다.
: 한편, MakeObjectInstance에서와 같이 하면 GetWindowLong 함수 호출 없이 this 포인터를 얻을 수 있고
: 윈도우 프러시저가 static일 필요가 없으므로 윈도우 프러시저 내에서 바로 클래스의 멤버에 액세스할 수 있습니다.
:
: class SomeClass
: {
: public:
:
: private:
:   HWND m_hWnd;
:    static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam);
:    LRESULT OnPaint(void);
:    LRESULT OnCommand(WPARAM wParam, LPARAM lParam);
:    LRESULT OnCreate();
:    LRESULT OnDestroy();
:   .........
: };
:
: int WINAPI WinMain(
:     HINSTANCE hInstance,    // handle to current instance
:     HINSTANCE hPrevInstance,    // handle to previous instance
:     LPSTR lpCmdLine,    // pointer to command line
:     int nCmdShow     // show state of window
:    )
: {
:  SomeClass  *pclass = new SomeClass();
:  WNDCLASS wc;
:  wc.lpfnWndProc = (WNDPROC)SomeClass::WndProc;
:  RegisterClass(&wc);
:  CreateWindow(,,,,,,,,,,pclass);
:  .......
: }
:
: LRESULT CALLBACK SomeClass::WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
: {
:  SomeClass  *pThis = (SomeClass *)GetWindowLong(hWnd, GWL_USERDATA);
:   switch (uMessage)
:   {
:    case WM_NCCREATE:
:       LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam;
:       pThis = (SomeClass *)(lpcs->lpCreateParams);
:       SetWindowLong(hWnd, GWL_USERDATA, (LONG)pThis);
:       pThis->m_hWnd = hWnd;
:     break;
:    case WM_CREATE :
:     return pThis->OnCreate();
:    case .......
:  }
: }

+ -

관련 글 리스트
50524 MakeObjectInstance에 대해 누가좀 가르쳐 주세요 장성호 2290 2007/09/10
50539     Re:MakeObjectInstance에 대해 누가좀 가르쳐 주세요 0 1764 2007/09/12
50547         Re:Re:MakeObjectInstance에 대해... 감사합니다. 장성호 1603 2007/09/13
50576             Re:Re:Re:MakeObjectInstance에 대해... 감사합니다. 0 1599 2007/09/15
50578                 Re:Re:Re:Re:MakeObjectInstance에 대해... 감사합니다. 장성호 1545 2007/09/16
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.