クライアントとサーバーの両方にC#でC ++ COMインターフェイスを使用する
質問
C#コードの一部を、COMを介してあらゆる種類の実装と対話させる必要があります。
その統合のユーザーにとって使いやすくするために、相互作用するインターフェイスをIDLに(関連する既存のDLLの一部として、ただしコクラスまたは実装なしで)含め、Tlbimpを実行して型を作成することでC#コードに組み込みました定義。
C#を実装し、Windowsレジストリ情報に基づいてCOMオブジェクトを作成し、オブジェクトを必要なインターフェイスにキャストしました。
その後、別のプロジェクトでインターフェイスのC#実装を作成し、登録しました。 メインプログラムはテストCOMオブジェクトを正しく作成しますが、インターフェイスにキャストできません(C# 'as'を使用するとnullオブジェクトを取得し、明示的なキャストのInvalidCastExceptionを取得します)。
インターフェイスがテストオブジェクトによって実装されていると識別されない理由を誰かが提案できますか?
これはIDLのインターフェイス定義です(VS 2005でC ++でコンパイルされています):
[
object,
uuid(B60C546F-EE91-48a2-A352-CFC36E613CB7),
dual,
nonextensible,
helpstring("IScriptGenerator Interface"),
pointer_default(unique)
]
interface IScriptGenerator : IDispatch{
[helpstring("Init the Script generator")]
HRESULT Init();
[helpstring("General purpose error reporting")]
HRESULT GetLastError([out] BSTR *Error);
};
これは、TlbimpによってC#用に作成されたスタブです。
[TypeLibType(4288)]
[Guid("B60C546F-EE91-48A2-A352-CFC36E613CB7")]
public interface IScriptGenerator
{
[DispId(1610743813)]
void GetLastError(out string Error);
[DispId(1610743808)]
void Init();
}
これはメインのC#コードの一部であり、ProgIDによってCOMオブジェクトを作成し、IScriptGeneratorインターフェイスにキャストします。
public ScriptGenerator(string GUID)
{
Type comType = Type.GetTypeFromProgID(GUID);
object comObj = null;
if (comType != null)
{
try
{
comObj = Activator.CreateInstance(comType);
}
catch (Exception ex)
{
Debug.Fail("Cannot create the script generator COM object due to the following exception: " + ex, ex.Message + "\n" + ex.StackTrace);
throw ex;
}
}
else
throw new ArgumentException("The GUID does not match a registetred COM object", "GUID");
m_internalGenerator = comObj as IScriptGenerator;
if (m_internalGenerator == null)
{
Debug.Fail("The script generator doesn't support the required interface - IScriptGenerator");
throw new InvalidCastException("The script generator with the GUID " + GUID + " doesn't support the required interface - IScriptGenerator");
}
}
そして、これが実装されているC#コードであり、動作するかどうかをテストします(動作しません):
[Guid("EB46E31F-0961-4179-8A56-3895DDF2884E"),
ProgId("ScriptGeneratorExample.ScriptGenerator"),
ClassInterface(ClassInterfaceType.None),
ComSourceInterfaces(typeof(SOAAPIOLELib.IScriptGeneratorCallback))]
public class ScriptGenerator : IScriptGenerator
{
public void GetLastError(out string Error)
{
throw new NotImplementedException();
}
public void Init()
{
// nothing to do
}
}
解決
もう一度-提案をありがとう。
最終的に自分で問題を解決することができました。上記の提案を試してみましたが、何の進展もありませんでした。次に、「テスト」コードの相互運用の名前空間を変更しました。Tlbimpを使用する際の引数の使用が異なるため、メインコードの名前空間とは異なります。これで問題は解決しました。
理由は次のとおりです。.NetはCOMオブジェクトを作成しますが、これが実際に.Netオブジェクトであることを検出すると、COMレイヤーをバイパスして直接通信します。その場合、queryInterface(インターフェイスGUIDを使用)は使用されず、C#名前空間が異なるため、インターフェイスは異なります。
これは、.Netコードとの統合を支援するために、IDL以外の元の相互運用機能アセンブリを公開する必要があることを意味します。
ありがとう、 インバー
他のヒント
インターフェイスでこれが必要だと思います
[InterfaceType(ComInterfaceType.InterfaceIsDual)]