1

マニフェスト、特に MSBuild タスクの GenerateApplicationManifest を使用して、メイン アプリケーションが Isolated COM を使用できるようにしました。クライアント マシンに DLL を登録しなくても、必要な DLL に実装されたすべての COM オブジェクトを作成できます。でも、私は貪欲です...

私たちのアプリケーション スイートには、通常、COM を介して呼び出される個別のアプリケーションもいくつかあります。これらについては、EXE to EXE 分離 COM はできないと言われています。厳密に言えば、それは本当ですが、私は 90% の道のりをたどりました。他のフォーラムでは、他の人が残りの道をたどる手がかりを与えているのを見てきました。

私の EXE サーバーの場合、マニフェストに EXE サーバーの名前のエントリとそのエントリのサブエントリがあり、ATL サーバーが を呼び出したときLoadRegTypeLib()に呼び出しが成功するようにしています。それはうまくいきます。

もちろん、難しいのは、クライアント アプリケーション マニフェストに EXE サーバーのエントリを配置して、CoCreateInstance()(サーバー EXE を起動し、COM が行う他のすべてのことを実行することによって) 成功することを期待できないことです。

どのEXEサーバーを起動するかを知っているので、かなりの偽物を作ることができます。クライアント アプリで呼び出しCreateProcess()てから呼び出しWaitForInputidle()て、クライアント アプリで CoCreateInstance() を使用できるようにサーバーを準備できます。

クライアントアプリでインターフェイスを呼び出しCoCreateInstance()IDispatch要求すると、呼び出しは成功し、呼び出すことができInvoke()、すべてが機能します。

ここで貪欲な部分が来ます...

IDispatch が機能することはまったく問題ありませんが、IDispatch から派生したデュアル インターフェイスを介して呼び出すことができるようにしたいと考えています。そのように記述されたコードがたくさんあり、構文がより単純であり、例外処理が既に存在するため、それを行いたいと考えています。

ただし、QueryInterface()インターフェイスでデュアル インターフェイスを呼び出すとIDispatch、E_NOINTERFACE が返されます。サーバー EXE の ATL サーバー オブジェクトにブレークポイントを設定し、サーバー側でインターフェイスを見つけて S_OK を返すことを確認できます。そのため、どういうわけかインターフェイスをクライアントにマーシャリングすることができないようです。

問題は、QueryInterface()カスタム/デュアル インターフェイスを成功させるにはどうすればよいかということです。<comInterfaceProxyStub>クライアント マニフェスト (およびサーバー マニフェスト) で と を使用してさまざまな組み合わせ<comInterfaceExternalProxyStub>を試し、インターフェイスをマーシャリングしようとしましたが、クライアントにまだE_NOINTERFACE戻りが見られます。

数年前の別のフォーラムで、インターフェイスをマーシャリングするために別のプロキシ/スタブ DLL が必要になる可能性があるという Hans Passant のコメントを見ましたが、詳細はあまりありませんでした。

登録不要のコンテキストでこれを解決することさえ可能ですか? プロキシ/スタブ ライブラリを作成する必要はありますか? その場合、マニフェスト エントリはクライアント アプリケーション (および/またはサーバー アプリおよび/またはプロキシ/スタブ DLL) でどのように表示されますか?

4

2 に答える 2

2

プロキシ/スタブ DLL がある場合は、処理する各インターフェイスfileの子要素を持つ要素として含めます (属性comInterfaceProxyStubを忘れないでください)。threadingModel

タイプ ライブラリがある場合は、それをfiletypelib要素を持つ要素として含め、タイプ ライブラリの各インターフェイスに要素を追加します (属性comInterfaceExternalProxyStubを忘れないでください)。ここで、 はオートメーション マーシャラー:です。tlbidproxyStubClsid32"{00020424-0000-0000-C000-000000000046}"

たとえば、標準のマーシャリング (プロキシ/スタブ DLL) を使用する場合:

<assembly ...>
    <file name="myps.dll">
        <comInterfaceProxyStub iid="{iid1}"
                               name="IMyDualInterface1"
                               baseInterface="{00020400-0000-0000-C000-000000000046}"
                               numMethods="8"
                               proxyStubClsid32="{proxyStubClsid32}"
                               threadingModel="Free"
                               />
    </file>
</assembly>

タイプ ライブラリ マーシャリングを使用する場合:

<assembly ...>
    <file name="mylib.tlb">
        <typelib tlbid="{tlbid}"
                 version="1.0"
                 helpdir=""
                 />
    </file>
    <comInterfaceExternalProxyStub iid="{iid2}"
                                   baseInterface="{00020400-0000-0000-C000-000000000046}"
                                   numMethod="8"
                                   name="IMyDualInterface2"
                                   tlbid="{tlbid}"
                                   proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
                                   />
</assembly>

実際、comInterfaceExternalProxyStub他の登録済み (分離されていない) プロキシ/スタブに適用されます。例えば:

<assembly ...>
    <comInterfaceExternalProxyStub iid="{iid2}"
                                   baseInterface="{00000000-0000-0000-C000-000000000046}"
                                   numMethod="4"
                                   name="IMyInterface3"
                                   proxyStubClsid32="{proxyStubClsid32}"
                                   />
</assembly>

この場合、{proxyStubClsid32}は登録済みのプロキシ/スタブ CLSID です。


私の記憶が正しければ、Windows XP がまだサポートされていた頃に戻ってcomInterfaceExternalProxyStub、proxy/stub DLL で for interfaces を使用してから、 comClassproxy/stub 要素でそれぞれの要素を宣言することに成功しました。file要素は事実上必要ありませんcomInterfaceProxyStub

ただし、これは良い方法ではありcomInterfaceExternalProxyStubません。実際には外部プロキシ/スタブにのみ使用する必要があります。文書化されている方法では、必要なプロキシ/スタブをアクティブにするときに、COM インフラストラクチャが分離された CLSID を検索しないように許可されているようです。

于 2016-12-14T15:42:06.600 に答える
0

さて、やっとたどり着くことができたのですが…

秘訣は、クライアント EXE でエントリの<comInterfaceProxyStub><file>に、サーバー EXE でエントリの<comInterfaceExternalProxyStub>下にある<assembly>ことでした。

要約すると、2 つの EXE と 1 つの ProxyStub 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>

上記では、サーバーの typelib のエントリが不要であることがわかります。iid は IMyServer2 の uuid で、baseInterface は IDispatch の uuid です。proxyStubClsid32 は IMyServer2 インターフェイスの uuid と同じですが、技術的には IID ではなく CLSID です。それが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>

上記では、2 つの重要な部分があります。まず、ATL サーバーがそのタイプ ライブラリに接続できるようにするためのタイプ ライブラリ エントリです。2 つ目は、外部プロキシ スタブ エントリです。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);
         }
      }
   }
}
于 2016-12-14T21:53:04.430 に答える