说简单点,就是正常情况下比如写了一个这样的def文件
LIBRARY MyDLL
EXPORTS
function
dosomething
此时如果用lib.exe工具从这个def文件生成lib文件,那么这个lib文件将从DLL导入function和dosomething,同时向链接器提供_function和_dosomething。这样的话对于__cdecl方式导出的函数是没有问题的。但是如果DLL的导出符号仍然是function和dosomething,但是调用方式却是__stdcall,就会遇上了问题:如果function有一个int型参数,那么链接器将去寻找_function@4而不是_function,因为找不到符号,所以链接失败了。
如果DLL中导出的符号名是function@4,此时生成的lib就会从DLL导入function@4并向链接器提供_function@4,但是实际上以__stdcall方式调用,但是最后导出的时候不带@nn的情况是非常多的。最典型的情况就是Windows API。而且实话说吧因为@nn这样的实在不怎么好看,所以我自己写DLL的时候也会通过使用DEF文件的方式或者#pragma comment(linker, “/EXPORT:xxxxxx”)这样的预编译语句让导出的函数不带如此的后缀。
于是这样需求就变得简单:让生成的导入库从DLL引入的时候是不带后缀的,但是向链接器提供的符号是带后缀的。
这并非不可能,调用Windows API的时候就是这样:Windows SDK里面那些LIB文件,就是实现这样的功能。对其格式进行研究,根本就是和普通的导入库一样。除了……呃,除了某些细节上的参数。
于是我的目的就是修改这些细节上的参数。Microsoft PE and COFF Specification里也提供了这方面的详细资料。
方法的话,遍历LIB文件中的每个member,LIB文件的格式可以从上面提到的资料中找到,不会太复杂。注意每个member的第一个字节似乎是对其到偶数边界的,没有对齐的用\n来补。在遍历的过程中分析前四个字节就知道这个member是不是声明了一个导入函数,如果是,根据导入的格式分析其头部信息,然后获得DLL名、函数名等。
在这个导入member的“头部信息”中,有关于导入名应该如何计算的属性。但是LIB.exe工具却没有这方面的参数可以调整,默认就是先前面给你加一个_下划线,然后方式是NoPrefix就是再把这个下划线去掉。也就是说,def文件中如果写的是function,那么它就先加一个下划线,变成_function,作为向链接器提供的符号。然后导入名生成方式是NoPrefix,也就是链接器在链接的时候会再从_function去掉前缀的下划线变回function作为从DLL导入的名。
这个导入名的计算方式有
IMPORT_ORDINAL:通过标号导入,此时不会计算导入名
IMPORT_NAME:原原本本的,和向链接器提供的符号保持一致
IMPORT_NAME_NOPREFIX:不带前缀的,LIB.EXE生成的就是这种。前缀是指?、@或者可选的下划线_
IMPORT_NAME_UNDECORATE:不带前缀并且只取@之前的部分,此时如果向链接器提供的符号名是_function@4,那么导入名就是function,也就是我们需要的。
有了这些资料,思路就非常清楚,比如有以下两个函数
__declspec(dllimport) int __stdcall add2(int, int);
__declspec(dllimport) int __stdcall add3(int, int, int);
然后DLL里的导出名分别是add2和add3而不是add2@8和add3@12,此时可以写这样一个DEF文件
LIBRARY doadd
EXPORTS
add2@8
add3@12
此时生成的lib文件中,向链接器提供的符号这一边已经是所需要的_add2@8和_add3@12了。然后这个时候要修正的是导入名计算方式,因为现在是IMPORT_NAME_NOPREFIX,要改成IMPORT_NAME_UNDECORATE才行。这个时候就要用十六进制编辑器来修改,但是手工做这种事情实在太麻烦,这种事情应该给程序去做。所以就写了个工具,来解决这种问题。

图片为修改一个函数的导入名计算方式后的截图。改完以后保存,就可以实现目的了。
这里给出这个自制工具的下载:(上传时间:2014年2月13日。工具有修改的话,这里可能不会同步更新。)
这里下载工具
http://download.csdn.net/detail/fwleiming/6919785
运行需要.net framework 2.0环境。修改完成后记得保存。
里面的DLL其实是COM组件形式的,我在资源里面嵌入了tlb文件,接口应该算是比较简单,想自己在程序里调用的话,要注意最初SetSource的时候送入的指针要在整个操作过程中有效(因为在.net环境中,对象的内存地址是有可能被GC给移动的)
p.s. 这坑爹的论坛我又不是发求助帖怎么还一定要积分……