通过准隔离COM使用EXE服务器
我已经能够使用清单,特别是MSBuild任务GenerateApplicationManifest,以便我们的主应用程序使用独立的COM。我可以创建在DLL中实现的所有COM对象,而无需在客户端计算机上注册DLL。但是,我很贪婪 我们的应用程序套件也有一些单独的应用程序,通常通过COM调用。对于这些,有人说你不能做EXE到EXE隔离COM。严格地说,这是真的,但我已经得到了90%的方式,在其他论坛上,我看到其他人提供线索,以获得剩余的方式 对于我的EXE服务器,我在清单中有一个带有EXE服务器名称的条目,在该条目中有一个子条目,因此当ATL服务器调用通过准隔离COM使用EXE服务器,com,registration-free-com,Com,Registration Free Com,我已经能够使用清单,特别是MSBuild任务GenerateApplicationManifest,以便我们的主应用程序使用独立的COM。我可以创建在DLL中实现的所有COM对象,而无需在客户端计算机上注册DLL。但是,我很贪婪 我们的应用程序套件也有一些单独的应用程序,通常通过COM调用。对于这些,有人说你不能做EXE到EXE隔离COM。严格地说,这是真的,但我已经得到了90%的方式,在其他论坛上,我看到其他人提供线索,以获得剩余的方式 对于我的EXE服务器,我在清单中有一个带有EXE服务器名
LoadRegTypeLib()
时,调用将成功。这很有效
当然,棘手的部分是,您不能将EXE服务器的条目放入客户端应用程序清单中,并期望CoCreateInstance()
成功(通过启动服务器EXE并执行COM所做的所有其他操作)
因为我知道要启动什么EXE服务器,所以我可以假装很多。我可以调用CreateProcess()
,然后在客户端应用程序中调用WaitForInputidle()
,使我的服务器为客户端应用程序中的CoCreateInstance()做好准备
如果我调用CoCreateInstance()
并在客户端应用程序中请求IDispatch
接口,则调用成功,我可以调用Invoke()
,一切正常
现在贪婪的部分来了
IDispatch可以正常工作,但我希望能够通过从IDispatch派生的双接口进行调用。我想这样做,因为我有很多这样编写的代码,语法更简单,异常处理已经存在
但是,当我在我的IDispatch
接口上为双接口调用QueryInterface()
时,我得到一个E_NOINTERFACE返回。我在服务器EXE中的ATL server对象中设置了断点,可以确认在服务器端,它找到接口并返回S_OK。因此,似乎不知何故接口无法封送回客户端
因此,问题是,我如何获得QueryInterface()
以使我的自定义/双接口成功?我已经尝试了在我的客户端清单(和服务器清单)中使用
和
的各种组合来尝试和封送接口,但我仍然看到E\u NOINTERFACE
在我的客户端中返回
几年前,我在另一个论坛上看到Hans Passant的评论,说可能需要一个单独的代理/存根DLL来封送接口,但没有太多细节
在无需注册的环境中,是否有可能解决这个问题?是否需要创建代理/存根库?如果是这样,在我的客户机应用程序(和/或服务器应用程序和/或代理/存根DLL)中,清单条目会是什么样子?如果您有代理/存根DLL,请将其作为
文件
元素,并为其处理的每个接口包含一个子comInterfaceProxyStub
元素(不要忘记threadingModel
属性)
如果您有一个类型库,请将其作为一个文件
元素包含在子类型库
元素中,并为类型库中的每个接口添加一个comInterfaceExternalProxyStub
元素(不要忘记tlbid
属性),其中proxyStubClsid32
是自动化封送器:“{00020424-0000-0000-C000-0000000000 46}“
例如,如果使用标准封送(代理/存根DLL):
如果使用类型库封送处理:
实际上,ComInterfaceExternalProxySub
适用于任何其他已注册(非隔离)的代理/存根。例如:
其中,在本例中,{proxyStubClsid32}
是注册的代理/stubclsid
如果我的记性还不错的话,回到Windows XP仍然受支持的时候,我已经成功地尝试在代理/存根DLL中为接口使用
comInterfaceExternalProxyStub
,然后在代理/存根文件
元素中声明相应的comInterfaceProxyStub
,实际上不需要comInterfaceProxyStub代码>元素
然而,这不是一个好的实践,ComInterfaceExternalProxySub
实际上应该只用于外部代理/存根,正如文档所述,在激活所需的代理/存根时,COM基础结构似乎不允许在孤立的CLSID中查找。嗯,我最终能够到达那里
诀窍是在我的客户端EXE中,在
条目下有一个
,在服务器EXE中,在
条目下有一个
总之,我有两个exe和一个代理存根DLL:MFCDialog.exe(客户端)、ExeServer2.exe(服务器)和ExeServer2PS.DLL(代理存根DLL).ExeServer2最初是一个ATL向导生成的带有代理/存根的EXE服务器。代理/存根DLL有点神秘。我根本没有接触过它。它没有唯一的源文件,只有一些由ExeServer2(EXE服务器项目)的MIDL编译生成的文件
调整ExeServer2.exe和MFCDialog.exe的清单文件后,我可以在手动启动服务器后封送接口
MFCDialog.exe.manifest的重要部分:
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<file name="ExeServer2PS.dll">
<comInterfaceProxyStub iid="{2985957C-3067-4361-A010-23735F13E4B9}" name="IMyServer2" numMethods="8" baseInterface="{00020400-0000-0000-C000-000000000046}" proxyStubClsid32="{2985957C-3067-4361-A010-23735F13E4B9}" threadingModel="Both"/>
</file>
<!-- unimportant stuff like DPI, UAC, ComCtrl32 removed-->
</assembly>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<file name="ExeServer2.exe">
<typelib tlbid="{50142018-B402-4FDE-B085-67ABCC128526}" version="1.0" helpdir=""/>
</file>
<comInterfaceExternalProxyStub name="IMyServer2" iid="{2985957C-3067-4361-A010-23735F13E4B9}" tlbid="{50142018-B402-4FDE-B085-67ABCC128526}" proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"/>
</assembly>
下面是对测试代码的按钮单击的响应:
void CMFCDialogDlg::OnExeServer2()
{
CLSID clsid;
HRESULT hr = CLSIDFromProgID(L"ExeServer2.MyServer2", &clsid);
if (FAILED(hr))
{
_com_error err(hr);
OutputDebugString(err.ErrorMessage());
}
CComDispatchDriver lpDisp;
hr = lpDisp.CoCreateInstance(__uuidof(ExeServer2Lib::MyServer2));
if (hr == REGDB_E_CLASSNOTREG)
{
SpinUpExe(_T("ExeServer2.exe"));
hr = lpDisp.CoCreateInstance(__uuidof(ExeServer2Lib::MyServer2));
}
if (FAILED(hr))
{
_com_error err(hr);
AfxMessageBox(err.ErrorMessage());
}
else
{
ExeServer2Lib::IMyServer2Ptr lpServer;
try
{
lpServer = lpDisp.p;
}
catch (_com_error e)
{
AfxMessageBox(e.ErrorMessage());
}
if (lpServer)
{
_bstr_t bstrtName = lpServer->Name;
CString strMsg = CString(_T("From IMyServer: ")) + (LPCTSTR)bstrtName;
AfxMessageBox(strMsg);
}
else
{
_variant_t vRet;
hr = lpDisp.GetPropertyByName(L"Name", &vRet);
if (FAILED(hr))
{
_com_error err(hr);
AfxMessageBox(err.ErrorMessage());
}
else
{
CString strMsg = CString(_T("From IDispatch: ")) + (LPCWSTR)vRet.pbstrVal;
AfxMessageBox(strMsg);
}
}
}
}
对于我的简单测试应用程序,我只有一个具有单个双接口的对象。在您的示例中,您有带(In)和(In)的元素。我需要每个条目中的一个吗?我使用ATL生成带有PS DLL的EXE,有趣的是,代理存根(通过检查注册表找到)的CLSID是
void CMFCDialogDlg::OnExeServer2()
{
CLSID clsid;
HRESULT hr = CLSIDFromProgID(L"ExeServer2.MyServer2", &clsid);
if (FAILED(hr))
{
_com_error err(hr);
OutputDebugString(err.ErrorMessage());
}
CComDispatchDriver lpDisp;
hr = lpDisp.CoCreateInstance(__uuidof(ExeServer2Lib::MyServer2));
if (hr == REGDB_E_CLASSNOTREG)
{
SpinUpExe(_T("ExeServer2.exe"));
hr = lpDisp.CoCreateInstance(__uuidof(ExeServer2Lib::MyServer2));
}
if (FAILED(hr))
{
_com_error err(hr);
AfxMessageBox(err.ErrorMessage());
}
else
{
ExeServer2Lib::IMyServer2Ptr lpServer;
try
{
lpServer = lpDisp.p;
}
catch (_com_error e)
{
AfxMessageBox(e.ErrorMessage());
}
if (lpServer)
{
_bstr_t bstrtName = lpServer->Name;
CString strMsg = CString(_T("From IMyServer: ")) + (LPCTSTR)bstrtName;
AfxMessageBox(strMsg);
}
else
{
_variant_t vRet;
hr = lpDisp.GetPropertyByName(L"Name", &vRet);
if (FAILED(hr))
{
_com_error err(hr);
AfxMessageBox(err.ErrorMessage());
}
else
{
CString strMsg = CString(_T("From IDispatch: ")) + (LPCWSTR)vRet.pbstrVal;
AfxMessageBox(strMsg);
}
}
}
}