| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛 | 
共有 1505 人关注过本帖
标题:Rust开发VFP FLL实践:编写木瓜myfll的CursorToStr和StrToCursor兼容函数, ...
只看楼主 加入收藏
bobwu
Rank: 1
等 级:新手上路
帖 子:20
专家分:0
注 册:2009-7-25
收藏
 问题点数:0 回复次数:19 
Rust开发VFP FLL实践:编写木瓜myfll的CursorToStr和StrToCursor兼容函数,附源码及编译方法
`cursorrust.fll` 是用 Rust 开发的 **32 位 Visual FoxPro 9 FLL**,实现木瓜 MyFll 的游标转字符串、字符串还原游标这组兼容函数,并对游标处理做了性能优化。本项目提供 `CursorToStrRust` 和 `StrToCursorRust` 两个 VFP 函数。

## 使用

```foxpro
SET LIBRARY TO cursorrust.fll ADDITIVE

CREATE CURSOR orders (order_id I, customer C(20))
INSERT INTO orders VALUES (1, "Alice")

lcWire = CursorToStrRust("orders", .T.)
llOK = StrToCursorRust(m.lcWire, "orders:received_orders")
IF m.llOK
    SELECT received_orders
    BROWSE
ENDIF
```

`CursorToStrRust(cCursorList[, lCompress])` 把已打开的游标编码为二进制字符串。`cCursorList` 可写一个游标名,也可用逗号分隔多个游标;`.T.` 启用 zlib 压缩,省略或 `.F.` 不压缩。

`StrToCursorRust(cWire[, cSelector])` 从该字符串建立游标,成功返回 `.T.`。`"orders:received_orders"` 表示读取字符串中名为 `orders` 的游标,并在当前 VFP 中建立 `received_orders`。字符串可能含 `CHR(0)`,保存或传输时必须原样保留全部字节。多游标、改名及错误处理见 [使用说明](使用说明.txt)。

## 本项目怎样提速

**序列化的普通路径**逐条遍历 VFP 游标,并通过扩展 API 读取字段。记录数越多,跨 VFP/Rust 边界的调用次数越多。`src/cursor_bridge.rs` 为符合条件的物理 DBF 增加了快速路径:用 VFP 的 `FILETOSTR(DBF(...))` 一次取得文件内容,解析 DBF 表头和记录区,直接构造相同的游标线格式。这减少了逐条定位和逐字段读取的调用。

快速路径先确认工作区只读,或处于**无缓冲、无事务**状态;字段必须完整且按原顺序排列,全部为非空的 `C`/`I`;没有 `FILTER()` 和 `ORDER()`。还要核对 DBF 文件路径、表头、字段描述、记录宽度、文件长度与 VFP 的记录数,并逐条检查删除标记。任何条件不符就回退到普通路径,继续按 VFP 当前工作区的过滤、排序、缓冲数据和字段选择读取。500 万条测试表符合这条路径的输入条件。

**还原的普通路径**每条记录调用 `_DBAppend`,每个字段再调用 `_DBReplace`,大量记录会产生大量 VFP 扩展 API 调用。VFP 9 SDK 的 `pro_ext.h` 实际声明了 `int FASTCALL _DBAppendRecords(int workarea, unsigned short nrecs, char FAR *buffer)`,但微软帮助文档没有介绍它的用法;这个批量追加接口的实际使用方法是本项目作者自行试验确认的,也是还原提速的关键。

本项目先把线格式记录转换成 VFP 的原生记录缓冲区,再把工作区号、批次记录数和缓冲区指针交给 `_DBAppendRecords`,由 VFP 引擎一次追加一批。每批最多 32,767 条,整块缓冲区最多 64 MiB。批量路径要求字段无 NULL,类型限于 `C`、`N`、`F`、`B`、`I`、`Y`、`L`,其中 `N`/`F` 宽度不超过 64。布局或字段值无法转换、或缓冲区超限时,改走逐条还原;如果 `_DBAppendRecords` 本身返回错误,则报告还原失败。程序不绕过 VFP 引擎直接写 DBF 文件。

`src/cursor_wire.rs` 进一步减少大量小记录的分配:不超过 8 字节的字段值和不超过 4 个字段的行放在结构体内部;解码后保留线格式缓冲区,按记录和字段位置取切片,避免先把所有记录再复制成另一套完整对象。压缩采用 zlib,能减少传输字节,但会增加计算时间。

## 编译

构建需要 Rust 1.77.2、`i686-pc-windows-gnu` 目标,以及 32 位 MinGW GCC 和 objdump;**不需要微软编译器**。仓库中的 `libwinapims.a` 是构建时使用的 VFP SDK 静态导入库。`build.bat` 顶部的 `MINGW_BIN` 明确指定 MinGW 的 `bin` 目录;换一台电脑编译时,将默认路径改成那台电脑的实际安装路径,或在运行前设置 `MINGW_BIN` 环境变量。脚本会检查 GCC 是否为 `i686-w64-mingw32`,并显式指定它作为 Cargo 的链接器。

在项目目录运行:

```bat
build.bat
```

脚本构建 32 位 DLL,检查 PE 格式和 VFP 必需导出,然后在项目目录生成 `cursorrust.fll`。

`cursorrust.fll` 可随 VFP 应用绿色部署:将文件放到 VFP 能找到的位置并用 `SET LIBRARY TO ... ADDITIVE` 加载。目标系统为 **Windows 7 SP1 及以上**;编译产物只静态依赖操作系统自带的 `KERNEL32.dll` 与 `msvcrt.dll`,不需要另装微软 VC++ 运行库。
cursorrust.zip (1.68 MB)


[此贴子已经被作者于2026-9-19 05:28编辑过]

收到的鲜花
  • trial03 于 2026-09-21 12:06 送鲜花  1朵  
搜索更多相关主题的帖子: 记录 路径 游标 VFP 字段 
2026-09-19 05:25
bobwu
Rank: 1
等 级:新手上路
帖 子:20
专家分:0
注 册:2009-7-25
收藏
 得分:0 
游标字符串转换函数使用说明

本库供 32 位 VFP 9 使用,提供两个函数:
CursorToStrRust(cCursorList[, lCompress]):把已打开的游标装入一个字符变量。
StrToCursorRust(cWire[, cSelector]):从该字符变量中还原游标。

使用前,把 cursorrust.fll 放到程序能找到的位置,并执行:
SET LIBRARY TO cursorrust.fll ADDITIVE

一、把游标装入变量

函数名:CursorToStrRust(cCursorList[, lCompress])
返回值:字符型变量,内容为二进制数据;失败时报告用户错误。

第一个参数 cCursorList:必填,字符型。填写已经打开的游标别名。
只装一个游标:CursorToStrRust("a")
装入两个游标:CursorToStrRust("a,b")
装入时改名:CursorToStrRust("a:data_a")
同时装入和改名:CursorToStrRust("a:data_a,b:data_b")

逗号用来分隔多个游标。冒号左边是当前打开的游标名,右边是它在结果变量中的名字。例如,"a:data_a" 读取游标 a,在结果变量中记作 data_a;原游标 a 不会改名。省略冒号时,结果变量中的名字与原游标名相同。

第二个参数 lCompress:可选,逻辑型。.T. 表示压缩;省略或填写 .F. 表示不压缩。例如:
lcData = CursorToStrRust("a,b", .T.)

结果变量可能包含 CHR(0)。保存或传输时必须原样保留所有字节,不能转换字符编码或删掉末尾空格。还原函数能自动识别是否压缩。

函数按调用时游标的可见记录和当前顺序读取。删除记录设置、记录过滤、字段过滤和当前索引顺序会影响结果。启用字段过滤时,计算字段不受支持。需要完整数据时,请先检查这些设置。

二、从变量还原游标

函数名:StrToCursorRust(cWire[, cSelector])
返回值:成功为 .T.,失败为 .F.。

第一个参数 cWire:必填,字符型。直接传入前一个函数返回的完整变量,不论它是否经过压缩。

第二个参数 cSelector:可选,字符型。填写“变量中的游标名:还原后的游标名”。例如:
StrToCursorRust(m.lcData, "a:newa")
表示从变量中取出名为 a 的游标,在当前程序中建立名为 newa 的游标。

如果装入时用了 "a:data_a",变量中的名字就是 data_a,还原时应写:
StrToCursorRust(m.lcData, "data_a:newa")

一次还原两个游标,用逗号分隔:
StrToCursorRust(m.lcData, "a:newa,b:newb")

省略第二个参数或传入空字符串,只还原变量中的第一个游标,且沿用变量中的名字。想还原多个游标,必须逐个列出。还原成功后,当前工作区会切换到最后还原的游标。目标名字如已被打开的游标占用,成功还原会替换它;失败后应检查目标游标状态。

一个变量中只有一个游标时,可以把第二个参数直接写成新名字,例如 StrToCursorRust(m.lcData, "newa")。多个游标时,这种写法表示查找变量中名为 newa 的游标。为避免混淆,建议始终使用“变量中的游标名:还原后的游标名”。

三、完整示例

以下代码先创建两个测试游标,再分别演示单游标、装入时改名、多游标和压缩。每段都会重新生成 lcData。

SET LIBRARY TO cursorrust.fll ADDITIVE
CREATE CURSOR a (id I, title C(20))
INSERT INTO a VALUES (1, "第一条")
CREATE CURSOR b (id I, title C(20))
INSERT INTO b VALUES (2, "第二条")

* 单游标:a 装入变量,还原为 newa。
lcData = CursorToStrRust("a")
llOK = StrToCursorRust(m.lcData, "a:newa")
IF llOK
    SELECT newa
    BROWSE
ENDIF

* 装入时改名:原游标是 a,变量中的名字是 data_a。
lcData = CursorToStrRust("a:data_a")
llOK = StrToCursorRust(m.lcData, "data_a:newa")

* 多游标:一个变量中同时存放 a 和 b。
lcData = CursorToStrRust("a,b")
llOK = StrToCursorRust(m.lcData, "a:newa,b:newb")

* 压缩:还原时无需再传压缩参数。
lcData = CursorToStrRust("a,b", .T.)
llOK = StrToCursorRust(m.lcData, "a:newa,b:newb")

四、名称限制

游标名以英文字母或下划线开头,后续可使用英文字母、数字和下划线。列表中不能有空项或多余冒号;变量中的游标名不能重复,还原后的游标名也不能重复,大小写不用于区分名称。
2026-09-19 05:26
schtg
Rank: 13Rank: 13Rank: 13Rank: 13
来 自:USA
等 级:贵宾
威 望:67
帖 子:2792
专家分:4967
注 册:2012-2-29
收藏
 得分:0 
2026-09-19 05:47
easyppt
Rank: 9Rank: 9Rank: 9
等 级:蜘蛛侠
威 望:1
帖 子:556
专家分:1076
注 册:2021-11-24
收藏
 得分:0 
感谢老师分享!!
2026-09-19 08:11
sam_jiang
Rank: 10Rank: 10Rank: 10
等 级:贵宾
威 望:14
帖 子:1272
专家分:1687
注 册:2021-10-13
收藏
 得分:0 
fll和dll的区别是什么?为什么rust生成的dll改名fll就直接能用过
2026-09-19 12:17
bobwu
Rank: 1
等 级:新手上路
帖 子:20
专家分:0
注 册:2009-7-25
收藏
 得分:0 
回复 5楼 sam_jiang
真正的 FLL 是要实现 FoxPro 的 LCK 接口、导出 _FoxTable 结构体的,加载方式是 SET LIBRARY TO xxx.fll,不看后缀名,哪怕是xxx.txt,xxx.dll都可以。如果 Rust 代码没有专门去手写导出 FoxPro LCK 的 _FoxTable 结构体,VFP 会当场报错:Error 1196: xxx.fll is not a FoxPro library file(不是有效的 FoxPro 库文件)。所有的 FLL 本质上都是标准的 Windows 32位 PE 动态链接库(即标准的 DLL)。但两者的调用约定、接口协议和运行环境完全不同。当年微软给 C/C++ 开发者提供了一套工具包叫 LCK(Library Construction Kit),核心头文件是 pro_ext.h。
当你写一个真正的 FLL 时:
你的函数接收的是 VFP 专有的 ParamBlk 结构体。
你必须向外导出一个叫 _FoxTable(FoxInfo 结构体)的元数据表,里面登记了:“我有哪些函数”、“函数名是什么”、“接受什么类型的 VFP 参数”。
当在 VFP 里执行:
SET LIBRARY TO mylck.fll
VFP 内部会去 GetProcAddress(hModule, "_FoxTable")。如果找到了这张表,VFP 就会把里面的函数注册到自己的全局函数符号表里,之后你就能像用 SUBSTR()、DATE() 一样直接调用它。

[此贴子已经被作者于2026-9-19 12:35编辑过]

2026-09-19 12:27
sam_jiang
Rank: 10Rank: 10Rank: 10
等 级:贵宾
威 望:14
帖 子:1272
专家分:1687
注 册:2021-10-13
收藏
 得分:0 
回复 6楼 bobwu

只知道c/c++可以编写fll,阁下用rust做到了
2026-09-19 15:11
金太狼
Rank: 2
等 级:论坛游民
帖 子:32
专家分:13
注 册:2023-2-11
收藏
 得分:0 

看这个风格,有点象AI写的。不知对不对?
2026-09-19 17:01
bobwu
Rank: 1
等 级:新手上路
帖 子:20
专家分:0
注 册:2009-7-25
收藏
 得分:0 
回复 8楼 金太狼
现在的AI写VFP,垃圾得要死,你太抬举AI了,亲。里面所有的黑科技,你是没看,还是没看懂?
至少以下4个黑科技,是全网首次公布:
_DBAppendRecords这个微软的文档从来不提的的API
libwinapims.a 它是本人改过的,不然编译有警告
vfp_rust.def 这个踩过无数次坑搞出来的
从2005年以来MinGW GCC 编译VFP的FLL,没见人提过
RUST写VFP的FLL,全网首次公开

cursorrustv2.zip (1.84 MB)


[此贴子已经被作者于2026-9-19 22:19编辑过]

2026-09-19 21:30
wcx_cc
Rank: 9Rank: 9Rank: 9
等 级:贵宾
威 望:52
帖 子:485
专家分:1437
注 册:2015-10-2
收藏
 得分:0 
大大的赞
2026-09-19 22:57
快速回复:Rust开发VFP FLL实践:编写木瓜myfll的CursorToStr和StrToCursor兼容函 ...
数据加载中...
 
   
关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.021988 second(s), 11 queries.
Copyright©2004-2026, BCCN.NET, All Rights Reserved