关于 Nim
Nim 是德国人 开发的编程语言,最初叫 Nimrod。Nim 有下面几个特点:
- 强制缩进语法
- AST 操控
- 编译到 C
- 静态编译 .exe 或 dll
- 指针
- gc
Nim 的标准库还可以,一些常用的算法、网络库都有,也有不少的 wrapper 库。
生成 DLL
Nim 可以很方便地生成 Dll 文件,只需要在导出的 proc 中加入编译指令 {.stdcall, exportc, dynlib.}
;编译时使用参数:nim c --app:dll -d:release src.nim
。
- stdcall,指定调用约定为 stdcall 方式。VB6 只支持 stdcall 调用约定。
- dynlib,指定导出到 DLL。
- exportc,导出到 C。
例如
proc return_multiply*(a, b: int): int {.stdcall, exportc, dynlib.} =
a * b
有一点需要注意的是,(似乎)因为编译器的问题,DLL 导出的函数名可能会编写的时候不一致,可以用 Dependency Walker 之类的工具查看。
比如上面的例子,导出的函数 return_multiply
,以 stdcall 调用方式编译后,入口为 return_multiply@8
。
VB6 代码,只需要声明就可以使用 DLL 中的函数。
Public Declare Function return_multiply Lib "test.dll" Alias "return_multiply@8" (ByVal a As Long, ByVal b As Long) As Long
对于简单的数据类型,VB6 与外部 DLL 交互还是比较简单的,问题是实际编程中更常见的复合数据类型,像字符串之类的应该如何处理。
VB6 字符串
VB6 是一个 IDE 也是一个语言。话说 MS 早期便是造 Basic 工具起家的,传说 Bill Gate 也参与编写当年 Basic 解释器的代码。VB 的发展几乎是 COM 组件的发展吏了,VB 也可以也认为是一个 MS 官方提供的 COM 组件快速使用工具。VB 的底层设计主要就是为了 COM 组件设计的。
COM 组件又复杂无庞大,是 Windows 的基础技术之一,就算 Windows 10 估计也留有不少古老代码。
VB 字符串类型实际上一种名为 BSTR
的 COM 字符串。因为 COM 组件被设计为可用于不同语言之间交互,所以在字符串设计上,是采用独立的做法。
BSTR
可以包含 NULL
字符串;由被调用者申请内存,调用者释放内存,并提供统一的内存申请释放方法;一个 BSTR
字符串由两部组成,第一部分表示字符串字节长度,第二部分是字符数组,字符数组不以 NULL 为终结标志。
一个 VB 字符串,实际是一个指向一个 BSTR
字符数组的第一个字符的指针。所有在与 C 代码交互时,一个 VB 字符串相当于一个 char*
指针,可以直接用于需要 cstring
的地方。
对于字符串必须要考虑编码问题。VB6 内部是 Unicode 编码的,但对外全部都是 ASCII。当年在开发 VB6 的时候,Windows API 只有 A 版本,字符串不编码之间的转换由编译器自动完成。可以这样认为,VB6 还不是一个完全体,可惜已经没有 VB7 了。
另一个与 VB 字符串相关的就是 LPSTR 类型,相当一部分的 Windows API 包含有 LPSTR
类型的参数。MS 官方是这样定义 LPSTR
:
The LPSTR type and its alias PSTR specify a pointer to an array of 8-bit characters, which MAY be terminated by a null character.
typedef char* PSTR, *LPSTR;
LPSTR 是 PSTR 的一个别名,是一个 8 位字符数组的指针,这个数组可能不是以 NULL 字符结尾的。大部分情况下 BSTR
和 LPTSTR
是指的同样的数据结构,可以互换。
Nim 示例
根据 VB 的这些特点,写一个简单的字符串处理函数。
import encodings
const
vbCodePage = "GB2312"
vbTrue* = 1
vbFalse* = 0
type
VBString* = cstring
VBBoolean* = int32
proc fromVBString*(a: VBString): string =
return encodings.convert($a, "UTF-8", vbCodePage)
proc `$` (a: VBString): string =
return fromVBString(a)
proc toVBString*(a: string): VBString =
return VBString(encodings.convert(a, vbCodePage, "UTF-8"))
Nim 的字符串是 UTF-8 编码的,VB 的字符串是按系统 CodePage 编码的(中文环境下为 GB2312),需要一些编码转换操作,使用 Nim 标准库 encodings 实现。
下面实现一个 MD5 计算函数让 VB 调用。
import md5
proc build*(str: string): string =
result = md5.getMD5(str)
# 计算 MD5
#
proc md5sum*(str: VBString): VBString {.stdcall, exportc, dynlib.} =
return md5sum.build($str)