Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/130.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/windows/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 在ATL项目中向进程外COM方法传递空指针的正确方法_C++_Windows_Visual C++_Com_Atl - Fatal编程技术网

C++ 在ATL项目中向进程外COM方法传递空指针的正确方法

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

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

如果重要的话,我使用的是在Win7上运行的VS2012。服务器项目以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() ;
}
我的理解是,将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项目,但是您需要采取其他步骤来激活其功能:

  • DLL需要在regsvr32中进行COM注册
  • DLL需要在注册表中将其CLSID注册为代理/存根COM类,这不是自动发生的:您需要使用
    register\u proxy\u DLL
    (在默认/模板项目中不存在)构建PS项目否则,实际服务器中的COM类需要适当地注册自己,将PS注册放入注册表
  • 32/64位 当服务器和客户端具有不同的位时,包括服务器DLL的DLL代理宿主,自定义代理/存根行为是一致的:您仍然可以传递
    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