며칠 전 별도로 진행되어오던 여러 프로젝트를 통합하던 과정에서 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 을 보니 그렇게 쓸 수 없다더군요.
그 외에도 문서가 아닌 경험적으로 알고 있는 사항이 좀 더 있을 것 같은데 생각이 나질 않네요.
역시, 공부가 필요합니다 ^^;
'개발' 카테고리의 다른 글
디버깅의 어려움과 즐거움 (5) | 2008.06.16 |
---|---|
기념일을 잊지 말자 - 마이플래너 0.1a (9) | 2008.06.06 |
Fph.exe 유감 그리고 강제 종료시키기 (53) | 2008.05.16 |
XmlLite 을 이용한 메뉴 XML 로딩하기 (13) | 2008.04.16 |
Visual Studio 2008 Profiler (7) | 2008.03.25 |
Source Line Counter (2) | 2008.03.19 |
댓글