2015-02-11 64 views
1

我想在clr 2.0中調用clr 4.0 contorl我有三個類。我的問題是第2課中的c.Add(x)。無法將com對象轉換爲控件

此行引發錯誤

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類:網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類:淨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(); 
    } 
} 
+0

爲什麼選擇負面投票? – DidIReallyWriteThat 2015-02-20 15:08:46

+0

你可以顯示「RyderCriticalPath」的代碼嗎? – 2015-02-20 15:33:35

+1

WindowsFormsApplication3在哪裏 – Sxntk 2015-02-20 15:53:08

回答

3

的問題是您正在混合兩個類型,一個來自.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屬性中,它將處理所有傳遞或接收的可遠程對象。

相關問題