如果 Visual FoxPro 中没有包含您的应用程序需要的功能,可以对其进行扩展,使用一个 32 位编译器,例如 Microsoft Visual C++® 4.0 或更高版本,创建一个 ActiveX 控件(.ocx 文件)或专用于 Visual FoxPro 的库文件(.fll 文件)。本章将讨论这两种程序。
注释 如果使用 Visual C++ version 2.x 来开发 ActiveX 控件,需要 Control Development Kit。本章假定您使用的是 Visual C++ 4.0 版本。
有关使用 ActiveX 控件或 FLL 的详细内容,请参阅第二十七章“使用外部库扩展 Visual FoxPro 的功能”。
本章内容要点∶
可以扩展 Visual FoxPro 的功能,方法是用 C 或 C++ 创建程序,此程序能完成您的应用程要求的任务。例如,如果应用程序需要直接访问 Windows 工具,您可以编写一个 C 或 C++ 程序调用 Windows API,然后向 Visual FoxPro 返回信息。
若要访问 Visual FoxPro API,可以创建三种类型的程序∶
每种类型的程序都各有优点。ActiveX 控件的优点∶
COM 对象的优点:
另一方面,一个 .fll 库∶
注释 如果想使用一个比 Visual FoxPro 5.0 更早版本的 Visual FoxPro .fll 库,必须在 Visual FoxPro 6.0 中重新编译该库。
可以使用 Microsoft Visual C++ 5.0 提供的“ActiveX Template Library”创建 COM 对象。有关使用 Visual C++ 5.0 创建 COM 对象的详细内容,请在 MSDN 库中搜索“ATL”。
与其他类似控件一样,您也可以创建专用于 Visual FoxPro 的 ActiveX 控件。大多数 C++ 编译器允许创建该控件的框架,也可以用 Microsoft Visual Basic® Control Creation 5.0 版创建。
以下章节将介绍使用 Microsoft Visual C++ 创建用于 Visual FoxPro 中的 ActiveX 控件的步骤。
若要为 ActiveX 控件创建一个项目
向导结束时,您可以立即生成 ActiveX 控件。但是,还需要定义该控件的属性和方法程序。
若要为 ActiveX 控件添加属性和方法程序
例如,若要创建一个 Version 属性,以整数形式返回一个 .ocx 文件的版本号(例如 101),您可以使用返回的长整型创建该属性,并添加如下的代码∶
#define VERSION 101 long CPyCtrl::GetVersion() { //
在此设置版本号return VERSION;
}
由于通常版本号是只读的,您不需创建 SetVersion( ) 函数。
实际上,FLL 库就是调用 Visual FoxPro API 的 DLL,因此您可以在开发环境中根据以下创建 DLL 的步骤创建一个 FLL 库。
若要为 FLL 库创建一个项目
在创建了 DLL 的基本结构以后,可以添加需要从 Visual FoxPro 中调用的函数。下面几节简单介绍使用 C 和 C++ 创建函数的方法。
要创建的每个函数库都有相同的基本结构。借助库结构模板,只需填充适用于您的库例程的部分即可。
Visual FoxPro 库模板有五个要素:
#include
语句可用下列模板创建 C 语言编写的库。
#include <Pro_exh.h> void Internal_Name(ParamBlk *parm) { //
该处填写函数代码。 }FoxInfo myFoxInfo[] = {
{"FUNC_NAME", (FPFI) Internal_Name, 0, ""},
};
FoxTable _FoxTable = {
(FoxTable *)0, sizeof(myFoxInfo)/sizeof(FoxInfo), myFoxInfo
};
对于 C++ 例程,可用下列模板。与 C 语言模板不同的是它把 FoxTable 结构声明为外部的。
#include <Pro_exh.h> void Internal_Name(ParamBlk *parm) { //
该处填写函数代码。 }FoxInfo myFoxInfo[] = {
{"FUNC_NAME", (FPFI) Internal_Name, 0, ""},
};
extern "C" {
FoxTable _FoxTable = {
(FoxTable *)0, sizeof(myFoxInfo)/sizeof(FoxInfo), myFoxInfo
};
}
若要使用头文件并创建一个已编译库,需要∶
在安装 Visual FoxPro 时,这两个文件都安装在 API 子目录下。
函数定义返回 void
,并需要下列参数:ParamBlk *parm
。ParamBlk
结构将在本章稍后的“传递和接收参数”中讨论。
除了以上文件之外,Visual FoxPro 库还有其他两个必需的要素是 FoxInfo 和 FoxTable 结构。
库函数通过 FoxInfo 结构与 Visual FoxPro 进行通信。Visual FoxPro 可以通过这个结构确定函数名、参数的个数和类型。FoxTable 结构是一个链接的列表,保持对 FoxInfo 结构的跟踪。有关 FoxInfo 和 FoxTable 结构的定义,请参阅 Visual FoxPro 在 API 目录中的 Pro_ext.h 文件。
FoxInfo 结构用于在 Visual FoxPro 和库之间传递函数名和参数描述信息。一个通用的 FoxInfo 结构如下所示:
FoxInfo arrayname[ ] = {
{funcName1, FPFI function1, parmCount1, parmTypes1}
{funcName2, FPFI function2, parmCount2, parmTypes2}
. . .
{funcNameN, FPFI functionN, parmCountN, parmTypesN}
};
其中的各个占位符定义如下:
arrayname
一个数据类型为 FoxInfo 的变量。注意在该数组中可以包含多个 FoxInfo 结构行。
funcName
供 Visual FoxPro 用户调用时使用的函数名称(大写字母并且不得超过 10 个字符)。
function
您的 C 语言例程的地址。这是函数的真正名称(区分大小写)。
parmCount
指定在 parmTypes 字符串中描述的参数个数,或者下列标志值之一。
值 | 说明 |
INTERNAL | 表示该函数不能直接从 Visual FoxPro 调用。 |
CALLONLOAD | 表示在加载库时调用例程。CALLONLOAD 不能调用那些返回结果给 Visual FoxPro 的函数。 |
CALLONUNLOAD | 表示该例程在卸载库时,或者发出 Visual FoxPro 的 QUIT 命令时被调用。CALLONUNLOAD 不能调用那些返回结果给 Visual FoxPro 的函数。 |
parmTypes
描述每个参数的数据类型。下表列出 parmTypes 的有效值。
值 | 说明 |
|
无参数 |
|
能传递任意类型。在函数体中,必须检查传递过来的参数类型。 |
|
字符型参数 |
|
日期型参数 |
|
整型参数 |
|
逻辑型参数 |
|
数值型参数 |
|
引用 |
|
日期时间型参数 |
|
货币型参数 |
|
对象类型参数 |
应该为每个传递给库的参数指定一个类型值。例如,若创建的函数接受一个字符和一个数值参数,那么可用“CN”作为 parmType。
注释 若要标明一个参数是可选的,请在前面加上一个句点“.”。只有后面的参数才能省略。
下列 FoxInfo
结构定义了只有一个函数的库,内部叫“dates
”,外部用“DATES
”来访问,并且接受一个字符类型的参数:
FoxInfo myFoxInfo[] = {
{ "DATES", (FPFI) dates, 1, "C" }
};
编译具有这个 FoxInfo 结构的库,并且在 Visual FoxPro 中使用 SET LIBRARY TO 命令加载该库,此后便可在 FoxPro 中用下列代码行来调用该函数:
=DATES("01/01/95")
FoxTable 是一个链接的列表结构,保持对给定库中所有 FoxInfo 结构的跟踪:
FoxTable _FoxTable = {nextLibrary, infoCount,infoPtr};
其中各占位符定义如下:
nextLibrary
Visual FoxPro 内部使用的一个指针,必须初始化为 0。
infoCount
在本库中定义的 Visual FoxPro 外部例程数目。
infoPtr
FoxInfo 结构中第一个数组元素的地址。这个名称必须与 FoxInfo 语句中列出的数组名相匹配。
下面是 FoxTable 语句的一个示例。如果 FoxInfo 数组名是 myFoxInfo
,那么您不必更改这个语句,直接就可以使用。
FoxTable _FoxTable = {
(FoxTable *) 0,
sizeof( myFoxInfo) / sizeof( FoxInfo ),
myFoxInfo
};
为了将程序与 Visual FoxPro 集成,可以调用 Visual FoxPro API 例程。这些 API 例程是可以从任何 C 或 C++ 程序中调用的函数,包括 .ocx 或 .fll 文件,它们使您可以访问变量,管理数据库操作,并且完成很多其他 Visual FoxPro 特定的任务。
下表列出了 Visual FoxPro 中可用的 API 调用的常用分类。有关每个 API 函数的详细内容,请参阅“API 库例程 A-Z”或“API 库例程的分类”。
若要使用 Visual FoxPro API 例程,必须包含 Visual FoxPro API 目录下的 Pro_ext.h 文件。该文件包含了允许您与 Visual FoxPro 共享信息的函数和结构的原型。
如果想编写一个 ActiveX 控件,也必须添加调用以初始化 API 和清除 API。
若要向自己的 ActiveX 对象添加 Visual FoxPro API 例程
#INCLUDE
包含 Pro_ext.h 文件,以及其他必需的头文件。_OCXAPI(AfxGetInstanceHandle(),DLL_PROCESS_ATTACH);
_OCXAPI(AfxGetInstanceHandle(),DLL_PROCESS_DETACH);
有关包含调用 Visual FoxPro API 的 .ocx 文件的示例,请参阅 Foxtlib.ocx。有关包含调用 Visual FoxPro API 的 .fll 库的示例,请参阅 Vfp98\Api\Samples 目录下具有 C 扩展名的示例程序∶ EVENT.C,HELLO.C 等等。
如果在您的 ActiveX 控件、COM 对象或FLL 库中调用 Visual FoxPro API ,则包含这个调用的代码与其他应用程序是不兼容的。因此,需要在程序中进行或多或少的测试,来确定该对象是否从 Visual FoxPro 中调用。
例如,如果使用 Microsoft Foundation Classes 创建了一个 ActiveX 控件,您可以改变该控件的结构代码来包含一个测试,如果一个不是 Visual FoxPro 的程序调用了该控件,则警告用户∶
if (!_OCXAPI(AfxGetInstanceHandle(),DLL_PROCESS_ATTACH)) { ::MessageBox(0,"This OCX can only be hosted by Visual Foxpro","",0); //
当宿主程序(使用程序)不是VFP
时,此处您可以书写需要的程序://
您可能拒绝载入或//
可能需要设置属性//
说明宿主程序不是VFP
,控件将使用其他方式//
来达到目的。 }
如果您使用 Microsoft ActiveX Template Library 创建 ActiveX 控件,请使用下列代码:
if (!_OCXAPI(_Module.GetModuleInstance(),DLL_PROCESS_ATTACH)) { ::MessageBox(0,"This OCX can only be hosted by Visual Foxpro","",0); //
当宿主程序(使用程序)不是VFP
时,此处您可以书写需要的程序://
您可能拒绝载入或//
可能需要设置属性//
说明宿主程序不是VFP
,控件将使用其他方式//
来达到目的。 }
在本示例中,控件并不退出,而是在通知用户后继续运行。您选择的方法取决于您想怎样使用该控件。例如,如果检测到该控件正在 Visual FoxPro 之外被使用,可以先设置一个标志,然后在控件中每次调用 Visual FoxPro API 的地方测试这个标志。如果标志表明该控件正在 Visual FoxPro 之外运行,可以在调用 API 前利用条件分支去选择其它方式完成相同的任务。
从 Visual FoxPro 中调用您的程序时,它可以接收参数。例如,当调用其中的一个方法程序时,一个 ActiveX 控件可以接收参数。类似地,一个 Visual FoxPro 程序可以调用您的 FLL 库中的一个函数,并且向它传递参数。
Visual FoxPro 可以按值或按引用向您的应用程序传递参数。在默认情况下,参数遵守 SET UDFPARMS 的设置。其他变量(例如数组或字段)和表达式按值传递。
若要想使一个参数强制按引用传递,请在变量引用前加上 @ 操作符。要想使一个参数强制按值传递,请将它放在括号里。
注释 在 Visual FoxPro 中,单个的数组元素都是按值传递的。当 SET UDFPARMS 设置为 VALUE,并且没有指定数组元素时,数组名本身就指向了数组的第一个元素(除非有前缀 @)。
由于 ActiveX 控件和 COM 对象是标准的 Windows 程序,所以从 Visual FoxPro 和您的程序传递参数不需特别的机制。可以象从任何 C 或 C++ 程序接收参数那样编写程序。
相反,FLL 库中的函数使用 FoxInfo 结构来接收来自 Visual FoxPro 的数据。FoxInfo 结构列出了库函数以及它们所需参数的个数和类型。例如,下面的 FoxInfo
结构属于一个库,该库中包含了一个函数,这个函数内部称之为 dates
,该函数接收一个字符型参数∶
FoxInfo myFoxInfo[] = {
{ "DATES", (FPFI) dates, 1, "C" }
};
在您的库里定义的函数实际上只接收了一个参数,也就是参数块的指针。这个参数块是在 ParamBlk
结构中定义的,它包含了从 Visual FoxPro 函数调用中传递来的所有参数信息。函数声明采用如下方式∶
void function_name(ParamBlk *parm)
例如,dates
函数的定义是∶
void dates(ParamBlk *parm)
ParamBlk
结构包含了一个整数,它代表了参数的个数,在它后面紧接着一个参数联合数组。该结构定义包含在 Pro_ext.h 中∶
/*
库函数的参数列表。*/
typedef struct {
short int pCount; /*
传递的参数个数*/
Parameter p[1]; /* pCount
个参数*/
} ParamBlk;
在 ParamBlk
结构中包含的 Parameter
typedef 是一个 Value 结构和 Locator 结构的联合。按值传递时按 Value 结构处理;按引用传递时,则按 Locator 结构处理。在 Visual FoxPro 中调用您的函数时,使用这些结构来访问传递给函数的参数。
下面的信息是从 Pro_exh.h
文件中提取出来的,显示了 Parameter
类型的定义:
/*
给库函数的参数。*/
typedef union {
Value val;
Locator loc;
} Parameter;
如果按值向您的函数传递一个参数,则使用 Value
结构来访问这个参数。下面的 Value
结构定义是从 Pro_ext.h 文件中提取出来的∶
//
一个表达式的值。Typedef struct {
char ev_type;
char ev_padding;
short ev_width;
unsigned ev_length;
long ev_long;
double ev_real;
CCY ev_currency;
MHANDLE ev_handle;
ULONG ev_object;
} Value;
下表说明了对于不同的数据类型,您可以在 Value
结构中传递和接收值。只有数据类型后面所列的结构域才能用于这种数据类型。
不同数据类型的 Value 结构内容
数据类型 | 结构域 | 值 |
字符型 |
|
‘C’ |
|
字符串长度 | |
|
指向字符串的 MHANDLE | |
数值型 |
|
‘N’ |
|
显示宽度 | |
|
小数位 | |
|
双精度 | |
整型 |
|
‘I’ |
|
显示宽度 | |
|
长整型 | |
日期型 |
|
‘D’ |
|
日期1 | |
日期时间型 |
|
‘T’ |
|
日期 + (秒/86400.0) | |
货币型 |
|
‘Y’ |
|
显示宽度 | |
|
货币值2 | |
逻辑型 |
|
‘L’ |
|
0 或 1 | |
备注型 |
|
‘M’ |
|
FCHAN | |
|
备注字段的长度 | |
|
备注字段的偏移量 | |
通用型 |
|
‘G’ |
|
FCHAN | |
|
通用字段的长度 | |
|
通用字段的偏移量 | |
对象型 |
|
‘O’ |
|
对象识别符 | |
Null |
|
‘0’ |
|
数据类型 |
1 日期以双精度浮点公历算法表示,使用“Collected Algorithms of the ACM”的“算法 199”来计算。
2 货币值为长整型值,在最后四位数字前有一个隐含的小数点。
注释 ev_length
是字符串长度的真正指示器。字符串不能以 null 作为终止符,因为字符串可以包含内嵌的 null 字符。
使用 Locator
结构可以处理按引用传递的参数。下列的 Locator 结构定义是从 Pro_ext.h 文件中抽取出来的。
typedef struct { char l_type; short l_where, /*
数据库编号,如果是内存变量则为-1
。*/
l_NTI, /*
变量名称表的偏移量。*/
l_offset, /*
在数据库中的索引。*/
l_subs, /* #
指定的下标0 <= x <= 2 */
l_sub1, l_sub2; /*
下标整型值。*/
} Locator;
下表说明了 Locator 结构中的域。
Locator 域 | 域的用途 |
l_type | 'R' |
l_where | 包含此域的表的编号,-1 代表一个内存变量。 |
l_NTI | 名称表索引。Visual FoxPro 内部使用。 |
l_offset | 表中域的编号。Visual FoxPro 内部使用。 |
l_subs | 只用于内存变量,下标的数目 (0 - 2)。 |
l_sub1 | 只用于内存变量,若 l_subs 非 0,则代表第一个下标。 |
l_sub2 | 只用于内存变量,若 l_subs 为 2,则代表第二个下标。 |
注释 最好检查 ev_type
中的参数类型,因为这样可以确定要通过 Value 结构访问的域是否正确。
下列示例使用 _StrCpy( )
返回一个字符类型数据给 Visual FoxPro,该类型的数据是两个字符型参数的连接。必须注意,虽然对每个参数的 Value 结构使用内存句柄进行连接操作,但是对该内存分配的更改并不影响按值传递的 Visual FoxPro 参数。
有关使用 Locator 结构管理按引用传递的参数的示例,请参阅本章稍后的“从一个 FLL 库中返回值”。
#include <Pro_exh.h> Example(ParamBlk *parm) { //
使用#define
可以使paramBlk
结构易于管理#define p0 (parm->p[0].val)
#define p1 (parm->p[1].val)
//
确保有足够的内存if (!_SetHandSize(p0.ev_handle, p0.ev_length + p1.ev_length))
_Error(182); // "
内存不足"
//
锁定句柄_HLock(p0.ev_handle);
_HLock(p1.ev_handle);
//
将句柄转换为指针并确保字符串是由null
作终止符((char *)_HandToPtr(p0.ev_handle))[p0.ev_length] = '\0';
((char *)_HandToPtr(p1.ev_handle))[p1.ev_length] = '\0';
//
用API
函数_StrCpy
连接字符串_StrCpy((char *)_HandToPtr(p0.ev_handle) + p0.ev_length,
_HandToPtr(p1.ev_handle));
//
向Visual FoxPro
返回已连接的字符串_RetChar(_HandToPtr(p0.ev_handle));
//
解除句柄锁定_HUnLock(p0.ev_handle);
_HUnLock(p1.ev_handle);
}
FoxInfo myFoxInfo[] = {
{"STRCAT", Example, 2, "CC"},
};
FoxTable _FoxTable = {
(FoxTable *) 0, sizeof(myFoxInfo)/sizeof(FoxInfo), myFoxInfo
};
用于从程序向 Visual FoxPro 返回值的方法,取决于创建的是 ActiveX 控件还是 FLL 库。
若要从一个 ActiveX 控件向 Visual FoxPro 返回值,请使用该控件的 RETURN 语句,返回单个的值,如下例所示∶
#define VERSION 101 //
此处是其他的代码long CPyCtrl::GetVersion()
{
//
在变量fVersion
中设置版本号。return VERSION;
}
若要从一个 FLL 库中返回值,请使用 API 函数,而不要使用 C 或 C++ 本身的命令。下表所列的函数允许您向 Visual FoxPro 返回值。
注释 不要使用下列 API 函数从一个 OCX 文件返回值;请使用 RETURN 语句。API 返回的函数只能用于 FLL 库中。
函数 | 说明 |
_RetChar(char *string) | 将函数的返回值设置为一个以 null 值结尾的字符串。 |
_RetCurrency(CCY cval, int width) | 将函数的返回值设置为一个货币值。 |
_RetDateStr(char *string) | 将函数的返回值设置为一个日期值。该日期按 mm/dd/yy[yy] 格式表示。 |
_RetDateTimeStr(char *string) | 将函数的返回值设置为一个日期时间值。该日期时间按 mm/dd/yy[yy] hh:mm:ss 格式表示。 |
_RetFloat(double flt, int width, int dec) | 将函数的返回值设置为一个浮点值。 |
_RetInt(long ival, int width) | 将函数的返回值设置为一个整型值。 |
_RetLogical(int flag) | 将函数的返回值设置为一个逻辑值。零被认为是 FALSE。任何非零值被认为是 TRUE。 |
_RetVal(Value *val) | 传递一个完整的 Visual FoxPro Value 结构;除了备注字段,任何 Visual FoxPro 数据类型都可以返回。若要返回一个包含内嵌零字符的字符串,或者返回一个 .NULL. 值,您必须调用 _RetVal( )。 |
注释 若要返回一个对象数据类型的值,请使用 _RetVal() 函数,此函数将填充在 Value 结构的 ev_object
域中。
下面的示例 Sum
接收对表中一个数值字段的引用,并用 _RetFloat
返回字段值的总和:
#include <Pro_exh.h> Sum(ParamBlk *parm) { //
声明变量double tot = 0, rec_cnt;
int i = 0, workarea = -1; // -1
是当前工作区Value val;
//
记录指针指向表头_DBRewind(workarea);
//
获得记录数rec_cnt = _DBRecCount(workarea);
//
循环遍历表for(i = 0; i < rec_cnt; i++)
{
//
将字段的值放入Value
结构_Load(&parm->p[0].loc, &val);
//
求和tot += val.ev_real;
//
指针指向工作区的下一个记录_DBSkip(workarea, 1);
}
//
将总计值返回给Visual FoxPro
_RetFloat(tot, 10, 4);
}
// Sum
函数接收一个引用参数FoxInfo myFoxInfo[] = {
{"SUM", Sum, 1,"R"}
};
FoxTable _FoxTable = {
(FoxTable *) 0, sizeof(myFoxInfo)/sizeof(FoxInfo), myFoxInfo
};
假定在当前打开的表中有一个叫 amount
的数值型字段,可在 Visual FoxPro 程序中用下列代码调用该函数;
? SUM(@amount)
Visual FoxPro API 例程经常需要特定 Visual FoxPro 数据结构的参数。下面几节提供了 Visual FoxPro 数据类型和附加数据结构的列表。有关类型和结构的实际定义,请参阅 Pro_ext.h 文件。
下列数据类型可在 Visual FoxPro API 例程中使用。
数据类型 | 说明 |
|
用于编辑窗口中打开的文件,指明文件中某一行的行号,首行为 1。 |
|
用于编辑窗口中打开的文件,指明文件中某个字符的偏移位置。在文件或备注中,首字符的偏移位置为0。 |
|
文件通道。每个由 Visual FoxPro 打开的文件,或者通过 API 用 _FCreate( ) 和 _FOpen( ) 打开的文件,都分配一个 FCHAN。 |
|
指向一个返回整数值的函数的 32 位指针。 |
|
为菜单中某一命令指定唯一标识。 |
|
指定给一个菜单的唯一标识。 |
|
一个唯一标识,每一个由 Visual FoxPro 分配的内存块,或通过 API 用 _AllocHand( ) 分配的内存块都具有这样一个标识。可以使用 _HandToPtr( ) 放弃对此指针的引用 |
|
命名表索引。每个内存变量和表字段的名称在该表中都有一项。 |
|
窗口句柄。每一个由 Visual FoxPro 打开的窗口,或者通过 API 用 _WOpen( ) 打开的窗口都有这样一个唯一的标识。 |
注释 因为 FAR 指针对 32 位编译器不适用,请在 Pro_ext.h 文件中的 #define
语句中将 FAR
,_far
和 __far
作为 null 值重新设置。
下表列出了在 Visual FoxPro API 库中使用的主要数据结构。
结构 | 说明 |
EventRec | 此结构用于描述在某个给定的时刻,系统正在进行的操作。 |
FoxInfo | 在 FLL 库中使用,用于 Visual FoxPro 与应用程序之间的通讯,不能在 .ocx 文件中使用。已在本章前面“使用 FoxInfo 和 FoxTable 结构”中讨论过。 |
FoxTable | 在 FLL 库中使用,用于 Visual FoxPro 与应用程序之间的通讯,不能在 .ocx 文件中使用。已在本章前面“使用 FoxInfo 和 FoxTable 结构”中讨论过。 |
Locator | 用于访问参数值 (FLL)、Visual FoxPro 变量或字段(FLL 和 OCX)的结构。 |
ParamBlk | 在 FLL 库中使用,用于 Visual FoxPro 与应用程序之间的通讯,不能在 .ocx 文件中使用。已在本章前面“使用 FoxInfo 和 FoxTable 结构”中讨论过。 |
Parameter | 在 FLL 库中使用,用于 Visual FoxPro 与应用程序之间的通讯,不能在 .ocx 文件中使用。已在本章前面“使用 FoxInfo 和 FoxTable 结构”中讨论过。 |
Point | 此结构定义了屏幕上一个点的水平和垂直坐标。坐标按行号和列号指定。 |
Rect | 此结构定义了屏幕上一个矩形的坐标。该矩形的左上角由( top, left) 定义,右下角由( bottom-1, right-1) 定义。坐标按行号和列号指定。 |
Value | 用于访问参数值 (FLL)、Visual FoxPro 变量或字段(FLL 和 OCX)的结构。 |
可以在您的 ActiveX 控件或 FLL 函数中访问 Visual FoxPro 变量或字段,进行读取或设置。另外,还可以创建可供 Visual FoxPro 访问的新变量。
变量和字段保存在 Visual FoxPro 的一个命名表中,它是一个数组,包含了所有当前定义的变量和字段的名称。可以使用命名表索引 (NTI) 来访问数组中的单个元素。有一个特殊的 API 函数,_NameTableIndex( ),根据您提供的名称返回一个已存在的变量和字段的索引。当确定了一个给定变量的 NTI 时,您可以使用 _Load( ) API 函数读取它,或者使用 _Store( ) 函数设置它。若要创建一个新变量,可以调用 API 函数 _NewVar( )。
若要访问 Visual FoxPro 变量或字段,可以使用在 Pro_ext.h 中定义的 Value 和 Locator 结构。如果创建一个 FLL 库,您可以使用访问传递给函数的参数的相同技术。有关 Value 和 Locator 结构的详细内容,请参阅本章稍前的“传递和接收参数”。
下面的示例是从 Vfp98\Api\Samples\Foxtlib 目录中的 Foxtlibctl.cpp 程序中提取的,它演示了在一个 ActiveX 控件中,如何使用 Value 和 Locator 结构来访问 Visual FoxPro 变量∶
long CFoxtlibCtrl::TLGetTypeAttr(long pTypeInfo, LPCTSTR szArrName)
{int nResult = 1;
TYPEATTR *lpTypeAttr;
Locator loc;
Value val;
OLECHAR szGuid[128];
char *szBuff;
__try {
if (_FindVar(_NameTableIndex(( char *)szArrName),-1,&loc)) {
((ITypeInfo *)pTypeInfo)->GetTypeAttr(&lpTypeAttr);
if (_ALen(loc.l_NTI, AL_ELEMENTS) < 16) {
_Error(631); //Array argument not of proper size.
}
//1 = Guid
StringFromGUID2(lpTypeAttr->guid, (LPOLESTR )&szGuid,sizeof(szGuid));
OLEOleToAnsiString(szGuid,&szBuff);
val.ev_type = 'C';
val.ev_length = strlen(szBuff);
val.ev_handle = _AllocHand(val.ev_length);
_HLock(val.ev_handle);
_MemMove((char *) _HandToPtr( val.ev_handle ), szBuff, val.ev_length);
OLEFreeString((void **)&szBuff);
_HUnLock(val.ev_handle);
loc.l_sub1 = 1;
_Store(&loc,&val);
_FreeHand(val.ev_handle);
//2 = LCID
loc.l_sub1 = 2;
val.ev_type = 'I';
val.ev_long = lpTypeAttr->lcid;
_Store(&loc,&val);
// code for values 3 - 16 here
((ITypeInfo *)pTypeInfo) -> ReleaseTypeAttr(lpTypeAttr);
}
} __except (EXCEPTION_EXECUTE_HANDLER) {
nResult = 0;
}
return nResult;
Visual FoxPro API 提供了对 Visual FoxPro 动态内存管理器的直接访问。对于请求内存分配的 API 例程,返回一个内存标识(或称作句柄)。Visual FoxPro 使用句柄而不是指针分段加载结构,这样可以提高管理内存的效率。
注释 本节所述的使用 Visual FoxPro API 管理内存的技术,同时适用于 ActiveX 控件和 FLL 库。
这里句柄是指一个内存句柄,本质上是一个指针数组的索引。数组中的这些指针指向由 Visual FoxPro 掌握的各内存块。在 API 中几乎所有对内存的引用都通过句柄来实现, 而不是传统的 C 指针。
若要在库中分配并使用内存
注释 为了避免破坏备注文件,在调用 _AllocMemo( ) 之前不要对备注文件进行写操作。
为了确定分配内存的地址,API 例程必须调用 _HandToPtr( ) 函数把句柄转换为指针。即使当 Visual FoxPro 内存管理器需要重新组织内存,以便后来的内存请求能获得更多的连续内存时,句柄也保持不变。同时还提供了增加、减少、释放和锁定内存分配的例程。
在创建外部例程时,应尽量减少内存使用。如果创建一个动态分配内存的外部例程,则请尽量少用内存。长时间锁定一个大的内存块时要特别小心。在不再需要锁定内存时,请记住使用 _HUnLock( ) 解锁内存句柄,因为锁定内存句柄会对 Visual FoxPro 的性能有不利的影响。
注意 过多地使用动态内存会侵占 Visual FoxPro 为缓冲区、窗口、菜单及其他系统资源分配的内存,降低 Visual FoxPro 的性能,因为为 API 例程分配的内存由 Visual FoxPro 内存管理器进行管理。分配大量的句柄并且一直保留这些句柄会导致 Visual FoxPro 内存不足并异常终止。
Visual FoxPro 环境没有内存保护。外部 API 例程不能提供有效性检查,而这种检查在一个标准 Visual FoxPro 程序中是固有的。如果内存崩溃,就会得到一些错误信息,比如“跨越句柄”、“内部一致性错误”和“压缩过程出现跨越节点”。
下面的 FLL 库中的函数演示了内存是如何分配的。假定示例中的字符型参数是正确的日期值,下面使用 _RetDateStr( ) 返回一个 Visual FoxPro 日期型值。
#include <Pro_exh.h> void dates(ParamBlk *parm) { MHANDLE mh; char *instring; if ((mh = _AllocHand(parm->p[0].val.ev_length + 1)) == 0) { _Error(182); // "
内存不足"
}
_HLock(parm->p[0].val.ev_handle);
instring = _HandToPtr(parm->p[0].val.ev_handle);
instring[parm->p[0].val.ev_length] = '\0';
_RetDateStr(instring);
_HUnLock(parm->p[0].val.ev_handle);
}
FoxInfo myFoxInfo[] = {
{"DATES", (FPFI) dates, 1, "C"}
};
FoxTable _FoxTable = {
(FoxTable *) 0, sizeof(myFoxInfo)/sizeof(FoxInfo), myFoxInfo
};
您创建的控件和库不具有自己的堆栈。相反,它使用调用程序的堆栈,在这里也就是 Visual FoxPro 的堆栈。您不能控制 Visual FoxPro 堆栈的大小,或者影响一个 ActiveX 或 .fll 文件可用的堆栈空间大小。
在正常情况下,这个差别并不重要。Visual FoxPro 的堆栈通常很大,足以保存在控件和库中需要为其分配内存的自动变量。如果遇到堆栈溢出的问题,可以动态地在堆栈中分配额外的内存。
下面说明了获取句柄和释放句柄的规则:
ev_type =
‘C’)时才创建一个句柄。其他数据类型的值存在于 Value 结构本身,加载一个字符串则把一个 MHANDLE 放在 Value 结构的 ev_handle
中。ParamBlk
中传递给他们的句柄。
注意 在编写一个调用别的函数的外部例程时,要注意遵守所有的规则并检查返回结果。一个有偏差的指针或句柄引用可能会损坏 Visual FoxPro 的内部数据结构,造成立刻的异常终止或者留下隐患,甚至可能丢失数据。
在创建了一个项目之后,您就可以连编并调试它。
在连编之前,需要建立项目的各种设置。有些设置取决于您是想创建控件或库的测试版还是发行版。通常,在对程序的工作状况感到满意前创建程序的测试版,然后再创建一个发行版。
若要指定测试版或发行版
若要建立项目设置
若要保证编译器能找到所需文件
在指定了这些设置之后,可以编译并链接您的程序。
若要编译并链接一个 .ocx 文件
在编译并链接 .ocx 文件时,Visual C++ 自动在创建控件的计算机上注册该控件。如果由于某些原因必须人工注册控件,可以采用下列过程∶
若要注册 ActiveX 控件
- 或者 -
在一个完整的 Visual FoxPro 应用程序中调试一个控件或库,通常比单独地调试控件或库更为困难。一种较好的方法是创建一个简单的测试程序来测试控件或库的操作。
Microsoft Visual C++ 4.0 或更高版本提供一个集成调试环境,可以方便地设置断点并单步调试所有代码。甚至可从 Visual C++ 中运行 Visual FoxPro。
若要启动 Microsoft Visual C++ 的调试功能
例如,输入
C:\Program Files\Microsoft Visual Studio\Vfp98\Vfp6.exe。
有关在 Visual C++ 中调试的详细内容,请参阅 Visual C++ 文档集。
可用任何调试器来调试一个库,只要该调试器能够正确处理嵌套在程序中的 INT 3 (_BreakPoint( ))。只要调试器能够做到下列几点,那么便可用来进行象征性的调试:
若要调试一个库
注释 在发布产品之前,一定要删除调试器中所有指定的断点。