我正在尝试在clr 2.0中调用clr 4.0 contorl我有三个类。我的问题是第2类中的行,c。添加(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()
在我看来,它无法从对象访问控件。
第1类:Net 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();
}
}
}
第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();
}
}
}
第3类:Net 4到net 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();
}
}
答案 0 :(得分:4)
问题是你混合了两种Control
类型,一种来自.NET 2,另一种来自.NET 4.这种情况根本无法完成。您无法在两个不同的CLR之间传递托管对象,就好像它们属于同一类型一样,这就是您必须首先使用COM的原因。托管的Control
对象获得"包裹"在ComObject中,但它无法转换为新的Control
类型,这就是您看到此异常的原因。 ( 可以将此对象视为Control
,但有一些繁重的代理,但它会带来更多可以解决的问题 - 但我稍后会研究这种可能性。)
那该怎么办?您可以将RyderCriticalPath
放入.NET 2程序集中,并使其实现放置在适配器程序集中的接口IRyderCriticalPath
。在.NET 2程序集中创建它的实例,并将其作为接口传递给LoadRyControl
。不要通过Control
。将c.Add(x);
移动到调用方法(.NET 2)。
当然,这就是我对你提供的代码所做的事情,但重点是你必须仅将托管对象作为接口传递,只暴露控制它所需的方法。
修改强>
正如所承诺的那样,我已经深入研究了代理Control
对象的可能性。是的,这是可能的,但不完全。只有"远程"类型可以代理。因此,除了可序列化类型之外,您无法访问非远程类型的任何属性。所以你仍然应该使用原始解决方案,因为这对你的问题不起作用(但它可能对其他人有用):
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
,它支持远程调用对象上的方法,透明地代理所有参数并返回值。它添加了一个处理所有这些的自定义封送程序,只需将其添加到MarshalAs
属性,它将处理所有传递或接收的远程对象。