COM编程:从接口定义到服务器实现
立即解锁
发布时间: 2025-08-20 01:59:30 阅读量: 1 订阅数: 3 


COM+编程实战:使用Visual C++和ATL构建企业级应用
### COM编程:从接口定义到服务器实现
#### 接口封送与内存管理
接口封送是COM编程中的重要环节,它有两种方式:基于MIDL生成代码的标准封送和自定义封送。标准封送性能高效,可由MIDL生成的代理/存根代码或类型库驱动。
- **代理/存根DLL**:需编译和链接以创建,要在客户端和服务器机器上注册。使用`regsvr32.exe`工具进行注册和注销操作,示例如下:
```plaintext
regsvr32.exe myproxystub.dll // 注册代理/存根到本地机器
regsvr32.exe –u myproxystub.dll // 注销代理/存根
```
若使用ATL创建COM服务器项目,ATL会生成模块定义文件和生成文件;若不使用ATL,可使用CreatePS实用工具。
- **类型库驱动的封送**:接口方法参数需使用自动化兼容类型,接口应标记为`oleautomation`或`dual`,类型库要在客户端和服务器机器上注册,可使用SDK提供的`LoadTypeLibEx` API进行注册。负责类型库封送的基础设施是通用封送器,目前实现在`OLEAUT32.DLL`中。
需注意,封送发生在接口级别,一个接口不能有多种封送机制,且只应注册代理/存根DLL或类型库中的一个。
在内存管理方面,参数的方向属性决定了客户端和服务器的角色:
| 参数类型 | 内存分配 | 内存释放 |
| ---- | ---- | ---- |
| [in] | 客户端 | 客户端 |
| [out] | 服务器 | 客户端 |
| [in, out] | 客户端 -> 服务器 -> 服务器 | 客户端 |
常用的内存管理API包括:
- 对于`BSTR`:`SysAllocString`、`SysReAllocString`、`SysFreeString`等。
- 对于`VARIANT`:`VariantInit`和`VariantClear`。
- 对于原始内存:`CoTaskMemAlloc`、`CoTaskMemReAlloc`和`CoTaskMemFree`。
以下代码展示了服务器端分配内存和客户端释放内存的过程:
```cpp
// Server code
STDMETHODIMP CMyExplore::GetMyLeaders(MYLEADERS *pDataArray)
{
pDataArray->lElements = 2;
// 原始内存分配
pDataArray->pData =
(MYLEADER*) ::CoTaskMemAlloc(2 * sizeof(MYLEADER));
// BSTR分配
pDataArray->pData[0].bsFirstName=SysAllocString(L"Mohandas");
pDataArray->pData[0].bsLastName=SysAllocString(L"Gandhi");
// VARIANT中的BSTR分配
VARIANT& v0 = pDataArray->pData[0].vTitle;
::VariantInit(&v0);
V_VT(&v0) = VT_BSTR;
V_BSTR(&v0) = ::SysAllocString(L"Mahatma");
// BSTR分配
pDataArray->pData[1].bsFirstName=SysAllocString(L"Winston");
pDataArray->pData[1].bsLastName=SysAllocString(L"Churchil");
// VARIANT中的BSTR分配
VARIANT& v1 = pDataArray->pData[1].vTitle;
::VariantInit(&v1);
V_VT(&v1) = VT_BSTR;
V_BSTR(&v1) = ::SysAllocString(L"Sir");
return S_OK;
}
// Client code fragment
MYLEADERS leaders;
HRESULT hr = pMyExplore->GetMyLeaders(&leaders);
// 释放内存
for(i=0; i<leaders.lElements; i++) {
MYLEADER* pLeader = &leaders.pData[i];
::VariantClear(&pLeader->vTitle); // 释放VARIANT中的BSTR
::SysFreeString(pLeader->bsFirstName); // 释放BSTR
::SysFreeString(pLeader->bsLastName); // 释放BSTR
}
::CoTaskMemFree(leaders.pData); // 释放原始内存分配
```
为减少人为错误,可使用ATL提供的包装类,如`CComBSTR`和`CComVariant`。
#### 多IDL文件组织与接口定义
在COM开发中,常需处理多个IDL文件。为避免接口定义错误和维护困难,理想情况下接口应定义在一个文件中,其他IDL文件可使用`import`关键字引用。常见的做法是在IDL文件开头导入`oaidl.idl`和`ocidl.idl`:
```idl
import "oaidl.idl";
import "ocidl.idl";
```
为防止类型库中定义重复,需使用`importlib`关键字,如导入`stdole2.tlb`和`stdole32.tlb`:
```idl
importlib("stdole32.tlb");
importlib("stdole2.tlb");
```
接下来,将C++接口转换为IDL接口,步骤如下:
1. **创建文件**:使用记事本或喜欢的编辑器创建`video.idl`文件。
2. **访问SDK定义的IDL文件**:
```idl
import "oaidl.idl";
import "ocidl.idl";
```
3. **定义接口块**:
```idl
[
]
interface IVideo : IUnknown
{
};
```
4. **添加接口属性**:必需的属性是`object`和`uuid`,建议添加`helpstring`和`pointer_default`。可使用`guidgen.exe`获取接口的GUID:
```idl
[
object,
uuid(318B4AD0-06A7-11d3-9B58-0080C8E11F14),
helpstring("IVideo Interface"),
pointer_default(unique)
]
```
5. **添加接口方法**:
```idl
interface IVideo : IUnknown
{
HRESULT GetSignalValue([out] long* val);
};
```
6. **添加方法属性**:
```idl
interface IVideo : IUnknown
{
[helpstring("Obtain the signal value")]
HRESULT GetSignalValue([out] long* val);
};
```
7. **重复步骤3 - 6定义`ISVideo`接口**:
```idl
[
object,
uuid(318B4AD1-06A7-11d3-9B58-0080C8E11F14),
helpstring("ISVideo Interface"),
pointer_default(unique)
]
interface ISVideo : IUnknown
{
[helpstring("Obtain the S-Video signal value")]
HRESULT GetSVideoSignalValue([out, retval] long* val);
};
```
8. **定义库块**:
```idl
[
]
library VcrLib
{
};
```
9. **添加库属性**:必需属性是`uuid`,建议添加版本号和`helpstring`:
```idl
[
uuid(318B4AD2-06A7-11d3-9B58-0080C8E11F14),
version(1.0),
helpstring("VCR Type Library")
]
```
10. **添加`importlib`语句**:
```idl
library VcrLib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");
};
```
11. **添加`coclass`块**:
```idl
[
]
coclass VCR
{
};
```
12. **定义`coclass`属性**:必需属性是`uuid`,建议添加`helpstring`:
```idl
uuid(318B4AD3-06A7-11d3-9B58-0080C8E11F14),
helpstring("VCR Class")
```
13. **列出要公开的接口**:
```idl
interface IVideo;
interface ISVideo;
```
最终的`Video.idl`文件如下:
```idl
// File Video.idl
import "oaidl.idl";
import "ocidl.idl";
[
object,
uuid(318B4AD0-06A7-11d3-9B58-0080C8E11F14),
helpstring("IVideo Interface"),
pointer_default(unique)
]
interface IVideo : IUnknown
{
[helpstring("Obtain the signal value")]
HRESULT GetSignalValue([out, retval] long* val);
};
[
object,
uuid(318B4AD1-06A7-11d3-9B58-0080C8E11F14),
helpstring("ISVideo Interface"),
pointer_default(unique)
]
interface ISVideo : IUnk
```
0
0
复制全文
相关推荐










