. 여분 메모리

MDI는 같은 종류의 문서를 편집하는 여러 개의 차일드 윈도우가 동시에 열릴 수 있는 구조인데 이때 각 차일드는 같은 윈도우 클래스로부터 만들어지고 윈도우 프로시저도 공유하므로 모양과 동작이 같을 수밖에 없다. 각 차일드는 공통점이 더 많지만 그래도 서로 구분되는 고유한 정보가 필요하다. 편집하는 문서가 다르고 설정상태도 개별적으로 바뀔 수 있다.

차일드별로 고유한 이런 정보들은 전역변수에 저장할 수 없으며 각 차일드가 개별적으로 가지고 있어야 한다. 이 정보를 저장할 수 있는 영역은 여분 메모리와 윈도우 프로퍼티 두 가지가 있다. 여분 메모리는 16비트 시절부터 사용되었던 좀 오래된 방법이고 윈도우 프로퍼티는 32비트 윈도우에서 권장되는 방법이나 두 방법 모두 대체성이 있으므로 어떤 것을 사용하든지 큰 차이는 없다. 여기서는 여분 메모리를 사용하는데 차일드의 윈도우 클래스 정의문을 보면 다음과 같은 대입문이 있다.

 

WndClassEx.cbWndExtra=sizeof(DWORD);

 

여분 메모리에 4바이트를 할당하라는 뜻이며 이 영역에 차일드별로 고유한 정보를 기억시킨다. 좀 더 큰 메모리를 할당하여 필요한 정보를 직접 넣을 수도 있지만 확장에 불리하기 때문에 보통은 4바이트만 할당하고 필요한 정보를 포함하는 구조체를 선언한 후 구조체의 포인터를 여분 메모리에 기억시키는 방법을 많이 사용한다. 여기서는 다음과 같이 정의된 SInfo 구조체를 차일드의 고유 정보 저장을 위해 사용한다.

 

struct SInfo

{

     CApiEdit Ae;

     TCHAR NowFile[MAX_PATH];

};

 

두 개의 멤버를 가지는데 Ae는 이 차일드가 텍스트 편집에 사용할 ApiEdit 컨트롤 객체이며 NowFile은 지금 편집하고 있는 파일의 경로이다. 이 구조체는 DGChildProc에서 차일드가 생성될 때인 WM_CREATE 메시지에서 할당된다. new 연산자로 SInfo 구조체를 할당하는데 이때 Ae 컨트롤의 생성자가 호출되어 필요한 초기화를 한다. 구조체에 포함된 객체를 생성해야 하기 때문에 반드시 new 연산자를 사용해야 하며 malloc으로 메모리만 할당해서는 안된다. SInfo 구조체를 만들자 마자 SetWindowLong 함수로 이 구조체의 번지를 여분 메모리의 0번 오프셋에 저장시켰다. 이제 이 구조체가 필요하면 언제든지 GetWindowLong으로 구할 수 있다.

구조체 할당 후 Ae객체의 Create 함수를 호출하여 객체를 생성한다. 이때 CApiEdit::OnCreate가 호출되며 컨트롤은 실질적인 초기화를 할 기회를 가지게 된다. NowFile에는 이름없음이라는 초기 파일명을 대입하여 아직 저장되지 않은 문서임을 표시하였다. WM_SIZE에서는 차일드의 작업영역 전체를 ApiEdit 컨트롤로 가득 채우며 WM_SETFOCUS에서는 차일드가 포커스를 받자 마자 ApiEdit 컨트롤에게 포커스를 다시 넘긴다. 이 메시지에서 이런 처리를 하려면 차일드는 자신이 가지고 있는 ApiEdit 객체를 찾을 수 있어야 하는데 그 장소가 바로 여분 메모리이다. 여분 메모리에 저장된 SInfo 구조체 포인터는 GetWindowLong 함수를 호출하여 구할 수 있으며 포인터를 구하면 이 구조체의 멤버인 Ae객체를 참조할 수 있다.

차일드가 파괴될 때인 WM_DESTROY에서는 할당된 SInfo 구조체를 제거한다. new 연산자로 구조체를 할당했으므로 제거할 때도 delete 연산자를 사용해야 한다. 구조체가 파괴되면 그 멤버인 ApiEdit 객체도 파괴되며 이때 OnDestroy와 파괴자가 호출되어 필요한 뒷정리를 하게 된다.