none
Form als Topmost im Fenster eines anderen Prozesses RRS feed

  • Frage

  • Hallo,

     

    Ich möchte eine Form mit Informationen Topmost im Fenster eines anderen Prozesses anzeigen. Meine ersten Gehversuche mit Form.Show() bringen aber keinen Erfolg. Auch ein PInvoke mit SetParent brachte bisher nichts (Ich sehe meine Form einfach nicht)

    Hat jemand ne Idee, wie ich vorgehen kann?

    Danke

    Michael

    Montag, 26. Juli 2010 13:15

Antworten

  • Hallo Michael,

    Thorsten's Idee funktioniert schon. Es ist vielleicht nur eine Frage der Reihenfolge bei der Fenstererstellung warum es bei Dir nicht funktioniert. Probier mal diesen Code aus:

    using System;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
    
    namespace NotepadHeadUpDisplay
    {
      public partial class Form1 : Form
      {
        [DllImport("user32.dll")]
        static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
    
        [DllImport("user32.dll")]
        static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, uint dwNewLong);
    
        const int GWL_HWNDPARENT = -8;
    
        public Form1()
        {
          Process p = Process.Start("notepad.exe");
          p.WaitForInputIdle();
          this.ShowInTaskbar = false;
          
          InitializeComponent();
        }
    
        private void Form1_Shown(object sender, EventArgs e)
        {
          Process[] procs = Process.GetProcessesByName("Notepad");
          if (procs.Length != 0)
          {
            SetWindowLong(this.Handle, GWL_HWNDPARENT, (uint)procs[0].MainWindowHandle);
          }
        }
      }
    }
    

    Auf meinem Rechner wird das .NET-Fenster nie hinter Notepad gezeigt und beim Minimieren/Maximieren von Notepad wird es ebenfalls mit minimiert/maximiert.

    Gruss
    Marcel

    Dienstag, 27. Juli 2010 21:19
  • Hallo,

    Ich möchte schon, dass meine Form ein echtes Child wird.

    OK, dann habe ich Dich falsch verstanden.

    Wie gesagt führt ein SetParent() auch dazu, dass meine Form in die Z-Order des Notepad eingefügt wird, nur wird meine Form vom Notepad Content übermalt. 

    das liegt wohl an den Fensterstilen des Edit Window von Notepad, diesen fehlt das Attribut WS_CLIPSIBLINGS, womit der Content anderer Controls einfach übermalt wird. Hier eine Möglichkeit speziell für Notepad:

      public partial class Form1 : Form
      {
    
        public Form1()
        {
          InitializeComponent();
        }
    
        const int GWL_STYLE = (-16);
        const int GWL_EXSTYLE = (-20);
    
        const uint WS_CLIPSIBLINGS = 0x4000000;
        const uint WS_CLIPCHILDREN = 0x2000000;
        const uint WS_POPUP = 0x80000000;
        const uint WS_CHILD = 0x40000000;
    
        [DllImport("user32.dll", SetLastError = true)]
        static extern uint GetWindowLong(IntPtr hWnd, int nIndex);
    
        [DllImport("user32.dll", SetLastError = true)]
        static extern int SetWindowLong(IntPtr hWnd, int nIndex, uint dwNewStyle);
    
        [DllImport("user32.dll")]
        static extern IntPtr SetParent(IntPtr hWnd, IntPtr dwNewLong);
    
        [DllImport("user32.dll", SetLastError = true)]
        static extern IntPtr FindWindowEx(IntPtr hwndParent, 
                         IntPtr hwndChildAfter, 
                         string lpszClass, 
                         string lpszWindow);
    
        [DllImport("user32.dll")]
        static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
    
        [DllImport("user32.dll")]
        static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
    
        private void button1_Click(object sender, EventArgs e)
        {
    
          Process proc = new Process();
          proc.StartInfo.FileName = "notepad";
          proc.Start();
    
          proc.WaitForInputIdle();
    
          IntPtr newParent = proc.MainWindowHandle;
          IntPtr editWindow = FindWindowEx(newParent, IntPtr.Zero, "Edit", null);
    
          uint notepadThread = GetWindowThreadProcessId(newParent, IntPtr.Zero);
          uint myThread = GetWindowThreadProcessId(this.Handle, IntPtr.Zero);
    
          AttachThreadInput(notepadThread, myThread, true);
    
          SetParent(this.Handle, newParent);
    
          if (editWindow != IntPtr.Zero)
          {
            uint style = GetWindowLong(editWindow, GWL_STYLE);
            style = style | WS_CLIPSIBLINGS;
            SetWindowLong(editWindow, GWL_STYLE, style);
          }
        }
      }
    

    Damit sollte das Fenster zunächst sichtbar sein. Es bleiben aber einige Probleme bestehen. Das Fenster wird nicht neu gezeichnet, wenn das Notepad Fenster vergrößert oder verschoben wird. Alle Bereiche die außerhalb des sichtbaren Bereichs lagen, bleiben weiß oder werden partiell neu gezeichnet. Das Fenster reagiert nicht mehr auf Klicks, sobald es von der ursprünglichen Position verschoben wird. Das scheint auch direkt mit den Neuzeichnen zusammenzuhängen. Ggf. muss man das Fenster einmalig mit SetWindowPos innerhalb des neuen Parent positionieren. Insgesamt bleibt das IMHO ein Hack.


    Thorsten Dörfler
    Microsoft MVP Visual Basic
    vb-faq.de
    Donnerstag, 29. Juli 2010 06:13
    Beantworter
  • Hallo Xantur,

    ich denke, wirklich sauber und generisch bekommt man das nicht hin, was Du willst - vorausgesetzt, Du kannst den externen Prozess nicht beeinflussen. Meine Meinung - .
    Da müsste man schon sehr viel Aufwand treiben IMHO.
    Ggf. mit Hooks und SetWindowPos nebst Monitoring.

    Ist denn Deine Applikation wirklich fremd? Oder kannst Du auf Quellen des Prozesses zugreifen?
    Oder wenn bekannte Schnittstellen zur Verfügung stehen.
    Wenn Du Quellen/Schnittstellen hast, gibt es ggf. noch eine saubere Lösung.

     


    ciao Frank
    Donnerstag, 29. Juli 2010 08:20

Alle Antworten

  • Hallo Michael,

    Wie findest Du dass andere Prozess?

    Grüße,

    Robert

    Montag, 26. Juli 2010 14:26
    Moderator
  • Hallo nochmal,

     

    zum Beispiel so:

    Process[] procs = Process.GetProcessesByName("Notepad");
    if (procs.Length != 0)
    {
      // DO something here
    }
    

    Montag, 26. Juli 2010 16:07
  • Hallo Xantur,

    in vielen Fällen reicht dann:

    [User32's SetForegroundWindow() API in C#]
    http://geekswithblogs.net/vaibhavgaikwad/archive/2007/11/01/116510.aspx

     


    ciao Frank
    Dienstag, 27. Juli 2010 05:05
  • Hallo nochmal,

     

    Das hilft mir nur begrenzt: Ich sehe meine Form im Notepad zwar, aber sobald ich in Notepad hineinklicke "verschwindet die Form". Sie ist aber noch da, was ich z. B. anhand eines in der Form gesetzten WaitCursors erkennen kann. 

    Meine Form wird nur scheinbar vom Editorteil des Notepad übermalt und bekommt selber scheinbar keine Zeichenaufforderung.

    Ciao

    Michael

    Dienstag, 27. Juli 2010 07:29
  • Hallo Michael,

    Ich möchte eine Form mit Informationen Topmost im Fenster eines anderen Prozesses anzeigen. Meine ersten Gehversuche mit Form.Show() bringen aber keinen Erfolg. Auch ein PInvoke mit SetParent brachte bisher nichts (Ich sehe meine Form einfach nicht)

    Deiner Beschreibung nach, möchtest Du den Owner für Dein Formular auf das Fenster des externen Prozesses festlegen, so dass dieses ähnlich dem Suchen Dialog in Notepad, immer über dem Fenster angezeigt wird, aber nicht ein Child dieses Fensters wird. Hier kannst Du P/Invoke mit SetWindowLongPtr verwenden:

     public partial class Form1 : Form
     {
      public Form1()
      {
       InitializeComponent();
      }
    
      [DllImport("user32.dll")]
      static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
    
      private void button1_Click(object sender, EventArgs e)
      {
       const int GWL_HWNDPARENT = -8;
    
       Process[] procs = Process.GetProcessesByName("Notepad");
       if (procs.Length != 0)
       {
        SetWindowLongPtr(this.Handle, GWL_HWNDPARENT, procs[0].MainWindowHandle);
       }
    
      }
     }
    

    Beachte den Hinweis in der Dokumentation, dass man GWL_HWNDPARENT nicht verwenden sollte. Das heißt, diese Funktion wird nicht supported und kann ich nachfolgenden Windows Versionen ohne Funktion sein oder ein unerwartetes Verhalten aufweisen. Die Dokumentation empfiehlt hier mit SetParent zu arbeiten. Leider verhält sich SetParent anders, als SetWindowLongPtr + GWL_HWNDPARENT und setzt das Fenster als Child des fremden Fensters ein, statt nur den Owner zu ändern.


    Thorsten Dörfler
    Microsoft MVP Visual Basic
    vb-faq.de
    Dienstag, 27. Juli 2010 08:27
    Beantworter
  • Hallo,

     

    Ich möchte schon, dass meine Form ein echtes Child wird. Es soll ein Heads Up Display werden und das geht am besten als Child Window. 

    Wie gesagt führt ein SetParent() auch dazu, dass meine Form in die Z-Order des Notepad eingefügt wird, nur wird meine Form vom Notepad Content übermalt.

    Ciao

    Michael

     

    Dienstag, 27. Juli 2010 09:16
  • Hallo Michael,

    Thorsten's Idee funktioniert schon. Es ist vielleicht nur eine Frage der Reihenfolge bei der Fenstererstellung warum es bei Dir nicht funktioniert. Probier mal diesen Code aus:

    using System;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
    
    namespace NotepadHeadUpDisplay
    {
      public partial class Form1 : Form
      {
        [DllImport("user32.dll")]
        static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
    
        [DllImport("user32.dll")]
        static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, uint dwNewLong);
    
        const int GWL_HWNDPARENT = -8;
    
        public Form1()
        {
          Process p = Process.Start("notepad.exe");
          p.WaitForInputIdle();
          this.ShowInTaskbar = false;
          
          InitializeComponent();
        }
    
        private void Form1_Shown(object sender, EventArgs e)
        {
          Process[] procs = Process.GetProcessesByName("Notepad");
          if (procs.Length != 0)
          {
            SetWindowLong(this.Handle, GWL_HWNDPARENT, (uint)procs[0].MainWindowHandle);
          }
        }
      }
    }
    

    Auf meinem Rechner wird das .NET-Fenster nie hinter Notepad gezeigt und beim Minimieren/Maximieren von Notepad wird es ebenfalls mit minimiert/maximiert.

    Gruss
    Marcel

    Dienstag, 27. Juli 2010 21:19
  • Hallo,

    Ich möchte schon, dass meine Form ein echtes Child wird.

    OK, dann habe ich Dich falsch verstanden.

    Wie gesagt führt ein SetParent() auch dazu, dass meine Form in die Z-Order des Notepad eingefügt wird, nur wird meine Form vom Notepad Content übermalt. 

    das liegt wohl an den Fensterstilen des Edit Window von Notepad, diesen fehlt das Attribut WS_CLIPSIBLINGS, womit der Content anderer Controls einfach übermalt wird. Hier eine Möglichkeit speziell für Notepad:

      public partial class Form1 : Form
      {
    
        public Form1()
        {
          InitializeComponent();
        }
    
        const int GWL_STYLE = (-16);
        const int GWL_EXSTYLE = (-20);
    
        const uint WS_CLIPSIBLINGS = 0x4000000;
        const uint WS_CLIPCHILDREN = 0x2000000;
        const uint WS_POPUP = 0x80000000;
        const uint WS_CHILD = 0x40000000;
    
        [DllImport("user32.dll", SetLastError = true)]
        static extern uint GetWindowLong(IntPtr hWnd, int nIndex);
    
        [DllImport("user32.dll", SetLastError = true)]
        static extern int SetWindowLong(IntPtr hWnd, int nIndex, uint dwNewStyle);
    
        [DllImport("user32.dll")]
        static extern IntPtr SetParent(IntPtr hWnd, IntPtr dwNewLong);
    
        [DllImport("user32.dll", SetLastError = true)]
        static extern IntPtr FindWindowEx(IntPtr hwndParent, 
                         IntPtr hwndChildAfter, 
                         string lpszClass, 
                         string lpszWindow);
    
        [DllImport("user32.dll")]
        static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
    
        [DllImport("user32.dll")]
        static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
    
        private void button1_Click(object sender, EventArgs e)
        {
    
          Process proc = new Process();
          proc.StartInfo.FileName = "notepad";
          proc.Start();
    
          proc.WaitForInputIdle();
    
          IntPtr newParent = proc.MainWindowHandle;
          IntPtr editWindow = FindWindowEx(newParent, IntPtr.Zero, "Edit", null);
    
          uint notepadThread = GetWindowThreadProcessId(newParent, IntPtr.Zero);
          uint myThread = GetWindowThreadProcessId(this.Handle, IntPtr.Zero);
    
          AttachThreadInput(notepadThread, myThread, true);
    
          SetParent(this.Handle, newParent);
    
          if (editWindow != IntPtr.Zero)
          {
            uint style = GetWindowLong(editWindow, GWL_STYLE);
            style = style | WS_CLIPSIBLINGS;
            SetWindowLong(editWindow, GWL_STYLE, style);
          }
        }
      }
    

    Damit sollte das Fenster zunächst sichtbar sein. Es bleiben aber einige Probleme bestehen. Das Fenster wird nicht neu gezeichnet, wenn das Notepad Fenster vergrößert oder verschoben wird. Alle Bereiche die außerhalb des sichtbaren Bereichs lagen, bleiben weiß oder werden partiell neu gezeichnet. Das Fenster reagiert nicht mehr auf Klicks, sobald es von der ursprünglichen Position verschoben wird. Das scheint auch direkt mit den Neuzeichnen zusammenzuhängen. Ggf. muss man das Fenster einmalig mit SetWindowPos innerhalb des neuen Parent positionieren. Insgesamt bleibt das IMHO ein Hack.


    Thorsten Dörfler
    Microsoft MVP Visual Basic
    vb-faq.de
    Donnerstag, 29. Juli 2010 06:13
    Beantworter
  • Hallo Xantur,

    ich denke, wirklich sauber und generisch bekommt man das nicht hin, was Du willst - vorausgesetzt, Du kannst den externen Prozess nicht beeinflussen. Meine Meinung - .
    Da müsste man schon sehr viel Aufwand treiben IMHO.
    Ggf. mit Hooks und SetWindowPos nebst Monitoring.

    Ist denn Deine Applikation wirklich fremd? Oder kannst Du auf Quellen des Prozesses zugreifen?
    Oder wenn bekannte Schnittstellen zur Verfügung stehen.
    Wenn Du Quellen/Schnittstellen hast, gibt es ggf. noch eine saubere Lösung.

     


    ciao Frank
    Donnerstag, 29. Juli 2010 08:20
  • Hallo Xantur (Michael),

    dürfen wir Deiner ausbleibenden Rückantwort entnehmen, dass die Antworten Dir weitergeholfen haben und Du Dein Problem lösen konntest ?
    Solltest Du noch "Rückfragen" dazu haben, so gib uns bitte Bescheid.

    Grüße,
    Robert

    Montag, 9. August 2010 09:53
    Moderator