如何使从COM库调用的程序集知道TypeConverter

本文关键字:程序集 TypeConverter 调用 何使 COM | 更新日期: 2023-09-27 18:20:27

我有一个c#项目,它由一个COM类型库(ComLib)、一个c#工具类库(ComTools)和一个用作COM类型库测试台的主c#项目(ComMaster)组成。

请参阅底部的代码。

当从ComMaster调用COM对象(不是作为COM对象,而是作为"普通"C#对象)时,一切都很好:从ComLib到ComTools的调用可以工作,并且ComTools可以在ComLib中找到TypeConverter。DoIt功能弹出一个漂亮的Messagebox,上面写着:

Original: Hello
Converted: Hello

现在我使用RegAsm ComLib.dll /codebase /tlb:ComLib.tlb发布com库。

我的问题是:当我从ExcelVba调用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'"。显然,这来自于ComTools.ToolFunc中令人讨厌的(T)(object)aString,当TypConverter无法将字符串转换为MyClass时,就会调用它。

现在,我的问题是:如何在不将ComTools中的引用设置为ComLib的情况下将连接到MyClass的TypeConverter移交给ComTools程序集(EDIT:这会导致循环引用!)?

我发现以下问题似乎是一样的(但尚未得到回答):来自VB6 的TypeConverter

EDIT:我刚刚尝试在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
    }

如何使从COM库调用的程序集知道TypeConverter

由于某种原因,当您运行非.NET应用程序时,某些类型不会像在纯.NET上下文中那样自动解析,但没有出现明显的错误或异常,所有类型都会优雅而安静地失败(这是一种遗憾,BTW…)。

您需要做的是帮助系统,并附加到AssemblyResolve事件,类似于以下内容:

AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
    {
        if (args.Name == Assembly.GetAssembly(typeof(ComLib)).FullName) // adapt to your needs
            return Assembly.GetAssembly(typeof(ComLib));
        return null;
    };