PB调用DLL的常见问题及处理方法
首先,为方便描述,先假设有一个DLL文件,名称为 test.dll,里面有个函数叫test。
第一类:通用型标准DLL
1、调用约定问题。Windows系统的标准DLL,通常有2种调用约定,即__cdecl和__stdcall,__stdcall约定在.h文件中通常又定义为 WINAPI和CALLBACK。咱们的PB只能调用__stdcall约定的DLL,不能调用__cdecl约定的DLL。
如果调用了__cdecl约定的函数,会报以上错误。关键是“specified argument type deffers from ….“,内容是给定的参数顺序与要求的不一样。
对于这两种约定的区别,有兴趣了解更多的,可以自行百度。
2、DLL依赖问题。有时因为test.dll还需要依赖其他DLL,导致调用失败。
依赖的DLL找不到时,通常会报类似这样的错误,然而这个提示并不明确,所以比较难查。在群共享里可以下载 loaddll.exe这个小工具,使用也比较简单,和DLL放在同一个目录,然后进入控制台命令行,输入 loaddll test.dll,观察控制台输出
如果是依赖问题,这里的错误号是126。可以通过百度搜索 “getlasterror 126”查找具体情况,如果是其错误号,搜索时使用其他错误号。
3、DLL路径搜索错误。这个错误通常常是DLL不在应用程序当前目录下造成的,错误截图同上。也许你会说“我肯定我的DLL就在程序目录下“,但请注意是”当前目录“,当前目录并不是只指应用程序目录,虽然初始时默认是应用程序目录。假设你的程序在d:\test目录,你使用 GetOpenFileName 函数选择了d:\document\work.txt,这时候,你的当前目录就是d:\document\,而这时的当前目录下当然找不到 test.dll了,你的test.dll在d:\test呢。这是许多PB程序员容易忽略的原因。解决方法通常有以下几种:
(1)在调用GetOpenFileName GetSaveFileName等函数前,先GetCurrentDirector函数保存当前目录,完成后,再 ChangeDiredtory切换回原当前目录。
(2)假设你的程序在d:\test目录,你可以直接设置系统的PATH环境变量指向这个目录。当然这个给实施人员带来一些小不便。
(3)程序初始化时,使用系统函数BOOL SetEnvironmentVariable(LPCTSTR lpName,LPCTSTR lpValue)把程序所在目录添加到进程环境变量里去,让DLL可以被搜索到。一个更加智能的方案是使用 pbidea 提供的设置函数
可以确保当前目录、当前目录下的子目录,以及指定的其他目录里存放的DLL都可以被 搜索到。
特别值得一提的是,使用oracle数据库时,它的客户端在连接时会改变当前路径。
4、各种函数声明错误。这种错误就比较复杂了,涉及面也比较广。
可以参考另一篇博客PowerBuilder中调用DLL参数类型_lxbin2003的博客-CSDN博客_powerbuilder调用dll,提升对DLL及函数的理解 ,从而正确声明和使用函数 。
特别注意,一般情况下声明函数时 library “test.dll”,而不是 system library “test.dll”,这里的”system”不是随便加的。只有按照PB特定写法的DLL才可以添加这个system关键字。如果对C/C++写PB可调用的DLL有兴趣的话,可以到群共享下载 ,看看 system library方式是如何实现的。
5、函数使用错误。DLL函数都是按照C约定开放接口。我们知道C的指针,总是让人不得不打起十二分精神来对待。你认为PB里不涉及到指针,你就错了。比如test函数
function int test(string a1,ref string a2,long a3) library "test.dll"
这个函数的C原型是这样的:
extern "C" __declspec(dllexport) int __stdcall test(char* a1, char* a2, int a3)
PB里的string,对应到C的原因,实际上就是指针。如果是输入型参数倒也没太大问题,如果是输出型参数,那就必须要预分配足够的内存空间。通常PB里预分配空间,使用space函数。
String ls_a2
ls_a2 = space(10000)
这样 ls_a2 这个变量就有 10000个字符的空间,注意,如果 是PB10以上版本,它实际上有了20000个字符的空间,因为一个 unicode字符占用2个char空间。
如果没预分配内存,或者预分配内存大小不够,程序也许能运行一小会,但后面不知道会在什么位置莫名其妙崩溃。
6、DLL开发者错误。许多人临时需要某个功能,操起VC就开始写DLL,但也因为对c/c++掌握程度问题,会导致一些BUG,这些只能PB运行不正常时,自己排查规律,如果确定是DLL的原因,得找DLL开发人员处理了。
7、一个容易忽视的问题。PB是32位程序,与你运行的操作系统是不是64位没有关系。所以,如果人家提供给你的DLL里有32位和64位,请一定要记住:你使用它的32位DLL,这与你的操作系统无关。
第二类:COM类DLL
曾经COM是非常热门的一个技术,它也是微软应用与操作系统捆绑的一个典范。然而,在PB中,其实并不建议使用COM方式的DLL。
目前COM方式的DLL,一个是历史遗留下来的古老应用,二是有些人图方便使用C#写DLL给PB用,三是有些PB版本(比如PB9)自己也支持写COM给其他应用使用。
COM方式的DLL,它首先需要注册,这个比较麻烦,在win7及以前版本中,还可以使用 run 方式进行自注册,但在WIN10及其后版本中,自注册就比较困难了,因为注册时需要管理员权限。一两台电脑注册COM可以接受,如果类似HIS这样的应用,一个医院动辄布署几百台电脑,需要一个一个去注册COM,那就呵呵了。尤其是C#写的COM,在不同操作系统上,可能依赖的.net framework还不一样,还需要安装这些额外的内容。
所以,建议PB尽量远离COM类的DLL。
第三类:PB自身的DLL
上面刚才提到PB有些本可以写COM。另外,PB自己的PBL也可以编译成DLL。如果是PB编译出来的DLL库,可以直接当作PBL一样添加到库列表中使用。如果给其他人提供PB的业务接口,这也不失为一个方案。只不过它有个限制:只能是同一个PB版本使用。
欢迎加入pbidea群,共同探讨PB技术。群号:624409252
大自在(QQ:781770213)
2022/5/8