第二十七章:使用外部库扩展 Visual FoxPro 的功能

通过使用 ActiveX 控件(.ocx 文件),ActiveX对象和动态链接库(dll),可以扩展 Visual FoxPro 功能。外部库不仅允许您访问其他程序,而且可以访问 Windows 本身。例如,可以使用一个 ActiveX 控件直接对 Windows 注册表进行读取和更新,也可以通过链接一个 Windows 的 dll 来调用系统级别的函数。

如果在外部库中没有所需的函数功能,可以使用一个三十二位的 C++ 编译器(如 Microsoft Visual C++® 4.0 或更高版本),或 Microsoft Visual Basic® Control Creation Edition version 5.0 创建自己的 ActiveX 控件。有关详细内容,请参阅第二十八章“访问 Visual FoxPro API”

本章内容要点:

使用外部库

在大部分情况下,Visual FoxPro 为开发应用程序提供了完整的工具。但是,偶尔也会遇到 Visual FoxPro 尚不能提供开发某个应用程序所需功能的情况。这时,可以在 Visual FoxPro 的外部,借助外部库的功能来实现。

Visual FoxPro 允许访问以下类型的外部库:

无论使用哪种库,您首先必须了解访问其控件或函数的规则。例如,如果想在一个表单中包含一个 ActiveX 控件,您必须知道管理该控件具有的属性、事件和方法程序。对于 ActiveX 控件,可以使用“Visual FoxPro 类浏览器”确定您能够使用的属性、事件和方法程序。同样,如果想调用一个 .dll 文件中的函数,您必须知道该函数的名称、参数的数目和数据类型以及返回值的数据类型。通常,该类信息可以从随库带的文档(手册或者联机帮助系统)中找到。要查询正在使用的 Windows 系统的 .dll 文件信息,请参阅对应版本的 Software Development Kit (SDK)。

访问 ActiveX 控件和对象

可以使用任意在您的计算机上使用的 ActiveX 控件。如果要使用一个 ActiveX 控件,需将其添加到一个表单中,然后设置它的属性,编写事件的处理代码,或者调用它的方法程序。使用“表单工具栏”或者“OLE 容器”控件可以向一个表单中添加 ActiveX 控件,也可以用代码来实现。有关在“表单设计器”中添加 ActiveX 控件的详细内容,请参阅第十六章“添加 OLE”

可以用代码创建一个 ActiveX 控件,就象创建其他的 Visual FoxPro 控件一样。但是,在创建控件之前,必须确定控件所在的类库名称,该名称将存储在 Windows 注册项中。如果无法确定控件所在类库的名称,使用“表单设计器”创建控件(在前面的章节中说明),然后就可以获得该控件的 OLEClass 属性。

可以使用 CREATEOBJECT( ) 函数直接创建 ActiveX 对象,不需要表单的实例。

若要使用代码创建 ActiveX 控件

  1. 调用 CREATEOBJECT( ) 创建一个表单。

  2. 调用新表单的 AddObject 方法程序来添加控件,指定该类为 olecontrol。注意,必须将该控件的类库名称作为 AddObject 方法程序的第三个参数来传递。

例如,下述程序创建一个新的表单,并向其添加一个 outline 控件:

oMyForm = CREATEOBJECT("form")
oMyForm.AddObject("oleOutline","olecontrol", ;
   "MSOutl.Outline")

在创建了表单和控件之后,可以调用表单的 Show 方法程序来显示表单,并设置该控件的 Visible 属性为“真”(.T.),来显示这个控件:

oMyForm.oleOutline.Visible = .T.
oMyForm.Show

一些 ActiveX 控件主要不是为用户交互作用而设计的。例如,计时器控件不支持用户交互操作。但是,仍可以在表单中创建这个控件,因为该控件一般可以通过一个默认的显示组件(用图标显示)来创建。通常,您不能更换该图标或调整其大小。

在应用程序中,如果不想显示非交互方式的控件,可以通过设置其 OLE 容器控件的 Visible 属性为“假”(.F.),或者设置它的 Left 属性为负值(如 -100),将其移到屏幕的可视区域以外来实现。另外的方法是将该控件添加到一个不可见的表单上(就是说,该表单的 Show 方法程序从未被调用)。无论怎样,您仍然可以调用该控件的方法程序,如同控件是可见的一样。

访问动态链接库

如果需要调用的函数在某 dll 中,可以链接该库,再调用该函数。在调用一个 dll 函数之前,必须了解该函数的调用协议,包括函数的名称,参数的数目和类型以及返回值类型。

在 Visual FoxPro 中,只能使用为三十二位环境编写的 dll。但是,如果需要访问一个十六位的 dll,可以使用 Foxtools.fll 中合适的函数来实现。有关详细内容,请参阅“帮助”中的 Foxtools (Foxtools.hlp) 主题。

若要调用一个 dll 函数

  1. 使用 DECLARE - DLL 命令注册 DLL 函数,函数的名称区分大小写。

    注意 如果指定 WIN32API 为库名称,Visual FoxPro 将在 Kernel32.dll、Gdi32.dll、User32.dll、Mpr.dll 和 Advapi32.dll 中查找被调用的 32 位 Windows DLL 函数。

  2. 象调用其它 Visual FoxPro 函数一样调用 DLL 函数。

例如,下面的程序将注册 Windows USER 系统中 DLL 库里的 GetActiveWindow( ) 函数,该函数将显示 Visual FoxPro 主窗口的句柄。GetActiveWindow( ) 无参数,但返回一个一位整数:

DECLARE INTEGER GetActiveWindow IN win32api
MESSAGEBOX(STR( GetActiveWindow() ) )

包含所要注册函数的 DLLl 必须存放在默认目录中,如 Windows 或 System 目录,或者在 DOS 路径中。

如果要调用的函数和 Visual FoxPro 中已存在的函数(本地函数或者前面声明的 dll 函数)重名,您可以为重复的函数名字取一个别名,然后用别名来调用它。

DECLARE INTEGER GetActiveWindow IN win32api AS GetWinHndl
MESSAGEBOX(STR( GetWinHndl() ) )

在退出 Visual FoxPro 之前,所链接的 DLL 函数一直保持有效,因此,在每个工作期中只须声明一次。如果不再想调用 DLL 中的函数,可以执行 CLEAR DLLS 命令将其从内存中清除以释放资源。

注释 执行 CLEAR dllS 命令时,将从内存中清除所有已声明的 dll 函数。

向一个 DLL 传递参数

在注册一个 dll 函数时,必须指定参数的数目和类型。默认情况下,数据按值传递。也可以在参数前面添加符号 (@) 来强制参数按引用传递。

一般,dll 函数遵循 C 语言中的数据类型规则,这和 Visual FoxPro 中的规定有所不同。例如,dll 函数不支持日期和货币数据类型。如果传递给 DLL 函数的数据不被该函数支持,那么在传递该数据之前,必须将其转换为适当的数据类型。例如,如下命令可以将日期类型转换为数值型的 Julian 格式:

cDate = sys(11, date())
nDate = val( cDate )

某些 dll 函数需要比较复杂的参数,如结构或数组。如果函数需要一个指向结构的指针,那么必须确定该结构的内部布局,然后在向 DLL 函数传递之前或从 DLL 函数返回之后,转换为 Visual FoxPro 中的字符串。例如,Windows 系统函数 GetSystemTime( ) 需要一个指向结构的指针,该结构包含八个字或 16 位的无符号整数,分别代表年、月、日等,其结构定义如下:

typedef struct _SYSTEMTIME { 
   WORD wYear ;
   WORD wMonth ;
   WORD wDayOfWeek ;
   WORD wDay ;
   WORD wHour ;
   WORD wMinute ;
   WORD wSecond ;
   WORD wMilliseconds ;
} SYSTEMTIME

为了在 Visual FoxPro 和 GetSystemTime( ) 函数之间传递数据,首先必须创建一个 40 字节的字符串缓冲区(初始时其内容为空格),然后把这个字符串的地址作为参数传递给函数,让函数向这个字符串填写要返回的数据。返回之后,必须以两个字符为一单元,进行分析,然后提取出结构的各个部分。下面的语句描述了如何提取该结构的三个字段:

DECLARE INTEGER GetSystemTime IN win32api STRING @
cBuff=SPACE(40)
=GetSystemTime(@cBuff)

tYear = ALLTRIM(STR(ASC(SUBSTR(cBuff,2)) *  ; 
   256 + ASC(SUBSTR(cBuff,1))))
tMonth = ALLTRIM(STR(ASC(SUBSTR(cBuff,4)) * ; 
   256 + ASC(SUBSTR(cBuff,3))))
tDOW = ALLTRIM(STR(ASC(SUBSTR(cBuff,6)) * ; 
   256 + ASC(SUBSTR(cBuff,5))))

如果要获得更多的信息,请运行 Visual Studio …\Samples\Vfp98\Solution\Winapi 中的示例表单 Systime.scx。有关如何向 DLL 函数传递参数的其他实例,请参阅 Visual Studio …\Samples\Vfp98\Classes 中的程序 Registry.prg。

如果在 Visual FoxPro 中使用的数据是数组,则在传递给 DLL 函数之前,必须遍历该数组,把它联结到一个用 C 语言样式的数组的单个字符串。如果 Windows 函数需要 16 位或 32 位的值,则在链接为字符串之前,必须将该值转换为等价的十六进制的形式。在传递包含数组数据的字符串时, Visual FoxPro 将该串的地址传送给 DLL,该 DLL 将其作为数组处理。实例程序请参阅 Visual Studio …\Samples\Vfp98\Solution\Winapi 中的示例表单 Syscolor.scx。

访问 Visual FoxPro 库

象 dll 一样,Visual FoxPro 库(.fll 文件)包含了可调用的函数。因为 .fll 文件是专门为 Visual FoxPro 内部调用建立的,因而更容易和 .fll 进行数据传递。

如果要使用一个 Visual FoxPro 库,首先指定 .fll 文件的名称,然后调用此函数。和注册 dll 函数不同,您无须单独注册 fll 文件中的每个函数,也没有必要指定该函数所需参数和数据类型。

注释 如果要使用一个早期版本的 Visual FoxPro .fll 库,必须在 Visual FoxPro 中重新编译。

若要调用一个 .fll 函数

  1. 使用 SET LIBRARY 命令注册 .fll 函数。

  2. 象调用其它函数一样调用库中的任意函数。

例如,下面的程序从 Foxtools.fll 库中调用一个函数,来指定 C:驱动器的类型:

SET LIBRARY TO "C:\Program Files\Microsoft ;
Visual Studio\Vfp98\Foxtools.fll"
? DriveType("C:")

如果所注册的 .fll 文件不只一个,请在 SET LIBRARY 命令中包含 ADDITIVE 关键字。否则,前面注册的 .fll 文件将被清除,被最近注册的文件所代替。

如果函数名称和 Visual FoxPro 中已存在的函数名称冲突,则最后定义的函数优先;如果链接库中的函数名称和 Visual FoxPro 中的内部函数名称冲突,Visual FoxPro 的内部函数优先。

在退出 Visual FoxPro 之前,.fll 文件中的函数一直保持有效,因此在每个工作期中只需注册一次。如果不想再使用 .fll 文件中的函数,可以使用 RELEASE LIBRARYRELEASE ALL 或者 SET LIBRARY TO 命令将其从内存中清除,以释放资源。