2

What is the right way to pass a NULL Pointer to a out of process COM Method in an ATL Project? I have created a COM Server hosted through a COM Surrogate

If it matters I am using VS2012 running on Win 7. The Server Project is compiled in 64 bit and the Client Project in 32 bit

The IDL looks similar to below

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);
};

The declaration is

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

and the definition

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

And my client Code is

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

My understanding was, adding unique to the qualifier would allow me to pass a NULL through a pointer, but strangely, I keep on getting the message

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

2 回答 2

4

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

BSTR bs = NULL;
LONG r = p->passNull(&bs);
...
SysFreeString(bs);
于 2013-06-13T00:54:20.153 回答
3

您错误地假设将参数标记为 [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);
};

通用代理不支持 [unique] 参数,这就是您遇到的问题。

如果您导入您创建的类型库和/或使用 COM/OLE 查看器检查它,您甚至不会注意到那里的唯一说明符,并且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_POINTERNULL 参数,如您所愿。您的定制 PS 将带您完成挑战。

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

所以你评论dualoleautomation在 IDL 上,你注册了 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

代理/存根注册

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

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

32/64 位

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

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

  • 您需要确保 MIDL 编译器在适当的位数/环境下执行
  • 您需要确保 MIDL 生成的文件不会在 32/64 版本之间混淆,尤其是。使文件转到不同的目录,以便以后从各自的位置包含它们

当您的服务器仅为 x64 但客户端为 Win32 时,您还必须确保同时注册了 Win32 和 x64 PS 库:客户端仍需要 32 位代理。

于 2013-06-14T23:09:44.853 回答