0

私の会社では、サードパーティの DLL を使用して一部のハードウェアに接続しています (hwLibここで名前を変更しました)。それはずっと前にVB6で書かれたと思います。DLL には、自身を登録するためのインストーラーなどが付属しています。

それを使用し、XPおよびWin7、32または64ビットで問題なく動作するC#アプリがあります。しかし、XP/32 ビットでは正常に動作するが、Win7/64 ビットではクラッシュする単純な C++ コンソール アプリを作成しました。コンソール アプリは次のようになります。

#include "stdafx.h"
using namespace System;

int main(array<System::String ^> ^args)
{
    using namespace hwLib;
    ChwLib^ myLib = gcnew ChwLib();
    String^ str = myLib->GetDllVersion();
    Console::WriteLine(L"Hello hwLib");
    Console::WriteLine(str);
    Console::ReadLine();  //to keep window open til you hit the "any" key
    return 0;
}

未処理の例外: System.InvalidCastException: タイプ 'hwLib.ChwLibClass' の COM オブジェクトをインターフェイス タイプ 'hwLib._ChwLib' にキャストできません。

IID '{E0560D1E-9A54-4EBF-83E8-D7BD2C936512}' を持つインターフェイスの COM コンポーネントでの QueryInterface 呼び出しが次のエラーにより失敗したため、この操作は失敗しました:

そのようなインターフェイスはサポートされていません (HRE SULT からの例外: 0x80004002 (E_NOINTERFACE))。System.StubHelpers.StubHelpers.GetCOMIPFromRCW(Object objSrc, IntPtr pCPCMD, Boolean& pfNeedsRelease) で hwLib.ChwLibClass.GetDllVersion() で main(String[] 引数) で mainCRTStartupStrArray(String[] 引数) で

はるかに大きく複雑なプログラムである C# プログラムは、ここに投稿しますが、同じシステム上で問題なく実行されます。

COM は私の時代より前です - おそらく 10 年か 15 年前に COM のコースを受講しましたが、覚えていません - これのデバッグを開始する方法について何か提案はありますか? ありがとう!!

4

1 に答える 1

4

COM は、スレッド化をサポートしないことを宣言する COM コンポーネントに対して、スレッド セーフを保証します。VB6 で記述されたコンポーネントは、確実にこれを行います。ThreadingModel という名前のレジストリのエントリによって指示されます。

テスト プログラムは、そのようなコンポーネントの安全なホームを提供しません。コンソール モード アプリは、マルチスレッド アパートメント (略して MTA) を作成します。スレッドセーフを提供しないことを約束します。次に、COM は独自の STA スレッドを作成して、コンポーネントのコードを実行します。コンポーネントへのすべての呼び出しは、メイン スレッドからそのヘルパー スレッドにマーシャリングされます。

しかし、あなたのケースでは壁にぶつかり、コンポーネントに必要なプロキシ/スタブが登録されていません。COM がメソッドの引数をコピーする方法を理解するために必要な追加のコード。COM ではなく、Reflection のおかげで .NET では簡単です。プロキシ/スタブは、HKCR\Interface レジストリ キーのエントリによって選択されます。VB6 コンポーネントは、タイプ ライブラリで動作する標準マーシャラーを常に使用します。E_NOINTERFACE エラー コードは、VB6 によって実装されていない方法を見つけるための COM の最後のあえぎである IMarshal インターフェイス用です。

登録を正しく行うことよりも、応急処置として、コンソール モード アプリで MTA スレッドではなく STA スレッドを作成できるようにすることです。これは非常に簡単です。属性を取得するだけです。

[STAThread]
int main(array<System::String ^> ^args)
// etc..

COM はそのヘルパー スレッドを作成しなくなり、呼び出しをマーシャリングする必要がなくなりました。これは実際には十分ではなく、STA スレッドもメッセージ ループをポンピングする必要があります。Application::Run() はおそらく大規模な C# プログラムで使用されます。メッセージ ループは、Control.BeginInvoke() および Dispatcher.BeginInvoke() と非常によく似た呼び出しをマーシャリングする方法を提供します。別のスレッドからコンポーネントを実際に呼び出すわけではないため、問題を回避できる可能性があります。しかし、多くの COM コンポーネントはメッセージ ループに依存して独自の処理を行っています。デッドロックが発生したり、コンポーネントがイベントを発生させなかったりすると、問題があることがわかります。たとえば、VB6 コードは Timer を使用できますが、そのメッセージ ループがないと動作しません。

于 2013-09-19T20:11:59.430 に答える