114 Using cmake to build MFC projects
Preprocessor flags that might need to be defined
| Preprocessor flag | Description |
|---|---|
| UNICODE | Used by Windows headers. Affects the character set the Windows header files treat as default. So if you define UNICODE, then GetWindowText will map to GetWindowTextW instead of GetWindowTextA, for example. Similarly, the TEXT macro will map to L"..." instead of "...". See TEXT vs. _TEXT vs. _T, and UNICODE vs. _UNICODE by Raymond Chen. |
| _UNICODE | Used by C-runtime/MFC headers. Affects the character set the C runtime header files treat as default. So if you define _UNICODE, then _tcslen will map to wcslen instead of strlen, for example. Similarly, the _TEXT macro will map to L"..." instead of "...". |
| _WINDOWS | |
| WIN32 | |
| _AFXDLL | Should be defined when using MFC in a Shared DLL. When using vcxproj projects if you pick "Use of MFC in a Shared DLL" then _AFXDLL is automatically defined. |
| _AFXEXT | MFC extension DLLs should be compiled with _AFXEXT defined. |
Unresolved winmain error
It's essentially a limitation when using the Ninja (and Makefile) generators in CMake, you have to explicitly specify the application entry point:
# CMakeLists.txt
...
target_link_options(${PROJECT_NAME} PRIVATE "/entry:wWinMainCRTStartup")
See Unresolved WinMain error in Unicode MFC application built with CMake using Ninja on stack overflow.
Also see the Unresolved WinMain error in Unicode MFC application built with CMake using Ninja generator issue on gitlab.kitware.com.
cedaAddDependentProject
cedaAddDependentProject(myproject) should be called on a non-XCPP library or executable
myproject which depends on one or more XCPP projects in the same workspace. This is to ensure xcpp is run
in order to generate the headers before trying to compile files in myproject.
cedaSetDefaultsForTarget
cedaSetDefaultsForTarget(myproject) should be called on a non-XCPP library or executable myproject to set
the locations for generated files, following the conventions used by XCPP projects.
Example CMakeLists.txt for an XCPP MFC extension DLL
See Microsoft documentation of MFC extension DLLs.
Requirements:
- Extension DLLs are built using the dynamic-link library version of MFC (also known as the shared version of MFC).
Therefore
set(CMAKE_MFC_FLAG 2)must be used. - Preprocessor macros
_AFXDLLand_AFXEXTmust be defined. - Don't instantiate a class derived from CWinApp
- Provide a DllMain function (see below)
- The executable that uses the extension DLL must be compiled with
_AFXDLLand use the shared MFC
# MFC extension DLL cxMFC written in Xc++
add_definitions(-DUNICODE -D_UNICODE -D_WINDOWS -D_AFXEXT -D_AFXDLL -DWIN32)
set(CMAKE_MFC_FLAG 2)
cedaTarget(XCPP NAMESPACE CedaGui PROJPATH CedaGui/cxMFC)
target_link_libraries(cxMFC
PUBLIC
Ceda::cxUtils
Ceda::cxLss
Ceda::cxObject
Ceda::cxPersistStore
Ceda::cxOperation
Ceda::cxWorkingSetIpc
Ceda::cxMessage
CedaGui::cxGeom
CedaGui::cxModel)
DllMain should be written like this:
extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
if (!AfxInitExtensionModule(cxMFC_DLL, hInstance)) return 0;
new CDynLinkLibrary(cxMFC_DLL);
}
else if (dwReason == DLL_PROCESS_DETACH)
{
AfxTermExtensionModule(cxMFC_DLL);
}
return 1;
}
CDynLinkLibrary allows the extension dll to provide resources, this works as long as they don't have overlapping resource ids.
Example CMakeLists.txt for a non-XCPP project
In the following example exMFC is a non-XCPP project.
cedaAddDependentProject(exMFC) is required because exMFC depends on XCPP projects
cxGeom, cxModel, cxMFC and therefore must be able to see the generated headers in those projects.
add_definitions(-DUNICODE -D_UNICODE -D_WINDOWS -D_AFXDLL -DWIN32)
# set CMAKE_MFC_FLAG to 1 for the static MFC library, or 2 for the shared one
set(CMAKE_MFC_FLAG 2)
add_executable(exMFC WIN32
ClassView.cpp
ClassView.h
exMFC.cpp
exMFC.h
exMFC.rc
exMFCDoc.cpp
exMFCDoc.h
exMFCView.cpp
exMFCView.h
FileView.cpp
FileView.h
framework.h
MainFrm.cpp
MainFrm.h
OutputWnd.cpp
OutputWnd.h
pch.cpp
pch.h
PropertiesWnd.cpp
PropertiesWnd.h
Resource.h
targetver.h
ViewTree.cpp
ViewTree.h)
target_link_libraries(exMFC
PUBLIC
Ceda::cxUtils
Ceda::cxLss
Ceda::cxObject
Ceda::cxPersistStore
Ceda::cxOperation
Ceda::cxWorkingSetIpc
Ceda::cxMessage
CedaGui::cxGeom
CedaGui::cxModel
CedaGui::cxMFC)
cedaAddDependentProject(exMFC)
cedaSetDefaultsForTarget(exMFC)
# See https://stackoverflow.com/questions/63924292/unresolved-winmain-error-in-unicode-mfc-application-built-with-cmake-using-ninja and
# https://gitlab.kitware.com/cmake/cmake/-/issues/21202
target_link_options(exMFC PRIVATE "/entry:wWinMainCRTStartup")