티스토리 뷰

MFC DLL에서 class에 vector를 담아서 Export 하려 시도 하였다.
처음에는 잘 되는 듯이 보였지만, class를 delete 하는 순간, 와장창 무너지기 시작했다.

계속해서 dbgheap.c의 _CrtIsValidHeapPointer(const void * pUserData) 안에 있는
HeapValidate( _crtheap, 0, pHdr(pUserData) ); 에서 FALSE을 리턴하여 ASSERT에 걸리게 되었다.

계속 디버깅을 해보니, vector의 소멸을 제대로 하지 못하고 있었다.
vector의 소멸자를 따라가 보니 dbgheap.c의 _crtheap이 caller의 heap을 가리키고 있는 것이 보였다.
아니, 애당초 call stack의 소멸 지점이 callee 인 dll이 아니라, caller인 application 이었다.

구글링을 해보니 STL 객체가 포함된 클래스를 내보내는 방법에 대한 msdn 문서가 있었다.
https://support.microsoft.com/ko-kr/kb/168958

중요한 것은, export 하는 class에서 사용하는 stl 타입의 선언을 아래와 같이 class 외부에서 한번 더 해줘야 하며,
template class __declspec(dllexport) std::vector<자료형>;
import 하는 class에서는 아래와 같이 선언해 줘야 한다는 것이다.
extern template class __declspec(import) std::vector<자료형>;
또한 STL 컨테이너 중에서 유일하게 vector<T> 타입만이 DLL Export가 가능하다.

위와 같이 해주니, vector의 소멸 시점에 정상적으로 callee인 DLL의 heap을 잡는 것이 보였고,
소멸 역시 제대로 이루어 졌다.

사족 )
이번 삽질을 통해 알아낸 재밌는 사실 중 하나는, MFC project의 경우 allocator delete(*)를 crt library에서 부르는 것이 아니라 mfc library에서 먼저 호출한다는 것이다.
이는 afxmem.cpp에 있는 void __cdecl operator delete(void* p)로 오게 된다. (결국엔 crt로 오기는 한다. [debug 모드에서 찍어보면 dbgheap.c로 간다])
crt library의 경우 중간 단계 없이 바로 delete로 간다.
이 순서는 재밌게도, 라이브러리 import 순서에 따르며 이렇게 되는 이유는, CRT 라이브러리가 new, delete 및 DllMain 함수에 대해서 약한 외부 링크를 사용하기 때문이다.
따라서 CRT 라이브러리가 링크 되기 전에 MFC 라이브러리를 링크 하면 MFC 라이브러리에 있는 new, delete 및 DllMain 함수가 먼저 호출되는 것이다.
C++도 아니고 C에서 이런 신박한 코드를 작성하다니 재밌다 :)
사족 참조 : https://support.microsoft.com/ko-kr/kb/148652
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함