在ATL项目中将NULL指针传递给进程外COM方法的正确方法
Right way to pass a NULL Pointer to a out of process COM Method in an ATL Project
在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上注释dual
和oleautomation
,注册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项目需要首先构建,但是您需要采取额外的步骤来激活其功能:
- DLL需要COM注册到regsvr32
- 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位代理。
- 获取从 C# 调用的 COM 方法的错误消息
- 将 COM 事件从 C# 触发到C++的正确方法是什么?
- com 释放方法不降至0
- C com 方法参数扣除
- 为什么我的COM智能指针从方法返回时会被删除
- 当从一个应用程序调用时,在DLL方法中创建COM接口指针是有效的,但当从另一个应用软件调用时则无效
- 如何在 JavaScript 代码中调用 COM 方法 (c++)
- 在 .NET 中调用 COM 枚举器的正确方法是什么?
- 删除/编辑 ATL COM DLL 属性/方法
- 当输出参数是类时,如何在C++中调用COM方法
- 可视化 如何删除 ATL COM C++中的方法
- 是否有手动组织COM封送处理的方法
- 使用本机/C++代码在VS2010中创建简单COM对象的最简单方法是什么
- 如何在 COM 中对方法执行返回类型
- COM属性方法和常规接口方法之间的区别是什么
- 初始化COM C++服务多线程的正确方法
- #导入COM DLL生成错误的方法签名
- COM / DCOM:服务器存根不会为现有接口中的新方法分配内存
- COM 方法仅在使用暂停间隔调用时返回正确的值
- COM:始终需要方法参数属性