COM タイプ ライブラリ (ComLib)、C# ツール クラスライブラリ (ComTools)、および COM タイプ ライブラリのテストベッドとして使用されるメイン C# プロジェクト (ComMaster) で構成される ac# プロジェクトがあります。
下部のコードを参照してください。
ComMaster から COM オブジェクトを呼び出すと (com オブジェクトとしてではなく、「通常の」C# オブジェクトとして)、すべて正常に動作します。ComLib から ComTools への呼び出しが機能し、ComTools は ComLib にある TypeConverter を見つけます。DoIt 関数は、次のような素敵なメッセージ ボックスをポップアップ表示します。
Original: Hello
Converted: Hello
を使用して com lib を公開しRegAsm ComLib.dll /codebase /tlb:ComLib.tlb
ます。
私の問題は次のとおりです。 たとえば、Excel-Vba から COM オブジェクトを呼び出すと、regasm で生成された typelib に参照を設定し、次のコードを作成します。
Sub TestComLib()
Dim c As New ComLib.ComLib
c.DoIt "My Test String"
End Sub
というランタイム エラーが表示されます"InvalidCastException: 'System.String' can't be converted into 'ComLib.MyClass'"
。明らかに、これは(T)(object)aString
TypConverter が文字列を MyClass に変換できない場合に呼び出される ComTools.ToolFunc の厄介なものから来ています。
さて、私の質問は次 のとおりです。ComTools で ComLib への参照を設定せずに、MyClass にアタッチされた TypeConverter を ComTools アセンブリに渡すにはどうすればよいですか (編集: これにより循環参照が発生します!)。
同じように見える次の問題を見つけました (ただし、回答はありません): TypeConverter from VB6
編集:型コンバーターを宣言する MyClass の属性をより明示的にしようとしました: [System.ComponentModel.TypeConverter("ComLib.MyClassConverter, ComLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")]
。しかし、それは役に立ちません:-(
編集:解決策
Simon Mourier のおかげで、ComLib クラスに次のコードを追加しました。
static ComLib()
{//static constructor, gets called before anything in here gets executed
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
if (args.Name == Assembly.GetAssembly(typeof(ComLib)).FullName)
return Assembly.GetAssembly(typeof(ComLib));
return null;
};
}
サンプル コード:
ComLib (インターフェイスを明示的に公開する C# クラス ライブラリとして作成されたプロジェクト):
namespace ComLib
{
[ComVisible(true), Guid("abcdef00-23aa-46d0-8ba8-c7548fa4d820")]//faked GUID
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IComLib
{
void DoIt(string aMsg);
}
[ComVisible(true), Guid("abcdef01-23aa-46d0-8ba8-c7548fa4d820")]//faked GUID
[ProgId("ComLib.ComLib")]
[ClassInterface(ClassInterfaceType.None)]
public class ComLib : IComLib
{
public void DoIt(string aMsg)
{
try {
MyClass c = new MyClass();
//call tool func in ComTools
c = ComTools.ComTools.ToolFunc<MyClass>(aMsg);
MessageBox.Show("Original: " + aMsg + "\n" + c.Text);
}
catch (Exception e) {
MessageBox.Show("Error: " + e.ToString());
}
}
}
[System.ComponentModel.TypeConverter(typeof(MyClassConverter))]
public class MyClass
{//dummy class wrapping a string
public string Text {get; set; }
}
public class MyClassConverter : System.ComponentModel.TypeConverter
{//converter for MyClass allowing conversions from string
public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, Type sourceType)
{ return sourceType == typeof(string); }
public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
var s = value as string;
if (s != null)
return new MyClass() {Text = "Converted: " + s};
return base.ConvertFrom(context, culture, value);
}
}
}
ComLib プロジェクトは、次のコードを含むComToolsプロジェクトを参照します。
namespace ComTools
{
public class ComTools
{
public static T ToolFunc<T>(string aString)
{//create an object of the given T type converting it from goiven string value
System.ComponentModel.TypeConverter typeConverter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(T));
if (typeConverter.CanConvertFrom(typeof(string)))
{//called from com, the correct type converter is not found
return (T) typeConverter.ConvertFrom(aString);//convert using the typeconverter found
}
else
{//last resort: try a cast
// ******* this throws an error when called from COM,
// ******* because the correct type converter is not found
return (T)(object)aString;//will not let me cast directly to T
}
}
}
}
ComMaster プロジェクトはComLibプロジェクトのみを参照し、次のコードを含みます。
static void Main()
{
ComLib.ComLib lib = new ComLib.ComLib();//create an instance of the lib
lib.DoIt("Hello");//call the exposed function
}