본문 바로가기

3.구현/VC++

MFC 대화상자에서 초기화 및 종료 API 순서

"메시지 처리에 대해서 잘 알고 있다면 다음 내용이 필요 없다."

MFC에서 기본적인 위도우 프레임웍이 설계되어 있고 그 곳에 맞게 필요한 코드를 넣어주면 된다.
프로그래밍에 있어서 가장 중요한게 자원 할당과 해제이다. 특히 C++에서는 메모리 관리를 프로그래머가 직접해주기 때문에 잘못된 메모리 관리는 시스템 전체에 안좋은 영향을 준다.

MFC에서 가장 무식하게 자원 할당과 해제를 하는 경우를 보면 생성자와 소멸자에 하는 경우이다. 실제 직접 생성자와 소멸자에 코딩하는 경우는 드물고 포인터 변수를 NULL로 초기화하는 정도이다.

그럼 MFC에서 실제 API 호출 순서를 보자. 응용프로그램 형태 중에서 대화상자를 중심으로 살펴보겠다.

작성자: 박재성 (http://ospace.tistory.com/, ospace114@empal.com)

Case 1) 대화상자 뛰우고 [X] 버튼 눌러 종료하기

Warning: no message line prompt for ID 0x8003.  
[CD1Dlg] PreInitDialog()  
[CD1Dlg] OnInitDialog()  
[CD1Dlg] OnClose()  
[CD1Dlg] OnCancel()  
[CD1Dlg] OnDestroy()  
[CD1Dlg] PostNcDestroy()  
[CD1Dlg] OnNcDestroy()

Case 2) 대화상자 뛰우고 "Cancel" 버튼 눌러 종료하기

Warning: no message line prompt for ID 0x8003.  
[CD1Dlg] PreInitDialog()  
[CD1Dlg] OnInitDialog()  
[CD1Dlg] OnCancel()  
[CD1Dlg] OnDestroy()  
[CD1Dlg] PostNcDestroy()  
[CD1Dlg] OnNcDestroy()

Case 3) 대화상자 뛰우고 "Ok" 버튼 눌러 종료하기

Warning: no message line prompt for ID 0x8003.  
[CD1Dlg] PreInitDialog()  
[CD1Dlg] OnInitDialog()  
[CD1Dlg] OnDestroy()  
[CD1Dlg] PostNcDestroy()  
[CD1Dlg] OnNcDestroy()

처음

위의 경우를 살펴보면, 처음 시작시 호출되는 함수가,

PreInitDialog()  
OnInitDialog()  

두 함수가 기본적으로 호출이 된다. OnInitDialog 함수는 많이 익숙한 이름이다. 대화상자에 가장 기본적인 초기화 코드를 넣는 함수이다.

(함수라는 용어보다 메소드, API, 멤버 함수등을 적합한 이름을 선택해야되는데 편의상 쉽게 이해하기 위해서 함수로 통일한다.)

중간

그리고 종료하는 종류에 따라서 중간에 호출되는 함수가 달라진다.

[X]와 "Cancel" 버튼을 눌렀을 경우에 공통적으로 OnCancel 함수가 호출된다. 단, [X] 버튼을 눌렀을 경우 추가로 OnClose 함수가 호출된다.
간혹 OnClose 함수가 대화상자 닫히는(Close) 경우로 착각해서 이 곳에 넣는 경우가 있다. 나도 그런 적이 있었다. 위의 함수 호출 순서에 나왔듯이 OnClose 함수는 경우에 따라서 호출되지 않은다.
"Ok" 버튼을 눌렀을 경우는 역기서는 별다른 함수 호출이 없다. 필요하다면 OnOk 함수 정도 정의하면 된다.

종료하는 경우는 살펴보자.

OnDestroy()  
PostNcDestroy()  
OnNcDestroy()  

세 종류의 함수가 호출된다. OnDestroy 함수는 메시지 핸들러로 WM_DESTROY 메시지를 수신하면 호출된다.
PostNcDestroy 와 OnNcDestroy 는 함수 재정의에 의해서 사용된다.
종료시 가장 많이 사용하는 함수가 OnDestroy와 OnNcDestroy 함수이다.
본인이 선호하는 것은 OnDestroy 함수이다. OnNcDestroy()는 함수를 재정의 해줘야하며 메시지 종료 (WM_DESTROY)에 의해서 종료될 경우 해당 자원 해제를 확인하는데 직관적이지 않기 때문이다.

소멸자와 생성자 사용에 대해서

앞의 예제를 실제 코드로 간략히 살펴보면, Class CD1Dlg 정의는 되어 있다고 가정한다.

CD1Dlg dlg; // (1)
if(dlg.DoModal() > 0) {
  // Ok
} else {
  // Cancel
}

와 같이 동작한다. 위의 코드에서 대화상자를 객체화하는 부분(1)를 저장해두었다가 필요시 호출해서 사용할 수 있다. 즉 다음과 같다.

if(m_pDlg == NULL) {m_pD1Dlg = new CD1Dlg; }
if(m_pD1Dlg->DoModal() > 0) {
  // Ok
} else {
  // Cancel
}

위와 같이 바뀌면 앞에서 초기화하는 부분

PreInitDialog()
OnInitDialog()

그리고, 종료하는 부분

OnDestroy()
PostNcDestroy()
OnNcDestroy()

이 호출하지 않는다고 생각할 수 있다. 근데, 전혀 그렇지 않다.
대화상자 DoModal함수 호출이 초기화하는 부분을 불러오게 된다.

PreInitDialog()
OnInitDialog()

종료하는 부분도 마찬가지이다. 그러면 대화상자 객체를 저장하는 이유는 뭘까?
대화상자를 빈번히 호출하면서 초기화가 오래걸리는 객체를 대화상자에 갖고 있다면, 한번 초기화한 후에 그 결과 객체를 계속 보관하고 필요시 가져와서 사용하면 성능 향상에 도움이 될 것이다.
(주! 이 부분은 여러 방법으로 가능하기 때문이 반드시 대화상자에 객체를 저장할 필요는 없다.)

그때 생성자에서 해당 객체를 생성하고 소멸자에서 해당 객체를 제거하면된다. 그러면 대화상자 뛰우고 종료하는 과정에서는 해당 객체가 사라지지 않는다.

덧글: MSDN도 참고 했지만 시간관계상 재 검토는 하지 않아서 틀린 부분도 있을 것이라 사료된다. 틀린 부분이 있거나 더 좋은 방법이 있다면 필히 메일로 내용을 보내주기 바란다. 좋은 프로그램 많이 만들기 바란다.

반응형