我正在尝试使用标准 Windows 命令提示符命令编译一个简单的共享库 DLL 文件。
DLL 文件将从以下名为
test.cpp
的 C++ 文件编译而来:
#include <iostream>
#include <windows.h>
extern "C" __declspec(dllexport) int test_fx(void)
{
std::cout << "test\n";
return 0;
}
虽然我可以使用 Visual Studio 2022 本机命令提示符(它正确设置了所有环境变量)将此 C++ 文件编译为 DLL,但没有出现任何问题,但尝试在常规 Windows 命令提示符结果中执行相同操作如下:
cl.exe
我知道常规的 Windows 命令提示符没有所有必需的环境变量设置,并且我可能必须使用链接标志等单独指定我正在使用的每个库。
我试图遵循
以下类似帖子的答案,但命令test.cpp
test.cpp(1): fatal error C1034: iostream: no include path set
和
vcvars32.bat
在我的命令提示符中是无法识别的命令。为普通 Windows 命令提示符包含所有 C++ 标准库路径和环境变量的正确方法是什么? vcvars32
等库的链接器标志是什么?
iostream
来构建 DLL。
为此做好准备,我建议您启动一次开发人员命令提示符,在其中键入cl.exe
,并记下以下环境变量中指定的路径:
SET
、INCLUDE
、LIBPATH
,以及变量 LIB
和 VCToolsVersion
中指定的版本。然后您需要为 WindowsSDKVersion
调用构建完整的命令行。以下页面介绍了 MSVC 命令行选项:
cl.exe
以编程方式执行
CreateProcess
,所以这里是一个执行此操作的示例程序(至少需要 C++17,因为我使用 cl.exe
进行路径操作):std::filesystem
如您所见,有许多常量,例如路径和版本,您必须根据您的系统找到并“配置”它们。在上面的示例代码中,命令行最终如下所示:
#include <filesystem>
#include <string>
#include <vector>
#include "windows.h"
using std::wstring;
using std::vector;
template <typename T>
wstring concat_with_prefixes(const wstring& prefix, const vector<T>& vec)
{
wstring result;
for (const auto& item : vec)
{
result += L" " + prefix + L"\"" + item.c_str() + L"\"";
}
return result;
}
int main()
{
using std::filesystem::path;
const path my_source_code_folder{ LR"(C:\MyTestApplications\MyDLL)" };
const wstring my_cpp_file_name{ L"test.cpp" };
const wstring my_obj_file_name{ L"test.obj" };
const wstring my_dll_file_name{ L"MyDLL.dll" };
const wstring msvc_toolset_version{ L"14.29.30133" };
const wstring win_sdk_version{ L"10.0.19041.0" };
const path msvc_folder
{
path{LR"(C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC)"}
/ msvc_toolset_version
};
const path win_sdk_base_folder{ LR"(C:\Program Files (x86)\Windows Kits\10)" };
const path win_sdk_include_folder{ win_sdk_base_folder / L"include" / win_sdk_version};
const path win_sdk_references_folder{ win_sdk_base_folder / L"References" / win_sdk_version};
const path win_sdk_lib_folder{ win_sdk_base_folder / L"lib" / win_sdk_version};
const wstring prefix_include{ L"/I " };
const vector<path> include_folders // Normally taken from the "INCLUDE" environment variable
{
msvc_folder / LR"(ATLMFC\include)",
msvc_folder / LR"(include)",
win_sdk_include_folder / LR"(ucrt)",
win_sdk_include_folder / LR"(shared)",
win_sdk_include_folder / LR"(um)"
};
const wstring prefix_define{ L"/D " };
const vector<wstring> preprocessor_definitions
{
L"WIN32",
L"_USRDLL",
L"_WINDLL",
L"_UNICODE",
L"UNICODE",
L"NDEBUG",
L"MYDLL_EXPORTS"
};
const wstring compiler_options
{
L"/permissive- /W4 /WX /Zc:wchar_t /Zc:inline /Zc:forScope /fp:precise "
L"/GS /Gy /O2 /Gd /Oy- /Oi /MD /FC /EHsc /diagnostics:column /analyze- /LD"
};
const wstring prefix_libpath{ L"/LIBPATH:" };
const vector<path> lib_folders // Normally from the "LIBPATH" and "LIB" environment variables
{
msvc_folder / LR"(ATLMFC\lib\x86)",
msvc_folder / LR"(lib\x86)",
msvc_folder / LR"(lib\x86\store\references)",
win_sdk_references_folder,
win_sdk_lib_folder / LR"(ucrt\x86)",
win_sdk_lib_folder / LR"(um\x86)",
};
const wstring libraries{ LR"("kernel32.lib" "user32.lib" "gdi32.lib" "advapi32.lib")" };
const path cl_exe{ msvc_folder / LR"(bin\Hostx86\x86\cl.exe)" };
wstring command_line{ L'"' + cl_exe.wstring() + L'"'};
command_line += concat_with_prefixes(prefix_include, include_folders)
+ concat_with_prefixes(prefix_define, preprocessor_definitions)
+ L' ' + compiler_options
+ L" /Fo\"" + my_obj_file_name + L'"'
+ L" /Fe\"" + my_dll_file_name + L'"'
+ L" \"" + my_cpp_file_name + L'"'
+ L" /link"
+ concat_with_prefixes(prefix_libpath, lib_folders)
+ L' ' + libraries;
command_line.resize(UNICODE_STRING_MAX_CHARS + 1);
STARTUPINFOW si{};
si.cb = sizeof si;
PROCESS_INFORMATION pi{};
if (!::CreateProcessW(cl_exe.c_str(), data(command_line),
nullptr, nullptr, false, 0, nullptr, my_source_code_folder.c_str(), &si, &pi))
{
::MessageBoxW(nullptr,
(L"Cannot start the compiler/linker driver process due to system error "
+ std::to_wstring(::GetLastError())).c_str(), nullptr, MB_OK);
}
::WaitForSingleObject(pi.hProcess, INFINITE);
::CloseHandle(pi.hThread);
::CloseHandle(pi.hProcess);
}
但是在您的系统上,它可能会有所不同,因为工具集/SDK 版本不同。