
DLL 出问题有两种常见的报错,它们看起来都在说"缺东西",但成因完全不同:

第二种更让人困惑:文件明明在,用工具扫描也报告一切正常,可程序就是说"里面没有那个函数"。
DLL修复工具 在这类问题上帮不上忙,原因和前面几篇讲过的一样——它检查的是文件层,而这里文件一个不少。
真正的原因在更细的一层:导出名对不上。
一个 DLL 提供的东西,是一张导出表。程序调用它,实际做的是按名字在这张表里查条目,查到就拿到地址,查不到就报"找不到入口点"。
所以"报找不到入口点"这句话的准确含义是:
文件找到了、也加载进来了,但导出表里没有我要的那个名字。
问题不在文件,而在"名字"上。 而这个"名字"为什么会对不上,有三种原因,每一种都很常见。
这是最反直觉的一条:导出名不等于你在代码里写的那个名字。
编译器和链接器会在名字上加东西,依据是调用约定和语言。
第一层是调用约定。 32 位程序里,不同的调用约定会带不同的装饰:
那个 8 不是"版本号",是参数占用的字节数。 所以同一个函数,只要参数变了,导出名跟着变。
第二层是语言。 C%2B%2B 支持重载,函数名里必须带上参数类型才能区分,于是名字会被编码成一长串:
所以"名字"实际上是"签名"——它描述的是"这一组参数的这个函数",而不是"叫 Foo 的那个函数"。
这就解释了一类很典型的故障:
开发那边说"这个函数导出了",你那边却说"找不到"——两边说的名字压根不是同一个字符串。
这一条是更硬的墙。
导出表允许只按序号导出:每个导出项有一个编号,名字那一项可以是空的。
这种 DLL 里,函数是"有编号、没名字"的。
于是"按名字查找"必然失败——不是你名字写错了,而是它从来没打算让人按名字找。
这种情况多见于:
这类库只能按序号调用。 而序号是脆弱的:它是按导出顺序排的,库更新一次、加一个函数,序号就可能整体错位。
所以按序号对接的准确性,完全依赖"双方用同一个版本"——一旦版本不同,序号指向的就可能是另一个函数,而且不报错。
这一条简单但容易忽略:按名字查找用的是区分大小写的字符串比较。
所以大小写差一个字母,就是找不到。
相关的另一种情况是拼写来源不可靠:名字是从某篇文章、某个论坛帖子里抄来的,而帖子里的写法本身就是错的或是大小写美化过的。
遇到"找不到入口点",最有效的一步是直接看导出表,不要猜。
在开发人员命令提示符里:
dumpbin /exports "C:\Windows\System32\vcruntime140.dll"输出里每行大致有三列:序号、名字、地址偏移。
看两个地方就够了:
把真实名字和你调用时用的字符串对一遍,绝大多数“找不到入口点”当场就能定位——这也是 DLL修复工具 报“一切正常”时,你仍然需要自己做的第一步。
如果 Dumpbin 不可用(它随开发工具提供),用十六进制查看工具看导出表也能得到同样信息——关键是看"有没有名字"和"名字的准确写法"。
这一条是上面几条的推论,但值得单独说,因为现象很像"版本不兼容"。
64 位环境下只有一种调用约定。
于是那种"名字里带 @参数字节数"的装饰,在 64 位下不再出现。 同一个函数:
如果调用方拿的是"带装饰的名字",去 64 位库里自然找不到。
反过来也一样:拿着 Foo 去按 32 位的约定找,同样对不上。
所以"同一个程序在一台机器上能跑、另一台上报找不到入口点",有时候不是环境坏了,而是它加载到了位数不同的那一份库,而两者的导出名规则不一样。
注意第三行——它是这一类问题里最危险的一种:名字找到了,但签名不一样,程序不会报错,而是直接崩或者算错。
关于 DLL修复工具 处理不了的这一类"文件都在却找不到函数",记住四条:
这里可以带走的经验是关于"接口契约"的:名字是给人看的,机器认的是导出表里的条目。
所以跨语言、跨编译器、跨版本对接时,真正需要固定的不是"功能",而是"名字"——要主动把它固定下来(用稳定的导出方式),而不是听任工具按自己的规则生成。
否则每换一次编译环境,接口就断一次,而报错信息只会告诉你"找不到入口点",不会告诉你“名字被改成了什么”。
https://www.ijinshan.com/functions/repairdll.html?channel=4104
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。