玩转FLL
一、什么是FLL库
Visual FoxPro 库(.fll 文件),类似 .dll 文件,包含可以象任何其他函数一样调用的函数。由于它们是为从 Visual FoxPro 中调用而特意创建的,因此,通常很容易将参数传入或传出 .fll 文件中的函数。
要使用 Visual FoxPro 库,通过用 SET LIBRARY 命令指定 .fll 文件名来注册 .fll 文件,随后正常调用该函数。不象注册 .dll 函数,不需要注册 .fll 中的单个函数,也不需要指定有关该函数所使用参数或数据类型的信息。
二、怎么使用FLL库
若要调用 .fll 函数通过发出 SET LIBRARY 命令注册该 .fll 库。象任何函数一样调用该库中的函数。
例如,下列代码从 Visual FoxPro 安装目录中的 Foxtools.fll
库中调用函数以确定 C: 驱动器的驱动器类型:
SET LIBRARY TO "C:\Program Files\Microsoft Visual FoxPro 9.0\Foxtools.fll"
? DriveType("C:")
如果需要注册多个 .fll 文件,在 SET LIBRARY 命令中包含 ADDITIVE 关键字。否则,清除先前注册的 .fll 文件并由最近注册的文件取代。
如果一个函数名与 Visual FoxPro 中已经存在的其他函数冲突,则最后定义的函数将优先。如果连接库中的函数与 Visual FoxPro 内部函数重名,则 Visual FoxPro 函数优先。
每个工作期只需要注册一次 .fll 文件中的函数,因为直到退出 Visual FoxPro 为止,它们一直保持可用。如果不再打算调用 .fll 文件中的函数,通过发出 RELEASE LIBRARY、RELEASE ALL 或 SET LIBRARY 命令命令将该 .fll 文件从内存中移去并释放资源。更多的信息,请参见 RELEASE LIBRARY 命令 和 RELEASE 命令。
三、从内存中删除单个外部 API 库
RELEASE LIBRARY <LibraryName>
参数
LibraryName 指定要从内存中删除的 API 库名称。
四、创建 Visual FoxPro 动态链接库
Visual FoxPro 动态链接库本质上是一个包含调用 Visual FoxPro API 的 DLL。在开发环境中,当你想添加调用的 Visual FoxPro 函数时,你可以创建基本的 DLL 结构。下列章节讲述了在 C++ 中创建 FLL 模板的模板示例。
设立库模板
每个 Visual FoxPro FLL 库有相同的基本结构。你可以使用一个关于结构的模板,所以仅需添加关于特殊库例程的代码。
在一个 Visual FoxPro 库模板中有五个要素:
★ #include 语句。
★ 函数定义。
该函数定义有一个空的返回值和传递 ParamBlk *parm 的参数。有关更多的关于ParamBlk 参数的信息请参见在外部库中的参数。
★ 函数代码。
★ FoxInfo 结构。
在 FLL 中的函数通过 FoxInfo 结构与 Visual FoxPro 结合。Visual FoxPro 使用 FoxInfo 确定该函数名和数字及参数的类型。
★
FoxTable 结构。
FoxTable 结构是一个明了 FoxInfo 结构的连接列表。
有关 FoxInfo 和 FoxTable 结构定义的更多信息,请参见
Pro_ext.h 文件。
还需要下列文件:
●
Pro_ext.h 头文件。
你可以打印该文件来查看函数的声明、typedefs 和在 Visual FoxPro API 中使用的结构。
●
Winapims.lib 文件
这两个文件可在 Microsoft Visual FoxPro ..\Samples\API 目录中找到。
五、模板示例
对于 C++ 例程,可以使用下列模板:
#include <Pro_ext.h>
//固定不动
//根据需要设计和起名函数
void Internal_Name(ParamBlk
*parm)
{
// Function code goes here.
}
//FLL库函数声明, Internal_Name是c++的函数名,FUNC_NAME是VFP调用的函数名。
FoxInfo myFoxInfo[] = {
{"FUNC_NAME", (FPFI) Internal_Name, 0, ""},
};
//下面固定照写
extern "C" {
FoxTable _FoxTable = {
(FoxTable *)0, sizeof(myFoxInfo)/sizeof(FoxInfo), myFoxInfo
};
}
六、Visual FoxPro 和 FLL 库直接传递函数名和参数描述信息
下列代码举例说明了 FoxInfo 结构的语法:
FoxInfo arrayname[ ] = {
{funcName1, FPFI function1, parmCount1, parmTypes1}
{funcName2, FPFI function2, parmCount2, parmTypes2}
. . .
{funcNameN, FPFI functionN, parmCountN, parmTypesN}
};
参数 说明
arrayname
指定 FoxInfo 类型的变量。
注意:
可以在该数组中包含几个 FoxInfo 结构行。
funcName
包含 Visual FoxPro 对函数调用的调用名。
function
指定 C 语言例程的地址。这是用于定义函数的精确(区分大小写)的名称。
parmCount
指定 parmTypes 串中描述的参数数量。
parmTypes
描述每个参数的数据类型。
下面列示了 parmTypes 的有效值。
" " 无参数。
"?" 指定可以传递任何类型。在函数体中,将需要检查传递来参数的类型。
"C" 指定 字符型 类型参数。
"D" 指定 日期型 类型参数。
"I" 指定 整数(Integer) 类型参数。
"L" 指定 逻辑型 类型参数。
"N" 指定 数值型 类型参数。
"R" 引用。
"T" 指定 日期时间型 类型参数。
"Y" 指定 货币型 类型参数。
"O" 指定 对象(Object) 类型参数。
七、PRG2FLL工具的作用
PRG2FLL工具作用
1、创建了C++下编译FLL库的模板。
2、增加了C++与VFP之间在C++程序下数据交换的接口。
3、简单翻译PRG代码为C++代码,在工具中方便把对应的PRG代码改为C++代码,来提高速度。
4、还包含有很多实用小工具,例如数据库,表单,类转PRG的功能。
5、还包含变量混淆和部分函数混淆的功能。
6、集成了C++的连接和编译功能。能够生成最终的FLL库,以及能运行前后代码。
八、VFP与C++的语法区别
1、VFP命令行最末分号“;”是续行作用,即命令分了几行。
C++命令行最末分号“;”是结束行作用,每个命令行以分号结束。
2、VFP命令不区分大小写,且有4字符简称,C++命令关键字必须是小写。
3、VFP的命令块以“关键字”开始,“END关键字”结束。
C++的命令块以字符“{”开始,字符“}”结束。
例如VFP命令
C++命令
IF 条件
命令1
命令2
ELSE
命令3
命令4
ENDIF
if (条件)
{
命令1;
命令2;
}
else
{
命令3;
命令4;
}
九、VFP与C++的变量区别
1、VFP变量可以不用预先定义,在赋值语句时,定义类型及变量值。
C++变量有严格的要求,必须先定义类型,后使用,在编译阶段有检错功能。
2、VFP的变量可以转换任意类型。
C++变量不能轻易转换类型,否则会程序错误或精度错误。编译阶段产生警告错误。
3、VFP的各个变量对应的C++变量类型。
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
双精度
整型(Integer)
ev_type
'I'
ev_width
显示宽度
ev_long
长整型(Long integer)
日期型
ev_type
'D'
ev_real
日期1
日期时间(Date Time)
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
备注字段偏移量
通用(General)
ev_type
'G'
ev_wdith
FCHAN
ev_long
通用型字段长度
ev_real
通用型字段偏移量
对象(Object)
ev_type
'O'
ev_object
对象标识符
Null
ev_type
'0' (零)
ev_long
数据类型
十、VFP传递参数数据给C++
访问 FLL 库中参数示例
Example(ParamBlk *parm)
//f(x)=x*x
{
int
i=parm->p[0].val.ev_long;
_RetInt(i*i, 10);
}
为了方便,我们工具通过使用 #define 快捷方式使得 paramBlk 结构易于管理。
#define p0 parm->p[0]
#define p1 parm->p[1]
#define p2 parm->p[2]
#define p3 parm->p[3]
#define p4 parm->p[4]
#define p5 parm->p[5]
#define p6 parm->p[6]
#define p7 parm->p[7]
在PRG2FLL中,也可以如下使用。
Example(ParamBlk *parm)
//f(x)=x*x
{
int
i=p0.val.ev_long;
_RetInt(i*i, 10);
}
传递参数类型参考上一节内容。
另外parm->pCount,返回vfp调用传递来的参数数量。
十一、C++返回参数数据给VFP
C++函数的结果以指定的数据类型返回给 Visual FoxPro。下面几个函数实现不同类型数据的返回给VFP的功能。
_RetChar( )
将函数的返回值设置为空终止串。
_RetCurrency( )
将库的返回值设置为货币值。
_RetDateStr( ) 将函数的返回值设置为日期。以 mm/dd/year 格式指定日期,其中,year 可以是两位或四位。
_RetDateTimeStr( ) 将库的返回值设置为日期时间。
_RetFloat( ) 将函数的返回值设置为浮点值。
_RetInt( ) 将函数的返回值设置为数字值。
_RetLogical( )
将函数的返回值设置为逻辑值。零被当作假(False),而将所有非零值当作真(True)。
_RetVal( ) 传递一个完整的 Visual FoxPro Value 结构,以便可以返回除备注以外的任何 Visual FoxPro 数据类型。必须调用 _RetVal( ) 来返回包含内植空字符的串。
十二、在C++执行VFP的语句和计算VFP的表达式
_Execute( ) 编辑并执行 stmt 中指定的空终止语句。
int _Execute(char FAR *stmt)
char FAR *stmt;
/* 要执行的语句. */
_Evaluate( ) 编译并执行 expr 中包含的 Visual FoxPro 表达式,将结果放置到 res。
int _Evaluate(Value FAR *res, char FAR *expr)
Value FAR *res;
/* 结果地址. */
char FAR *expr;
/* 表达式.
*/
十三、VFP和C++控制命令的转换
1、IF命令
VFP命令格式,[]内容指可选项。
IF 逻辑表达式 [THEN]
命令块
[ELSE
命令块]
ENDIF
对应的C++命令格式。
if (条件)
{
命令块;
}
[else
{
命令块;
} ]
2、DO WHILE条件循环命令
VFP命令格式:
DO WHILE lExpression
Commands
[LOOP]
[EXIT]
ENDDO
对应的C++命令格式。
while (条件)
{
Commands;
[continue;]
[break;]
}
3、FOR
ENDFOR循环命令
VFP命令格式:
FOR VarName = nInitialValue TO nFinalValue [STEP nIncrement]
Commands
[EXIT]
[LOOP]
ENDFOR | NEXT
对应的C++命令格式。
for(VarName= nInitialValue; VarName<= nFinalValue; VarName= VarName+ nIncrement)
{
Commands;
[break;]
[continue;]
}
4、DO CASE条件选择命令
执行第一个条件表达式结果为真(.T.)的一组命令。VFP的条件类型多样,CASE命令对应的是逻辑表达式,而C++的类似命令switch(变量) {case 值: { };},因此无法直接转换。因此可以转换为C++的嵌套IF命令语句格式。
VFP命令格式:
DO CASE
CASE lExpression1
[Commands1]
[CASE lExpression2
[Commands2]
...
[CASE lExpressionN
[Commands]
[OTHERWISE
[Commands3]
ENDCASE
C++对应的命令。
if (Expression1)
{
[Commands1];
}
else if (lExpression2)
{
[Commands2];
}
else
{
[Commands3];
}
注意:VFP的2个特殊命令SCAN ... ENDSCAN和FOR EACH ... ENDFOR,没有办法直接转换为对应的C++命令。因此要独立保留在PRG程序体内。
十四、用C++建立VFP变量
我早期采用的加密方式,就是用FLL库函数来大量建立一些变量。我们可以想象,程序缺少了这些变量,怎么能运行下去。不过我现在公开给大家。
VFP变量标志分为公有(0)和私有(1)。
我们工具提供有简化的VFP变量建立函数。
void JLBL(char *Name,int flag)
flag=0表示公用,flag=1表示私有,只有本过程内有效。
例如我们在VFP中建立几个全局变量,
PUBLIC
A,DEMO
local lcstr
在PRG2FLL开发工具中这样表示。
JLBL(“a”,0);
JLBL(“DEMO”,0);
JLBL(“lcstr”,1);
十四、用C++建立VFP数组变量
我们工具提供有简化的VFP数组变量建立函数。
void JLSZ(char *Name,int n,int a,int b,int flag)
参数n表示数组维数,=0变量,=1一维数组,=2二维数字。
flag=0表示公用,flag=1表示私有,只有本过程内有效。
例如,我们在VFP中建立数组
PUBLiC HQ[50]
DIME CCBB[500,12]
在PRG2FLL开发工具中这样表示。
JLSZ(“HQ”,1,50,0,0);
JLSZ(“CCBB”,2,500,12,1);
十五、C++与VFP在内存中的数据交换
传统使用方式如下结构:
【VFP程序调用FLL函数,把结果保存到变量中】【FLL函数处理数据并返回给VFP】【计算机内存中VFP变量或数组】
我们如果需要使用C++来提高数据处理速度,一般不要用FLL库处理数据并返回数据结果的函数,我们可以把数据处理写在FLL函数体内,返回处理结果0或者1就可以了。
新的使用结构如下:
【VFP程序启动FLL函数处理服务】【FLL函数循环处理数据并回写内存】【计算机内存中VFP变量或数组】
处理过程写在函数体内,FLL直接用我们的工具数据接口访问并修改这些计算机内存中的变量或数组值的内容。
数据计算以整型和小数类型为主,我们提供了这2中数据类型的访问接口。
获取VFP内存变量的值。
__inline int vari(char *name)
__inline double varf(char *name)
把C++值保存给VFP内存变量。
__inline int varii(char *name,int i)
__inline double varff(char *name,double ff)
举例说明使用方法,例如,我们在VFP计算N=1+…+10000的值。
VFP命令:
N=0
for i=1 to 10000
n=n+i
next
? “n=”,n
改为FLL处理如下
void FAR chengxu1(ParamBlk FAR *parm)
{
int n=0,i=0;
for (i=1;i<=10000;i++)
n+=i;
JLBL(“n”,1);
varii(“n”,n);
_RetChar("");
}
FoxInfo myFoxInfo[] = {
//--------------------------
//在这里增加程序处理接口
{"_chengxu1",(FPFI) chengxu1,0,""},
};
改用FLL库的VFP命令,就下面2条,处理数据,输出结果。
_chengxu1()
? “n=”,n
我们可以想象,即使破解出PRG源码的人,看到这样的代码,是不是傻掉了?
而且实际测试过,C++回写数据比VFP处理数据花费时间要少很多。
十六、C++和VFP的注释语句
在VFP中注释命令有“*”和“&&”。
在C++中注释命令是“//”和“/*
…..
*/”。
“&&”在C++中是逻辑运算符,相当于VFP的“.AND.”。所以在互译命令时要注意改变注释命令符号。
具体用法不用细讲,不清楚请问“百度”。
十七、C++获取VFP控制语句的参数
为了实现VFP控制语句,翻译为C++控制语句,必须要在C++读取到VFP的控制命令的参数才行。
1、C++获取逻辑表达式结果
我们机器翻译了IF,DO WHILE,DO CASE等命令,这些用命令的条件是逻辑表达式。因此我们提供接口。
__inline int FAR TJPD(char *expr)
参数expr是VFP控制命令的逻辑表达式。返回结果转为C++的逻辑表达式结果。
例如下面VFP命令。
IF i<n1
s1=s1+ccbbb[i,7]
ELSE
s1=s1+ccbbb[i,7]
ccbbb[i,8]=s1/n1
s1=s1-ccbbb[i+1-n1,7]
ENDIF
PRG2FLL自动翻译为C++命令如下。
if (TJPD("i<n1 "))
{
_Execute("s1=s1+ccbbb[i,7] ");
}
else
{
_Execute("s1=s1+ccbbb[i,7] ");
_Execute("ccbbb[i,8]=s1/n1 ");
_Execute("s1=s1-ccbbb[i+1-n1,7] ");
}
2、C++获取整数表达式结果
对于FOR 变量=… ENDFOR循环命令的参数是数字表达式。我们这个规定必须是整型表达式,且STEP的参数是正值,才能正确翻译。
因此我们工具提供接口。
__inline int FAR TJPDI(char *expr)
参数expr是VFP的FOR控制语句的数学表达式。返回结果转为C++的数学表达式结果。
例如VFP命令如下:
n=0
FOR i=1 TO 100 step 2
n=n+i
NEXT
PRG2FLL自动翻译为下面C++语句。
_Execute("n=0 ");
_Execute("i=1");
int i=0;
for (i=TJPDI("1") ;i<=TJPDI("100");i=i+TJPDI("2"))
{
varii("i",i);
//把C++的i变量值赋值给VFP的i变量。
_Execute("n=n+i ");
i=vari("i");
//把VFP的i变量值赋值给C++的i变量。
}
因为PRG2FLL程序在静态翻译时,无法确定STEP参数表达式的正负值。所以我们默认为正表达式,如果使用负表达式,手动改变上面翻译结果。
VFP命令
for i=100 to 1 step -2
手动翻译为对应的C++命令如下。
for (i=TJPDI("100") ;i>=TJPDI("1");i=i+TJPDI("-2"))
十八、FLL库函数的再加工
我们前面看见,PRG2FLL翻译的代码,依赖于_Execute()函数,如果这个函数在循环体内,不但起不到加密效果,而且大大降低了运算速度。
我们继续改进FLL库函数。
_Execute("n=0 ");
_Execute("i=1");
int i=0;
for (i=TJPDI("1") ;i<=TJPDI("100");i=i+TJPDI("2"))
{
varii("i",i);
//把C++的i变量值赋值给VFP的i变量。
_Execute("n=n+i ");
i=vari("i");
//把VFP的i变量值赋值给C++的i变量。
}
上面程序,我们改进一下。假定我们就是为了得到n变量的值,修改为C++的函数体如下。
int
n=0,i=0;
for (i=1;i<=100;i=i+2)
{
n=n+I;
}
varii("n",n);
//把C++的n变量值赋值给VFP的n变量。
现在的计算速度将成倍提高,而且也不容易分析出原来的VFP的程序流程。
(作者:独狼 QQ:2775205 未完,待续….)