紫水晶编程技术论坛 - 努力打造成全国最好的编程论坛

 找回密码
 加入我们

QQ登录

只需一步,快速开始

搜索
查看: 8288|回复: 1

[开源] 用 C++ 编写 FLL 来增强 VFP 的功能

[复制链接]

1214

主题

1566

帖子

11

精华

管理员

菜鸟

Rank: 125Rank: 125Rank: 125Rank: 125Rank: 125

积分
93743

贡献奖关注奖人气王精英奖乐于助人勋章

发表于 2010-5-11 21:18:48 | 显示全部楼层 |阅读模式
时常会有人问到:除了 VFP, 如果要学习第二种语言应该如何选择? 这是一个难于回答的问题。如果确实要我回答,我会选择 C++。原因是没有任何一种语言能象 C++ 一样与 VFP 联系得如此的紧密:VFP 本身是用 C++ 开发的, 可以在 C++ 中开发一种叫 .FLL 的 VFP 专用函数库(这在其它语言中是没有的)以及在特定情况下可以在 C++ 中直接用 VFP 的命令来操作 VFP。
    你也许使用过一些 DLL。在使用 DLL 中的函数前,你必须逐个地用 Declare 命令定义它们。而使用 FLL 时则不必这样做,你只需用一条命令 SET LIBRARY TO XXX.FLL 就行了,然后,你就可以使用该 FLL 中的所有函数了。FLL 是 DLL 的简化版本,它只能从 VFP 中调用,并且它有一些 DLL 所不具有的待点:如,可能直接运行 VFP 命令等,它也拥有一个标记为 CALLONLOAD 的函数,该函数在 FLL 函数库被载入时自动运行。因此你可以在该函数中进行适当的系统初始化。在本文的附件中有一个示例 FLL 库和一个调用该 FLL 的 .PRG 文件,该 .PRG 文件非常简单,除了运行前的环境设置和运行后的环境清理命令外,起作用的命令只有一条: SET Library To Myfll.fll, 在此 FLL 的 CALLONLOAD 函数中,我们使用了一条 Wait window 命令来显示版权信息,创建并激活了一个简单的菜单,然后创建了一个表并浏览该表。这一切都是在你所看不见的情况下处理的,实现的代码也很简单,如下:

void SETUP(ParamBlk* parm)
{
        _Execute("SET SYSMENU TO");
        _Execute("SET SYSMENU AUTOMATIC");
        _Execute("DEFINE PAD Pad OF _MSYSMENU PROMPT \x022文件\x022 COLOR SCHEME 3 MESSAGE \x022打开工作表, 设置打印机, 或退出本系统\x022");
        _Execute("DEFINE PAD Edit OF _MSYSMENU PROMPT \x022编辑\x022 COLOR SCHEME 3");
        _Execute("DEFINE PAD Administration OF _MSYSMENU PROMPT \x022维护\x022 COLOR SCHEME 3");
        _Execute("DEFINE PAD Help OF _MSYSMENU PROMPT \x022帮助\x022 COLOR SCHEME 3");
        _Execute("ON PAD Pad OF _MSYSMENU ACTIVATE POPUP 文件f");
        _Execute("ON PAD Edit OF _MSYSMENU ACTIVATE POPUP 编辑e");
        _Execute("ON PAD Administration OF _MSYSMENU ACTIVATE POPUP 维护a");
        _Execute("ON PAD Help OF _MSYSMENU ACTIVATE POPUP help");
        _Execute("DEFINE POPUP 文件f MARGIN RELATIVE SHADOW COLOR SCHEME 4");
        _Execute("DEFINE BAR 1 OF 文件f PROMPT \x022打开\x022");
        _Execute("DEFINE BAR 2 OF 文件f PROMPT \x022\x05c\x02d\x022");
        _Execute("DEFINE BAR 3 OF 文件f PROMPT \x022打印设置\x022");
        _Execute("DEFINE BAR 4 OF 文件f PROMPT \x022\x05c\x02d\x022");
        _Execute("DEFINE BAR 5 OF 文件f PROMPT \x022退出\x022");
        _Execute("ON SELECTION BAR 1 OF 文件f");
        _Execute("ON SELECTION BAR 3 OF 文件f");
        _Execute("ON SELECTION BAR 5 OF 文件f");
        _Execute("ON SELECTION MENU _MSYSMENU");
        _Execute("ON SELECTION POPUP 文件f");
            _Execute("wait window \x028\x022 VFP SMART-FLL \x022\x029");
        _Execute("create table temp \x028 id I,name c\x028 10\x029, salary n\x028 12,2\x029\x029");
        _Execute("insert into temp \x028 id,name,salary \x029 values \x028 1,\x022James\x022,1200 \x029");
        _Execute("insert into temp \x028 id,name,salary \x029 values \x028 2,\x022Tony\x022,1350 \x029");
        _Execute("insert into temp \x028 id,name,salary \x029 values \x028 3,\x022Jacobs\x022 ,1300 \x029");
        _Execute("Brow");
        _Execute("USE IN TEMP");
        _Execute("Delete file temp.dbf");
}

    仔细看看上面的程序,其实就是 VFP 代码的翻版。因此,采用此方法,你可以简单地把你的关键的 VFP 代码用 C++ 来写(看谁还有本事把它们反编译成 VFP 源代码,哈哈哈哈。)。经过一定的处理,可以说,没有人能够反编译你的代码。

    好了,今天先到这里,下一步我将逐步告诉你如何在 C++ 中创建一个 FLL。
【VB】QQ群:1422505加的请打上VB好友
【易语言】QQ群:9531809  或 177048
【FOXPRO】QQ群:6580324  或 33659603
【C/C++/VC】QQ群:3777552
【NiceBasic】QQ群:3703755

1214

主题

1566

帖子

11

精华

管理员

菜鸟

Rank: 125Rank: 125Rank: 125Rank: 125Rank: 125

积分
93743

贡献奖关注奖人气王精英奖乐于助人勋章

 楼主| 发表于 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 指令。
如同调试普通程序一样继续调试。
注释 在发布产品之前,一定要删除调试器中所有指定的断点。
【VB】QQ群:1422505加的请打上VB好友
【易语言】QQ群:9531809  或 177048
【FOXPRO】QQ群:6580324  或 33659603
【C/C++/VC】QQ群:3777552
【NiceBasic】QQ群:3703755
您需要登录后才可以回帖 登录 | 加入我们

本版积分规则

手机版|Archiver|紫水晶工作室 ( 粤ICP备05020336号 )

GMT+8, 2024-5-10 18:47 , Processed in 0.023546 second(s), 20 queries , Gzip On.

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表