从PB9开始,增加了PBNI功能,可以直接采用VC来编写可视和不可视的组件给PB的应用程序调用,以实现PB应用功能的扩展.采用这种技术架构,可以非常方便的把原来的VC中的功能,封装为PB的可视对象或不可视对象,给PB的应用程序使用,而不需要在程序中定义各种API函数.而且由于PBNI编译后的PBD文件,并不是使用PB的代码指令,用pbkiller和Shudepb都无法对期进行反编译,可以有效的保护软件系统核心部分代码的安全.
即然PB程序采用调用DLL文件的方式来进行软件注册授权控制,很容易被绕开,那么该如何解决这一问题呢?
从PB9开始,增加了PBNI功能,可以直接采用VC来编写可视和不可视的组件给PB的应用程序调用,以实现PB应用功能的扩展.采用这种技术架构,可以非常方便的把原来的VC中的功能,封装为PB的可视对象或不可视对象,给PB的应用程序使用,而不需要在程序中定义各种API函数.而且由于PBNI编译后的PBD文件,并不是使用PB的代码指令,用pbkiller和Shudepb都无法对期进行反编译,可以有效的保护软件系统核心部分代码的安全.
下面,我们采用VC6来开发一个PBNI组件,用于封装上一篇文件中所实现的CheckKey和Add函数功能.(如果是PB9,则建议采用VC6作为开发环境,如果是PB10以上版本,则建议采用VS2003作为开发环境.)
配置开发环境
首先,按照帮助文件的说明,配置VC6的开发环境,以便使用PBNI的应用向导,以及向开发环境增加PBNI的头文件和库文件的目录.
1.把C:\Program Files\Sybase\PowerBuilder 9.0\SDK\PBNI\wizards\pbext.awx文件拷到C:\Program Files\Microsoft Visual Studio\COMMON\MSDev98\Bin\IDE目录,以便在VC6中可以使用PBNI应用程序向导.
2.打开VC6,选择工具---选择---目录,增加以下选项.
C:\Program Files\Sybase\PowerBuilder 9.0\SDK\PBNI\include
C:\Program Files\Sybase\PowerBuilder 9.0\SDK\PBNI\Lib
新建一个PBNI工程
1.文件---新建---工程,选择PBNI Extension App Wizard,输入工程名称,选择工程的保存路径,按确定.
2.输入对象名称,n_cst_service,由于VC6的向导只能增加不可视对象,所以该对象最好生成的是不可视对象组件.
3.按完成,向导出自动生成PBNI所需要的各种全局函数,并增加了n_cst_Service这个不可视对象的定义,以及相对应的VC类对象.向导会为n_cst_Service增加SampleMethod1和SampleMethod2两个函数,这两个函数没什么意义,需要把它改成需要的CheckKey的Add函数.
实现对象功能
1.打开PBX_GetDescription函数,定义生成PB对象的描述定义.向导生成的n_cst_service对象的代码如下:
Code
PBXEXPORT LPCTSTR PBXCALL PBX_GetDescription()
{
static const TCHAR class_descs[] =
{
"class n_cst_service from nonvisualobject\n"
// TODO : Add in your PBNI class functions here.
"function long SampleMethod1()\n"
"function long SampleMethod2()\n"
"end class\n"
};
return class_descs;
}
把SmpleMethod1和SampleMethod2删除掉,并增加CheckKey和Add函数的定义,n_cst_service的描述定义最后为:
Code
PBXEXPORT LPCTSTR PBXCALL PBX_GetDescription()
{
static const TCHAR class_descs[] =
{
"class n_cst_service from nonvisualobject\n"
// TODO : Add in your PBNI class functions here.
"function boolean CheckKey(string key)\n"
"function int Add(int a , int b )\n"
"end class\n"
};
return class_descs;
}
2.打开n_cst_service.h文件,修改n_cst_service对应的vc类的定义,删除SampleMethod1和SampleMethod2函数的定义,并增加ChcekKey和Add函数的定义,最后代码如下
Code
class Cn_cst_service : public IPBX_NonVisualObject
{
enum MethodIDs
{
mid_CheckKey = 0,
mid_Add = 1
};
virtual void Destroy();
public:
Cn_cst_service();
Cn_cst_service(IPB_Session* pIPB_Session, pbobject pbobj);
~Cn_cst_service();
// IPBX_UserObject methods.
PBXRESULT Invoke
(
IPB_Session *session,
pbobject obj,
pbmethodID mid,
PBCallInfo *ci
);
protected:
PBXRESULT CheckKey
(
IPB_Session *session,
pbobject obj,
PBCallInfo *ci
);
PBXRESULT Add
(
IPB_Session *session,
pbobject obj,
PBCallInfo *ci
);
3.打开n_cst_service.cpp文件,同样删除向导生成的函数,并增加CheckKey和Add函数的功能,最后生成的代码如下:
Code
// IPBX_UserObject method.
PBXRESULT Cn_cst_service::Invoke
(
IPB_Session *session,
pbobject obj,
pbmethodID mid,
PBCallInfo *ci
)
{
PBXRESULT pbrRet = PBX_E_INVOKE_FAILURE; // First assume function associated with "mid" is not found.
if (mid == mid_CheckKey)
{
pbrRet = (PBXRESULT)CheckKey
(
(IPB_Session*)session,
(pbobject)obj,
(PBCallInfo*)ci
);
}
if (mid == mid_Add)
{
pbrRet = (PBXRESULT)Add
(
(IPB_Session*)session,
(pbobject)obj,
(PBCallInfo*)ci
);
}
return pbrRet;
}
// PB callable Cn_cst_service method.
PBXRESULT Cn_cst_service::CheckKey
(
IPB_Session *session,
pbobject obj,
PBCallInfo *ci
)
{
PBXRESULT pbrRet = PBX_OK;
LPCSTR key =session->GetString( ci->pArgs->GetAt(0)->GetString() ); //取传入的参数
if(strcmp(key,"12345678")==0)
{
ci -> returnValue -> SetBool(true); //设置返回值
}
else
{
ci -> returnValue -> SetBool(false);
}
return pbrRet;
}
// PB callable Cn_cst_service method.
PBXRESULT Cn_cst_service::Add
(
IPB_Session *session,
pbobject obj,
PBCallInfo *ci
)
{
PBXRESULT pbrRet = PBX_OK;
int a, b, result;
a =(int)ci->pArgs->GetAt(0)->GetInt();
b =(int)ci->pArgs->GetAt(1)->GetInt();
result = a+b;
ci -> returnValue -> SetInt(result);
return pbrRet;
} 4.编译程序,生成pbx文件.
5.用pbx2pbd90程序,生成pb应用程序使用的pbd文件.
pbx2pbd90 {dir}\PBNI_Test.pbd {dir}\PBNI_Test.PBX
引用程序
1.把PBNI_Test.PBX和PBNI_Test.PBD拷贝到pb程序应用的目录下,用pb9打开应用程序,,在程序的库文件列表中增中上面生成的PBD文件.
2.打开w_main窗口,CheckKey按钮的代码更改为:
Code
Code
n_cst_service n_service
n_service = Create n_cst_service
IF n_service.CheckKey("12345678") Then
MessageBox("提示1","注册码12345678正确!")
ELSE
MessageBox("提示1","注册码12345678错误!")
END IF
IF n_service.CheckKey("abcdefg") Then
MessageBox("提示2","注册码abcdefg正确!")
ELSE
MessageBox("提示2","注册码abcdefg错误!")
END IF
Destroy n_service
Add按钮的代码更改为:
n_cst_service n_service
n_service = Create n_cst_service
MessageBox('Add计算',"100+30="+string(n_service.Add(100,30)))
Destroy n_service
3.运行程序,点击CheckKey按钮,弹出下面两个窗口
点击Add按钮,弹出下面窗口.
4.使用pbkiller打开上面生成的pbd文件,显示错误信息,不能被反编译.
后注
使用PBNI可以防止代码被pbkiller和shudepb反编译,也可以防止直接采用dll的方式,被直接绕开的情况,相对要安全一点.但这种安全也是相对的,所以一般还需要在程序增加对这个pbd文件进行验证,如根据文件的MD5值,确定该文件没有被修改或被替换.