COM+编程中的调用上下文、对象激活与多线程编程
立即解锁
发布时间: 2025-08-20 01:59:31 阅读量: 1 订阅数: 3 


COM+编程实战:使用Visual C++和ATL构建企业级应用
### COM+编程中的调用上下文、对象激活与多线程编程
#### 1. 调用上下文
在处理方法调用时,对象可能希望获取其调用者的更多信息,例如检查调用者的凭据。然而,对象上下文仅存储对象自身的上下文信息,并不包含调用者的信息。
为了解决这个问题,COM+ 为当前方法调用定义并关联了一个上下文。方法可以通过调用 `CoGetCallContext` API 来获取这个调用上下文,其原型如下:
```cpp
WINOLEAPI CoGetCallContext( /*IN*/ REFIID riid,
/*OUT*/ void **ppInterface );
```
调用上下文支持许多接口,参数 `riid` 指定了所请求接口的 IID。我们关注的接口有 `IServerSecurity` 和 `ISecurityCallContext`。
#### 2. 对象激活
对象激活由 COM+ 提供的服务控制管理器(SCM)处理。当客户端使用 `CoCreateInstance(Ex)` 或 `CoGetClasObject` 请求激活对象时,SCM 会定位并加载适当的组件,然后将原始对象或其代理的接口指针交给客户端。
对象激活主要有以下三种基本场景:
| 场景 | 描述 | 运行方式 |
| ---- | ---- | ---- |
| 1 | 激活器和组件都在本地机器上,且组件属于库应用程序 | 激活的对象将与激活器在同一进程中运行 |
| 2 | 激活器和组件都在同一台机器上,且组件属于服务器应用程序 | 激活的对象将与激活器在不同进程中运行 |
| 3 | 组件在远程机器上 | 组件只能指定属于服务器应用程序 |
下面详细介绍这三种场景:
- **进程内激活**:客户端通过调用 `CoCreateInstance` API 创建实例,其原型如下:
```cpp
STDAPI CoCreateInstance(REFCLSID rclsid, LPUNKNOWN pUnkOuter,
DWORD dwClsContext, REFIID riid, LPVOID* ppv);
```
如果被激活对象的运行时要求与激活器相同,激活器将获得实际对象的原始引用。若运行时要求不匹配,SCM 会为对象创建代理 - 存根对,并在客户端和服务器端设置一个或多个拦截策略。代理和存根通过 ORPC 通道进行通信。拦截策略基于客户端(激活器)和服务器对象的上下文差异设置,客户端策略在方法调用时和返回后调用,服务器端策略在调用到达和离开服务器时调用。对于进程内通信,ORPC 通道使用最有效的通信传输方式。
```mermaid
graph LR
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
A[客户端]:::process -->|CoCreateInstance| B[SCM]:::process
B -->|加载组件| C[组件]:::process
A <-->|ORPC通道| C
```
- **进程外激活(本地主机)**:请求的组件属于服务器应用程序,客户端仍使用 `CoCreateInstance` 激活对象。此时,SCM 会创建一个代理进程(`DllHost.exe`),并将组件加载到该代理进程中。其余机制与跨上下文通信类似,不同之处在于 ORPC 通道使用的传输方式是 LRPC(轻量级 RPC),它是 RPC 的 Microsoft 专有变体,调用语法与 DCE RPC 运行时相同,但经过优化以减少数据复制并消除对网络代码的访问。
- **远程激活(分布式计算)**:要远程激活组件,需要在实例创建时指定远程机器的名称。由于 `CoCreateInstance` API 没有提供此功能,SDK 定义了其扩展版本 `CoCreateInstanceEx`,原型如下:
```cpp
HRESULT CoCreateInstanceEx(
REFCLSID rclsid, //CLSID of the object to be created
IUnknown *punkOuter, //If part of an aggregate,
//the controlling IUnknown
DWORD dwClsCtx, //CLSCTX values
COSERVERINFO *pServerInfo, //Machine on which the object
//should be instantiated
ULONG cmq, //Number of MULTI_QI structures
//in pResults
MULTI_QI *pResults //Array of MULTI_QI structures
);
```
`COSERVERINFO` 结构主要用于标识远程机器,默认允许所有 UNC 和 DNS 名称。它的次要用途是在对象激活时指定不同的安全协议或客户端身份。`CoCreateInstanceEx` 可以在一次 API 调用中获取多个接口指针。以下是一个实例化远程对象并获取 `IUnknown` 接口指针的代码示例:
```cpp
HRESULT
CPLCreateInstance(
LPCOLESTR pwszMach, // [in] Remote machine
const CLSID& clsId, // [in] Class ID
IUnknown** ppOut, // [out, retval] instance handle
DWORD dwClsCtx // [in] CLSCTX values
)
{
*ppOut = NULL;
COSERVERINFO serverInfo;
serverInfo.dwReserved1 = 0;
serverInfo.pwszName = const_cast<LPOLESTR>(pwszMach);
serverInfo.pAuthInfo = NULL;
serverInfo.dwReserved2 = 0;
MULTI_QI mqiEntry;
mqiEntry.pIID = &IID_IUnknown;
mqiEntry.pItf = NULL;
mqiEntry.hr = 0;
HRESULT hr = ::CoCreateInstanceEx(clsId,
NULL,
dwClsCtx,
&serverInfo,
1,
&mqiEntry);
if (FAILED(hr)) {
return hr;
}
_ASSERT (NULL != mqiEntry.pItf);
*ppOut = mqiEntry.pItf;
return hr;
}
```
设置代理 - 存根和拦截策略的机制与进程外激活类似,不同之处在于与远程主机通信时,ORPC 通道在 Windows 2000 上首选使用传输控制协议(TCP),在 Windows NT 4.0 上首选使用用户数据报协议(UDP)。
#### 3. 在不同上下文中执行
- **使用激活器的上下文**:如果被激活对象的配置与激活器的上下文不匹配,对象将在不同的上下文中激活。但有些组件不需要自己的配置服务,而是希望使用激活器的配置服务。COM+ 支持一种配置选项,确保对象只能在其激活器的上下文中激活。如果激活器的上下文无法支持新对象,`CoCreateInstance` 将失败并返回错误 `CO_E_ATTEMPT_TO_CREATE_OUTSIDE_CLIENT_CONTEXT`。如果成功,新对象的所有调用将在激活器的上下文中处理。
- **使用调用者的上下文**:组件实现者可能认为组件不需要任何 COM+ 服务
0
0
复制全文
相关推荐









