借助WDK7.1让高版本VC生成的可执行文件不依赖msvcrtXX.dll
2017-11-16 17:42
1246 查看
高版本VS链接到msvcrt.lib
上一篇中介绍的方法适用面并不广,毕竟C++有着一大堆的优良特性和类库,比如STL、boost、MFC和QT等等,在普通的开发中只使用Win32API确实有点苦行僧的感觉。下面我们就尝试使用高版本的VC++生成可以链接到msvcrt.dll的程序。众所周知,要想让程序链接到msvcrt.dll,需要合适的msvcrt.lib,而高版本VS自带的msvcrt.lib默认会链接到自己的crt库的Dll导致依赖问题。好在经过探索,发现WDK7.1中提供了一套切实可行的解决方案。WDK7.1
WDK7.1是微软提供的驱动程序开发包,匹配的目标平台是Win7 SP1。但其中包含了完整的头文件、库文件和编译链接器,也可以用于编写用户程序。最为重要的是,其中有我们需要的msvcrt.lib。在这里,我们只需要其中的inc和lib两个文件夹下的文件,前者是WDK提供的头文件路径,后者是动态库路径,其中包含了Win32API、C库、MFC库和C++的库。 下载WDK7.1请移步https://www.microsoft.com/en-us/download/details.aspx?id=11800,下载安装过程在此按下不表。需要说明的是,我们只需要WDK安装路径下的inc和lib两个文件夹中的部分文件,所以安装后可以备份一下对应内容,以后就可以直接用对应文件夹中的文件而不必再次安装。
测试代码
下面,我们以这样一段程序为例,看看在VS2017下如何生成链接到msvcrt.dll的程序:#include <windows.h> #include <stdio.h> #include <vector> #include <iostream> #include <exception> using namespace std; int main() { try { vector<int> vInput(10, 3); cin >> vInput[4]; for (auto itr = vInput.begin(); itr != vInput.end(); itr++) { cout << (*itr) << endl; } throw new exception("Hehe"); } catch (exception* e) { cout << e->what() << endl; MessageBox(NULL, TEXT("Hello world"), TEXT("Hello world"), MB_OK); } return 0; }
配置头文件路径
在VS2017中,取消其默认的头文件路径,全部改为DDK下的对应文件路径:WinAPI头文件路径在”WDK安装目录\inc\api”目录下
C库头文件路径在”WDK安装目录\inc\crt”目录下,其中还包含旧式的iostream.h等C++头文件
C++相关的头文件在”WDK安装目录\inc\api\crt\stl70”目录下
ATL相关的头文件在”WDK安装目录\inc\atl71”目录下
MFC相关的头文件在”WDK安装目录\inc\mfc42”目录下
根据项目使用到的库类型对号入座即可,在本例中,我们需要使用到WinAPI、C库和C++中STL的文件,因此在VS的包含目录中加入上述路径并取消默认的路径。
配置库文件路径
在VS2017中,取消其默认库文件路径,全部改为DDK下的对应文件路径WinAPI库文件路径在”WDK安装目录\lib\win7\i386”下,这里指定的是x86的,其他架构的自己去WDK安装目录\lib\win7\下找
C/C++库文件路径在”WDK安装目录\lib\Crt\i386”下,依然是x86的,其他的自己找。
ATL库文件路径在”WDK安装目录\ATL\i386”下。
MFC库文件路径在”K:\WinDDK\lib\Mfc\i386”下。
依然是根据项目选择库路径,这里我们需要加入前两项。加入后的配置如下图所示:
修改链接选项
在链接选项中,忽略掉所有VS默认引入的库文件,然后加入WDK中对应的lib库:加入msvcrt.lib或者msvcrtd.lib(后者为debug版本需要的lib)
如果使用了C++的库,加入ntstc_msvcrt.lib
加入WinAPI对应的库,如kernel32.lib、user32.lib
加入Win2K或者WinXP对应的msvcrt.obj文件,如msvcrt_winxp.obj或者msvcrt_win2000.obj。否则生成的exe虽然只依赖msvcrt.dll,但是会导入一些Vista以后才有的函数,而在上述目标文件中对这些函数进行了转接处理。如大名鼎鼎的__except_handler4_common,在Win7中,这是一个由msvcrt.dll导出的函数,但XP的msvcrt.dll中没有,所以msvcrt_winxp.obj链接后进行了如下处理,没有直接链接到msvcrt.dll库中:
本项目由于使用了C++库且希望支持xp,链接加入的lib库如下:
解决VS2015以后遇到的一系列C++错误问题
首先先关掉安全检查,避免生成一堆安全检查的函数最后找不到符号链接:在完成上述操作后,编译阶段报错:
1>------ 已启动生成: 项目: Try01, 配置: Release Win32 ------ 1>main.cpp 1>K:\WinDDK\inc\api\crt\stl70\iosfwd(202): error C2144: 语法错误:“_Elem”的前面应有“;” 1>K:\WinDDK\inc\api\crt\stl70\iosfwd(290): note: 参见对正在编译的 类 模板 实例化 "std::char_traits<_Elem>" 的引用 1>K:\WinDDK\inc\api\crt\stl70\iosfwd(202): error C4430: 缺少类型说明符 - 假定为 int。注意: C++ 不支持默认 int //省略一堆错误,全部来自C++库头文件 1>K:\WinDDK\inc\api\crt\stl70\xlocale(446): error C2143: 语法错误: 缺少“;”(在“<end Parse>”的前面) 1>已完成生成项目“Try01.vcxproj”的操作 - 失败。 ========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========
这里需要在编译时加入两个宏:
_STL70_;_STATIC_CPPLIB;
在VS2017中配置如下:
上述问题解决后,继续尝试编译仍然报错:
1>------ 已启动生成: 项目: Try01, 配置: Release Win32 ------ 1>main.cpp 1>main.obj : error LNK2001: 无法解析的外部符号 "void __cdecl operator delete(void *,unsigned int)" (??3@YAXPAXI@Z) 1>K:\Try01\Release\Try01.exe : fatal error LNK1120: 1 个无法解析的外部命令 1>已完成生成项目“Try01.vcxproj”的操作 - 失败。 ========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========
可以看到,找不到的是C++中至关重要的delete函数,但这里的函数有两个参数,很明显是新版本引入的。经过一番搜索,发现在编译时加入如下命令即可去除该错误,同时加入另一个命令是因为在搜索时发现有人遇到了该问题。
/Zc:sizedDealloc- /Zc:threadSafeInit-
解决链接中的错误
完成上述操作后,链接过程依然报错:1>------ 已启动生成: 项目: Try01, 配置: Release Win32 ------ 1>main.cpp 1>main.obj : error LNK2001: 无法解析的外部符号 ___std_terminate 1>K:\Try01\Release\Try01.exe : fatal error LNK1120: 1 个无法解析的外部命令 1>已完成生成项目“Try01.vcxproj”的操作 - 失败。 ========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========
经过分析,该函数是新版的VS通用C库中vcruntime140.dll中导出的函数。使用VS调试结合IDA静态分析了一下:
这里又是个wrapper,最终调用的是从ucrtbase.dll中的_terminate函数,其入口部分反汇编代码如下:
再比较msvcrt.dll中导出的_terminate函数,可以看出二者完成的是同一个功能:
最终解决方案:在我们的CPP文件中增加如下代码:
void __std_terminate() { terminate(); }
至此,终于生成了可执行程序,下面检查一下该文件的导入表:
再检查一下能否在XP系统上运行:
哈哈,成功了。
参考链接
https://tieba.baidu.com/p/739007330?red_tag=0349926189http://www.360doc.com/content/17/0518/23/34844485_655141695.shtml
https://zhuanlan.zhihu.com/p/22355765
https://srad.jp/~Ab./journal/529028/
http://co63oc.blog.51cto.com/904636/1034980
https://github.com/Chuyu-Team/VC-LTL
http://blog.csdn.net/yangyihongyangjiying/article/details/44730099
https://stackoverflow.com/questions/44826818/link-errors-when-trying-to-compile-against-an-old-std-library-and-windows-sdk
http://blog.csdn.net/better0332/article/details/3552856
http://blog.csdn.net/zhiweiyouzhishenghuo/article/details/8177264
相关文章推荐
- VC++通过动态生成并加载DLL,实现可执行文件的自删除
- VC++通过动态生成并加载DLL,实现可执行文件的自删除
- VC++通过动态生成并加载DLL,实现可执行文件的自删除
- winserver2008,运行可执行文件,提示 激活上下文生成失败。 找不到从属程序集 Microsoft.VC90.DebugCRT,processorArchitecture="x86"
- [置顶] 【PE】Windows平台下为可执行文件或动态库dll添加版本信息
- VC2010 利用 def 文件生成 dll 文件的方法
- Code::Blocks生成的EXE文件执行错误解决:The program can't start because libgcc_s_dw2-1.dll is missing
- c#重命名文件 - 抛弃MoveTo,而引用vc 中rename生成rename.dll
- VC2010 利用 def 文件生成 dll 文件的方法
- VC++和Matlab混合编程(在VC中调用将.m文件生成的DLL)
- VC2010 利用 def 文件生成 dll 文件的方法
- 64位win7下vc2010如何调用matlab2012a中生成的dll文件
- idea生成maven项目的包含依赖jar包的可执行jar文件
- NetBeans IDE 7.4 Beta版本build JavaFX时生成的可执行jar包执行时找不到依赖的jar包
- VC2010 利用 def 文件生成 dll 文件的方法 转载
- vc++ 将可执行文件链接到 DLL
- 关于VC中生成的PE(exe, dll, sys...)文件中对函数名称的修饰
- BCB调用VC生成的DLL文件
- 如何给VC之Win32生成的.exe文件添加版本信息
- C++文件操作——获取可执行文件或DLL版本信息