2011-02-17 12 views
35

हम डब्ल्यूपीएफ में एक लेआउट मैनेजर विकसित कर रहे हैं जिसमें व्यूपोर्ट्स हैं जिन्हें उपयोगकर्ता द्वारा स्थानांतरित/आकार/आदि किया जा सकता है। व्यूपोर्ट आम तौर पर लेआउट मैनेजर में हमारे नियंत्रण में मौजूद प्रदाताओं के माध्यम से डेटा (चित्र/फिल्में/आदि) से भरे जाते हैं। मेरा काम यह जांचना है कि व्यूपोर्ट में किसी भी बाहरी विंडोज ऐप (यानी नोटपैड, कैल्क, एडोब रीडर, आदि) को होस्ट करना संभव है या नहीं। मुझे कई समस्याएं आती हैं।डब्ल्यूपीएफ विंडो में बाहरी ऐप होस्टिंग

अधिकांश संसाधन HwndHost क्लास का उपयोग करने के लिए इंगित करते हैं। मैं माइक्रोसॉफ्ट से इस walkthrough के साथ प्रयोग कर रहा हूँ: http://msdn.microsoft.com/en-us/library/ms752055.aspx

मैंने इसे अनुकूलित किया है ताकि सूची बॉक्स को बाहरी एप्लिकेशन से विंडोज हैंडल के साथ बदल दिया गया हो। किसी को भी मेरी मदद कर सकते हैं इन सवालों के साथ:

  1. पूर्वाभ्यास एक अतिरिक्त स्थिर उप खिड़की जिसमें ListBox रखा गया है कहते हैं। मुझे नहीं लगता कि मुझे बाहरी ऐप्स के लिए इसकी आवश्यकता है। अगर मैं इसे छोड़ देता हूं, तो मुझे बाहरी ऐप को एक बाल विंडो बनाना होगा (उपयोगकर्ता 32.dll से Get/SetWindowLong का उपयोग GWL_STYLE को WS_CHILD के रूप में सेट करना होगा)। लेकिन अगर मैं ऐसा करता हूं, तो ऐप का मेनू बार गायब हो जाता है (WS_CHILD शैली की वजह से) और अब यह इनपुट प्राप्त नहीं करता है।
  2. यदि मैं उप विंडो का उपयोग करता हूं, और बाहरी ऐप को उस चीज का बच्चा उचित रूप से काम करता है, लेकिन कभी-कभी बाहरी ऐप ठीक से पेंट नहीं करता है।
  3. इसके अलावा, मुझे व्यूपोर्ट में आकार बदलने के लिए बाल विंडो की आवश्यकता है। क्या यह संभव है?
  4. जब बाहरी ऐप एक बच्चे की खिड़की (यानी नोटपैड-> सहायता-> के बारे में) उत्पन्न करता है, तो यह विंडो HwndHost द्वारा होस्ट नहीं की जाती है (और इस प्रकार व्यूपोर्ट के बाहर ले जाया जा सकता है)। क्या कोई रास्ता है जिससे मैं इसे रोक सकता हूं?
  5. चूंकि मुझे बाहरी एप्लिकेशन और लेआउट मैनेजर के बीच कोई और बातचीत की आवश्यकता नहीं है, क्या मुझे लगता है कि मुझे संदेशों को पकड़ने और अग्रेषित करने की आवश्यकता नहीं है? (सूची बॉक्स में चयन परिवर्तनों को पकड़ने के लिए walkthrough उप विंडो में एक HwndSourceHook जोड़ता है)।
  6. जब आप (अनमोडिफाइड) उदाहरण VS2010 चलाते हैं और विंडो बंद करते हैं, तो VS2010 यह नहीं देखता कि प्रोग्राम समाप्त हो गया है। यदि आप सभी को तोड़ते हैं, तो आप स्रोत के बिना असेंबली में समाप्त होते हैं। कुछ गंध चल रही है, लेकिन मुझे यह नहीं मिल रहा है।
  7. walkthrough खुद को बहुत ही गड़बड़ लगता है, लेकिन मुझे इस विषय पर कोई बेहतर दस्तावेज नहीं मिला है। कोई अन्य उदाहरण?
  8. एक अन्य दृष्टिकोण HwndHost का उपयोग नहीं करना है, लेकिन WindowsFormHost पर चर्चा के रूप में here पर चर्चा की गई है। यह काम करता है (और बहुत आसान है!) लेकिन मेरे पास आवेदन के आकार पर नियंत्रण नहीं है? इसके अलावा, WinFormHost वास्तव में इसके लिए वास्तव में नहीं है?

सही दिशा में किसी भी पॉइंटर्स के लिए धन्यवाद।

+0

हाय, मैं निश्चित रूप से बिंदु 8. –

उत्तर

22

खैर ... अगर सवाल 20 साल पहले की तरह पेश किया गया था, एक, उत्तर होगा "बेशक, 'OLE' को देखो!", यहाँ क्या है "ऑब्जेक्ट लिंकिंग और एम्बेडिंग" करने के लिए एक कड़ी है:

http://en.wikipedia.org/wiki/Object_Linking_and_Embedding

आप इस लेख को पढ़ने के हैं, तो आप इंटरफेस इस कल्पना परिभाषित की संख्या देखेंगे, क्योंकि इसके लेखक नहीं सोचा कि यह मजेदार था, लेकिन यह सामान्य मामलों में प्राप्त करने के लिए तकनीकी रूप से कठिन है, क्योंकि

यह वास्तव में कुछ ऐप्स द्वारा समर्थित है (ज्यादातर माइक्रोसॉफ्ट वाले, जैसे माइक्रोसॉफ्ट ओएलई का लगभग एकमात्र प्रायोजक था ...)

आप इन ऐप्स (यहाँ पर एसओ लिंक देखते हैं कुछ DSOFramer कहा जाता है का उपयोग कर एम्बेड कर सकते हैं: MS KB311765 and DsoFramer are missing from MS site) एक घटक है कि आप OLE सर्वर होस्ट करने के लिए अनुमति देता है, (यानी: किसी अन्य प्रक्रिया के रूप में चल बाहरी ऐप्स) नेत्रहीन एक आवेदन के अंदर। माइक्रोसॉफ्ट ने कुछ साल पहले माइक्रोसॉफ्ट को कुछ हद तक छोड़ दिया था, जो इस बिंदु पर अब समर्थित नहीं है कि द्विआधारी को खोजने में काफी मुश्किल है!

यह (मई) अभी भी सरल ओएलई सर्वर के लिए काम करता है, लेकिन मुझे लगता है कि मैंने कहीं पढ़ा है, यह वर्ड 2010 जैसे नए माइक्रोसॉफ्ट अनुप्रयोगों के लिए भी काम नहीं करता है। तो, आप इसे समर्थन देने वाले एप्लिकेशन के लिए DSOFramer का उपयोग कर सकते हैं। आप इसे आज़मा सकते हैं।

दूसरों अनुप्रयोगों के लिए, अच्छी तरह से, आज, आधुनिक दुनिया में हम रहते है, आप की मेजबानी नहीं है अनुप्रयोगों, बाहरी प्रक्रिया में भाग गया, आप घटकों की मेजबानी, और वे inprocess चलाने के लिए माना जाता सामान्य रूप में कर रहे हैं। यही कारण है कि आप सामान्य में जो करना चाहते हैं, उसे करने में आपको बड़ी कठिनाइयों का सामना करना पड़ेगा। एक समस्या जिसका आप सामना करेंगे (और कम से कम विंडोज के हाल के संस्करणों के साथ) सुरक्षा है: आपकी प्रक्रिया को मैं कैसे भरोसा नहीं कर सकता हूं मेरी विंडोज़ और मेरी प्रक्रिया द्वारा बनाए गए मेनू :-)?

फिर भी, आप विभिन्न विंडोज हैक का उपयोग करके एप्लिकेशन द्वारा काफी आवेदन कर सकते हैं। SetParent मूल रूप से :-)

सभी हैक्स की माँ यहाँ कोड का एक टुकड़ा है कि आप बात नमूना फैली हुई है, स्वत: आकार बदलने जोड़ने, और कैप्शन बॉक्स को हटा दिए है। यह दर्शाता है implicitely नियंत्रण बॉक्स, सिस्टम मेनू को निकालने का तरीका, एक उदाहरण के रूप:

public partial class Window1 : Window 
{ 
    private System.Windows.Forms.Panel _panel; 
    private Process _process; 

    public Window1() 
    { 
     InitializeComponent(); 
     _panel = new System.Windows.Forms.Panel(); 
     windowsFormsHost1.Child = _panel; 
    } 

    [DllImport("user32.dll")] 
    private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); 

    [DllImport("user32.dll", SetLastError = true)] 
    private static extern int GetWindowLong(IntPtr hWnd, int nIndex); 

    [DllImport("user32")] 
    private static extern IntPtr SetParent(IntPtr hWnd, IntPtr hWndParent); 

    [DllImport("user32")] 
    private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags); 

    private const int SWP_NOZORDER = 0x0004; 
    private const int SWP_NOACTIVATE = 0x0010; 
    private const int GWL_STYLE = -16; 
    private const int WS_CAPTION = 0x00C00000; 
    private const int WS_THICKFRAME = 0x00040000; 

    private void button1_Click(object sender, RoutedEventArgs e) 
    { 
     button1.Visibility = Visibility.Hidden; 
     ProcessStartInfo psi = new ProcessStartInfo("notepad.exe"); 
     _process = Process.Start(psi); 
     _process.WaitForInputIdle(); 
     SetParent(_process.MainWindowHandle, _panel.Handle); 

     // remove control box 
     int style = GetWindowLong(_process.MainWindowHandle, GWL_STYLE); 
     style = style & ~WS_CAPTION & ~WS_THICKFRAME; 
     SetWindowLong(_process.MainWindowHandle, GWL_STYLE, style); 

     // resize embedded application & refresh 
     ResizeEmbeddedApp(); 
    } 

    protected override void OnClosing(System.ComponentModel.CancelEventArgs e) 
    { 
     base.OnClosing(e); 
     if (_process != null) 
     { 
      _process.Refresh(); 
      _process.Close(); 
     } 
    } 

    private void ResizeEmbeddedApp() 
    { 
     if (_process == null) 
      return; 

     SetWindowPos(_process.MainWindowHandle, IntPtr.Zero, 0, 0, (int)_panel.ClientSize.Width, (int)_panel.ClientSize.Height, SWP_NOZORDER | SWP_NOACTIVATE); 
    } 

    protected override Size MeasureOverride(Size availableSize) 
    { 
     Size size = base.MeasureOverride(availableSize); 
     ResizeEmbeddedApp(); 
     return size; 
    } 
} 

यह मूलतः सभी विंडोज "पारंपरिक" हैक्स है। आप आइटम मेनू को भी हटा सकते हैं जिसे आप पसंद नहीं करते हैं, जैसा कि यहां बताया गया है: http://support.microsoft.com/kb/110393/en-us (फ़ॉर्म के नियंत्रण-मेनू बॉक्स से मेनू आइटम कैसे निकालें)।

आप "winword.exe" द्वारा "notepad.exe" को भी प्रतिस्थापित कर सकते हैं और यह काम करने के लिए लगता है। लेकिन इसके लिए सीमाएं हैं (कीबोर्ड, माउस, फोकस इत्यादि)।

गुड लक!

+3

प्रतीक्षा, WINAPI कॉल नेट प्रोग्रामर के लिए "हैक्स" माना जाता है के लिए जाना चाहते हैं? o_O –

+1

@ TamásSzelei - एपीआई समर्थित है, लेकिन आपको अपने कैप्शन, फ्रेम और पैरेंट रिलेशनशिप को बदलने जैसी अन्य अनुप्रयोगों के साथ खेलना नहीं है। यह लक्ष्य आवेदन को बहुत अच्छी तरह से दुर्घटनाग्रस्त कर सकता है। –

+0

पर्याप्त मेला। बीटीडब्ल्यू, मेनविंडोहैंडल अक्सर काम नहीं करता है (उदाहरण के लिए नवीनतम आईई, क्रोम, फ़ायरफ़ॉक्स के साथ)। एचडब्ल्यूएन प्राप्त करने के लिए विंडो क्लास के साथ FindWindow का उपयोग करने के लिए थोड़ा और विश्वसनीय दृष्टिकोण है। इसके अलावा, क्या आप बाहरी ऐप पर क्लिक होने पर भी विंडो फोकस रखने का तरीका जानते हैं? –

1

समाधान अविश्वसनीय रूप से शामिल है। बहुत सारे कोड यहां कुछ संकेत दिए गए हैं।

सबसे पहले, आप सही रास्ते पर हैं।

आपको HwndHost और HwndSource चीज़ का उपयोग करना होगा। यदि आप नहीं करते हैं, तो आपको दृश्य कलाकृतियां मिलेंगी। झिलमिलाहट की तरह। एक चेतावनी, यदि आप होस्ट और स्रोत का उपयोग नहीं करते हैं, तो ऐसा लगता है कि यह काम करेगा, लेकिन अंत में नहीं होगा - इसमें यादृच्छिक छोटी बेवकूफ कीड़े होंगी।

कुछ संकेतों के लिए इसे देखें। यह पूरा नहीं है, लेकिन यह आपको सही दिशा में जाने में मदद करेगा। http://microsoftdwayneneed.codeplex.com/SourceControl/changeset/view/50925#1029346

आपको जो कुछ भी पूछ रहा है उसे नियंत्रित करने के लिए आपको Win32 में जाना होगा। आपको संदेशों को पकड़ने और अग्रेषित करने की आवश्यकता है। आपको यह नियंत्रित करने की ज़रूरत है कि कौन सी खिड़कियां बच्चे की खिड़कियां "स्वयं" हैं।

जासूस ++ बहुत उपयोग करें।

4

इस धागे में जवाब पढ़ने और कुछ परीक्षण करने और त्रुटि करने के बाद मैं कुछ अच्छी तरह से काम करता हूं जो कुछ अच्छी तरह से काम करता है, लेकिन निश्चित रूप से कुछ चीजों को विशेष मामलों के लिए आपका ध्यान रखना होगा।

मैं अपने मेजबान वर्ग के लिए आधार वर्ग के रूप में इस्तेमाल किया HwndHostEx, तो आप इसे यहाँ पा सकते हैं: http://microsoftdwayneneed.codeplex.com/SourceControl/changeset/view/69631#1034035

उदाहरण कोड:

public class NotepadHwndHost : HwndHostEx 
{ 
    private Process _process; 

    protected override HWND BuildWindowOverride(HWND hwndParent) 
    { 
     ProcessStartInfo psi = new ProcessStartInfo("notepad.exe"); 
     _process = Process.Start(psi); 
     _process.WaitForInputIdle(); 

     // The main window handle may be unavailable for a while, just wait for it 
     while (_process.MainWindowHandle == IntPtr.Zero) 
     { 
      Thread.Yield(); 
     } 

     HWND hwnd = new HWND(_process.MainWindowHandle); 

     int style = NativeMethods.GetWindowLong(hwnd, GWL.STYLE); 

     style = style & ~((int)WS.CAPTION) & ~((int)WS.THICKFRAME); // Removes Caption bar and the sizing border 
     style |= ((int)WS.CHILD); // Must be a child window to be hosted 

     NativeMethods.SetWindowLong(hwnd, GWL.STYLE, style); 

     return hwnd; 
    } 

    protected override void DestroyWindowOverride(HWND hwnd) 
    { 
     _process.CloseMainWindow(); 

     _process.WaitForExit(5000); 

     if (_process.HasExited == false) 
     { 
      _process.Kill(); 
     } 

     _process.Close(); 
     _process.Dispose(); 
     _process = null; 
     hwnd.Dispose(); 
     hwnd = null; 
    } 
} 

HWND, NativeMethods और enums DwayneNeed पुस्तकालय से रूप में अच्छी तरह आता है (Microsoft.DwayneNeed.User32)।

बस एक WPF विंडो में बच्चे के रूप में नोटपैडहैंडहोस्ट जोड़ें और आपको वहां होस्ट की गई नोटपैड विंडो देखना चाहिए।

0

करने के लिए अपने जवाब की जाँच करें: How to run an application inside wpf application?

मैं Notepad DwayneNeed गुड़ के बिना काम उदाहरण पाने में कामयाब रहे। मैंने बस SetParent() और बूम जोड़ा ... वह सिर्फ DwayneNeed उदाहरण की तरह काम करती है।

4

साइमन मॉरीयर का जवाब बेहद अच्छी तरह लिखा गया है। हालांकि, जब मैंने इसे अपने द्वारा बनाए गए एक Winform ऐप के साथ करने की कोशिश की, तो यह असफल रहा।

_process.WaitForInputIdle(); 

द्वारा

while (_process.MainWindowHandle==IntPtr.Zero) 
      { 
       Thread.Sleep(1); 
      } 

बदला जा सकता है और सब कुछ ठीक से।

महान प्रश्न और आपके सभी उत्तरों के लिए धन्यवाद।

1

मेरे पास यह उत्पादन में चल रहा है और अब तक एक WPF एप्लिकेशन में इतना अच्छा है। सुनिश्चित करें कि आप यूआई थ्रेड से SetNativeWindowInWPFWindowAsChild() पर कॉल करें जो window का मालिक है।

public static bool SetNativeWindowInWPFWindowAsChild(IntPtr hWndNative, Window window) 
    { 
     UInt32 dwSyleToRemove = WS_POPUP | WS_CAPTION | WS_THICKFRAME; 
     UInt32 dwExStyleToRemove = WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE; 

     UInt32 dwStyle = GetWindowLong(hWndNative, GWL_STYLE); 
     UInt32 dwExStyle = GetWindowLong(hWndNative, GWL_EXSTYLE); 

     dwStyle &= ~dwSyleToRemove; 
     dwExStyle &= ~dwExStyleToRemove; 

     SetWindowLong(hWndNative, GWL_STYLE, dwStyle | WS_CHILD); 
     SetWindowLong(hWndNative, GWL_EXSTYLE, dwExStyle); 

     IntPtr hWndOld = SetParent(hWndNative, new WindowInteropHelper(window).Handle); 
     if (hWndOld == IntPtr.Zero) 
     { 
      System.Diagnostics.Debug.WriteLine("SetParent() Failed -> LAST ERROR: " + Marshal.GetLastWin32Error() + "\n"); 
     } 
     return hWndOld != IntPtr.Zero; 
    } 

यहां उपयोग किए गए मूल Win32 API का उपयोग किया गया है। (के बाद यह सेट है यहाँ क्योंकि मैं आकार/में एक्स्ट्रा कलाकार फ़ोकस विंडो हैं)

 [StructLayout(LayoutKind.Sequential)] 
     private struct RECT 
     { 
      public Int32 left; 
      public Int32 top; 
      public Int32 right; 
      public Int32 bottom; 
     } 
     [DllImport("user32.dll", SetLastError = true)] 
     private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent); 
     [DllImport("user32.dll")] 
     private static extern UInt32 SetWindowLong(IntPtr hWnd, int nIndex, UInt32 dwNewLong); 
     [DllImport("user32.dll")] 
     private static extern UInt32 GetWindowLong(IntPtr hWnd, int nIndex); 
     [DllImport("user32.dll")] 
     private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); 
     [DllImport("user32.dll")] 
     private static extern IntPtr SetFocus(IntPtr hWnd); 
     [DllImport("user32.dll")] 
     private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags); 

     private static int GWL_STYLE = -16; 
     private static int GWL_EXSTYLE = -20; 

     private static UInt32 WS_CHILD = 0x40000000; 
     private static UInt32 WS_POPUP = 0x80000000; 
     private static UInt32 WS_CAPTION = 0x00C00000; 
     private static UInt32 WS_THICKFRAME = 0x00040000; 

     private static UInt32 WS_EX_DLGMODALFRAME = 0x00000001; 
     private static UInt32 WS_EX_WINDOWEDGE = 0x00000100; 
     private static UInt32 WS_EX_CLIENTEDGE = 0x00000200; 
     private static UInt32 WS_EX_STATICEDGE = 0x00020000; 

     [Flags] 
     private enum SetWindowPosFlags : uint 
     { 
      SWP_ASYNCWINDOWPOS = 0x4000, 
      SWP_DEFERERASE = 0x2000, 
      SWP_DRAWFRAME = 0x0020, 
      SWP_FRAMECHANGED = 0x0020, 
      SWP_HIDEWINDOW = 0x0080, 
      SWP_NOACTIVATE = 0x0010, 
      SWP_NOCOPYBITS = 0x0100, 
      SWP_NOMOVE = 0x0002, 
      SWP_NOOWNERZORDER = 0x0200, 
      SWP_NOREDRAW = 0x0008, 
      SWP_NOREPOSITION = 0x0200, 
      SWP_NOSENDCHANGING = 0x0400, 
      SWP_NOSIZE = 0x0001, 
      SWP_NOZORDER = 0x0004, 
      SWP_SHOWWINDOW = 0x0040 
     } 
     private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1); 
     private static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2); 
     private static readonly IntPtr HWND_TOP = new IntPtr(0); 
     private static readonly IntPtr HWND_BOTTOM = new IntPtr(1); 
संबंधित मुद्दे