COM自动化编程:数据类型、接口与集合的全面解析
立即解锁
发布时间: 2025-08-20 01:59:30 阅读量: 1 订阅数: 3 


COM+编程实战:使用Visual C++和ATL构建企业级应用
# COM自动化编程:数据类型、接口与集合的全面解析
## 1. 自动化概述
COM的目标之一是让组件开发者能选择自己喜欢的编程语言。然而,每种语言有其独特的数据类型,且不同语言数据类型间没有安全的映射,这给COM带来了挑战。接下来将详细探讨这些问题及COM的解决方案。
## 2. 基本数据类型
不同语言中,许多数据类型的语义并不相同。例如,Visual C++中的`char`类型在VB中没有等效匹配,`short`类型在VB中对应`integer`类型。此外,日期和货币数据类型也缺乏统一的定义。
COM的解决方案是限制跨所有语言工作的基础数据类型。SDK提供了一个枚举类型`VARTYPE`,用于精确定义每个自动化兼容数据类型的语义。以下是一些重要的`VARTYPE`及其在C++和VB中的可比数据类型:
| VARTYPE | IDL解释 | Microsoft VC++ | Microsoft VB |
| ---- | ---- | ---- | ---- |
| VT_I2 | 16位有符号 | short | Integer |
| VT_I4 | 32位有符号 | long | Long |
| VT_DATE | 日期 | DATE (double) | Date |
| VT_CY | 货币 | CY (int64) | Currency |
| VT_R4 | 单精度小数 | float | Single |
| VT_R8 | 双精度小数 | double | Double |
| VT_UNKNOWN | 接口指针 | IUnknown* | 接口引用 |
需要注意的是,无符号数据类型虽看似自动化兼容(MIDL不会报错),但并非所有语言都支持。若目标是自动化编程,请勿使用无符号数据类型。
## 3. 字符串
C/C++语言对以NULL结尾的OLECHAR字符串提供了足够的支持,但VB和Java等其他编程语言更喜欢长度前缀的OLECHAR字符串。为满足所有编程社区的需求,SDK引入了扩展数据类型`BSTR`。
`BSTR`是长度前缀且以NULL结尾的OLECHAR字符串。长度前缀表示字符串占用的字节数(不包括终止NULL),并以四字节整数形式存储在字符串的第一个字符之前。
不能将`LPWSTR`用作`BSTR`,但可以将`BSTR`用作`LPWSTR`。因为封送器使用`BSTR`指向位置之前的内存中指定的值来打包数据,如果将`LPWSTR`用作`BSTR`,该内存位置可能是无效地址或包含错误值,可能导致访问冲突或传输大量错误数据。
为管理`BSTR`,SDK提供了几个API函数,其中两个重要的API用于处理内存分配问题:
```cpp
BSTR SysAllocString(const OLECHAR* sz);
void SysFreeString(BSTR bstr);
```
`SysAllocString`可用于分配和初始化`BSTR`,`SysFreeString`可用于释放之前通过调用`SysAllocString`分配的内存。
当`BSTR`作为`[in]`参数传递时,调用者负责在调用方法前构造参数,并在方法返回后释放内存。示例代码如下:
```cpp
// 接口方法定义
HRESULT RevisedStringParam([in] BSTR bstrVal);
// 客户端代码
LPOLESTR pwszName = OLESTR("Alexander, The Great");
BSTR bstrName = ::SysAllocString(pwszName);
HRESULT hr = pMyExplore->RevisedStringParam(bstrName);
::SysFreeString(bstrName);
```
ATL通过提供`CComBSTR`类简化了`BSTR`的使用,客户端代码可改写为:
```cpp
#include <atlbase.h>
...
CComBSTR bstrName = "Alexander, The Great";
HRESULT hr = pMyExplore->RevisedStringParam(bstrName);
```
Visual C++的原生COM支持定义了`_bstr_t`类来处理`BSTR`,客户端代码示例如下:
```cpp
#include <comdef.h>
...
_bstr_t bstrName = "Alexander, The Great";
HRESULT hr = pMyExplore->RevisedStringParam(bstrName);
```
当`BSTR`作为`[out]`参数传递时,服务器负责分配字符串,调用者负责释放它。示例代码如下:
```cpp
// 接口方法定义
HRESULT GetString([out] BSTR* pVal);
// 服务器端代码
STDMETHODIMP CMyExplore::GetString(BSTR *pVal)
{
*pVal = ::SysAllocString(OLESTR("Alexander, The Great"));
if (NULL == *pVal) {
return E_OUTOFMEMORY;
}
return S_OK;
}
// 客户端代码
BSTR bstrName = NULL;
HRESULT hr = pMyExplore->GetString(&bstrName);
if (SUCCEEDED(hr)) {
// 使用bstrName
::SysFreeString(bstrName);
}
```
`CComBSTR`类的析构函数会自动调用`SysFreeString`,可避免内存泄漏。但`_bstr_t`类不提供将实例用作`[out]`参数的功能。
选择`CComBSTR`还是`_bstr_t`取决于具体需求。`CComBSTR`可用于`[out]`参数,而`_bstr_t`提供实例的引用计数,在实例频繁赋值给其他实例时更高效。
## 4. 布尔值
SDK定义了`VARIANT_BOOL`数据类型来处理布尔变量,其可能的值为`VARIANT_TRUE`和`VARIANT_FALSE`,分别对应VB和VBScript中的`True`和`False`。
需要注意的是,IDL定义的`boolean`数据类型仅在C/C++和Java中受支持,若要定义自动化兼容的布尔参数,应始终使用`VARIANT_BOOL`。
## 5. 变体
VB和Java等编程语言支持类型化数据,即变量可定义为持有特定数据类型。但VBScript和Jscript等脚本语言为简化编程,采用无类型数据,仅支持一种数据类型——变体。变体可以包含任何类型的数据,当为变体分配特定数据类型或将一个变体赋值给另一个变体时,运行时系统会自动进行必要的转换。
SDK定义了一个判别联合`VARIANT`来处理变体,每个受支持的数据类型都有对应的判别值,以`VARTYPE`表示。以下是一些常用的`VARTYPE`:
| 类型 | 描述 |
| ---- | ---- |
| VT_EMPTY | 无关联值 |
| VT_NULL | 包含SQL风格的NULL值 |
| VT_I2 | 2字节有符号整数(short) |
| VT_I4 | 4字节有符号整数(long) |
| VT_R4 | 4字节实数(float) |
| VT_R8 | 8字节实数(double) |
| VT_CY | 64位货币 |
| VT_DATE | 日期(double) |
| VT_BSTR | BSTR |
| VT_DISPATCH | IDispatch* |
| VT_ERROR | HRESULT |
| VT_BOOL | VARIANT_BOOL |
| VT_VARIANT | VARIANT* |
| VT_IUNKNOWN | IUnknown* |
| VT_DECIMAL | 16字节定点数 |
| VT_UI2 | 无符号短整型 |
| VT_UI4 | 无符号长整型 |
| VT_I8 | 有符号64位整数(int64) |
| VT_UI8 | 无符号64位整数 |
| VT_INT | 有符号机器整数 |
| VT_UINT | 无符号机器整数 |
若要表示变体是引用,可将`VT_BYREF`标志与上述标签组合。`VARIANT`也可表示为`VARIANTARG`类型,通常`VARIANTARG`用于表示方法参数,`VARIANT`用于引用方法结果。
变体在使用前必须初始化,使用后必须清除。SDK提供了以下两个API用于这些目的:
```cpp
void Varia
```
0
0
复制全文
相关推荐










