2013. 4. 29. 15:47

실행가능한 DLL 제작 (편리한 DLL Test(DLL 테스트), DLL Debug(DLL 디버그), DLL Unit Test, 개발 방식 지원)

DLL은 자체적으로 실행할 수 없기에, Run하게 되면 다음과 같이 실행할 .exe 경로를 받게 됩니다.


그래서 DLL을 개발중이라면, 테스트를 위해서 실행에 필요한 Test .exe를 따로 제작할 필요가 있습니다. 이는 꽤나 귀찮은 일인데, 왜냐하면, .dll과 .exe로 모듈이 나눠지기 때문에, include 파일이나 전역 메모리, 등등 신경써야 할 부분이 많아지게 됩니다(예를 들어, export 함수 하나 생성되면, 동반되는 작업이 많이 걸림(함수 선언, GetProcAddress 부분 수정, Wrapper class 수정, ...)). 따라서 이러한 불편함을 해소하기 위해, 새로운 모듈 추가 없이 .dll 자체를 실행 가능하게 만드는 방법을 공유하겠습니다. 아래 내용들을 적용하는데 실제 1분도 걸리지 않으니, 부담없이 작업할 수 있습니다.

우선 다음과 같이 프로젝트 설정에서 Configuration 하나를 복사합니다. Debug 혹은 Release를 선택해야 합니다. 물론 두개 모두 포함할 수 있습니다. (아래 테스트 project는 debug->NT32Debug, release->NT32Release로 rename되어 있음을 우선 알려드립니다.)


그럼 다음과 같이 New를 선택합니다.


그럼 다음과 같이 NT32Debug를 가지고 NT32Debug-Executable을 하나 생성합니다.
(참고로, NT32Release를 가지고 오면, Release로 빌드된 실행 Test 빌드를 구하게 될 것입니다.)


그럼 Visual Studio의 Main창의 Toolbar에서, 현재의 Configuration을 변경합니다.


그다음 아래와 같이 Configuration Type을 Application (.exe)로 변경합니다 !!!


여기에서 우선 빌드를 한번 해봅니다.
그럼,

error LNK2019: unresolved external symbol _WinMain@16 referenced in function ___tmainCRTStartup
와 같은 오류가 발생합니다. 혹시, 그 이외의 오류가 발생하는 경우, 직접 해결하시기 바랍니다. 프로젝트가 NT32Debug에서 NT32Debug-Executable로 복사될 때 복사되지 않는 영역중 하나가 Output Directory인데, 다음 부분이 오류의 원인이 되는지 참고해 보시기 바랍니다.


일단, _WinMain@16오류는, WinMain 함수를 넣으면 됩니다. 보통, DLL이름.cpp로 이뤄진 파일에 다음과 같이 넣으면 됩니다. 물론, 다른 Configuration에 영향을 주면 안되기 때문에, Define 하나 추가한뒤 해당 코드를 추가하면 해결될 것입니다. (참고. 위와 같이 기존 Configuration의 Output Directory와 겹치게되면, pdb 파일 불일치로 인한 심볼 연결 실패로, 디버깅이 안될 수 있습니다. 경우에 따라서는, $(SolutionDir)\..\..\Build\$(ConfigurationName)와 같이 경로를 분리하는 것도 좋은 방법입니다. 물론, 테스트를 위해 PostBuild Step에서 기존 Debug 경로에 심볼등을 복사하는 코드가 필요할지도 모릅니다.)


SOMEDLLAPI BOOL __cdecl SOME_DLL_EXPORT_01(VOID)
{
	...
}

SOMEDLLAPI BOOL __cdecl SOME_DLL_EXPORT_02(VOID)
{
	...
}

// NT32Debug-Executable configuration인 경우,
// WinMain을 둬서, 실행가능한 DLL 형태가 되도록 한다.
#ifdef TEST_EXECUTABLE
#include 

int __stdcall WinMain( __in HINSTANCE hInstance, __in_opt HINSTANCE hPrevInstance, __in_opt LPSTR lpCmdLine, __in int nShowCmd)
{
	SOME_DLL_EXPORT_01();
	SOME_DLL_EXPORT_02();
	...
	return 0;
}

#endif // DEBUG_EXECUTABLE
즉, 위 주황색 code만 추가한뒤 빌드하고, 실행하면 바로 SOME_DLL_EXPORT_,,,() 함수를 실행하여 테스트를 시작합니다. 물론, 다른 Configuration은 영향받지 않으며, NT32Debug-Executable로 빌드된 경우에만 해당되니 안심해도 됩니다. 그리고, 해당 WinMain에 Unit Test등의 코드나 해당 시나리오 코드를 넣으면 더욱 효과적일 것입니다.

여하튼, 이러한 방식이 이뤄지면, 평상시 DLL 개발시에는 "NT32Debug-Executable"로 설정하여, 편리하게 디버깅하고, 최종 테스트는 실제적인 .exe와의 호출을 테스트하면 될 것입니다.

참고로, 만일 Console 모드의 .exe로 만들고자 한다면,
int __stdcall WinMain( __in HINSTANCE hInstance, __in_opt HINSTANCE hPrevInstance, __in_opt LPSTR lpCmdLine, __in int nShowCmd)
==>
int main(int argc, char* argv[])
와 같이 WinMain 함수의 prototype을 변경하면 됩니다.
그리고 아래와 같이, Linker 설정의 system 값을 변경시켜 주면, printf(...)등을 사용할 수 있는 console 프로젝트로 실행될 것입니다.