好吧,我最终能够到达那里......
诀窍是在我的客户端 EXE 中有<comInterfaceProxyStub>
一个<file>
条目,而在服务器 EXE 中有<comInterfaceExternalProxyStub>
一个<assembly>
条目。
总而言之,我有 2 个 EXE 和 1 个 ProxyStub DLL:MFCDialog.exe(客户端)、ExeServer2.exe(服务器)和 ExeServer2PS.dll(代理存根 DLL)。ExeServer2 最初是一个带有代理/存根的 ATL 向导生成的 EXE 服务器。代理/存根 DLL 有点神秘。我根本没有碰过它。它没有唯一的源文件,只有一些从 MIDL 为 ExeServer2(EXE 服务器项目)编译生成的文件。
调整 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>
在上面,您可以注意到不需要服务器类型库的条目。iid 是 IMyServer2 的 uuid,baseInterface 是 IDispatch 的 uuid,proxyStubClsid32 与 IMyServer2 接口的 uuid 相同——尽管它在技术上是 CLSID 而不是 IID。这就是 ATL 生成它的方式。
ExeServer2.exe.manifest 的重要部分:
<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>
在上面,有两个重要的部分……首先是 typelib 条目,以便 ATL 服务器可以连接到它们的 typelib。第二个是外部代理存根条目。iid 是 IMyServer2 的 uuid,tlbid 是服务器(ExeServer2)的类型库,proxyStubClsid32 是默认的自动化代理存根 CLSID。
这是启动 Exe 服务器的代码(ATL 服务器不需要特殊参数):
BOOL SpinUpExe(CString strExeName)
{
STARTUPINFO info;
ZeroMemory(&info, sizeof(info));
info.cb = sizeof(info);
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(pi));
TCHAR szDir[MAX_PATH];
GetModuleFileName(0, szDir, MAX_PATH);
CString strDir(szDir);
strDir = strDir.Mid(0, strDir.ReverseFind(_T('\\')));
CString sExe = strDir + CString(_T("\\")) + strExeName;
BOOL bSuccess = CreateProcess(sExe, NULL, NULL, NULL, FALSE, 0, NULL, strDir, &info, &pi);
if (!bSuccess)
{
DWORD dw = GetLastError();
_com_error err(dw);
}
else
{
WaitForInputIdle(pi.hProcess, 5000);
}
return bSuccess;
}
以下是对测试代码的按钮单击的响应:
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);
}
}
}
}