2013-10-31 6 views
8

यहां चुनौती है। मैं फ्रेमवर्क की WebBrowserSite कक्षा से प्राप्त कर रहा हूं। मेरी व्युत्पन्न कक्षा का एक उदाहरण, ImprovedWebBrowserSite, WebBrowser.CreateWebBrowserSiteBase के माध्यम से वापस किया गया है, जिसे मैं WebBrowser कक्षा के अपने व्युत्पन्न संस्करण में ओवरराइड करता हूं - विशेष रूप से कस्टम साइट ऑब्जेक्ट प्रदान करने के लिए। फ्रेमवर्क का WebBrowser कार्यान्वयन आगे अंतर्निहित अप्रबंधित WebBrowser ActiveX नियंत्रण तक पहुंच जाता है।WebBrowserSite: व्युत्पन्न कक्षा में एक निजी COM इंटरफ़ेस विधि को कैसे कॉल करें?

अब तक, मैंने IDocHostUIHandler को ImprovedWebBrowserSite कार्यान्वयन (जैसे this) में ओवरराइड करने में कामयाब रहा है। अब मैं अधिक कोर COM इंटरफ़ेस की तलाश कर रहा हूं, जैसे IOleClientSite, जिसे मैं WebBrowserSite पर पास-थ्रू करना चाहता हूं। उन सभी को ComImport के साथ COM के संपर्क में लाया गया है, लेकिन फ्रेमवर्क के WebBrowserSite/UnsafeNativeMethods के कार्यान्वयन द्वारा private या internal के रूप में घोषित किया गया है। इस प्रकार, मैं व्युत्पन्न कक्षा में explicitly re-implement नहीं कर सकता। मुझे अपने खुद के संस्करणों को परिभाषित करना है, जैसे मैंने IDocHostUIHandler के साथ किया था।

तो सवाल यह है कि, मैं अपने व्युत्पन्न वर्ग से WebBrowserSite में परिभाषित निजी या आंतरिक COM इंटरफ़ेस की विधि कैसे कॉल करूं? उदाहरण के लिए, मैं IOleClientSite.GetContainer पर कॉल करना चाहता हूं। मैं प्रतिबिंब का उपयोग कर सकता हूं (जैसे this), लेकिन यह आखिरी उपाय होगा, दूसरा स्क्रैच से WebBrowser को फिर से कार्यान्वित करने के लिए दूसरा होगा।

मेरे सोच, है फ्रेमवर्क के निजी UnsafeNativeMethods.IOleClientSite और अपने ImprovedWebBrowserSite.IOleClientSite दोनों कॉम इंटरफेस, ComImport विशेषता, एक ही GUID और समान विधि हस्ताक्षरों के साथ घोषित कर रहे हैं क्योंकि। .NET 4.0+ में COM Type Equivalence है, इसलिए प्रतिबिंब के बिना इसे करने का कोई तरीका होना चाहिए।

[अद्यतन] अब जब कि मैं एक solution मिल गया है, मेरा मानना ​​है कि यह WebBrowser की WinForms version नियंत्रण को अनुकूलित में कुछ नए और दिलचस्प संभावनाओं को खोलता है।

प्रश्न का यह संस्करण my initial attempt के बाद बनाया गया था ताकि अधिक सार तत्व में समस्या को तैयार करने के लिए एक कमेंटेटर द्वारा भ्रामक कहा जा सके। टिप्पणी बाद में हटा दी गई है, लेकिन मैंने दोनों संस्करणों को रखने का फैसला किया।

मैं इस समस्या को हल करने के लिए प्रतिबिंब का उपयोग क्यों नहीं करना चाहता था? कुछ कारणों के लिए:

  • निर्भरता के रूप में एक COM इंटरफेस, द्विआधारी v-तालिका अनुबंध के बारे में है जिसके साथ विपरीत, WebBrowserSite के कार्यान्वयन के द्वारा दिए गए आंतरिक या निजी तरीकों की वास्तविक प्रतीकात्मक नाम, पर।

  • भारी प्रतिबिंब कोड। उदाहरण के लिए, बेस के निजी TranslateAccelerator को Type.InvokeMember के माध्यम से कॉल करने पर विचार करें, और मेरे पास कॉल करने के लिए ~ 20 तरीके हैं।

  • हालांकि कम महत्वपूर्ण, दक्षता: प्रतिबिंब के माध्यम से देर से कॉल किया गया कॉल वी-टेबल के माध्यम से एक COM इंटरफ़ेस विधि को सीधे कॉल से हमेशा कम कुशल होता है।

+0

कारण है कि आप उन्हें ओवरराइड करना चाहते हैं और नहीं बस उनकी घोषणाओं कॉपी-पेस्ट करते हैं? – VladL

+0

@VladL, [.NET 4.x स्रोत] पर एक नज़र डालें (http://referencesource.microsoft.com/netframework.aspx)। 'WebBrowserSite' ActiveX होस्टिंग के रूप में बहुत सी चीजें करता है, मैं केवल COM इंटरफेस के कार्यान्वयन को कॉल नहीं कर सकता। मैं निश्चित रूप से इंटरफ़ेस घोषणाओं को कॉपी-पेस्ट कर सकता हूं, लेकिन मुझे मूल कार्यान्वयन भी कॉल करना होगा। – Noseratio

+1

मुझे नहीं लगता कि आप गैर सार्वजनिक प्रकारों के बीच समानता प्राप्त कर सकते हैं। यदि आप एक गैर-सार्वजनिक प्रकार में TypeIdentifier विशेषता जोड़ने का प्रयास करते हैं, तो आपको इस प्रकार की त्रुटि मिल जाएगी: "असेंबली 'blabla ...' से 'IOleClientSite' प्रकार लोड नहीं किया जा सका। प्रकार को टाइप समतुल्य के लिए योग्य के रूप में चिह्नित किया गया है, लेकिन इसे सार्वजनिक के रूप में चिह्नित नहीं किया गया है। " –

उत्तर

7

अंत में, मेरा मानना ​​है कि मैंने @EricBrown से कुछ मदद के साथ समस्या using Marshal.CreateAggregatedObject हल कर दी है।

यहाँ कोड है कि WebBrowserSite OLE इंटरफेस को अनुकूलित, एक उदाहरण के रूप IOleClientSite का उपयोग कर, WebBrowserSite के निजी COM-दिखाई कार्यान्वयन बुला संभव बनाता है। इसे अन्य इंटरफेस तक बढ़ाया जा सकता है, उदा। IDocHostUIHandler

using System; 
using System.Diagnostics; 
using System.Linq; 
using System.Runtime.InteropServices; 
using System.Windows.Forms; 

namespace CustomWebBrowser 
{ 
    public partial class MainForm : Form 
    { 
     public MainForm() 
     { 
      InitializeComponent(); 
     } 

     private void MainForm_Load(object sender, EventArgs e) 
     { 
      var wb = new ImprovedWebBrowser(); 
      wb.Dock = DockStyle.Fill; 
      this.Controls.Add(wb); 
      wb.Visible = true; 
      wb.DocumentText = "<b>Hello from ImprovedWebBrowser!</b>"; 
     } 
    } 

    // ImprovedWebBrowser with custom pass-through IOleClientSite 
    public class ImprovedWebBrowser: WebBrowser 
    { 
     // provide custom WebBrowserSite, 
     // where we override IOleClientSite and call the base implementation 
     protected override WebBrowserSiteBase CreateWebBrowserSiteBase() 
     { 
      return new ImprovedWebBrowserSite(this); 
     } 

     // IOleClientSite 
     [ComImport(), Guid("00000118-0000-0000-C000-000000000046")] 
     [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 
     public interface IOleClientSite 
     { 
      void SaveObject(); 

      [return: MarshalAs(UnmanagedType.Interface)] 
      object GetMoniker(
       [In, MarshalAs(UnmanagedType.U4)] int dwAssign, 
       [In, MarshalAs(UnmanagedType.U4)] int dwWhichMoniker); 

      [PreserveSig] 
      int GetContainer([Out] out IntPtr ppContainer); 

      void ShowObject(); 

      void OnShowWindow([In, MarshalAs(UnmanagedType.I4)] int fShow); 

      void RequestNewObjectLayout(); 
     } 

     // ImprovedWebBrowserSite 
     protected class ImprovedWebBrowserSite : 
      WebBrowserSite, 
      IOleClientSite, 
      ICustomQueryInterface, 
      IDisposable 
     { 
      IOleClientSite _baseIOleClientSite; 
      IntPtr _unkOuter; 
      IntPtr _unkInnerAggregated; 
      Inner _inner; 

      #region Inner 
      // Inner as aggregated object 
      class Inner : 
       ICustomQueryInterface, 
       IDisposable 
      { 
       object _outer; 
       Type[] _interfaces; 

       public Inner(object outer) 
       { 
        _outer = outer; 
        // the base's private COM interfaces are here 
        _interfaces = _outer.GetType().BaseType.GetInterfaces(); 
       } 

       public CustomQueryInterfaceResult GetInterface(ref Guid iid, out IntPtr ppv) 
       { 
        if (_outer != null) 
        { 
         var ifaceGuid = iid; 
         var iface = _interfaces.FirstOrDefault((t) => t.GUID == ifaceGuid); 
         if (iface != null) 
         { 
          var unk = Marshal.GetComInterfaceForObject(_outer, iface, CustomQueryInterfaceMode.Ignore); 
          if (unk != IntPtr.Zero) 
          { 
           ppv = unk; 
           return CustomQueryInterfaceResult.Handled; 
          } 
         } 
        } 
        ppv = IntPtr.Zero; 
        return CustomQueryInterfaceResult.Failed; 
       } 

       ~Inner() 
       { 
        // need to work out the reference counting for GC to work correctly 
        Debug.Print("Inner object finalized."); 
       } 

       public void Dispose() 
       { 
        _outer = null; 
        _interfaces = null; 
       } 
      } 
      #endregion 

      // constructor 
      public ImprovedWebBrowserSite(WebBrowser host): 
       base(host) 
      { 
       // get the CCW object for this 
       _unkOuter = Marshal.GetIUnknownForObject(this); 
       Marshal.AddRef(_unkOuter); 
       try 
       { 
        // aggregate the CCW object with the helper Inner object 
        _inner = new Inner(this); 
        _unkInnerAggregated = Marshal.CreateAggregatedObject(_unkOuter, _inner); 

        // turn private WebBrowserSiteBase.IOleClientSite into our own IOleClientSite 
        _baseIOleClientSite = (IOleClientSite)Marshal.GetTypedObjectForIUnknown(_unkInnerAggregated, typeof(IOleClientSite)); 
       } 
       finally 
       { 
        Marshal.Release(_unkOuter); 
       } 
      } 

      ~ImprovedWebBrowserSite() 
      { 
       // need to work out the reference counting for GC to work correctly 
       Debug.Print("ImprovedClass finalized."); 
      } 

      public CustomQueryInterfaceResult GetInterface(ref Guid iid, out IntPtr ppv) 
      { 
       if (iid == typeof(IOleClientSite).GUID) 
       { 
        // CustomQueryInterfaceMode.Ignore is to avoid infinite loop during QI. 
        ppv = Marshal.GetComInterfaceForObject(this, typeof(IOleClientSite), CustomQueryInterfaceMode.Ignore); 
        return CustomQueryInterfaceResult.Handled; 
       } 
       ppv = IntPtr.Zero; 
       return CustomQueryInterfaceResult.NotHandled; 
      } 

      void IDisposable.Dispose() 
      { 
       base.Dispose(); 

       // we may have recicular references to itself 
       _baseIOleClientSite = null; 

       if (_inner != null) 
       { 
        _inner.Dispose(); 
        _inner = null; 
       } 

       if (_unkInnerAggregated != IntPtr.Zero) 
       { 
        Marshal.Release(_unkInnerAggregated); 
        _unkInnerAggregated = IntPtr.Zero; 
       } 

       if (_unkOuter != IntPtr.Zero) 
       { 
        Marshal.Release(_unkOuter); 
        _unkOuter = IntPtr.Zero; 
       } 
      } 

      #region IOleClientSite 
      // IOleClientSite 
      public void SaveObject() 
      { 
       Debug.Print("IOleClientSite.SaveObject"); 
       _baseIOleClientSite.SaveObject(); 
      } 

      public object GetMoniker(int dwAssign, int dwWhichMoniker) 
      { 
       Debug.Print("IOleClientSite.GetMoniker"); 
       return _baseIOleClientSite.GetMoniker(dwAssign, dwWhichMoniker); 
      } 

      public int GetContainer(out IntPtr ppContainer) 
      { 
       Debug.Print("IOleClientSite.GetContainer"); 
       return _baseIOleClientSite.GetContainer(out ppContainer); 
      } 

      public void ShowObject() 
      { 
       Debug.Print("IOleClientSite.ShowObject"); 
       _baseIOleClientSite.ShowObject(); 
      } 

      public void OnShowWindow(int fShow) 
      { 
       Debug.Print("IOleClientSite.OnShowWindow"); 
       _baseIOleClientSite.OnShowWindow(fShow); 
      } 

      public void RequestNewObjectLayout() 
      { 
       Debug.Print("IOleClientSite.RequestNewObjectLayout"); 
       _baseIOleClientSite.RequestNewObjectLayout(); 
      } 
      #endregion 
     } 
    } 
} 
+0

यह नेट 4.5 में अच्छी तरह से काम करता है लेकिन नेट 4.0 में एक FatalExecutionEngineError यह कहता है "रनटाइम में घातक त्रुटि आई है। त्रुटि का पता 0x6ef47786 पर था, 0x2e18 थ्रेड पर। त्रुटि कोड 0xc0000005 है। यह त्रुटि सीएलआर में या असुरक्षित या गैर-सत्यापन योग्य हिस्सों में एक बग हो सकती है उपयोगकर्ता कोड। " निम्न पंक्ति में यह त्रुटि आती है: _baseIDocHostUiHandler = (NativeMethods.IDocHostUIHandler) Marshal.GetTypedObjectForIUnknown (_unkInnerAggregated, typeof (NativeMethods.IDocHostUIHandler)); कोई विचार? –

+0

@VallabhaV, .NET 4.0 के लिए कोई उच्च उम्मीद नहीं, मैं इसके लिए अतिरिक्त समय पर छोटा हूं, क्षमा करें। – Noseratio

+0

लिंकिंग [यह] (http://stackoverflow.com/a/26796534/1768303) प्रासंगिक के रूप में। – Noseratio

4

बस एक विचार लेकिन शायद आप here से कुछ स्रोत कोड का उपयोग कर सकते हैं। आप पुनर्मूल्यांकन करेंगे लेकिन यह आपको वह दे सकता है जो आप चाहते हैं।

+0

धन्यवाद, मैंने वास्तव में पहले ही फ्रेमवर्क स्रोतों का अध्ययन किया है - यह समझने में बहुत मददगार है कि 'WebBrowser' कैसे काम करता है। हालांकि इस मामले में मैं इसके साथ सबसे अच्छा कर सकता हूं, 'असुरक्षित विधि' की परिभाषाओं को उधार लेना। – Noseratio

+0

मुझे उम्मीद है कि जिन लोगों ने सवाल कम किया था, वे बात करेंगे और क्यों समझाएंगे, लेकिन उन्होंने नहीं किया। ** जबकि @ डेनेक्सक्स्टोन का जवाब वास्तविक समस्या का समाधान नहीं है, मैं सराहना करता हूं कि उसने वास्तव में मदद करने की कोशिश की, इसलिए बकाया उसके पास जाता है। ** – Noseratio

संबंधित मुद्दे