0

まず、私は C#、.NET、および COM 相互運用性については初めてだと言わざるを得ません。

COM オブジェクトを自分が作成したインターフェイス型にキャストしようとすると、次のエラー メッセージが表示されます。

エラー メッセージ: タイプ 'System.__ComObject' の COM オブジェクトをインターフェイス タイプ 'Observer.IObserver' にキャストできません。IID '{13478219-8C3B-4849-99D9-27CEF1A49A55}' を持つインターフェイスの COM コンポーネントでの QueryInterface 呼び出しが次のエラーのために失敗したため、この操作は失敗しました: No such interface supported (HRESULT からの例外: 0x80004002 (E_NOINTERFACE)) .

Windows 7 で VS2010 (.NET Framework 3.5) を使用しています。

これが私のインターフェースです(オブザーバークラスライブラリプロジェクト):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace Observer
{
    [ComImport]
    [Guid("2B2D0BC7-A7C6-4924-A3DE-42F7075E5947")]
    public interface IObservable
    {
        void attach(IObserver observer);
        void detach(IObserver observer);

        void notify();
    }

    [ComImport]
    [Guid("13478219-8C3B-4849-99D9-27CEF1A49A55")]
    public interface IObserver
    {
        void update(IObservable observable);
    }
}

この dll をビルドするときに、次の警告が表示
されます。Observer.dll には、COM 相互運用に登録できる型が含まれていません (インターフェイスが他の dll に実装されているため)。
IObserver がレジストリに表示されません (HKEY_CLASSES_ROOT\Interface にありません)。

失敗したコードは次のとおりです (Syntheses クラス ライブラリ プロジェクト):

// Arguments de la méthode permettant de récupérer un objet selon son chemin
Object[] args;
// Objet représentant l'état technique de la synthèse
Observer.IObserver pEtatTechniqueSynthese;

args = new Object[] { m_sFullName + "/Etat_Technique" };

pEtatTechniqueSynthese = m_typeSite.InvokeMember("FindObject2",
                        BindingFlags.InvokeMethod,
                        null, m_pSite, args) as Observer.IObserver;

//Here pEtatTechniqueSynthese is null

//This call works fine without casting when pEtatTechniqueSynthese's type is Object
//but I need to cast it because my Observer.IObservable's attach method waits for an Observer.IObserver
//If I don't cast I get a "Exception has been thrown by the target of an invocation"
//=> InnerException : "La valeur n'est pas comprise dans la plage attendue"
//I don't know the exact translation, but it sort of means "Value not in expected range"
pEtatTechniqueSynthese = (Observer.IObserver)m_typeSite.InvokeMember("FindObject2",
                        BindingFlags.InvokeMethod,
                        null, m_pSite, args);

//Exception raised

pEtatTechniqueSynthese の実数型 Etat_Technique (Syntheses クラス ライブラリ プロジェクト) は、 Observer.IObserver を実装します。

public class Etat_Technique : CODRA.SDK.DotNetUtils.COM.IObjectWithSite, Observer.IObservable, Observer.IObserver, ICalculateurEtatTechnique

私のアセンブリはすべて COM 可視です。Observer プロジェクトのビルド オプション [COM 相互運用に登録] がオンになっていて、厳密な名前のキー ファイルでアセンブリに署名しました。

COM サーバー コード (サード パーティ コンポーネント) にはアクセスできませんが、問題は私のコードにあると確信しています。
誰かが私が行方不明になっていることについての手がかりを持っていますか?

=============================================

サードパーティ ソフトウェアの詳細:

このソフトウェアは、オブジェクトを管理します。
データ構造はオブジェクト ツリーのようで、ルート ノードはサイトと呼ばれます。
dll クラスがオブジェクトにアクセスする場合、サイトから "FindObject2" メソッドを呼び出して、オブジェクト パスを渡す必要があります。このメソッドは明らかに COM オブジェクトを返すので、メソッドを呼び出したり、プロパティを取得したりできます...

独自のオブジェクト タイプを開発し、それらをソフトウェアに追加して、クラスを記述できます (アセンブリ、クラス、dll、プロパティなどを指定します)。

そこで、私が開発したオブジェクトを取得し、それを COM オブジェクトからそれが実装するインターフェイスにキャストし直します。
オブジェクトを Object として宣言し、そのメソッドを呼び出すと、問題なく機能します。
Observable にアタッチするには Observer が必要なので、キャストする必要があります。
attach メソッドの引数を Object にすれば解決するのですが、そうするとインターフェースを使う意味がありません。

=============================================

Michael Edenfield の回答に基づく詳細情報:

[ComImport括弧ON]

ComImport 属性は推測でした。サードパーティのコードとやり取りするために指定された utils ファイルにアクセスできるため、これを使用しました。
このファイルで定義されているインターフェイスを次に示します。サード パーティ ソフトウェアのドキュメントに記載されている方法で実装すると、サイト ルート オブジェクトにアクセスできます。
彼らがそれを使うなら、私も使わなければならないと思いました。

[ComVisible(true)]
[ComImport]
[Guid("FC4801A3-2BA9-11CF-A229-00AA003D7352") ]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IObjectWithSite
{
    void SetSite([MarshalAs(UnmanagedType.IUnknown)]
        [In] object pSite);
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#")]
    void GetSite([In] ref Guid riid, [Out] out IntPtr pvSite);
}

私はこの属性を削除します。あなたは私よりも根本にあるものをよく理解しています。さらに、問題は同じなので、この属性は役に立たなかった。

[ComImport 括弧 OFF]

悪いことに、FindObject2 は効果的に {System.__ComObject} を返すため、キャストが機能せず、InvalidCastException に何度も何度も何度も悩まされます...
良い点は、それが必要な基本クラスを知っていることです。なれ。

「サード パーティ製ソフトウェアに関する詳細情報」の部分で説明した回避策は機能します。つまり、attach メソッドで Object パラメータを取得し、IObserver の代わりに Object を管理します。これを行うには、Reflection を使用して IObserver の update メソッドを呼び出します。

foreach (Object observer in m_observers)
{
    Object[] args = new Object[] { this };
    observer.GetType().InvokeMember("update",
                            BindingFlags.InvokeMethod,
                            null, observer, args);
}

ええええええええええええええええええええええええええええええええええええええええええええええええええええええええ しかし、私がよりきれいなものを見つけなければ、このくだらないことで仕事は終わります。

4

1 に答える 1

1

何を達成しようとしているのかはよくわかりませんが、ここでいくつかの異なる COM 相互運用の概念を混同しているようです。

を使用して COM インターフェイスをインポートすることComImportAttributeは、インターフェイスがどこかの外部タイプ ライブラリで既に定義されている場合にのみ意味があります。これが独自のクラス用に C# で作成した新しいインターフェイスである場合、それを COM インポート属性として宣言しても効果はありません。これは、実際の COM オブジェクトがインターフェイスを実装することはないためですQueryInterface。問題の説明から、ComImport完全に間違った方法です。

独自のインターフェイスを COM クライアントに公開する必要がある場合は、GUID を与えるだけで済みます。このComImport属性を省略したComVisible場合、相互運用機能が有効になっていると仮定して、すべてのクラスまたはインターフェイスが登録されます。ただし、COM クライアントはインターフェイスを認識し、それを実装するために再コンパイルする必要があります。また、これで問題が解決するとは思いません。

サードパーティ ライブラリがカスタム クラスをどのようにインスタンス化しているのか正確にはわかりません。それが COM 経由で行われる場合は、それが機能するように CoClass とインターフェイス定義をエクスポートする必要があります。アセンブリとクラスの情報を要求しているという事実から、これは必要ないと思われます。

マネージ C# インターフェイスを実装する、どこかで作成されたマネージ C# オブジェクトを取得しているように思えます。取得する必要があるだけです。メソッドが実際に型のインスタンスを返す場合FindObjectは、最初に戻り値を具体的な C# 型に型キャストしてから、それをインターフェイスにケース化する必要があります。a で型キャスト演算子を使用しComObjectてインターフェイスにキャストしようとすると、実行されますが、QueryInterfaceほぼ確実に失敗します。

私はこれを試したことはありませんが、最初の推測として、最初に System.Object に型キャストすることをお勧めします。そこから、C# はマネージ型メタデータを使用して、インターフェイスが使用可能であるかどうかを判断し、適切にキャストする必要があります。

もちろん、FindObject何らかの方法で C# クラスにラップされた実際の COM オブジェクトを が返す場合は、最初にその戻り値から基になるクラスを取得する方法を理解する必要があります。

サード パーティ製ソフトウェアのドキュメントに含まれているインターフェイスはIObjectWithSite. これは Windows によって定義されたよく知られたインターフェイスであるため、このインターフェイスを実装する予定がある場合は使用する必要があります。[ComImport]この場合、サイト ルート オブジェクトはIObjectWithSite::SetSite()カスタム オブジェクトを呼び出して自分自身を渡します。これにより、埋め込みオブジェクトはコンテナーについて「認識」します。そのインターフェースは外部で定義されているため、「インポート」して、全員が同じインターフェースを実装する必要があります。

于 2012-05-21T20:51:05.073 に答える