在ATL项目中将NULL指针传递给进程外COM方法的正确方法

Right way to pass a NULL Pointer to a out of process COM Method in an ATL Project

本文关键字:方法 COM 进程 项目 ATL NULL 指针      更新时间:2023-10-16

在ATL项目中传递NULL指针到进程外COM方法的正确方法是什么?我已经创建了一个COM服务器通过COM代理

托管

如果它的问题,我使用VS2012在win7上运行。服务器项目用64位编译,客户端项目用32位编译

IDL类似于下面的

import "oaidl.idl";
import "ocidl.idl";
[   
    object, 
    uuid(37EFA952-7036-4398-93A6-6CDAD9DFC005), 
    dual,   
    nonextensible,  
    pointer_default(unique)
]
interface IGame : IDispatch
{   
    [id(1)] HRESULT passNull([in, out, unique] BSTR* avast, [out, retval] LONG* r);
};

声明为

STDMETHOD(passNull)(BSTR* avast, LONG* r);

和定义

STDMETHODIMP CGame::passNull(BSTR* avast, LONG* r)
{   
    *r= 0;
    return S_OK;
}

我的客户端代码是

void main()
{   
    CoInitialize( NULL ) ;  
    IGamePtr p( __uuidof( Game ) ) ;    
    p->passNull(NULL);  
    p= NULL ;   
    CoUninitialize() ;
}

我的理解是,添加唯一的限定符将允许我通过指针传递NULL,但奇怪的是,我一直得到消息

_hr 0x800706f4 : A null reference pointer was passed to the stub. 

我不知道你在做什么,CGame::passNull()函数根本没有洞察参数应该如何处理。但是存根有一个合法的婊子,你真的传递一个NULL指针。有效的客户端代码如下所示:

BSTR bs = NULL;
LONG r = p->passNull(&bs);
...
SysFreeString(bs);

您错误地认为将参数标记为[unique]足以让NULL指针通过。这不是实际发生的事情。

通过如下所示将接口声明为dual/oleautomation,您注册了一个通用代理(也称为PSOAInterface)来编组公寓之间的接口。

[
    object,
    uuid(955C1132-96DC-4221-86A3-BE4C8CEB698C),
    dual,
    oleautomation,
    nonextensible,
    pointer_default(unique)
]
interface IFoo : IDispatch
{
    [id(1)] HRESULT Bar1([in, unique] BSTR* psValue);
    [id(2)] HRESULT Bar2([in, out, unique] BSTR* psValue);
};

通用代理不尊重[唯一]参数,这就是你被卡住的地方。

如果你导入你创建的类型库和/或用COM/OLE Viewer检查它,你甚至不会注意到那里的唯一说明符,并且PARAMFLAG没有一个标志来帮助代理理解唯一性。

此代码将在指定行失败:

A(pFoo->Bar1(&sValue));
A(pFoo->Bar1(NULL)); // <<-- A null reference pointer was passed to the stub. 
A(pFoo->Bar2(&sValue));
A(pFoo->Bar2(NULL)); // <<-- A null reference pointer was passed to the stub.  

在你的ATL项目中,你通常有一个卫星代理/存根"PS"项目。您不需要将它用于由通用代理编组的接口,但是如果您选择这样做,您将使[unique]工作。

这样,上述所有四个调用都将到达服务器,而不会击中RPC_X_NULL_REF_POINTER,并根据需要使用NULL参数。您的定制PS将带您完成挑战。

这里是一个测试项目,如果你想要一个:NullBstrArgument(或,SVN链接)。

在IDL上注释dualoleautomation,注册COM服务器和PS库,在运行时有:

psValue 0x007CE150 // #1 call argument on server side
sValue 0x007CE02C "Application"
psValue 0x00000000 // #2 call argument on server side, NULL is OK
psValue 0x007CE0D0 // #3 call argument on server side
sValue 0x007CE02C "Bar2"
psValue 0x00000000 // #4 call argument on server side, NULL is OK

代理/存根登记

Proxy/stub DLL项目需要首先构建,但是您需要采取额外的步骤来激活其功能:

  1. DLL需要COM注册到regsvr32
  2. DLL需要在注册表中将其clsid注册为代理/存根COM类,这不是自动发生的:您需要使用REGISTER_PROXY_DLL构建PS项目(不存在于默认/模板项目中),否则实际服务器中的COM类需要注册自己适当地将PS注册到注册表中。

32/64位数

当服务器和客户端是不同的位元时,包括DLL代理托管服务器DLL,自定义代理/存根行为是一致的:你仍然可以传递NULL参数,它们通过良好。

位构建的问题是默认的Visual Studio项目布局设置的东西令人困惑和不正确,解决方案是不平凡的:

  • 你需要确保MIDL编译器以适当的位/环境执行
  • 您需要确保MIDL生成的文件不会在32/64版本之间混淆,特别是使文件进入不同的目录,以便稍后从各自的位置包含它们

你还必须确保你有两个 Win32和x64 PS库注册当你的服务器是x64,但客户端是Win32:客户端仍然需要一个32位代理。