TN011: DLL의 일부로 MFC 사용
이 참고에서는 MFC 라이브러리를 Windows DLL(동적 연결 라이브러리)의 일부로 사용할 수 있는 일반 MFC DLL에 대해 설명합니다. Windows DLL 및 빌드 방법에 대해 잘 알고 있다고 가정합니다. MFC 라이브러리에 대한 확장을 만들 수 있는 MFC 확장 DLL에 대한 자세한 내용은 MFC의 DLL 버전을 참조하세요.
DLL 인터페이스
일반 MFC DLL은 애플리케이션과 DLL 간의 인터페이스가 C와 유사한 함수 또는 명시적으로 내보낸 클래스에 지정되어 있다고 가정합니다. MFC 클래스 인터페이스를 내보낼 수 없습니다.
DLL과 애플리케이션 모두 MFC를 사용하려는 경우 둘 다 MFC 라이브러리의 공유 버전을 사용하거나 라이브러리의 복사본에 정적으로 연결할 수 있습니다. 애플리케이션과 DLL은 모두 MFC 라이브러리의 표준 버전 중 하나를 사용할 수 있습니다.
일반 MFC DLL에는 다음과 같은 몇 가지 이점이 있습니다.
DLL을 사용하는 애플리케이션은 MFC를 사용할 필요가 없으며 Visual C++ 애플리케이션일 필요는 없습니다.
MFC에 정적으로 연결되는 일반 MFC DLL의 경우 DLL의 크기는 사용 및 연결된 MFC 및 C 런타임 루틴에만 따라 달라집니다.
MFC에 동적으로 연결되는 일반 MFC DLL을 사용하면 공유 버전의 MFC를 사용하여 메모리를 절약하는 것이 중요할 수 있습니다. 그러나 공유 DLL, Mfc version.dll> 및 Msvvcrt<version.dll>을 DLL과 함께 배포해야 합니다.<
DLL 디자인은 클래스를 구현하는 방법과는 독립적입니다. DLL 디자인은 원하는 API로만 내보냅니다. 따라서 구현이 변경되면 일반 MFC DLL은 여전히 유효합니다.
MFC에 정적으로 연결되는 일반 MFC DLL을 사용하는 경우 DLL과 애플리케이션 모두 MFC를 사용하는 경우 DLL과 다른 버전의 MFC를 원하는 애플리케이션에 문제가 없습니다. MFC 라이브러리는 각 DLL 또는 EXE에 정적으로 연결되므로 어떤 버전이 있는지 의심의 여지가 없습니다.
API 제한 사항
일부 MFC 기능은 기술 제한으로 인해 또는 일반적으로 애플리케이션에서 해당 서비스를 제공하기 때문에 DLL 버전에 적용되지 않습니다. 현재 버전의 MFC에서는 적용할 수 없는 CWinApp::SetDialogBkColor
함수만 있습니다.
DLL 빌드
MFC에 정적으로 연결되는 일반 MFC DLL을 컴파일할 때는 기호 _USRDLL
를 _WINDLL
정의해야 합니다. 다음 컴파일러 스위치를 사용하여 DLL 코드를 컴파일해야 합니다.
/D_WINDLL 컴파일이 DLL에 대한 것임을 의미합니다.
/D_USRDLL 일반 MFC DLL을 빌드하는 것을 지정합니다.
또한 이러한 기호를 정의하고 MFC에 동적으로 연결되는 일반 MFC DLL을 컴파일할 때 이러한 컴파일러 스위치를 사용해야 합니다. 또한 기호 _AFXDLL
를 정의해야 하며 DLL 코드를 다음으로 컴파일해야 합니다.
- /D_AFXDLL 동적으로 MFC에 연결하는 일반 MFC DLL을 빌드하도록 지정합니다.
애플리케이션과 DLL 간의 인터페이스(API)를 명시적으로 내보내야 합니다. 인터페이스를 낮은 대역폭으로 정의하고, 가능하면 C 인터페이스만 사용하는 것이 좋습니다. 직접 C 인터페이스는 더 복잡한 C++ 클래스보다 기본 쉽게 확인할 수 있습니다.
C 및 C++ 파일에서 포함할 수 있는 별도의 헤더에 API를 배치합니다. 예제는 MFC 고급 개념 샘플 DLLScreenCap의 헤더 ScreenCap.h 를 참조하세요. 함수를 내보내려면 모듈 정의 파일()의 섹션에 EXPORTS
함수를 입력합니다. DEF) 또는 함수 정의에 포함 __declspec(dllexport)
이러한 함수를 클라이언트 실행 파일로 가져오는 데 사용합니다 __declspec(dllimport)
.
MFC에 동적으로 연결되는 일반 MFC DLL에서 내보낸 모든 함수의 시작 부분에 AFX_MANAGE_STATE 매크로를 추가해야 합니다. 이 매크로는 현재 모듈 상태를 DLL에 대한 모듈 상태로 설정합니다. 이 매크로를 사용하려면 DLL에서 내보낸 함수의 시작 부분에 다음 코드 줄을 추가합니다.
AFX_MANAGE_STATE(AfxGetStaticModuleState( ))
WinMain -> DllMain
MFC 라이브러리는 일반적인 MFC 애플리케이션에서와 같이 CWinApp 파생 개체를 초기화하는 표준 Win32 DllMain
진입점을 정의합니다. 일반적인 MFC 애플리케이션에서 와 같이 InitInstance 메서드에 모든 DLL 관련 초기화를 배치합니다.
애플리케이션이 기본 메시지 펌프를 소유하기 때문에 CWinApp::Run 메커니즘은 DLL에 적용되지 않습니다. DLL이 모덜리스 대화 상자를 표시하거나 기본 프레임 창이 있는 경우 애플리케이션의 기본 메시지 펌프는 CWinApp::P reTranslateMessage를 호출하는 DLL 내보내기 루틴을 호출해야 합니다.
이 함수를 사용하려면 DLLScreenCap 샘플을 참조하세요.
MFC가 제공하는 함수는 DllMain
DLL이 언로드되기 전에 파생된 CWinApp
클래스의 CWinApp::ExitInstance 메서드를 호출합니다.
DLL 연결
MFC에 정적으로 연결되는 일반 MFC DLL을 사용하면 DLL을 Nafxcwd.lib 또는 Nafxcw.lib 및 Libcmt.lib라는 C 런타임 버전과 연결해야 합니다. 이러한 라이브러리는 미리 빌드되어 있으며 Visual C++ 설정을 실행할 때 지정하여 설치할 수 있습니다.
예제 코드
전체 샘플은 MFC 고급 개념 샘플 프로그램 DLLScreenCap을 참조하세요. 이 샘플에서 유의해야 할 몇 가지 흥미로운 사항은 다음과 같습니다.
DLL의 컴파일러 플래그와 애플리케이션의 컴파일러 플래그는 다릅니다.
링크 줄 및 . DLL에 대한 DEF 파일과 애플리케이션에 대한 DEF 파일은 다릅니다.
DLL을 사용하는 애플리케이션은 C++에 있을 필요가 없습니다.
애플리케이션과 DLL 간의 인터페이스는 C 또는 C++에서 사용할 수 있고 DLLScreenCap.def로 내보내지는 API입니다.
다음 예제에서는 정적으로 MFC에 연결하는 일반 MFC DLL에 정의된 API를 보여 줍니다. 이 예제에서 선언은 C++ 사용자에 대한 블록으로 extern "C" { }
묶입니다. 이것은 몇 가지 장점이 있습니다. 먼저 비 C++ 클라이언트 애플리케이션에서 DLL API를 사용할 수 있습니다. 둘째, C++ 이름 맨글링이 내보낸 이름에 적용되지 않으므로 DLL 오버헤드를 줄입니다. 마지막으로 , 에 명시적으로 추가하는 것이 더 쉽습니다. 이름 맨글링에 대해 걱정할 필요 없이 DEF 파일(서수로 내보내기용).
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
struct TracerData
{
BOOL bEnabled;
UINT flags;
};
BOOL PromptTraceFlags(TracerData FAR* lpData);
#ifdef __cplusplus
}
#endif
API에서 사용하는 구조체는 MFC 클래스에서 파생되지 않으며 API 헤더에 정의됩니다. 이렇게 하면 DLL과 애플리케이션 간의 인터페이스 복잡성이 줄어들고 C 프로그램에서 DLL을 사용할 수 있습니다.