无法将 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
属性,它将处理所有传递或接收的远程对象。
我正在尝试在 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
属性,它将处理所有传递或接收的远程对象。