VC6-DLL与BCB5-EXE连结运行时静态库函数似乎存在问题?

[入库:2005年8月19日] [更新:2007年3月24日]

本文简介:选择自 junjiao 的 blog


关于vc6-dll与bcb5-exe连结运行时静态库函数存在问题的研究


测试环境:

xp-pro + vc6/sp5/sp6 + bcb5/upd


问题现象:

以前在bcb5上开发了script.dll,并与bcb5同平台上开发的hce.exe运行很稳定,没有发现什么明显问题。

本月测试了stl/boost相关内容,随便改造了script.dll,主要是更换本人自行开发容器模板为stl类型,由于仅更换了容器模板,算法本身未作什么大的更改,因此计算结果应与现有的数据完全一致。

经过简单的表达式和脚本测试,结果无误,并发布了一个免费的计算器程序,以进行更广泛的公开测试。

为了进行大数据量测试,本人将其与原bcb5开发的 hce.exe 动态连接运行(即:替换原bcb5开发的script.dll库),突然发现计算结果不甚一致,有一定的误差,虽然不大,但理论上不应有任何不同。

于是深入分析了一下script.dll与hce.exe的源代码,结果发现hce.exe中格式化数据的函数是sprintf,但sprintf函数在与两种开发工具产生的 script.dll 动态连接运行时,得到的结果不一样!!!!

比如:浮点数0.031025,在程序中需要保留5位小数,hce.exe+bcb版dll得到的结果为0.03103,而hce.exe+vc6版得到的结果却为0.03102。很明显vc6版存在一定的问题。

为了搞清楚原因,先是google了“四舍五入”,发现有一种“四舍六入五成双”的算法,确实后者更为科学一些。但后来又发现vc6版dll中对 0.001105 取5位小数得到的结果为 0.00111,很显然也不符合“四舍六入五成双”。

但一想vc6也不简单,怎么会犯这么低级的错误。于是动手写了个测试程序,用vc6-cl.exe编译运行

#include <cstdio>
void main(void)
{
printf("%.*f\n", 5, 0.031025);
}

得到结果为:0.03103,很正确! 可见vc6本身没有这个问题。再用bcb5编译,结果同样正确。说明二者独立运行时没有这样的问题。

于是开始怀疑,vc6版的dll与bcb5版的exe动态连结运行时,存在某种潜在的“库”连结问题!

这了验证这个猜测,于是考虑设计两个简单的示例程序,一个将在vc6下编译为script.dll文件,其中随便export一个函数即可。另一个将在bcb5下编译为.exe文件,并动态载入前面这个script.dll,并在载入点前后用相同的源代码向std::cout打印结果。

下面是dll测试程序(script.cpp):

#include "windows.h"
#include
int winapi dllentrypoint(hinstance hinstdll, unsigned long fdwreason, void * lpvreserved)
{
hinstdll;fdwreason;lpvreserved;return true;
}
#define dllexport __declspec( dllexport ) dllexport int __stdcall __dll_func()
{
char buf[64]; memset(buf,0,sizeof(buf));
sprintf(buf, "%.*f\n", 5, 0.031025);
return 0;
}

用vc6的命令行编译

cl.exe /ld script.cpp

得到 script.dll 文件

下面是exe测试程序script_test.cpp:

#include <cstdio>
#include <iostream>
#include <cmath>
int main(void)
{
char buf[64]; memset(buf,0,sizeof(buf));

sprintf(buf, "%.*f\n", 5, 0.031025); //得到0.03103
std::cout << buf;

hinstance _hinst=::loadlibrary("script.dll");//只需要load一下dll,不需调用任何函数

sprintf(buf, "%.*f\n", 5, 0.031025); //得到0.03102 !!!!!
std::cout << buf;

::freelibrary(_hinst);

return 0;
}

用bcb5命令行编译

bcc32 script_test.cpp

得到 script_test.exe 文件

然后运行 script_test.exe ,得到结果却为:

0.03103
0.03102

测试结果与预料的结果完全一致! 可见现在已经找到了问题重现的方法。

为了进一步测试,将两个.cpp文件交换编译器再进行测试,即互换编译器,bcb5产生dll,vc6产生exe,然后运行,结果为:

0.03103
0.03103

是正确的!

再次交换顺序,即用同一种编译器分别生成两套.dll与.exe,再运行,均得到正确的结果。

由此可见,问题应在vc6产生的dll身上,因为只有vc6产生的dll与bcb5产生的exe无法正确运行(而反之则行)。

现在开始从vc6制造dll的选项入手,看看是否能通过编译选项解决这个问题。

首先在vc6的命令行上加上编译选项 /mt,静态连接,

cl.exe /ld /mt script.cpp

生成script.dll,用bcb5生成的.exe测试,结果还是不正确。

再换 /md 选项,

cl.exe /ld /md script.cpp

生成script.dll,用bcb5生成的.exe一测试,结果正确了!!!!!这正是期望看到的结果。

看来问题就出在这里了。根据以上复杂的组合过程可以得到以下结论:

1、vc6生成的.dll与bcb5生成的.exe动态连结运行时,可能会引起sprintf()等函数运行不正常。

2、鉴于以上问题,有可能 stdio.h 中描述的函数如sprintf()在该情况下均可能出问题。

3、为了避免这个问题,在vc6的dll编译选项中必须添加上 /md 选项。

其实本人在这里还是存在一些疑问,比如:在loadlibrary()载入.dll后,怎么会直接影响到.exe中的静态库函数呢?

由于目前时间和精力水平有限,没有继续深入。目前只能记住存在这样的问题及相应的解决方法。在此作个记录,以免日后忘了。

本文关键:VC6-DLL与BCB5-EXE连结运行时静态库函数似乎存在问题?
  相关方案
Google
 

本站最佳浏览方式为 分辨率 1024x768 IE 6.0(或更高版本的 IE浏览器)

go top