STL 兼容的 SafeArrays 类






4.42/5 (15投票s)
2001年12月19日
6分钟阅读

173691

2332
封装了 SafeArray 的细节,使其与 STL 的风格一致。隐藏了所有繁琐的细节。
它做什么?
它允许在所有自动化支持的类型的 SAFEARRAY
上透明地使用 STL 迭代器、算法和习惯用法。
何时应使用它?
无论何时你想从 C++ 向自动化客户端传递纯数据,或者从自动化客户端接收纯数据。对于 VB 客户端,数组的使用很简单——现在你从 C++ 使用数组可以更加强大。随着最近对 UDTs 的自动化封送支持,这种技术是将任意数量的语义空数据从一个进程传输到另一个进程的有效手段。你甚至可以使用 SAFEARRAY
来传输 COM 接口(或 IDispatch
)指针的数组。
为何应使用它?
看看这个!
#include <algorithm> #include "VectorSafeArray.h" #include "VectorSafeArrayRecord.h" IID StructIID = {0xA8B2548C,0x9324,0x43B2,0xB1,0xAD,0x48,0xE2,0x84,0x05,0x2A,0x7E}; typedef safearrayvec::RecordType<TestStructure1, &LIBID_ATLDEMO, &StructIID, 1, 0> Struct1; typedef safearrayvec::VectorSafeArray< TestStructure1, Struct1> Struct1Array; STDMETHODIMP CTestVBStructure::ReverseArray(struct tagSAFEARRAY **sp) { Struct1Array a1; // Since sp is an in,out parameter hand over // ownership to the Array class for now. a1.Attach( sp); // Let STL do the business std::reverse( a1.begin(), a1.end()); // Now transfer ownership back to the out parameter *sp = a1.Detach(); return S_OK; }
只需 **五** 行代码即可反转用户定义结构 SAFEARRAY
的顺序!这同样可以适用于任何自动化数据类型上的任意算法。不要再考虑用复杂的方式来完成它了!
我该如何使用它?
下载源代码(天哪!)。使头文件可用于你的项目。有一个小的库文件,其中包含几个静态数据成员的定义——将其包含在你的链接中,或者选择性地将适当的 .cpp 文件添加到你的项目中。你需要 #include "VectorSafeArray.h"
以及至少一个 VectorSafeArrayXXX
文件。
如果你正在使用一个结构体,你需要在 IDL 文件中声明它,并确保它有一个关联的 GUID。然后 typedef
RecordType
以便匹配。
对于 BSTR
、VARIANT
、IUnknown*
和 IDispatch*
,有预定义的类型可供使用。对于基本数据类型(自动化兼容),只需 typedef FundType<type, VARENUM>
。
示例代码
示例代码包含一个 ATL DLL,它提供两个接口,通过两个类(没有特别的原因),以及一个 MFC DLL,它有一个类和接口,演示了该库的用法。关联的 VB6 客户端用于驱动示例并作为粗略的测试平台。还有一个 VBScript 示例,它使用 VARIANT
将数组作为 [in/out]
参数传递。
它有多高效?
还算可以!VectorSafeArray<>
类提供与 std::vector<>
相同的复杂度保证,但由于它使用 Windows 32 SafeArrayxxx
函数,所以速度较慢。提供的 RecordType<>
模板实现使用 IRecordInfo
函数来分配、复制和销毁内存。这显然比较慢,但使得实现更容易。如果发现这在你的应用程序中是个问题,你应该替换它为自定义类来处理你的结构。
VariantType
类也非常慢,它使用 VariantXXX
函数进行复制、分配和销毁。同样,如果这在你的应用程序中是个问题,请考虑替换它。
它是如何工作的?
很高兴你问了!但如果你真的不想知道,你仍然可以愉快地使用它——就像我们其他人使用 STL、ATL 或其他任何东西一样!
我之所以被激励去编写一个 UDTs 的 SAFEARRAY
的通用处理器,是因为我发现自己写了大量的代码来测试一个想法。一旦开始,我觉得如果我要包装 SAFEARRAY
,就应该以最有用的方式来做:这直接引导我完成了现在的实现。我选择模仿 std::vector<>
,因为这个类的内部结构就是一堆内存,所有包含的项都是连续排列的,就像一维 SAFEARRAY
一样!所以我想,我可以‘利用’很多现有的代码。
VectorSafeArray<>
是 Visual C++ 6.0 附带的 <vector> 头文件的重写。更改是为了适应底层存储由 SAFEARRAY
拥有这一事实,并且所有内存分配、重新分配和释放都通过 SafeArrayXXX
函数完成。因此,该类没有 Allocator 模板参数。
为了支持 STL 容器操作,一个类需要具有一致的复制构造函数、默认构造函数和赋值操作。SAFEARRAY
中由自动化支持的各种类型对于这些操作有非常不同的要求。其中要求最苛刻的是用户定义的结构。模板类 RecordType
template< typename sT, const IID *pLIBID, const IID *pTypeID, const int vMajor = 1, const int vMinor = 0> class RecordType : public sT
通过使用 IRecordInfo
提供所需语义。typename sT
是用户的结构类型,其余参数引用定义类型库,IRecordInfo
将从中初始化。因为 SAFEARRAY
是‘用户结构’的连续集合,所以很明显 RecordType
不能有任何数据成员或虚函数——因为如果有,它将不再适合 SAFEARRAY
中的内存!(参见下面的*注释*)。但为了验证传递的数组的正确性,容器需要能够访问存储的类型。每个 XXXXType
实例都持有一个 TInfo
类的静态副本,该类存储验证 SAFEARRAY
所需的 Flags。这个 TInfo
类有一个函数 void Validate(SAFEARRAY *)
,它验证 SAFEARRAY
的 Flags,并可选地通过函数指针调用 XXXXType
的静态成员。XXXXType
还持有一个第二个函数指针供 XXXXType
验证代码使用(如果需要)。这个第二个函数指针目前仅由 RecordType<>
用于访问缓存的 ITypeInfo *
副本。
通过 ITypeInfo
实现的这种方法相对较慢。如果发现测试时的速度是个问题,请将通用的 RecordType
替换为特定的实现,该实现利用你对存储结构的了解来优化构造和复制。例如,一个只有基本类型的结构可以通过编译器的按位复制(如果提供了复制构造函数)来复制。
FundType<'>
template< typename T, const VARENUM vt> struct FundType
用于所有基本类型的类型化数组。SAFEARRAY
将保存一个 VT 类型标识符,该类将针对提供的模板参数验证它。对于有效值和类型,请参阅 Platform SDK:Automation VARENUM
- 标有 S 的类型是 OK 的。
VariantType
类型是完整的,不需要任何参数。
template< typename iType, const unsigned short Flag> struct InterfaceType
有两个 typedef
过的实例可用于 IUnknown
和 IDispatch
。
typedef InterfaceType< IUnknown, FADF_UNKNOWN> IUnknownType; typedef InterfaceType< IDispatch, FADF_DISPATCH > IDispatchType;
限制
多线程尚未经过测试。我相信该库对多线程访问应该是安全的,前提是只有一个线程一次访问任何单个容器实例。唯一需要关注的区域是 VariantType
中 ::VariantCopy
的使用以及 RecordType<>
中 IRecordInfo *
的访问——如果两个或多个相同记录类型的数组实例正在使用,它们可能会被两个或多个线程同时调用。这两者都可以通过 #define MULTITHREADING
来保护。
该库在 UNICODE 和 MBCS 中都可以编译。事实上,MFC 示例是 MBCS,ATL 是 UNICODE。
使用 SAFEARRAY(type) *p
参数编译 IDL 文件时,您将看到此消息:“warning MIDL2039 : interface does not conform to [oleautomation] attribute :
”。此警告可以安全地忽略。
注释
MFCTest 和 VectorSafeArray
已在 .NET Beta 2 (VC++ 7) 下编译和测试,并与 VB6 VBForMFC 一起测试。代码运行良好,有几个关于有符号/无符号比较的警告,可以安全地忽略。
该库包含必要的行,通过使用 Loki 库的一小部分——Andrei Alexandrescu“Modern C++ Design”,Addison Wesley——来强制执行所需的编译时大小等效性。如果你还没有读过它,并且对 C++ 感到好奇,它会让你大开眼界!VC++ 的*下一个*版本(.NET 发布之后!)应该能够编译它!其中一些思想如果 VC++ 支持模板特化,将适用于这个库!