C++ 在ATL项目中向进程外COM方法传递空指针的正确方法
在ATL项目中,向进程外COM方法传递空指针的正确方法是什么?我已经创建了一个通过COM代理托管的COM服务器 如果重要的话,我使用的是在Win7上运行的VS2012。服务器项目以64位编译,客户端项目以32位编译 IDL看起来与下面的类似C++ 在ATL项目中向进程外COM方法传递空指针的正确方法,c++,windows,visual-c++,com,atl,C++,Windows,Visual C++,Com,Atl,在ATL项目中,向进程外COM方法传递空指针的正确方法是什么?我已经创建了一个通过COM代理托管的COM服务器 如果重要的话,我使用的是在Win7上运行的VS2012。服务器项目以64位编译,客户端项目以32位编译 IDL看起来与下面的类似 import "oaidl.idl"; import "ocidl.idl"; [ object, uuid(37EFA952-7036-4398-93A6-6CDAD9DFC005), dual, nonex
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() ;
}
我的理解是,将unique添加到限定符将允许我通过指针传递NULL,但奇怪的是,我一直在得到消息
_hr 0x800706f4 : A null reference pointer was passed to the stub.
我不知道您在做什么,CGame::passNull()函数根本没有给出如何处理参数的细节。但是存根有一个合法的婊子,你真的在传递一个空指针。有效的客户端代码如下所示:
BSTR bs = NULL;
LONG r = p->passNull(&bs);
...
SysFreeString(bs);
您错误地认为将参数标记为[unique]就足以让
NULL
指针通过。事实并非如此
通过如下所示将您的接口声明为dual
/oleautomation
,您可以注册一个通用代理(也称为psoanterface
)来封送公寓之间的接口
[
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);
};
Universal proxy不支持[唯一]参数,这就是您陷入困境的地方
如果导入您创建的类型库和/或使用COM/OLE Viewer检查它,您甚至不会注意到其中的唯一说明符,并且没有帮助代理理解唯一性的标志
此代码将在指定行失败:
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.
代理/存根注册
需要首先构建代理/存根DLL项目,但是您需要采取其他步骤来激活其功能:
register\u proxy\u DLL
(在默认/模板项目中不存在)构建PS项目否则,实际服务器中的COM类需要适当地注册自己,将PS注册放入注册表NULL
参数,并且它们可以正常运行
bitness构建的问题在于默认的Visual Studio项目布局设置混乱且不正确,解决方案并不简单:
- 您需要确保MIDL编译器以适当的位/环境执行
- 您需要确保MIDL生成的文件不会在32/64版本之间混淆,尤其是使文件转到不同的目录,以便以后从相应的位置包括它们
当您的服务器仅为x64,但客户端为Win32时,您还必须确保同时注册了Win32和x64 PS库:客户端仍然需要一个32位代理。为什么要传递
NULL
指针?我知道你想利用unique
,但它看起来更像是一种黑客行为,不传递NULL
s不是更安全吗?为什么这个参数不是[in,out]?当它刚刚进入时,它应该是BSTR,而不是BSTR*@HansPassant:很高兴见到你(我知道你可以救我:-))。不管怎么说,这都没关系,即使对[进来,出去]来说也是一样的。编辑问题以更改参数[输入,输出]。顺便说一句,因为unique只适用于[in],我想把它排除在外discussion@RomanR.:这是一个非常简单的示例,但在实际场景中,被调用函数的行为将根据参数值而变化,即NULL或notnull。如果必须确保始终传递NOTNULL,则需要维护同一函数的多个版本。考虑一下这个例子,我在一个现有的功能上创建了一个包装器,它可以接受NULL并相应地进行行为。如果这一切都在进行中,它就可以正常工作。可能是一个代理/存根。你能详细说明一下你使用的“COM代理”吗?我说不出话来。你能解释一下,为什么这是工作的而不是我的呢?与C语言或C++语言中引用的参数进行比较。引用必须有效且非NULL,因为它产生参数值存储位置,所以实际值可能为NULL。我不知道MIDL是否有语法来表示引用可能是空的,只是我自己从未使用过。我想我的结论太快了。您所展示的方法之所以有效,是因为您实际上并没有传递NULL,而是传递一个以NULL结尾的空BSTR。它们是两种不同的东西。因为我已经分配了赏金,我将打开一个新的。不,这是一个空指针。BSTR中没有魔法,它只是字符串指针的typedef。直到从SysAllocString()的返回值赋值后,它才会启动。小心那些乐于将NULL解释为空字符串的代码。我不太相信。原因1:正如我所看到的,您不需要一个惟一的属性来传递一个空的以null结尾的字符串,就像我们在这里所做的那样。原因2:如果参数类似于long*
,这将不起作用。我想知道的是博客中提到的内容。我知道这是纯COM,但我希望在我的ATL代码中有类似的行为。我使用了您的测试项目,当服务器和客户机以不同的位运行时,我似乎无法在进程外COM中使用卫星代理/存根来代替通用代理/存根。当32位客户端调用QueryInterface时,它会窥视WOW6432节点以查找密钥ProxyStubClsid32,而不是在HKCR\Interface\{GUID}\ProxyStubClsid32中搜索。如果您想了解我在做什么,可以检查x64服务器中的附加文件,因为DLL代理可以在win32客户端上正常工作,请参阅上面的更新。我更新了我的项目
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