无法将 com 对象转换为 Control

Cannot cast com object to Control

我正在尝试在 clr 2.0 中调用 clr 4.0 控制器,我有三个 classes。我的问题是 class 2, c.Add(x).

中的行

这一行抛出错误

Unable to cast object of type 'System.__ComObject' to type 'System.Windows.Forms.Control'.

堆栈跟踪

at System.StubHelpers.InterfaceMarshaler.ConvertToManaged(IntPtr pUnk, IntPtr itfMT, IntPtr classMT, Int32 flags)
   at Net4ToNet2Adapter.IClassAdapter.LoadRyderControl(Int32 atacode, Int32 eventid, Control c)
   at Net2Assembly.RyderQuestion..ctor() in C:\Users\casmith\Desktop\C#\Net2Assembly\RyderQuestion.cs:line 28
   at Net2Assembly.Program.Main() in C:\Users\casmith\Desktop\C#\Net2Assembly\Program.cs:line 17
   at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

在我看来,它无法从对象访问控件。

Class 1: 网络 2 CLR

namespace Net2Assembly
{
  public partial class RyQuestion : Form
  {
   private IClassAdapter _ryderControl;

   public RyQuestion()
    {
     InitializeComponent();

     var classAdapterType = Type.GetTypeFromProgID("Net4ToNet2Adapter.MyClassAdapter");

     var classAdapterInstance = Activator.CreateInstance(classAdapterType);

     var myClassAdapter = (IClassAdapter)classAdapterInstance;

     _ryControl = myClassAdapter;

     myClassAdapter.LoadRyControl(17, 291457,this.Panel1);
   }

   public void LoadQuestionsTC()
   {
    _ryControl.LoadQuestionsTC();
   }

   public void LoadQuestionsCloseout()
   {
    _ryControl.LoadQuestionsCloseout();
   }
  }
}

Class 2: 我的 CLR 4 程序集

 namespace Net4Assembly
{
  public class RyderControlWrapper
   {
    private WindowsFormsApplication3.RyCriticalPath _ryControl;

    public void LoadRyControl(int atacode, int eventid,Control c)
    {
         WindowsFormsApplication3.RyderCriticalPath x = new WindowsFormsApplication3.RyCriticalPath(atacode, 2945784);
        _ryControl = x;

        c.Add(x); //Bad line :(
    }

    public void LoadQuestionsTC()
    {
        _ryControl.LoadQuestionsTC();
    }

    public void LoadQuestionsCloseout()
    {
        _ryControl.LoadQuestionsCloseout();
    }
  }
}

Class 3:网络 4 到网络 2 适配器

namespace Net4ToNet2Adapter
{
  public class MyClassAdapter : IClassAdapter
  {
    private RyControlWrapper _rcWrapper = new RyControlWrapper();

    public void LoadRyControl(int atacode, int eventid,Control c)
    {
        _rcWrapper.LoadRyControl(atacode, eventid,c);
    }

   public void LoadQuestionsTC()
    {
        _rcWrapper.LoadQuestionsTC();
    }

   public void LoadQuestionsCloseout()
   {
       _rcWrapper.LoadQuestionsCloseout();
   }
 }
}

namespace Net4ToNet2Adapter
{
  [ComVisible(true)]
  public interface IClassAdapter
  {
    void LoadRyderControl(int atacode, int eventid, Control c);
    void LoadQuestionsTC();
    void LoadQuestionsCloseout();
  }
}

问题是您混合了两种 Control 类型,一种来自 .NET 2,另一种来自 .NET 4。这根本无法完成。您不能在两个不同的 CLR 之间传递托管对象,就好像它们属于同一类型一样,这就是您必须首先使用 COM 的原因。托管 Control 对象在 ComObject 中获得 "wrapped",但无法转换为新的 Control 类型,这就是您看到此异常的原因。 (它 可以将此对象视为 Control,使用一些繁重的代理,但它会带来更多它可以解决的问题 - 但我稍后会研究这种可能性。)

那怎么办?您可以将 RyderCriticalPath 放入 .NET 2 程序集中,并使其实现放置在适配器程序集中的接口 IRyderCriticalPath。在 .NET 2 程序集中创建它的实例并将其作为接口传递给 LoadRyControl。不要传递 Control。将 c.Add(x); 移动到调用方法 (.NET 2)。

当然,这就是我对您提供的代码所做的,但关键是您必须仅将托管对象作为接口传递,仅公开控制它所需的方法。

编辑:
正如承诺的那样,我已经深入研究了代理 Control 对象的可能性。是的,有可能,但不完全。只能代理 "remotable" 类型。因此,您不能访问非远程类型的任何属性,可序列化类型除外。所以你仍然应该使用原来的解决方案,因为这对你的问题不起作用(但它可能对其他人有用):

using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;
using System.Windows.Forms;

namespace Net4ToNet2Adapter
{
    [ComVisible(true)]
    [Guid("E36BBF07-591E-4959-97AE-D439CBA392FB")]
    public interface IMyClassAdapter
    {
        void DoNet4Action( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(ProxyMarshaler))] Control c);
    }

    [ComVisible(true)]
    [Guid("9F973534-E089-4C22-A481-54403B97DED9")]
    public interface IProxyProvider
    {
        Type Type{get;}
        object Instance{get;}
        object Invoke(string method, Type[] signature, object[] args);
    }

    public class ProxyMarshaler : ICustomMarshaler
    {
        private static readonly ProxyMarshaler instance = new ProxyMarshaler();

        public static ICustomMarshaler GetInstance(string cookie)
        {
            return instance;
        }

        public IntPtr MarshalManagedToNative(object ManagedObj)
        {
            return Marshal.GetIUnknownForObject(new ProxyProvider(ManagedObj));
        }

        public object MarshalNativeToManaged(IntPtr pNativeData)
        {
            IProxyProvider prov = (IProxyProvider)Marshal.GetObjectForIUnknown(pNativeData);
            return new ComProxy(prov).GetTransparentProxy();
        }

        public void CleanUpNativeData(IntPtr pNativeData)
        {
            Marshal.Release(pNativeData);
        }

        public void CleanUpManagedData(object ManagedObj)
        {
            ComProxy proxy = (ComProxy)RemotingServices.GetRealProxy(ManagedObj);
            proxy.Dispose();
        }

        public int GetNativeDataSize()
        {
            return -1;
        }


        private class ProxyProvider : IProxyProvider
        {
            public Type Type{get; private set;}
            public object Instance{get; private set;}

            public ProxyProvider(object instance)
            {
                Instance = instance;
                Type = instance.GetType();
            }

            public object Invoke(string method, Type[] signature, object[] args)
            {
                MethodInfo mi = Type.GetMethod(method, signature);
                if(mi == null || mi.IsStatic) throw new NotSupportedException();
                DeproxyArgs(args);
                object ret = mi.Invoke(Instance, args);
                ProxyArgs(args);
                return ProxyValue(ret);
            }

            public static bool IsProxyable(Type t)
            {
                return t.IsInterface || typeof(MarshalByRefObject).IsAssignableFrom(t) || t == typeof(object);
            }

            public static void DeproxyArgs(object[] args)
            {
                for(int i = 0; i < args.Length; i++)
                {
                    args[i] = DeproxyValue(args[i]);
                }
            }

            public static void ProxyArgs(object[] args)
            {
                for(int i = 0; i < args.Length; i++)
                {
                    args[i] = ProxyValue(args[i]);
                }
            }

            public static object DeproxyValue(object val)
            {
                var pp = val as IProxyProvider;
                if(pp != null)
                {
                    if(val is ProxyProvider) return pp.Instance;
                    else return ComProxy.GetProxy(pp);
                }
                return val;
            }

            public static object ProxyValue(object val)
            {
                ComProxy proxy = ComProxy.GetProxy(val);
                if(proxy != null)
                {
                    return proxy.Provider;
                }else if(val != null && ProxyProvider.IsProxyable(val.GetType()))
                {
                    return new ProxyProvider(val);
                }
                return val;
            }
        }

        private sealed class ComProxy : RealProxy, IDisposable
        {
            public IProxyProvider Provider{get; private set;}

            public ComProxy(IProxyProvider provider) : base(provider.Type == typeof(object) ? typeof(MarshalByRefObject) : provider.Type)
            {
                Provider = provider;
            }

            public static object GetProxy(IProxyProvider provider)
            {
                return new ComProxy(provider).GetTransparentProxy();
            }

            public static ComProxy GetProxy(object proxy)
            {
                if(proxy == null) return null;
                return RemotingServices.GetRealProxy(proxy) as ComProxy;
            }

            public override IMessage Invoke(IMessage msg)
            {
                IMethodCallMessage msgCall = msg as IMethodCallMessage;
                if(msgCall != null)
                {
                    object[] args = msgCall.Args;
                    try{
                        ProxyProvider.ProxyArgs(args);
                        object ret = Provider.Invoke(msgCall.MethodName, (Type[])msgCall.MethodSignature, args);
                        ProxyProvider.DeproxyArgs(args);
                        ret = ProxyProvider.DeproxyValue(ret);
                        return new ReturnMessage(ret, args, args.Length, msgCall.LogicalCallContext, msgCall);
                    }catch(TargetInvocationException e)
                    {
                        return new ReturnMessage(e.InnerException, msgCall);
                    }catch(Exception e)
                    {
                        return new ReturnMessage(e, msgCall);
                    }
                }
                return null;
            }

            ~ComProxy()
            {
                Dispose(false);
            }

            private void Dispose(bool disposing)
            {
                if(disposing)
                {
                    Marshal.FinalReleaseComObject(Provider);
                }
            }

            public void Dispose()
            {
                Dispose(true);
                GC.SuppressFinalize(this);
            }
        }
    }
}

它使用一个接口 IProxyProvider 支持远程调用对象的方法,透明地代理所有参数和 return 值。它添加了一个处理所有这些的自定义编组器,只需将其添加到 MarshalAs 属性,它将处理所有传递或接收的远程对象。