目前 Windows 有两种 C 运行时:所有 NT 版本都有的 MSVCRT(msvcrt.dll)和最低支持 Vista 的 UCRT(ucrtbase.dll)。在使用 MSVC 时,还会引入主版本关联的 VC++ Runtime(msvcpXXX.dll,vcruntimeXXX.dll)现在有几个问题:
1
hackpro 2022-03-08 05:36:12 +08:00 via iPhone
如果希望兼容性的话 考虑静态链接吗
|
2
thedrwu 2022-03-08 05:59:26 +08:00 via Android 1
1.没用过不好回答
2.可以不链接任何 crt ,用纯 Windows api 。尤其是为了把体积缩小到极致的时候。golang 怎么做不清楚。 3.申请释放内存用同一个 crt 4.同上,即使静态链接 5.同上 欢迎补充 |
3
thedrwu 2022-03-08 06:16:22 +08:00 via Android
补充上一楼。
以前为了兼容性, 小程序我链的都是 msvcrt.dll 。其中的 bug 都成 feature 将错就错了。后来随着宽带普及,软盘淘汰,不需要榨干最后一点体积。再后来 visual studio 也免费了,根据第三方库下载安装对应版本的 vc 编译不是个事了。 既然都跟 golang 比了,不在乎这点体积。 |
4
0o0O0o0O0o 2022-03-08 08:39:56 +08:00 via iPhone
|
5
ysc3839 2022-03-08 09:15:40 +08:00 via Android 1
1.理论上是的,因为 msvcrt 为了兼容性可能有些妥协。ucrt 可能更加符合新的 C 标准
2.否的,只是 mingw 项目不开发 libc ,直接用了微软的 libc ,msys2/cygwin(有 POSIX 兼容层那个模式)就是使用自己开发的 libc ,不使用系统的 3.一直可以,许多系统 DLL 就是依赖 msvcrt 的,但一直都不可以互操作 4.可以,msvc 是用 /MT /MTd 这两个选项 https://docs.microsoft.com/en-us/cpp/build/reference/md-mt-ld-use-run-time-library 5.一直可以,同样也是不可用互操作 |
6
littlefishcc 2022-03-08 09:19:28 +08:00
我会回答第五条:
依赖不同版本,可以用 GetProcAddress 指向不同版本的运行库。 |
7
3dwelcome 2022-03-08 09:48:25 +08:00
“如果我希望给别人用的 dll 有较好的兼容性,在保证导出 API 函数都是 C 风格时,可以将 VC++ Runtime 静态链接进 dll 吗?如果可以,怎么配置?”
楼主没搞懂,使用动态 VC Runtime DLL 这点,对于程序开发很重要的。 你用静态 Runtime 库的话,由 malloc 分配的内存,是没办法在各个模块之间共享的。 要共享,就只能让所有模块,都使用同一个 VC Runtime DLL 。换句话说,你写代码肯定不是为了兼容性去用静态链接库. |
8
ysc3839 2022-03-08 10:02:14 +08:00 via Android 1
@3dwelcome 但是没谁规定一定要用 libc 提供的 malloc 来分配内存,Windows 有很多可跨模块的分配器,比如 process heap allocator 和 COM task allocator ,甚至可以不传递所有权。
|
9
lonewolfakela 2022-03-08 16:17:16 +08:00
4. 会导致使用你的 dll 的程序也必须静态链接 VC++ Runtime ;然而如果那个程序本身用的 Runtime 和你版本不一样的话,全部静态链接到一个 exe 里之后,鬼知道会发生什么后果……
|
10
ysc3839 2022-03-08 16:46:45 +08:00 via Android
@lonewolfakela 不一定的,不涉及 malloc 以及 C++的互操作的话就不需要。比如说调用方传数据进来的情况,可以把数据拷一份,或者让调用方提供个回调函数,在调用回调函数前都需要保持数据有效。传数据出去的情况,可以让调用方提供缓冲区。以上两种情况还可以都用系统提供的进程级的内存分配器。
|
11
lonewolfakela 2022-03-08 19:24:16 +08:00
@ysc3839 可是,微软的 C++链接器会直接拒绝混用两种模式的情况啊……
https://stackoverflow.com/questions/3469841/mixing-code-compiled-with-mt-and-md |
12
ysc3839 2022-03-08 20:12:26 +08:00 via Android
@lonewolfakela 你发的这是静态链接时的情况呀。楼主说的是自己编译一个 DLL 提供给别人用,就没有涉及静态链接。
|
13
lonewolfakela 2022-03-08 20:51:31 +08:00
@ysc3839 哦抱歉,是我搞混了。
|
14
jim9606 OP @3dwelcome
@lonewolfakela 关于是否混用 VC++ Runtime 的问题,我的理解(从 Linux 迁移过来的理解,可能有错)是如果使用共享 VC++ Runtime dll 会导致 VC++导出的符号对所有 dll/exe 可见,不同主版本的 VC++符号会被互相覆盖,会导致某个链接 VC12 的调用实际上调用了 VC14 的符号,从而产生问题。如果将 VC++静态链接进 dll ,这些 VC++符号就是对其他 dll/exe 不可见的私有符号。我用 VS2022 测试了下,使用 /MT /MTd 时 dumpbin 显示只依赖 kernel32.dll ,所以应该是把 VC14 和 UCRT 都静态链接进去了。那么在满足 ABI 稳定和谁分配谁释放的前提下,这个 dll 是不是可以安全地给 MSVC13 编译? 另外,这种混用 Runtime 的方法是不是在 Linux 下不可能实现?例如 musl 的程序加载静态链接 glibc 的 so ? |
15
3dwelcome 2022-03-09 00:21:22 +08:00
@jim9606 Linux 的 so ,和 windows 的 dll 设计不太一样。
dll 就是一个完整的闭环体系,缺函数没办法编译通过。 而 so 编译时缺胳膊少腿都没问题,只要 elf 符号完整,最后代码就和变形金刚一样,能组合在一起完整运行。 dll 也很少会覆盖符号,这个情况只在 linux 见过,windows 上我是没见过。 |
16
lonewolfakela 2022-03-09 00:50:18 +08:00 1
不过话又说回来,真的要想稳定性兼容性都比较好、少出各种诡异问题的话,我觉得还是给比如 MSVC10 、11 、12 、14 各自编译一份比较好——其实也不太多,只需要 4 份就能从 VS2010 一直支持到 VS2022 了。或者如果可以选择不支持那么老的版本的 VS 的话,直接只针对 MSVC14.0 编译一份动态链接 Runtime 的 DLL ,就能支持从 VS2015 到 VS2022 ,在很多时候已经挺够用的了。
|
17
ysc3839 2022-03-09 01:38:50 +08:00 via Android 1
|
18
jim9606 OP @lonewolfakela
那当然是各自编译一份最好,这个毋庸置疑。现在 UCRT 和 VC++14 提供向后兼容是不错,但谁知道以后 UCRT 会不会变成 MSVCRT 那样的包袱呢。 其实还有一点,在 Windows 上升级 VC++ Redist 是相对简单安全的事情,实在不行就费点内存磁盘用静态链接 /本地部署,也很安全。尽管 glibc 、libstdc++、libgcc 在近几年也提供类似的兼容性,但在 Linux 发行版上升级这些库是件挺有风险的事,反正我是没胆做这种尝试。特别是 glibc 旧版兼容的问题还不好通过静态链接来解决。 |