原创: Jeff Louie 摘自:http://www.geocities.com/ 翻译:叉叉(kchjxx)
好像再也没有比SafeArrays更易混淆的概念了。首先,这方面的文档相当稀缺;其次,SafeArrays 可以盛放指向系统堆上分配的资源的指针。你可以把SafeArrays当作一个可以包含数组信息的VB Array,当然,你也可以把SafeArrays 当作一个高安全级别的封装类。在ASP COM编程中,SafeArrays可以封装数据给脚本使用,在从COM组件向ASP或脚本对象传值时非常有用,列表(List)可以得到有效的传递了。下面是一些例子(带有注释),这些例子是经过我对文档和源码精心研究后得出了,它们表达了我对SafeArrays及其内存分配最好的理解。
用SafeArrayPutElements构建SafeArray
你可以通过SafeArrayPutElements把元素放进SafeArray中,这样简化了数据访问,但是增大了构建数组的开销。常用到的是构建一个VT_BSTR型的VARIANT的SafeArray,这样一个复杂的结构需要与各种脚本语言兼容。要想让这样一个结构被脚本使用,还是有点困难。
在下面的例子中,我构建了一个拥有10个元素的SafeArray,把元素索引用 ltoa()转换ANSI编码字符串,然后再通过ATL转换宏A2W把ANSI编码字符串转成Unicode编码的,最后通过字符串长度,在系统堆上分配确定长度的内存空间,把Unicode转成BSTR,并返回该字符串指针。这也可以通过A2BSTR得到,但是这个宏隐藏了内存的分配,因为我要演示内存的分配。
这个很重要的指针是COM组件中BSTR在内存中的地址。当作为[out, retval]返回时,调用了脚本引擎去释放字符串在系统中的真实内存。你可以把BSTR当作在COM组建中调用new()和在脚本中调用delete()。这个例子是创建一个BSTR VARIANTs型SafeArray的复杂的过程。
char buffer[ 20 ];
HRESULT hr = S_OK;
// Create SafeArray of VARIANT BSTRs
SAFEARRAY * pSA;
SAFEARRAYBOUND aDim[ 1 ];
aDim[ 0 ].lLbound = 0 ;
aDim[ 0 ].cElements = 10 ;
pSA = SafeArrayCreate(VT_VARIANT, 1 ,aDim);
if (pSA != NULL) ... {
_variant_t vOut;
for (long l= aDim[0].lLbound; l< (aDim[0].cElements + aDim[0].lLbound); l++) ...{
ltoa(l,buffer,10);
vOut= A2W(buffer); // assignment operator automates memory allocation for BSTR
if (hr= SafeArrayPutElement(pSA, &l, &vOut)) ...{
SafeArrayDestroy(pSA); // does a deep destroy on error
return hr;
}
}
}
// clean up here only if you do not return SafeArray as an [out, retval]
SafeArrayDestroy(pSA); // again does a deep destroy
根据我所阅读的文档,这个重要的指针就是SafeArrayPutElement正确拷贝这个VARIANT到SafeArray中了,估计是通过对BSTR的深拷贝。所有SafeArray中存在的元素都会被合理的释放。为了避免内存泄漏,你必须在每次PUT之后,从源VARIANT上调用VariantClear()来回收分配给源BSTR的内存。
_variant_t类是类似于CComVariant的一个自动内存管理的包装类。既然重载运算符可以将Unicode字符串转成BSTR,那么使用_variant_t可以大大的简化上面的代码。
_variant_t& operator=(const wchar_t* pSrc) throw(_com_error); // Assign a VT_BSTR
为了使用_variant_t类,你必须在头文件中加入#include <comdef.h>。我在简化的版本中消除了the array of longs,在我阅读了Shelley Powers "Developing ASP Components Second Edition",构建了下面的代码:
char buffer[ 20 ]; // used to store ANSI string
HRESULT hr = S_OK;
// Create SafeArray of VARIANT BSTRs
SAFEARRAY * pSA;
SAFEARRAYBOUND aDim[ 1 ]; // a one dimensional array
aDim[ 0 ].lLbound = 0 ; // Visual Basic arrays start with index 0
aDim[ 0 ].cElements = 10 ;
pSA = SafeArrayCreate(VT_VARIANT, 1 ,aDim); // create a 1D SafeArray of VARIANTS
if (pSA != NULL) ... {
long aLong[1];
// iterate over array adding VARIANTs of type VT_BSTR
for (long l= aDim[0].lLbound; l< (aDim[0].cElements + aDim[0].lLbound); l++) ...{
VARIANT vOut;
VariantInit(&vOut);
vOut.vt= VT_BSTR; // set type
ltoa(l,buffer,10); // convert long to ANSI string value
vOut.bstrVal= ::SysAllocString(A2W(buffer)); // system wide "new"
aLong[0]= l; // set index value
if (hr= SafeArrayPutElement(pSA, aLong, &vOut)) ...{ // "correctly" copies VARIANT
VariantClear(&vOut); // release BSTR from memory on error
SafeArrayDestroy(pSA); // does a deep destroy on error
return hr;
}
VariantClear(&vOut); // does a deep destroy of source VARIANT
} // end iteration
}
// clean up here only if you do not return SafeArray as an [out, retval]
SafeArrayDestroy(pSA); // again does a deep destroy
转载请注明出处