阿杰 发表于 2011-7-17 20:54:26

vfp 中使用 BStr, SafeArray

当我们调用其它语言编写的 DLL 或使用某些 COM / OCX 对象时,我们经常碰到要传递 BSTR 类型的字符串和 SafeArray 类型数组的问题。

BSTR来自于 Basic 的字符串结构( Basic STRing),其结构是: + +
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 =
FADF_AUTO AND FADF_FIXEDSIZE
位置
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...


马大哈 发表于 2011-7-22 14:33:51

SafeArray是个好东西:)
页: [1]
查看完整版本: vfp 中使用 BStr, SafeArray