BNI 是powerbuilder 的本机扩展,是很强大的东东,最近因为工作需要,稍作了点研究,希望对未做过PBNI的朋友,有所帮助。
用最简单的的话来讲,(不准确),就是Java ,C++ 里可以加载PB的虚拟机,利用PB的对象,语言,主要是利用PB在某些方面的强大功能,让他们能很好的结合。PB 安装目录 SDK下的文件非常重要,要阅读下PB自带的帮助文档。
Powerbuilder9 的版本只支持 vs6,10.5的最高支持到VS2005 ,如果要想让PBNI 支持到VS2008,并且集成到appwiz里,需要手动来做。%Syba\Pb105\PowerBuilder 10.5\SDK\PBNI\Wizards\VCWizards\PBNIWizard8,此目录下的deploy.bat 文件有时执行不一定成功,手动就可以,按照里边的步骤做,做完后,%Syba\Pb105\PowerBuilder 10.5\SDK\PBNI\Wizards\VCProjects 8.0,这个目录下的文件PBNIWizard.vsz 文件用UE打开,将8.0 改为9.0 ,再复制到相应目录,就可以在VS2008里用向导创建PBNI工程了。
还有一点要提醒的是,一定要使用向导来创建PBNI,否则很容易挂哦
谈论 PowerBuilder 9开发技术讲座-PowerBuilder Native Interface$PBNI$[华软编程论坛]
PowerBuilder 9开发技术讲座— PowerBuilder Native Interface(PBNI)
PowerBuilder 9现在对于其他开发语言的支援,有了全新的突破,在以往使用
PowerBuilder开发程式时,要和C++或是Java程式互通有无是有一些折衷的办
法,但是总是没有办法做到简易而且全面性的支援。现在只要透过PowerBuilder
9的PBNI技术,就可以让PowerBuilder的程式呼叫Java,或是在一个C++的程
式中引用PowerBuilder NVO物件函数。
以往的PowerBuilder程式只能够透过外在函数呼叫的方式来存取C/C++的函
数,但在PowerBuilder 9.0之中扩增了一项强而有力的介面-「PowerBuilder
Native Interface」,简称PBNI。透过PBNI的开发方式,PowerBuilder开发人员
不仅可以使用物件导向的方式来存取C/C++函数,而且还可反向地让C/C++程式
呼叫PowerBuilder之中的物件,达到应用程式的整合。更甚者,在藉由JNI与advertirs
PBNI两者的结合,Java应用程式也可双向地与PowerBuilder程式沟通。
何谓PBNI
在谈什么是PBNI之前,我们先来谈谈下面三个问题:
1.开发人员有办法用PowerBuilder程式呼叫C或是C++的程式吗?
2.开发人员有办法用PowerBuilder程式呼叫一些外部元件像是Java EJB元件、
Web Service元件、Java Class程式等诸如此类的元件吗?
3.开发人员有办法用反过来,用C或是C++呼叫已经使用PowerBuilder开发好
的程式吗?
上述三个问题,在过去的PowerBuilder其实都可以做到某种程度的地步,只是都
有些问题。传统上使用PowerBuilder开发上述的程式时,如果要呼叫C或是C++
的程式,是可以使用宣告外部函数的方式来使用一个已经撰写好的DLL函数,
例如:
FUNCTION ulong GetSysColor (int index) LIBRARY "USER32.DLL”
FUNCTION boolean sndPlaySoundA (string SoundName, uint Flags) LIBRARY
"WINMM.DLL" 崇拜英文
可是如果是下面的程式呢:
BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARAM
lParam);
这个可是个大问题了,因为这个Windows DLL Function中会用到所谓的「Callback」
函数的技术,所谓的Callback Function指的是今天有A和B两个物件,在程式中
A物件呼叫B物件的Function,而在该B物件的Function又会回头呼叫A物件的
其他Function,这就叫「Callback」。在PowerBuilder呼叫C的Function后,在这
个C的Function中要再回头呼叫PowerBuilder的函数是不可能用引用外部函数的
方式来达到这个目地的。除了Callback Function使用困难之外,使用外部函数也
有资料型态的限制,以及没有办法使用物件导向的方式开发等种种的困难及问
题。
再来谈谈PowerBuilder呼叫外部的元件的方法,在以前能够让PowerBuilder呼叫
EJB元件,就只能透过一些协力厂商开发的「COM Bridge」,让PowerBuilder程
式透过COM元件来呼叫Java程式。至于要让Java或是C++来呼叫PowerBuilder
虚伪的英文程式的话,过去最常见的方法就是把这个PowerBuilder的程式包装成为「OLE
automation rver」。这些方法都不是一个真正解决的好方法,说穿了,这些方法
跟本就没有办法直接和PowerBuilder的核心「PowerBuilder Visual Machine」做沟echoes
通,所以在过去的版本的PowerBuilder,是一直有这种和其他语言程式不能沟通
的困扰,这也是大家一直认为,PowerBuilder是一个封闭不开放的开发工具。
PowerBuilder 9这个版本有几个突破性的技术,而PBNI就是其中一个。所谓的
美国voaPBNI (PowerBuilder Native Interface),指的是PowerBuilder提供一个「原生介面
(Native Interface)」,透过这个介面可以使得PowerBuilder提高了对其他程式语言
的扩展能力,比方说透过该介面可以存取任何类型的外部应用应用程式,或是让
外界其他的程式语言存取或是呼叫PowerBuilder开发的程式,下面是一个简单的
PBNI的示意图:
此主题相关图片如下:1.jpg
在上面这张图中,PBNI提供了两道让外界可以和PowerBuilder核心(PBVM)的介
面窗口,第一个对外的窗口是指在图的右半边,我们可以开发「PB Extension」,
PB Extension其实最后会变成DLL,透过该技术,C或是C++的DLL程式可以包
装成为一个「PBD」的档案,而该PBD的档案就可以在开发程式时,加到Library
Search Path中,让PowerBuilder直接存取PBD里的物件函数,你可以把它当作是
一个很像PowerBuilder NVO的东西来对待它。第二个对外的窗口是指在图的左半
边,你可以把PowerBuilder Virtual Machine 「内嵌」到一个C++的应用程式中,
在C++程式中就可以直接呼叫PowerScript
Function。
PBNI的元素
PBNI提供了一些基本的元素,透过这些元素,程式开发人员可以快速的引用外
部程式语言,下面是常见到的PBNI元素:
?? PBNI提供的介面(Interface):
?? IPB_VM:这个介面的作用,在于当你要用C++或是其他的程式语言来
呼叫PowerBuilder开发的程式,或是你希望要和PowerBuilder的核心
「PBVM」进行互动,或是沟通协调,你可以使用这个介面。
?? IPB_Session:这是一个抽象的介面,这个介面可以用来定义诸如存取
PowerScript里面的资料、建立PowerBuilder物件和呼叫PowerScript函数
操作的方法
?? IPB_Value:这个介面你可以把它想像成是它就是代表PowerBuilder的
值。这些值可以是PowerBuilder的标准资料型态,例如String、Long、
Integer、Char等等。所以这个介面提供了关于每个变数的资讯,包括变
数的类型、标记、存取权限(Public、Private和Protected)、变数值或参数
存取方式(例如Call by Value或是Reference)。
?? IPB_Arguments:这个介面可以让使用者在PowerBuilder VM和「PB
Extension」间传递参数。
?
? IPBX_NonVisualObject和IPBX_VisualObject:这两介面很意思,因为它
们可以在C++程式中实作出来,而且是放在PB Extension里面,你在
PowerBuilder中就可以用PBD的方式看到你实作出来的物件,而要写这
些可见或是不可见的物件,靠的就是IPBX_NonVisualObject和
IPBX_VisualObject介面。
?? IPBX_Marshaler:这个介面是当你要出一个「PB marshaler extension 」时,
一定要实作出IPBX_Marshaler这个`介面。这个介面尤其是你要由
PowerBuilder呼叫Java程式时,一定要用到的一个介面。
?? PBNI提供的Structures:
?? PBCallInfo:这个Structure可以在开发PBNI程式时,让PBNI和
PowerBuilder之间呼叫的函数保持参数和回传值的资讯。如果要存取在
PBCallInfo中的资讯,可以使用IPB_Arguments介面来获得PBCallInfo。
?? PBArrayInfo:PBArrayInfo是一个C++的structure,这个Structure可以
在阵列中保持一些资讯。
?? PBNI提供的Globle Function:
什么是日光浴
如果你要写一个PowerBuilder extension的程式(说穿了就是用C++写一个
DLL档啦),这个物件必须要汇出两个Global Functions,让这个程式可以「内
嵌」 PowerBuilder VM并且建立实体出来。下面是PBNI提供的Globle
Function:
?? PBX_GetDescription()
?? PBX_CreateNonVisualObject()
?? PBX_CreateVisualObject()
?
? PBX_InvokeGlobalFunction()
?? PBNI提供的Helper class:
Helper Class指的是一些辅助的类别物件,PBNI提供像是PBObjectCreator、
PBArrayAccessor和PBEventTrigger等辅助类别,透过这些辅助类别物件可使
PBNI在开发上更简单。
PBNI的开发方式
在了解PBNI有那些元素后,读着应该也了解到何谓PBNI,并且知道PBNI能帮
我们做什么。在针对
不同的目地,PBNI也有不同的开发方式,常见的PBNI开
发目地为下列四个,在后面的部份会祥细的说明PBNI的开发方式为何:
??建立PB extensions
?
fine怎么读
?建立PB marshaler extensions
??建立PB visual extensions
??内嵌PBVM到C++的应用程式中
建立PB extensions步骤
之前有跟各位读者提过,PBNI提供了两个对外的方法,其中一种就是将C或是
C++写好的DLL档案,透过PBNI提供的介面来包装成一个PowerBuilder认得的
PBD档案,这种方式称之为建立「PB Extensions」。在开发一个PB Extensions的
程式时,我们必须先设想好,最后我们要产生的PBD中,会有那些物件。比方
说,我现在手头上正在写一个C++的程式,我希望这个C++的程式最后透过PBNI
的帮助,产生一个PBD档案,而且在这个PBD里面有一个Funtion物件,而这
个Function物件会对照于我在C++里面写好的Function,让我只要呼叫该Function
物件,就等于是执行C++里的程式。刚才的设想中,开发的步骤如下:
1.使用C++的开发工具建立一个C++专案。
2.在C++的程式中,汇入PBNI SDK提供给C++的相关表头档(h档案)。
3.在C++的程式中,透过PBX_GetDescription()这个PBNI提供的函数,告知
到时后会汇出一个Globle Function。
4.因为要做的是一个Globle Function物件,所以在C++的程式中,透过
PBX_InvokeGlobalFunction()这个PBNI提供的函数,实作该Function的程
式出来。
5.将开发好的C++程式编辑成DLL档。
6.透过PBNI提供的「」小工具,将这个DLL档案转换为PBD
档。
7.打开PowerBuilder后,将这个PBD档加到Library Search Path中。
8.开发相关的PowerBuilder程式,并且呼叫这个PBD档的Globle Function。
定义要汇出的物件类别
在上述的步骤中,PBX_GetDescription()这个PBNI提供的函数是一定要有的,因
为这个函数是用来产生相关的类别定义,而这个类别定义最后会在将DLL档案
转换成PBD档时,跟据你在PBX_GetDescription()函数中的定义,产生相对应的
PowerBuilder物件。下面在C++的程式,最后会产生一个PB的Globle Function物
件,这个Globle Function物件名称是GetUrName(),而它的回传值是String资料
型态:
PBXEXPORT LPCTSTR PBXCALL PBX_GetDescription()
{
static const TCHAR desc[] = {
"globalfunctions \n"
"function string GetUrName()\n"
"end globalfunctions \n"
};
return desc ;
}
再举一个例子,下面写在C++里的PBX_GetDescription()函数程式,最后会产生一
个PowerBuilder的可视物件「flagext」,在这个可视物件中,有两个事件为分别
为「onclick」Event和「ondoubleclick」 Evnet;在可视物件中,还有两个物件
Function 「ttext(string txt)」和「tflag(int flag)」:
PBXEXPORT LPCTSTR PBXCALL PBX_GetDescription()
{
static const TCHAR desc[]
= {
"class flagext from uro b j e c t\n"
"event int onclick()\n"
"event int ondoubleclick()\n"
"subroutine ttext(string txt)\n"
"subroutine tflag(int flag)\n"
"end class\n"
};
return desc;
}
实做类别物件程式码
之前提到的步骤中,除了PBX_GetDescription()之外,我们还会步骤四中看到一个
PBNI提供的Function,叫PBX_InvokeGlobalFunction(),这是因为我们要实作出
Globle Function的程式,所以就必须要使用PBX_InvokeGlobalFunction()函数;相同
的道理,如果在PBX_GetDescription()中我们准备建立的是一个NVO物件,那就
要用PBX_CreateNonVisualObject()函数实作出NVO物件的程式;如果在
PBX_GetDescription()中我们准备建立的是一个可视的PowerBuilder物件,那就要
用PBX_CreateVisualObject()函数实作出这一个可视物件的程式。下面是一个在
C++中使用PBX_InvokeGlobalFunction()来实作出一个Globle Function程式的例
子:
PBXEXPORT PBXRESULT PBXCALL PBX_InvokeGlobalFunction
( sportif
IPB_Session* pbssion,
LPCTSTR functionName,
PBCallInfo* ci
)
{
if ( strcmp( functionName, "geturname" ) == 0 )
{
CWinAPI *WinAPI = new CWinAPI( pbssion ) ;
WinAPI->PBNIGetUrName ( ci ) ;
if ( WinAPI != NULL ) delete WinAPI ;
return PBX_OK ;
} ;
return PBX_E_NO_SUCH_CLASS ;
}
在上面的程式码中,读者可以发现,程式PB Extension和PB的核心PBVM的沟
通是透过IPB_Session这个指标变数来保持C++和PowerBuilder程式的连结,而
在PBNI和PowerBuilder之间呼叫的函数保持参数和回传值的资讯就是透过之前
介绍的PBCallInfo这个指标结构来保存,下面是PBVM和PB Extension之间的关
系示意图:
此主题相关图片如下:2.jpg
下面是完整的程式码,这个程式码中,在pbniwinapi.cpp程式实作出类别
「CWinAPI」,在这个Class中,有一个PBNIGetUrName()函数会透过Windows
作业系统的API取得电脑的使用者名称,而main.cpp中会汇出一个PB Globle
Function叫「GetUrName()」:
pbniwinapi.cpp
#include <WINDOWS.H>
#include <stdio.h>
#include "PBNIWINAPI.h"
CWinAPI::CWinAPI( IPB_Session * pSession )
: m_pSession( pSession )
{
}
CWinAPI::~CWinAPI(void)
{
}
void CWinAPI::PBNIGetUrName (
whether和if的区别PBCallInfo *ci )
{
LPTSTR lpszSystemInfo;
procrastinator
DWORD cchBuff = 256;
TCHAR tchBuffer[1024];
lpszSystemInfo = tchBuffer;
GetUrName ( lpszSystemInfo, &cchBuff) ;
ci->returnValue->SetString ( lpszSystemInfo ) ;
}
void CWinAPI::Destroy()
{
delete this ;
}
main.cpp
#include <windows.h>
#include <pbext.h>
#include "pbniwinapi.h"
BOOL APIENTRY DllMain(
HANDLE hModule,
DWORD reasonForCall,
LPVOID lpRerved
)
{
switch( reasonForCall )
{
ca DLL_PROCESS_ATTACH:
ca DLL_THREAD_ATTACH:
ca DLL_THREAD_DETACH:
ca DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
PBXEXPORT LPCTSTR PBXCALL PBX_Get