AfxGetInstanceHandle() 함수가 NULL 을 리턴하는 경우

2008.05.13 19:19개발


며칠 전 별도로 진행되어오던 여러 프로젝트를 통합하던 과정에서 AfxGetInstanceHandle() 이 NULL 을 리턴하는 문제를 발견했습니다.

실행화일이 이제 겨우 Load 될려는 시점에서 문제가 발생한 터라 특별한 디거빙정보는 없고 디버거는 아래 MFC 라인에서 멈춰있었습니다.

 

_AFXWIN_INLINE HINSTANCE AFXAPI AfxGetInstanceHandle()

           { ASSERT(afxCurrentInstanceHandle != NULL);

                     return afxCurrentInstanceHandle; }

 

대략 난감할 때면 언제나 그렇듯 구글링을 통해 여러가지 가능성을 점검하다가 아래 문서를 발견했습니다.

 

How To Debug MFC Module and Thread State Problems

 

이전에도 이 문서를 본적이 있었는데 그 때는 제대로 써 먹지 못했지만 이번에는 정말 제대로 써 먹었습니다.

 

결국 dll 의 인스턴스 핸들이 사라진 이유는 MFC 에서 관리하는 모듈 인스턴스가 어떤 이유에서인지 변경되었다는 것을 의미한다고 위 문서에 적혀 있습니다.

 

그래서 문서에 나와있는 데로 아래 내용을 여러 DLL 과 실행파일의 stdafx.h 에 추가하고

 

#ifdef _DEBUG

   #define MODULE_TRACE()  TRACE("%s(%d) : Module State nInst = 0x%X\n", \

         __FILE__, __LINE__, AfxGetModuleState()->m_hCurrentInstanceHandle)

   #define THREAD_TRACE()  TRACE("%s(%d) : Thread State Address = 0x%X\n",\

         __FILE__, __LINE__, AfxGetThreadState())

   #else

   #define MODULE_TRACE()

   #define THREAD_TRACE()

   #endif  //_DEBUG

                                         

의심이 갈만한 소스에는 다음 두 줄을 추가해서

 

MODULE_TRACE();

THREAD_TRACE();

 

모듈과 Thread 의 상태가 변경되는 곳이 있는 지 찾아 봤습니다.

결국 한참을 헤매긴 했지만 Application 의 MainFrame 이 생성되기 직전 ATL 관련 COM 객체를 생성하면서 전역 모듈 상태가 변경되는 현상을 발견했습니다.

이전까지 작동하던 코드여서 정확하게 왜 문제가 생겼는지는 분석해 보진 못했지만, 모듈/쓰레드 추적기 덕분에 제 딴엔 참 어려운 문제를 해결 할 수 있었습니다.

 

사실 MFC 에서 모듈 상태변경은 참 귀찮은 부분 중에 하나입니다.

MFC Regular Dll 을 새로 만들면 프로젝트.cpp 소스에 다음과 같은 주석이 달립니다. 가끔씩 빼 먹어도 잘 작동하는 편인데, 이 부분에서 뭔가 심각한 MFC 상태 변경과정을 거치는 모양입니다.

 

//

//         참고:

//

//                   이 DLL이 MFC DLL에 대해 동적으로 링크되어 있는 경우

//                   해당 DLL에서 내보내고 MFC로 호출하는 모든 함수의

//                   시작 부분에

//                   AFX_MANAGE_STATE 매크로가 들어 있어야 합니다.

//

//                   예:

//

//                   extern "C" BOOL PASCAL EXPORT ExportedFunction()

//                   {

//                              AFX_MANAGE_STATE(AfxGetStaticModuleState());

//                              // 함수의 나머지 본문은 여기에 옵니다.

//                   }

//

//                   이 매크로는 MFC로 호출하기 전에

//                   각 함수에 반드시 들어 있어야 합니다.

//                   즉, 매크로는 함수의 첫 번째 문이어야 하며

//                   개체 변수의 생성자가 MFC DLL로

//                   호출할 수 있으므로 개체 변수가 선언되기 전에

//                   나와야 합니다.

//

//                   자세한 내용은

//                   MFC Technical Note 33 및 58을 참조하십시오.

 

 

근데 여태까지 MFC Technical Note 33 및 58를 읽어 보지 못해서, 확장 DLL 과 Regular Dll을 경험적으로만 파악하고 있습니다.

  • 확장 dll 은 리소스 아이디가 메인 어플리케이션과 같이 사용되고 Regular Dll 은 따로 놉니다.

  • ATL 은 Regular Dll 에만 추가 가능하네요

  • Regular Dll 에서 생성한 STL 컨테이너 데이터를 확장 DLL로 넘어오면 문제가 생겨서 고생한 적이 있습니다. 결국 MSDN 을 보니 그렇게 쓸 수 없다더군요.

 

 

그 외에도 문서가 아닌 경험적으로 알고 있는 사항이 좀 더 있을 것 같은데 생각이 나질 않네요.

역시, 공부가 필요합니다 ^^;