方法文档 - 用 Webutil 调用客户机上的 C 库

引言

许多表单应用程序向外调用第三方的代码(如 C)在表单范围之外执行处理。例如,执行计算或与硬件设备连接。执行此功能的传统方法是通过 ORA_FFI,它提供了到这些外部程序的接口。然而,当您将表单应用程序移动到 Web 时,通过 ORA_FFI 的调用现在将在应用服务器(而不是客户机)上运行。可能需要在客户机上执行代码,而不是在应用服务器上调用第三方程序(例如,软件需要与手持扫描仪或测量设备交互作用)。WebUtil 提供了一个接口,用于在客户机上调用 C 代码。

使用 ORA_FFI 的一个示例应用程序

在开始讨论如何使用 WebUtil 运行此代码之前,在此示例中我们将首先研究客户端/服务器应用程序如何使用 C 程序以及彼此间的交互方式。

//Simple C function that just puts something in a string
// and returns the length of that string both as the RC and as a param
// Keeping the C interface as simple as possible.

__declspec( dllexport ) int PopulateString ( char *FormsBuffer, int *BuffLen)
{
int LocalLength;
strcpy(FormsBuffer,"A Fixed string from within the C program");
*BuffLen = strlen(FormsBuffer);
LocalLength = strlen(FormsBuffer);
return LocalLength;
}

在此示例中,C 程序以名为 ffisamp.dll 的 DLL 形式使用。这是一个非常简单的 C 程序,它包含一个称为 PopulateString 的函数,该函数只是接受了一个字符串和字符串长度,然后将一个新的字符串作为一个参数返回,将该字符串的字符数同时作为参数和返回代码返回。从此处下载这个文件。

如何构造一个 ORA_FFI 调用

ORA_FFI 调用作为打包的函数定义。此包处理整个库或该库的一个函数。每个库函数必须分别注册,且需要 PL/SQL 函数来调用它。

分析 ORA_FFI 代码

可从此处获得表单源代码;相关代码为:

lh_ffisamp := ora_ffi.find_library('ffisamp.dll');
lh_ffisamp := ora_ffi.load_library(NULL,'ffisamp.dll');

这将创建一个到 DLL 的句柄,并为我们将其加载完毕。

fh_PopulateString := ora_ffi.register_function(lh_ffisamp, 'PopulateString',ora_ffi.C_STD);

将调用的特定函数是在一个变量中注册的。


ora_ffi.register_parameter(fh_PopulateString,ORA_FFI.C_CHAR_PTR);
ora_ffi.register_parameter(fh_PopulateString,ORA_FFI.C_INT_PTR);
ora_ffi.register_return(fh_PopulateString,ORA_FFI.C_INT);

这三个调用将注册这些参数。第一个调用传入字符串缓冲区,将其更改后传出。第二个调用传入缓冲区长度,将其更新后传出。最后一个调用用于返回代码,它既是输出参数又指出字符串缓冲区长度。

如何构造 WebUtil_C_API 调用?

WEBUTI_C_API 调用与 ORA_FFI 调用的结构类似,假设它们本质上执行同样的功能也不奇怪。然而,二者之间还是存在一些细微的差别。与 ORA_FFI 相比,WEBUTIL_C_API 具有更为灵活的接口。例如,库不需注册;而当注册库的一个函数时即将该库包含进去。即使函数本身也不需显式注册,只需对库函数进行调用,再由调用处理注册。然而,坚持现有的 ORA_FFI 结构将使转换更为容易。

分析 WEBUTIL_C_API 代码

可在此处获得表单的源代码。

f_handle := WEBUTIL_C_API.register_function('ffisamp.dll','PopulateString');

此调用与 ORA_FFI.Register_Function 类似,然而,因为此函数调用方法是可重载的,且可将库名和函数作为参数,所以此调用是可选的。然而,如此使用 WEBUTIL_C_API.INVOKE_XXX 会造成一定的系统开销,因此显式注册频繁使用的函数(如循环内部的函数或表单之间共享的函数)是一种很好的做法。

args := WEBUTIL_C_API.create_parameter_list;
param1 := WEBUTIL_C_API.add_parameter(args,WEBUTIL_C_API.C_CHAR_PTR,WEBUTIL_C_API.PARAM_INOUT ,StringBuffer, 255);
param2 := WEBUTIL_C_API.add_parameter(args,WEBUTIL_C_API.C_INT_PTR ,WEBUTIL_C_API.PARAM_INOUT ,StringLength);

下面这个调用是与 register_parameter 等效的 ORA_FFI 调用,它定义传递给 DLL 的参数列表。在最后两句中,传递了指向参数列表的句柄,每个参数具有经过定义的类型、状态(输入、输出或输入输出)和值。

rc := WEBUTIL_C_API.Invoke_Int( 'ffisamp.dll','PopulateString',args);

此调用现在将调用命名的函数并传递参数。注意 WEBUTIL_C_API 函数的名称取决于返回的类型。

message('The New value for String is:'||WEBUTIL_C_API.Get_Parameter_String(args, param1));
message('New Length '||to_char(WEBUTIL_C_API.Get_Parameter_Number(args, param2)));

这两个调用将输出字符串缓冲区和字符串长度参数的值,这两个参数已因对 C 程序的调用而更新

WEBUTIL_C_API.Destroy_Parameter_List(args);

WEBUTIL_C_API.Deregister_Function(f_handle);

调用 Destroy_Parameter_List 将清除参数列表,还可以选择调用 Deregister_Function 来卸载库。关闭表单时将自动卸载所有库。然而,一旦不再需要某个函数即对其取消注册是一种很好的做法;这有助于避免长期运行的应用程序出现内存泄漏。

设置

有关设置 WebUtil 的步骤,请参阅可在软件下载部分获得的《精通 WebUtil 手册》。然而,要注意的要点是应将 dll 放在 \lib 目录中。

WebUtil 如何查找 DLL

如上所述,设置 WebUtil 的完整文档内容可从 WebUtil 手册中获得。然而,对于本文而言,值得关注的是如何查找 DLL 的要点。

webutil.cfg 文件中指定了下载到客户机的文件的位置(参阅与 webutil 基本代码相关的 install.syslib.location)。这确保了所有 WebUtil 所需的 DLL 和 EXE 文件都已下载到台式机,能够防止今后出现任何配置问题。

DLL 将被下载到客户机 PATH($JINITIATOR_HOME/bin) 下可用的位置。注册函数时,因为函数是从 PATH 中选取的,所以 WEBUTIL_C_API 实际上并不需要文件路径。

启动使用 WebUtil 的表单时,它将检查 $JINITIATOR_HOME/bin 目录,以查看 webutil.cfg 文件中的所有可执行文件是否都在该目录中。它从服务器端的 $WEBUTIL_HOME/lib 目录下载任何所需的新文件或更改了的文件。当前客户机上的目标目录是不可配置的。

致谢

感谢 LogicaCMG 的 A.P. Clarke 帮忙提供关于这些特性的文档。


寄送此页面
Printer View 打印机视图