ehxz 发表于 2022-9-17 21:29:40

PowerBuilder (pb)的扩展DLL开发(超级篇)(一)

FROM:https://blog.csdn.net/lxbin2003/article/details/118193313?spm=1001.2014.3001.5502
原创:lxbin2003


PB的扩展DLL开发(超级篇)

(PB史上第一次开放的开发技术)

PowerBuilder (pb)作为一个基于数据库的CS开发工具,在功能方面不够全面,需要使用 DLL做功能扩展。

通常为PB写DLL,有3种方法。

方法1:通用DLL。这种方式的DLL,所有能写标准DLL的语言都可以写。但缺点也比较明显,无法直接访问PB对象和属性、事件这些个性内容,不合适直接返回字符串,通过参数返回数据时,需要预分配内存,如果计算错误,会导致程序崩溃。

方法2:PBNI法(PowerBuilder Native Inte**ce),即官方开放式接口。此方法突破方法1的限制,可以访问PB内部对象、事件、属性。但也存在不同版本之间有一定的兼容性问题。目前广为使用。

这里,我介绍的是第3种方法,即 system library 方法。

一、system library 方法的来由

一次偶然的机会,逛到了http://www.vodacoder.com/Sources/fastfuncs这个网站,看到了全新的DLL for PB开发方法。不过,这个里面的DEMO都不够成熟,网上查不到任何与此相关的内容,官方也无相关内容。

于是本人开始潜心研究(研究过程此处省略百万字...........)。初步掌握了system library 的开发方法,分享出来与大家共同研究。

二、system library 方法与方法1的区别

1、普通标准库DLL的函数声明:

function long test() library "test.dll"

如果是PB9以下版本的DLL,升级到高版本时,会自动加上;ansi,成了:

function long test() library "test.dll" alias for "test;ansi"

这里说一下,“;ansi”这个东西很恶心,误导害了一大堆PB程序员,以为高版本声明必须要加这么个玩艺。事实上,在使用winapi时,高版本有对应的函数,根本不需要;ansi这么个东西。PB升级时自动加上这个,纯属脱裤子放屁。

例如:

PB9:
FUNCTION ulong SetWindowText(ulong hwnd,ref string lpString) LIBRARY "user32.dll" ALIAS FOR "SetWindowTextA"

它升级到PB10以上,会自动变成
FUNCTION ulong SetWindowText(ulong hwnd,ref string lpString) LIBRARY "user32.dll" ALIAS FOR "SetWindowTextA;ansi"
。这个功能很垃圾。实际上应该是:

PB10及以上:
FUNCTION ulong SetWindowText(ulong hwnd,ref string lpString) LIBRARY "user32.dll" ALIAS FOR "SetWindowTextW"

注意,不加;ansi,而是最后的那个A改为W,表示使用UNICODE编码。

2、system library 的声明方式

看上面标准库的声明,它有个 library 关键词。那么system library方法,自然就应该是 system library 作为关键词了。system librar 就是PB自己内部函数的实现方式。

function long systest() system library "test.dll" alias for "systest"

注意:"system library"这个关键词,它告诉 PB,我是自己人,不是请来打工的那些个家伙。

实际上,我们在PB里用的函数,全部都是以system library方式存在于PBVMxxx.DLL中。

例如 :如果你不喜欢 MessageBox 这个名称,想使用 MsgBox 这个名称 ,而所有功能要一样。可以这样声明一个函数:

(假设是PB9) function int MsgBox(string title,string text) system library "pbvm90.dll" alias for "fnMessageBox"

(假设是PB10) function int MsgBox(string title,string text) system library "pbvm100.dll" alias for "fnMessageBox"

然后 MsgBox("","hello") 与 MessageBox("","hello")功能完全一样,只不过函数改了个名而已。用system librar方式声明的函数,即使是从PB9升级到高版本,PB也不会自动加上“;ansi”这么个垃圾东西,因为是自己人嘛,不是外来打工的家伙,待遇不一样的。

三、如何开始写自己的system library DLL

system library DLL有自己的固有特征,它通常是:

#include "pbSysLib.h" //这是我自己实现的一个头文件,具体作用就是加载PBVMxxxx.dll,导出相关SDK函数

__declspec(dllexport) DWORD __stdcall FuncName(POB_THIS obThis,int nArgCount)
{
         BOOL isnull;
         return 1;
}
FuncName 是函数名,POB_THIS obThis 这个由PB调用时自动传入,它指向一个结构,可以唯一标识一个PB的虚拟空间,或者也可以理解为一个session。int nArgCount 指的是本次调用有几个参数,而真正的参数放在obThis里的一个指针数组里,通过参数个数,可以去取这些个参数。

因此,system library DLL 函数还是比较简单的,这也符合PB这种古董级文物的身份,简单而有效。

接下来,就是使用PBVM的各种内部函数,来驾驶我们的各种功能,可以在C、C++里,访问PB的所有功用,并为PB扩展所有需要的功能。

这里就一个具体函数,做一些说明

PB里的声明:
function string TestRef(readonly string src,ref string dst) system library "PBJson.dll" alias for "TestRef"

使用时可以是:

string src,dst

src = "hello world"

TestRef(src,dst)

这时候dst的结果是:this is return by ref:

DLL里的函数源码是:

DLLEXPORT DWORD WINAPI TestRef(POB_THIS obThis, int nArgs)
{
         BOOL success = FALSE;
         BOOL isnull = FALSE;
         POT_LVALUE_INFO info = NULL;
         TCHAR *lpSrc = (TCHAR *)ot_get_valptr_arg(obThis, &isnull);//取第一个参数,指针型,指向 src
         POB_DATA lv = ot_get_next_lvalue_arg(obThis, &info); //取第二个参数,注意第二个参数是ref类型
         POB_DATA v = NULL;
         if (lv)
         {
                   POT_REF_PAK v = (POT_REF_PAK)lv->val.ptr; //获取得引用定义
                   POB_DATA lpArg = ot_access_ref_data(obThis, v); //获取得引用定义里实际指向PB的那个变量,即上面的 dst
                   if (lpSrc && lpArg && ob_get_data_type(lpArg) == STRING_TYPE) //判断,确实都是有效指针,并且第二个参数是 string类型
                   {
                            TCHAR buffer = { 0 };
                            _stprintf(buffer, _T("this is return by ref: [%s]"), lpSrc);
                            ob_set_data_ptr(lpArg, ob_dup_string(obThis, buffer), STRING_TYPE, 1); //注意这里面有个ob_dup_string,它返回了一个从buffer复制出来的字符串的字符串,把这个变量绑定给引用的dst变量,而不是直接把 buffer 绑定给dst变量。
/**
特别强调,这里的 ob_dup_string 是必不可少的。PB有自己的内存管理,这个复制的字符串,在PB函数生命周期结束后,它会被自动释放,内存回收。
这里如果写成:
                            TCHAR *buffer = new buffer;
                            _stprintf(buffer, _T("this is return by ref: [%s]"), lpSrc);
                            ob_set_data_ptr(lpArg, buffer, STRING_TYPE, 1);
也就是自己分配了内存,并且返回,这个函数调用后,PB是会崩溃的。原因是buffer没有使用PB的内存堆。
                            TCHAR *buffer = ob_alloc_string(obThis,1024);
                            _stprintf(buffer, _T("this is return by ref: [%s]"), lpSrc);
                            ob_set_data_ptr(lpArg, buffer, STRING_TYPE, 1);
如果是这样,那就毫无问题了,因为TCHAR *buffer = ob_alloc_string(obThis,1024);是在PB自己的内存堆上分配 的内存,可以被PB正确释放回收。
*/
                            success = TRUE;
                   }
         }
         OB_DATA obReturn = { 0 };
         ob_set_data_string(&obReturn, success, BOOL_TYPE, OB_INSTVAR_FIELD);
         ot_set_return_val(obThis, &obReturn);
         return 1;
}


如果你对system library相关开发方式感兴趣,可到QQ群624409252共享里大自在的专用目录下下载DEMO。
————————————————
版权声明:本文为CSDN博主「lxbin2003」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lxbin2003/article/details/118193313

页: [1]
查看完整版本: PowerBuilder (pb)的扩展DLL开发(超级篇)(一)

免责声明:
本站所发布的一切破解补丁、注册机和注册信息及软件的解密分析文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。

Mail To:Admin@SybaseBbs.com