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

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

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

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

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

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

方法2:PBNI法(PowerBuilder Native Interface),即官方开放式接口。此方法突破方法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: [hello word]

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[1024] = { 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[1024];

                            _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。

  • 13
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值