|
楼主 |
发表于 2010-5-11 21:19:50
|
显示全部楼层
编写 FLL 文件时的注意事项:
1 pro_ext.h 和 winapims.lib 是 C++ 与 VFP 通信的桥梁,每一个 FLL 中必须包含这两个文件。
2 库函数通过 FoxInfo 结构与 Visual FoxPro 进行通信。
3 FoxTable 结构是一个链接的列表,用于保持对 FoxInfo 结构的跟踪。
4 每一个要从 VFP 中调用的函数必须是 void 型,这并不是说不能从 C++ 中返回值到 VFP, 而是必须用
winapims.lib 中的内部机制来返回值到 VFP。
5 一个标记为 CALLONLOAD 的函数函数不能返回任何值到 VFP。
6 第一个与 VFP 通信的函数必须在 FoxInfo 结构中有一行定义。要从 VFP 接收参数的 FLL 函数必须通
过 ParamBlk 结构来接收参数。参数的个数及类型在 FoxInfo 结构中定义。
详细情况,请参阅 VFP 6.0 中文帮助手册中的《访问 Visual FoxPro API》一节,内容如下:
访问 Visual FoxPro API
如果 Visual FoxPro 中没有包含您的应用程序需要的功能,可以对其进行扩展,使用一个 32 位编译器,
例如 Microsoft Visual C++? 4.0 或更高版本,创建一个专用于 Visual FoxPro 的库文件(.fll 文件)
。本章将讨论这种程序。
本章内容要点∶
创建一个库
添加 Visual FoxPro API 调用
传递和接收参数
返回值给 Visual FoxPro
向 Visual FoxPro API 函数传递参数
访问 Visual FoxPro 变量和字段
管理内存
创建一个库
可以扩展 Visual FoxPro 的功能,方法是用 C 或 C++ 创建程序,此程序能完成您的应用程要求的任务。
例如,如果应用程序需要直接访问 Windows 工具,您可以编写一个 C 或 C++ 程序调用 Windows API,然
后向 Visual FoxPro 返回信息。
若要访问 Visual FoxPro API,可以创建专用于 Visual FoxPro 的 DLL 文件。因为这种 DLL 文件只能被
Visual FoxPro 调用,习惯上用 .fll 作为扩展名命名。
一个 .fll 库的优点∶
如果用过 Visual FoxPro 的早期版本,您可能很熟悉它。
注释 如果想使用一个比 Visual FoxPro 5.0 更早版本的 Visual FoxPro .fll 库,必须在 Visual
FoxPro 6.0 中重新编译该库。
创建一个基本的 FLL 库
实际上,FLL 库就是调用 Visual FoxPro API 的 DLL,因此您可以在开发环境中根据以下创建 DLL 的步
骤创建一个 FLL 库。
若要为 FLL 库创建一个项目
启动 Microsoft Visual C/C++。
在“File”菜单中,选择“New”。
在“New”对话框中选择“Project Workspace”。
在“Project Workspace”对话框中指定项目的名称。
在“Type”列表中选择“Dynamic-Link Library”。
在创建了 DLL 的基本结构以后,可以添加需要从 Visual FoxPro 中调用的函数。下面几节简单介绍使用
C 和 C++ 创建函数的方法。
建立一个库模板
要创建的每个函数库都有相同的基本结构。借助库结构模板,只需填充适用于您的库例程的部分即可。
Visual FoxPro 库模板有五个要素:
#include 语句
函数定义
函数代码
FoxInfo 结构
FoxTable 结构
C 模板示例
可用下列模板创建 C 语言编写的库。
#include
void Internal_Name(ParamBlk *parm)
{
// 该处填写函数代码。
}
FoxInfo myFoxInfo[] = {
{"FUNC_NAME", (FPFI) Internal_Name, 0, ""},
};
FoxTable _FoxTable = {
(FoxTable *)0, sizeof(myFoxInfo)/sizeof(FoxInfo), myFoxInfo
};
C++ 模板示例
对于 C++ 例程,可用下列模板。与 C 语言模板不同的是它把 FoxTable 结构声明为外部的。
#include
void Internal_Name(ParamBlk *parm)
{
// 该处填写函数代码。
}
FoxInfo myFoxInfo[] = {
{"FUNC_NAME", (FPFI) Internal_Name, 0, ""},
};
extern "C" {
FoxTable _FoxTable = {
(FoxTable *)0, sizeof(myFoxInfo)/sizeof(FoxInfo), myFoxInfo
};
}
使用模板
若要使用头文件并创建一个已编译库,需要∶
头文件 Pro_exe.h。可以打印该文件来查看函数定义、类型定义,以及在 Visual FoxPro API 中使用的结
构。
Winapims.lib 文件。
在安装 Visual FoxPro 时,这两个文件都安装在 API 子目录下。
函数定义返回 void,并需要下列参数:ParamBlk *parm。ParamBlk 结构将在本章稍后的“传递和接收参
数”中讨论。
除了以上文件之外,Visual FoxPro 库还有其他两个必需的要素是 FoxInfo 和 FoxTable 结构。
使用 FoxInfo 和 FoxTable 结构
库函数通过 FoxInfo 结构与 Visual FoxPro 进行通信。Visual FoxPro 可以通过这个结构确定函数名、
参数的个数和类型。FoxTable 结构是一个链接的列表,保持对 FoxInfo 结构的跟踪。有关 FoxInfo 和
FoxTable 结构的定义,请参阅 Visual FoxPro 在 API 目录中的 Pro_ext.h 文件。
FoxInfo 结构
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 的有效值。
值 说明
""
无参数
"?"
能传递任意类型。在函数体中,必须检查传递过来的参数类型。
"C"
字符型参数
"D"
日期型参数
"I"
整型参数
"L"
逻辑型参数
"N"
数值型参数
"R"
引用
"T"
日期时间型参数
"Y"
货币型参数
"O"
对象类型参数
应该为每个传递给库的参数指定一个类型值。例如,若创建的函数接受一个字符和一个数值参数,那么可
用“CN”作为 parmType。
注释 若要标明一个参数是可选的,请在前面加上一个句点“.”。只有后面的参数才能省略。
下列 FoxInfo 结构定义了只有一个函数的库,内部叫“dates”,外部用“DATES”来访问,并且接受一个
字符类型的参数:
FoxInfo myFoxInfo[] = {
{ "DATES", (FPFI) dates, 1, "C" }
};
编译具有这个 FoxInfo 结构的库,并且在 Visual FoxPro 中使用 SET LIBRARY TO 命令加载该库,此后
便可在 FoxPro 中用下列代码行来调用该函数:
=DATES("01/01/95")
FoxTable 结构
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 API 调用
为了将程序与 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 共享信息的函数和结构的原型。
传递和接收参数
从 Visual FoxPro 中调用您的程序时,它可以接收参数。一个 Visual FoxPro 程序可以调用您的 FLL 库
中的一个函数,并且向它传递参数。
Visual FoxPro 可以按值或按引用向您的应用程序传递参数。在默认情况下,参数遵守 SET UDFPARMS 的
设置。其他变量(例如数组或字段)和表达式按值传递。
若要想使一个参数强制按引用传递,请在变量引用前加上 @ 操作符。要想使一个参数强制按值传递,请将
它放在括号里。
注释 在 Visual FoxPro 中,单个的数组元素都是按值传递的。当 SET UDFPARMS 设置为 VALUE,并且没
有指定数组元素时,数组名本身就指向了数组的第一个元素(除非有前缀 @)。
相反,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 结构来访问这个参数。下面的 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 结构中传递和接收值。只有数据类型后面所列的结构域
才能用于这种数据类型。
不同数据类型的 Value 结构内容
数据类型 结构域 值
字符型 ev_type
‘C’
ev_length
字符串长度
ev_handle
指向字符串的 MHANDLE
数值型 ev_type
‘N’
ev_width
显示宽度
ev_length
小数位
ev_real
双精度
整型 ev_type
‘I’
ev_width
显示宽度
ev_long
长整型
日期型 ev_type
‘D’
ev_real
日期1
日期时间型 ev_type
‘T’
ev_real
日期 + (秒/86400.0)
货币型 ev_type
‘Y’
ev_width
显示宽度
ev_currency
货币值2
逻辑型 ev_type
‘L’
ev_length
0 或 1
备注型 ev_type
‘M’
ev_wdith
FCHAN
ev_long
备注字段的长度
ev_real
备注字段的偏移量
通用型 ev_type
‘G’
ev_wdith
FCHAN
ev_long
通用字段的长度
ev_real
通用字段的偏移量
对象型 ev_type
‘O’
ev_object
对象识别符
Null ev_type
‘0’
ev_long
数据类型
1 日期以双精度浮点公历算法表示,使用“Collected Algorithms of the ACM”的“算法 199”来计算。
2 货币值为长整型值,在最后四位数字前有一个隐含的小数点。
注释 ev_length 是字符串长度的真正指示器。字符串不能以 null 作为终止符,因为字符串可以包含内嵌
的 null 字符。
Locator 结构的定义
使用 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 结构中的域。
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 结构访问的域是否正确。
访问 FLL 库中参数的示例
下列示例使用 _StrCpy( ) 返回一个字符类型数据给 Visual FoxPro,该类型的数据是两个字符型参数的
连接。必须注意,虽然对每个参数的 Value 结构使用内存句柄进行连接操作,但是对该内存分配的更改并
不影响按值传递的 Visual FoxPro 参数。
有关使用 Locator 结构管理按引用传递的参数的示例,请参阅本章稍后的“从一个 FLL 库中返回值”。
#include
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
用于从程序向 Visual FoxPro 返回值的方法。
从一个 FLL 库中返回值
若要从一个 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
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 API 例程经常需要特定 Visual FoxPro 数据结构的参数。下面几节提供了 Visual FoxPro
数据类型和附加数据结构的列表。有关类型和结构的实际定义,请参阅 Pro_ext.h 文件。
Visual FoxPro API 数据类型
下列数据类型可在 Visual FoxPro API 例程中使用。
数据类型 说明
EDLINE
用于编辑窗口中打开的文件,指明文件中某一行的行号,首行为 1。
EDPOS
用于编辑窗口中打开的文件,指明文件中某个字符的偏移位置。在文件或备注中,首字符的偏移位置为0
。
FCHAN
文件通道。每个由 Visual FoxPro 打开的文件,或者通过 API 用 _FCreate( ) 和 _FOpen( ) 打开的文
件,都分配一个 FCHAN。
FPFI
指向一个返回整数值的函数的 32 位指针。
ITEMID
为菜单中某一命令指定唯一标识。
MENUID
指定给一个菜单的唯一标识。
MHANDLE
一个唯一标识,每一个由 Visual FoxPro 分配的内存块,或通过 API 用 _AllocHand( ) 分配的内存块
都具有这样一个标识。可以使用 _HandToPtr( ) 放弃对此指针的引用
NTI
命名表索引。每个内存变量和表字段的名称在该表中都有一项。
WHANDLE
窗口句柄。每一个由 Visual FoxPro 打开的窗口,或者通过 API 用 _WOpen( ) 打开的窗口都有这样一
个唯一的标识。
注释 因为 FAR 指针对 32 位编译器不适用,请在 Pro_ext.h 文件中的 #define 语句中将 FAR,_far 和
__far 作为 null 值重新设置。
Visual FoxPro API 数据结构
下表列出了在 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)的结构。
访问 Visual FoxPro 变量和字段
可以在您的 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 结构的详细内
容,请参阅本章稍前的“传递和接收参数”。
管理内存
Visual FoxPro API 提供了对 Visual FoxPro 动态内存管理器的直接访问。对于请求内存分配的 API 例
程,返回一个内存标识(或称作句柄)。Visual FoxPro 使用句柄而不是指针分段加载结构,这样可以提
高管理内存的效率。
注释 本节所述的使用 Visual FoxPro API 管理内存的技术,同时适用于 FLL 库。
使用句柄
这里句柄是指一个内存句柄,本质上是一个指针数组的索引。数组中的这些指针指向由 Visual FoxPro 掌
握的各内存块。在 API 中几乎所有对内存的引用都通过句柄来实现, 而不是传统的 C 指针。
若要在库中分配并使用内存
用 _AllocHand( ) 分配一个句柄。
用 _HLock( ) 锁定该句柄。
用 _HandToPtr( ) 把句柄转换为一个指针。
使用该指针引用内存。
用 _HUnLock( ) 解锁句柄。
注释 为了避免破坏备注文件,在调用 _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
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 堆栈的大小,或者影响一个 .fll 文件可用的堆栈空间大小。
在正常情况下,这个差别并不重要。Visual FoxPro 的堆栈通常很大,足以保存在控件和库中需要为其分
配内存的自动变量。如果遇到堆栈溢出的问题,可以动态地在堆栈中分配额外的内存。
遵守句柄规则
下面说明了获取句柄和释放句柄的规则:
用户必须负责释放他们分配出去的所有句柄,包括由 _Load() 这样的函数分配的句柄。
_Load() 只有当加载的内存变量是一个字符串(也就是 ev_type = ‘C’)时才创建一个句柄。其他数据
类型的值存在于 Value 结构本身,加载一个字符串则把一个 MHANDLE 放在 Value 结构的 ev_handle 中
。
在 FLL 库中 Visual FoxPro 负责释放所有由 _RetVal( ) 返回的句柄。即使这些句柄由用户分配,他们
也不能释放这些句柄。
用户不可以释放在 ParamBlk 中传递给他们的句柄。
注意 在编写一个调用别的函数的外部例程时,要注意遵守所有的规则并检查返回结果。一个有偏差的指针
或句柄引用可能会损坏 Visual FoxPro 的内部数据结构,造成立刻的异常终止或者留下隐患,甚至可能丢
失数据。
连编和调试库
在创建了一个项目之后,您就可以连编并调试它。
连编项目
在连编之前,需要建立项目的各种设置。有些设置取决于您是想创建控件或库的测试版还是发行版。通常
,在对程序的工作状况感到满意前创建程序的测试版,然后再创建一个发行版。
若要指定测试版或发行版
在“Build”菜单中,选择“Set Default Configuration”。
选择是创建控件的测试版还是发行版。
选择“OK”。
若要建立项目设置
从“Build”菜单中选择“Settings”命令。
在“Settings For”下,选择是创建程序的测试版还是发行版。
单击“C/C++”选项卡,然后进行下列设置∶
在“Category”列表中,选择“Code Generation”。
在“Calling Convention”列表中,选择“_fastcall”。
在“Use run-time library”列表中,选择“Multithreaded DLL”。
选择“Link”选项卡,然后在“Object/Library Modules”文本框中添加下列库∶
如果连编一个 .fll,请添加 Visual FoxPro API 目录中的 WINAPIMS.LIB。
设置“Ignore all default libraries”。
选择“OK”。
若要保证编译器能找到所需文件
从“Tools”菜单选择“Options”。
单击“Directories”选项卡。
在“Show directories for”列表框中选择“Include files”。
在“Directories”工具栏中,单击“Add”按钮。
添加具有 Pro_ext.h 的目录。
在“Show directories for”列表框中选择“Library files”。
在“Directories”工具栏中,单击“Add”按钮。
调试一个 FLL 库
在一个完整的 Visual FoxPro 应用程序中调试一个控件或库,通常比单独地调试控件或库更为困难。一种
较好的方法是创建一个简单的测试程序来测试控件或库的操作。
用 Microsoft Visual C++ 调试
Microsoft Visual C++ 4.0 或更高版本提供一个集成调试环境,可以方便地设置断点并单步调试所有代码
。甚至可从 Visual C++ 中运行 Visual FoxPro。
若要启动 Microsoft Visual C++ 的调试功能
从“Build”菜单选择“Settings”命令。
在“Project Settings”对话框中单击“Debug”选项卡。
在“Executable for debug session”文本框中,输入路径和 VFP6.EXE。
例如,输入 C:\Program Files\Microsoft Visual Studio\Vfp98\Vfp6.exe。选择“OK”。
在库中设置一个断点。
在“Build”菜单中,选择“Debug”。然后,在子菜单中选择“Go”。
当显示信息“VFP.EXE does not contain debugging information.”时,选择“Yes”继续。
有关在 Visual C++ 中调试的详细内容,请参阅 Visual C++ 文档集。
用其他调试器进行调试
可用任何调试器来调试一个库,只要该调试器能够正确处理嵌套在程序中的 INT 3 (_BreakPoint( ))。只
要调试器能够做到下列几点,那么便可用来进行象征性的调试:
从一个映象文件中制作一个符号表。
独立于程序加载该符号表。
重新给这些符号分配一个新地址。
若要调试一个库
在函数要开始调试的地方添加一个 _BreakPoint( ) 调用。
连编该控件或库。
启动调试器。
如果调试器支持符号调试,请加载库的符号表。
启动 Visual FoxPro。
从 Visual FoxPro 中调用库例程。
当运行到断点时,调整符号基址,把符号与库的实际加载位置对齐。
给指令指针 (IP) 寄存器加 1,跳过 INT 3 指令。
如同调试普通程序一样继续调试。
注释 在发布产品之前,一定要删除调试器中所有指定的断点。 |
|