当我们调用其它语言编写的 DLL 或使用某些 COM / OCX 对象时,我们经常碰到要传递 BSTR 类型的字符串和 SafeArray 类型数组的问题。
BSTR 来自于 Basic 的字符串结构( Basic STRing),其结构是:[Length prefix] + [Data string] + [Terminator]
Length prefix - 长度前缀, 4 字节, 不包含尾部结束标识的数据长度
Data string - 字符串数据, UniCode(Window)/Ansi(Mac) 格式, 中间可以包含 /0x00
Terminator - 结束标识, 两个 /0x00
要在 vfp 中构造这样的字符串有两种办法:
1. 利用 api 函数来构造,例如:
m.cAnsiStr = 'ABCDEFG'
m.cUnicodeStr = StrConv( m.cAnsiStr, 5 )
Declare Long SysAllocString In Oleaut32 String sz
m.pBStr = SysAllocString( m.cUnicodeStr+Chr(0)+Chr(0) ) && 指向 BSTR 的指针
* ---------------------------------------------------------------------------------------
* 这里我们可以看看 pBStr 指向地址的内容
* ? StrConv( Sys( 2600, m.pBStr-4, 20 ), 15 )
* 显示: 0E00000041004200430044004500460047000000
* 其中
* 长度前缀: 0E000000 - 14,也就是 cUnicodeStr 的长度
* 字符数据: 4100420043004400450046004700 - cUnicodeStr 的内容
* 结束符号: 0000
* ---------------------------------------------------------------------------------------
Declare Long SysFreeString In Oleaut32 String bstr
SysFreeString( m.pBStr - 4 ) && 释放这个 BSTR
2. 自己构造,例如,用于 Window 平台的 BSTR:
m.cAnsiStr = 'ABCDEFG'
m.cUnicodeStr = StrConv( m.cAnsiStr + Chr(0), 5 )
m.cBStr = BinToC( Len( m.cAnsiStr )*2, '4rs' ) + m.cUnicodeStr
实际应该用哪种要看被调用的函数声明方式,第二种方法传送的是指向完整结构的 BSTR 指针,因为 vfp 没有函数可以得到它自己的字符串地址指针,所以只能传送整个串的起始地址;但第一种方式传递的是指向 BSTR 中数据起始位置的指针,这也是为什么调用 SysFreeString 时要减去 4 的原因,不减去这个 4,SysFreeString 会返回串冲突错误(错误码 32)。
如果你同时熟悉 vfp 和 vb 的话,那么下面的代码:
m.cAnsiStr = 'ABCDEFG'
m.cUnicodeStr = StrConv( m.cAnsiStr + Chr(0), 5 )
m.cBStr = BinToC( Len( m.cAnsiStr )*2, '4rs' ) + m.cUnicodeStr
? Func1( m.cBStr ) && 这里传送的相当于 vb 中 VarPtr( cBStr )
Declare Long SysAllocString In Oleaut32 String sz
m.pBStr = SysAllocString( m.cUnicodeStr )
? Func1( m.pBStr ) && 这里传送的相当于 vb 中 StrPtr( cUnicodeStr )
===========================================================================
SafeArray 安全数组
SafeArray 的结构其实就是 vb 中数组结构,MS 的描述如下:
typedef struct tagSAFEARRAY {
USHORT cDims; // 这个数组有几维?
USHORT fFeatures; // 这个数组有什么特性?
ULONG cbElements; // 数组的每个元素有多大?
ULONG cLocks; // 这个数组被锁定过几次?
PVOID pvData; // 这个数组里的数据放在什么地方?
SAFEARRAYBOUND rgsabound[ 1 ];
} SAFEARRAY
其中的 SAFEARRAYBOUND 定义如下:
typedef struct tagSAFEARRAYBOUND {
unsigned long cElements; // 这一维有多少个元素?
long lLbound; // 它的索引从几开始?
} SAFEARRAYBOUND
如果在 vb 中用 Dim MyArray ( 1 TO 8, 2 TO 10 ) As Long 来定义一个数组的话,实际的内存结构为:
cDims = 2 | fFeatures = | 位置 0 |
cbElements = 4 LenB(Long) | 4 | |
cLocks = 0 | 8 | |
pvData(指向真数组) | 12 | |
rgsabound(0).cElements = 8 | 16 | |
rgsabound(0).lLbound = 1 | 18 | |
rgsabound(1).cElements = 9 | 22 | |
rgsabound(1).lLbound = 2 | 26 |
只有其中的“真数组”才存有真正的数组数据,它们与 C 中的数组结构是相同的。
同样的,Oleaut32 中也提供了创建/销毁/维护 SafeArray 的函数: SafeArrayCreate / SafeArrayDestroy / SafeArray...