7

C# コードから呼び出したい外部コンポーネント (C++) があります。

コードは次のようなものです。

using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace dgTEST
{
    class Program
    {
        [STAThread]
        static void Main(string[] args)
        {
            ExtComponentCaller extCompCaller = new ExtComponentCaller();
            result = extCompCaller.Call(input);

            Thread t = new Thread(new ThreadStart(() =>
            {
                try
                {
                    result = extCompCaller.Call(input);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                }
            }));

            t.SetApartmentState(ApartmentState.STA);
            t.Start();
            t.Join();
        }
    }
}

問題は、最初の呼び出しで、外部コンポーネントが呼び出され、結果が返されたことです。

しかし、別のスレッドでそれを呼び出そうとすると、例外が発生しました: System.InvalidCastException: Unable to cast COM object of type 'System.__ComObject' ... 。STAThread が原因で、この例外がスローされたと確信しています。Main 関数から [STAThread] 属性を削除すると、外部コンポーネントの最初の呼び出しでも同じことが発生し、正常に機能したためです。

この外部コンポーネントを他のスレッドから呼び出して、この例外を取り除くにはどうすればよいですか?

アップデート - - - - - - -

他のクレイジーなことが今起こっています。F5 を使用して Visual Studio からプログラムを起動すると、最初の呼び出しでも問題が発生しますが、バイナリ .exe ファイルを直接実行すると動作します (他のスレッドからは :( ではありません)。デバッグからリリースへのビルドと、F5 キーを使用して Visual Studio から開始すると、最初の呼び出しが再び機能します。

なぜそれが起こるのですか?

事前に助けてくれてありがとう!

よろしく、 ゾリ

4

1 に答える 1

2

ねじ切りは決して細かいことではありません。スレッド化をサポートするコードが明示的に文書化されていない場合、99% の確率でスレッド化がサポートされていません。

そして明らかに、このコンポーネントはスレッド化をサポートしていません。別の STA スレッドを作成することは魔法の解決策ではなく、別のスレッドであることには変わりありません。InvalidCastException は、作成しようとしているようなワーカー スレッドからの呼び出しをマーシャリングするために必要なプロキシ/スタブ サポートも欠落していることを示します。スレッドセーフではないコードに対してスレッドセーフな呼び出しを行うために必要です。[STAThread] のコントラクトを破ったとしても、メッセージ ループをポンピングする必要があります。これは、ワーカー スレッドからスレッド セーフではないコンポーネントへの呼び出しを可能にするメッセージ ループです。Application.Run() からメッセージ ループを取得します。

これがバックが止まるところです。スレッドセーフではありません。メイン スレッドを修正したり、ベンダーや作成者にプロキシ/スタブの提供を依頼したりしても、目的を達成できず、作成したワーカー スレッドで実際には実行されません。したがって、次のようになります。

    static void Main(string[] args)
    {
        Thread t = new Thread(new ThreadStart(() =>
        {
             ExtComponentCaller extCompCaller = new ExtComponentCaller();
             result = extCompCaller.Call(input);
        }));

        t.SetApartmentState(ApartmentState.STA);
        t.Start();
        t.Join();
    }

これにより、呼び出し元と同じスレッドでオブジェクトが作成されるため、スレッドセーフになります。このワーカー スレッドがメッセージ ループをポンプしないという問題がまだあります。COM コンポーネントはそれに依存する傾向があります。それが問題かどうかは、デッドロックや実行されないイベントからわかります。メインスレッドから呼び出したときにテストプログラムですでに問題なく機能している場合は、おそらくポンピングしなくても問題ありません。

于 2013-04-19T13:39:52.190 に答える