Laden...

Technologie zur Umsetzung einer 3-teiligen Bedienoberfläche, die externe Programme hostet

Erstellt von adm1n vor 7 Jahren Letzter Beitrag vor 7 Jahren 3.989 Views
A
adm1n Themenstarter:in
51 Beiträge seit 2014
vor 7 Jahren
Technologie zur Umsetzung einer 3-teiligen Bedienoberfläche, die externe Programme hostet

Hallo Forum,

ich bräuchte mal eure Expertise!

Ziel ist eine Bedienoberfläche (siehe Zeichnung unten), die den Bildschirm vertikal in drei Bereiche unterteilt. Im oberen und unteren Bereich soll es möglich sein, eine beliebige App zu starten (Browser, Multimedia-Player, PDF-Reader, usw.) und der mittlere Bereich besteht aus Buttons, mit dem man die Apps für den oberen und unteren Bereich auswählen kann. Die Plattform ist Windows 10.

Habt ihr Ideen / Vorschläge, wie man das am sinnvollsten aufbaut?
WinForms, WPF oder UWP? (WinForms nur, weil ich bisher keine andere Lösungen gefunden habe)

Habe ein paar sehr alte Beiträge gefunden. Gibt es hier bessere / neuere Möglichkeiten?


+--------------------------------+
|                                |
|                                |
|                                |
|                                |
|      Beliebiger Inhalt         |
|      Beliebige App             |
|                                |
|                                |
|                                |
|                                |
+--------------------------------+
|                                |
| Buttons, um Inhalt/App für     |
| oberen/unteren Bereich zu      |
| steuern                        |
|                                |
+--------------------------------+
|                                |
|                                |
|                                |
|      Beliebiger Inhalt         |
|      Beliebige App             |
|                                |
|                                |
|                                |
|                                |
|                                |
+--------------------------------+

Thanks!

Die Grenzen meiner Sprache bedeuten die Grenzen meiner Welt - Tractatus 5.6, Ludwig Wittgenstein

4.931 Beiträge seit 2008
vor 7 Jahren

Die UI-Technologie ist hierbei weniger wichtig, viel mehr besteht das Problem darin, externe Programme hosten zu können.
Der übliche Weg unter Windows ist per SetParent, s.a. externes Programm im Fenster der eigenen Anwendung ausführen, aber dies funktioniert meist nur für einfache Programme wie Notepad o.ä. wirklich gut.

Für die von dir genannten Programme würde ich eher schauen, ob du nicht passende .NET-fähige Komponenten verwenden kannst.

A
adm1n Themenstarter:in
51 Beiträge seit 2014
vor 7 Jahren

Vielen Dank für deine Antwort.

Hatte gehofft, dass es hier eine bessere Möglichkeit gibt.
Werde wahrscheinlich Window Tabifier modifizieren und auf meine Bedürfnisse anpassen.

Falls noch jemand eine bessere / zeitgemäße Idee hat - gerne her damit!

Danke!

Die Grenzen meiner Sprache bedeuten die Grenzen meiner Welt - Tractatus 5.6, Ludwig Wittgenstein

A
adm1n Themenstarter:in
51 Beiträge seit 2014
vor 7 Jahren

Habe beim starten des Prozesses (der gehostet werden soll) ein merkwürdiges Verhalten.
Würde diesen gerne unsichtbar starten, damit man die Software erst in der Hosting-App sieht, also wenn das Parent geändert wurde.

Starte z.B. die Software RealVNC folgendermaßen


                var startInfo = new ProcessStartInfo();
                startInfo.FileName = ExePath;
                startInfo.WindowStyle = ProcessWindowStyle.Hidden; // <--- funktioniert nicht
                startInfo.RedirectStandardInput = true;
                startInfo.RedirectStandardOutput = false;
                startInfo.Arguments = null;
                startInfo.ErrorDialog = false;
                startInfo.UseShellExecute = false;

                ChildProcess = new Process();
                ChildProcess.StartInfo = startInfo;
                ChildProcess = Process.Start(ExePath, null, null, null, null);

                Thread.Sleep(1000);
                ChildProcess.WaitForInputIdle();

                AppWinProcessHandle = ChildProcess.MainWindowHandle;

Die Software wird sichtbar gestartet und erst nach 1 Sekunde Wartezeit auf das neue Parent gesetzt.

Jemand eine Idee, wie ich die Software unsichtbar starten kann?

Die Grenzen meiner Sprache bedeuten die Grenzen meiner Welt - Tractatus 5.6, Ludwig Wittgenstein

16.806 Beiträge seit 2008
vor 7 Jahren

Vermutlich wirst Du hier direkt mit der Windows API und Windows(OS) Handles sowie Window (=Fenster)-Handles arbeiten müssen.
.NET deckt diese APIs in der Form gar nicht im Framework ab.

A
adm1n Themenstarter:in
51 Beiträge seit 2014
vor 7 Jahren

Das Problem ist, dass ich den MainWindowHandle vom ChildProcess erst nach ein paar Millisekunden bekomme und in der Zeit das Fenster schon kurz aufblitzt.. Kann also direkt mit der Windows API gar nichts machen, oder?

Die Wartezeit habe ich nun folgendermaßen auf das Minimum reduziert, indem ich nur so lange warte, bis der MainWindowHandle vom ChildProcess vorhanden ist.


                var startInfo = new ProcessStartInfo();
                startInfo.FileName = ExePath;
                startInfo.WindowStyle = ProcessWindowStyle.Hidden;
                startInfo.RedirectStandardInput = true;
                startInfo.RedirectStandardOutput = false;
                startInfo.Arguments = Arguments;
                startInfo.ErrorDialog = false;
                startInfo.UseShellExecute = false;

                ChildProcess = new Process();
                ChildProcess.StartInfo = startInfo;
                ChildProcess = Process.Start(ExePath, null, null, null, null);

                ChildProcess.WaitForInputIdle();

                while(ChildProcess.MainWindowHandle == IntPtr.Zero)  // <----------
                    Thread.Sleep(100);

                AppWinProcessHandle = ChildProcess.MainWindowHandle;

Jedoch blitzt das Fenster trotzdem kurz auf.

Die Grenzen meiner Sprache bedeuten die Grenzen meiner Welt - Tractatus 5.6, Ludwig Wittgenstein

16.806 Beiträge seit 2008
vor 7 Jahren

Naja, habs ja grad schon geschrieben..... du wirst hier mit .NET selbst nicht weit kommen. Merkste ja langsam selbst. Nicht, weil es mit .NET nicht möglich wäre, sondern weil .NET nicht alles der Windows 32 API implementiert.

Die Process-Klasse verwendet die Win32 Schnittstelle CreateProcess.
Dort kann man nen Parent Environment definieren, was bei der Process-Klasse nicht möglich ist. Könnte sein, dass das Dir schon hilft - oder eben ne andre Schittstelle.

Kannst jetzt also schauen, ob Win32 Mittel und Wege hat, die Du brauchst und damit arbeiten, oder eben mit dem arbeiten, was Dir .NET zur Verfügung stellt.
Alles andre liegt jetzt an Dir.

A
adm1n Themenstarter:in
51 Beiträge seit 2014
vor 7 Jahren

Ah, hab dich vorhin falsch verstanden, sorry 😃

Hab das ganze nun mal soweit umgebaut, dass ich mit der Win32-API den Prozess erstelle. Laut der MSDN-Seite müsste das Fenster versteckt starten, wenn ich STARTUPINFO dwFlags auf STARTF_USESHOWWINDOW setzte und wShowWindow auf SW_HIDE:


                var si = new WinAPI.STARTUPINFO();
                var pi = new WinAPI.PROCESS_INFORMATION();
                si.cb = Marshal.SizeOf(si);
                si.dwFlags = WinAPI.STARTF_USESHOWWINDOW; // hide process
                si.wShowWindow = WinAPI.SW_HIDE;           // hide process

                var processFlags = WinAPI.CREATE_NO_WINDOW;
                bool result = WinAPI.CreateProcess(null, ExePath, IntPtr.Zero, IntPtr.Zero, false, processFlags, IntPtr.Zero, null, ref si, ref pi);
                if(!result)
                    return;

                ChildProcess = Process.GetProcessById(pi.dwProcessId);

Jedoch poppt das Fenster (in meinem Fall der Internet Explorer (iexplore.exe) trotzdem auf.

Selbes Verhalten wie bei der .NET-Process-Klasse.

WinAPI ist einfach nur eine statische Klasse, wo ich das ganze Win32-Zeug reinkopiert habe.

Die Grenzen meiner Sprache bedeuten die Grenzen meiner Welt - Tractatus 5.6, Ludwig Wittgenstein

771 Beiträge seit 2009
vor 7 Jahren

Das liegt an jedem Process selbst, wie er diese Flags auswertet - und der IExplorer ignoriert diese anscheinend.
Kann man auch sehen, wenn man eine Verknüpfung erstellt und dort dann unter "Ausführen" die Werte "Minimiert" oder "Maximiert" einstellt.

A
adm1n Themenstarter:in
51 Beiträge seit 2014
vor 7 Jahren

Okay schade, heißt also, dass ich auf den Internet-Explorer-Prozess hier keinen Einfluss habe.
Beim Notepad und Calc ist es genau das gleiche Verhalten.

Falls jemand bisschen rumspielen will, hier mein aktueller Stand (CodeBehind eines UserControls):



        private Process ChildProcess;

        private IntPtr AppWinProcessHandle;

        public void LoadApp()
        {
            var exePath = @"C:\Program Files\Internet Explorer\iexplore.exe";

            var si = new WinAPI.STARTUPINFO();
            var pi = new WinAPI.PROCESS_INFORMATION();
            si.cb = Marshal.SizeOf(si);
            si.dwFlags = WinAPI.STARTF_USESHOWWINDOW;
            si.wShowWindow = (short)WinAPI.SW_HIDE;

            // create child process
            var processFlags = WinAPI.CREATE_NO_WINDOW;
            if(!WinAPI.CreateProcess(null, exePath, IntPtr.Zero, IntPtr.Zero, false, processFlags, IntPtr.Zero, null, ref si, ref pi))
                return;

            // get child process
            ChildProcess = Process.GetProcessById(pi.dwProcessId);
            ChildProcess.WaitForInputIdle();

            // get window handle
            while(ChildProcess.MainWindowHandle == IntPtr.Zero)
                Thread.Sleep(100);
            AppWinProcessHandle = ChildProcess.MainWindowHandle;

            // set parent
            var win = Window.GetWindow(this.AppContainer);
            var helper = new WindowInteropHelper(win);
            WinAPI.SetParent(AppWinProcessHandle, helper.Handle);

            // style child window
            var style = WinAPI.GetWindowLong(AppWinProcessHandle, WinAPI.GWL_STYLE);
            style = style & (uint)~WinAPI.WS_SYSMENU & (uint)~WinAPI.WS_CAPTION & (uint)~WinAPI.WS_THICKFRAME;
            //style = style | WinAPI.WS_CHILD;
            WinAPI.SetWindowLong(AppWinProcessHandle, WinAPI.GWL_STYLE, style);

            // set width and height of process to the size of the UserControl
            WinAPI.MoveWindow(AppWinProcessHandle, 0, 0, (int)this.ActualWidth, (int)this.ActualHeight, true);

            // show child window
            WinAPI.ShowWindow(AppWinProcessHandle, WinAPI.SW_SHOW);
        }

WinAPI kapselt wie gesagt die ganzen Standard-Win32-Methoden (kann ich bei Bedarf auch hochladen).
AppContainer ist der Name vom Control (in diesem Fall ein StackPanel) in einem UserControl in dem ich die App hoste.

Namespaces:


using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interop;

Grüße!

Die Grenzen meiner Sprache bedeuten die Grenzen meiner Welt - Tractatus 5.6, Ludwig Wittgenstein

M
184 Beiträge seit 2012
vor 7 Jahren

Dein Problem ist immer noch, dass die Anwendung nicht versteckt startet, oder?
Hier mein Code um notepad.exe versteckt zu starten:

ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "notepad.exe";
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
Process proc = Process.Start(startInfo);

Und hier um den Internet Explorer versteckt zu starten:
~~

ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "iexplore.exe";
startInfo.Arguments = "-embedding";
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
Process proc = Process.Start(startInfo);

~~
Edit: vergiss es, da war ich etwas zu voreilig....

A
adm1n Themenstarter:in
51 Beiträge seit 2014
vor 7 Jahren

Jawoll, vielen Dank MorphieX.

Mit -embedding geht es beim Internet Explorer tatsächlich.

Mit folgender Lösung wird der InternetExplorer-Prozess hidden gestartet und erst wenn dieser dem neuen Parent zugewiesen ist wieder angezeigt. Das kurze aufblitzen während dem erzeugen und Parent ändern ist somit weg 😃


            var ExePath = @"C:\Program Files\Internet Explorer\iexplore.exe";
            var Arguments = "-embedding";
            var WindowClassName = "IEFrame";

            /*
             * .NET-Framework
             */
            ProcessStartInfo startInfo = new ProcessStartInfo();
            startInfo.FileName = ExePath;
            startInfo.Arguments = Arguments;
            startInfo.WindowStyle = ProcessWindowStyle.Hidden;
            ChildProcess = Process.Start(startInfo);

            ChildProcess.WaitForInputIdle();
            Thread.Sleep(1000);

            // get window handle
            AppWinProcessHandle = WinAPI.FindWindow(WindowClassName, null);

            // set parent
            var mainWin = new WindowInteropHelper(Window.GetWindow(this.AppContainer));
            WinAPI.SetParent(AppWinProcessHandle, mainWin.Handle);

            // set width and height of process to the size of the UserControl
            WinAPI.MoveWindow(AppWinProcessHandle, 0, 0, (int)this.ActualWidth, (int)this.ActualHeight, true);

            // show child window
            WinAPI.ShowWindow(AppWinProcessHandle, WinAPI.SW_SHOW);
            WinAPI.EnableWindow(AppWinProcessHandle, true);

Schade nur, dass das bei jeder Software individuell ist und es keine generische Lösung für alle geht.

Jemand eine Idee, wie ich an den Prozess-Handler ohne FindWindow() komme? Sonst brauche ich für jede Software den Klassennamen, um den Prozess zu finden.

Die Grenzen meiner Sprache bedeuten die Grenzen meiner Welt - Tractatus 5.6, Ludwig Wittgenstein

A
adm1n Themenstarter:in
51 Beiträge seit 2014
vor 7 Jahren

Komisch ist jedoch, dass es mit der Win32-API nicht funktioniert:


            var ExePath = @"C:\Program Files\Internet Explorer\iexplore.exe";
            var Arguments = "-embedding";
            var WindowClassName = "IEFrame";

            /*
             * Win32-API
             */
            var si = new WinAPI.STARTUPINFO();
            var pi = new WinAPI.PROCESS_INFORMATION();
            si.cb = Marshal.SizeOf(si);
            si.dwFlags = WinAPI.STARTF_USESHOWWINDOW;
            si.wShowWindow = (short)WinAPI.SW_HIDE;

            var processFlags = WinAPI.CREATE_NO_WINDOW;
            if(!WinAPI.CreateProcess(ExePath, Arguments, IntPtr.Zero, IntPtr.Zero, false, processFlags, IntPtr.Zero, null, ref si, ref pi))
                return;

            // get child process
            ChildProcess = Process.GetProcessById(pi.dwProcessId);
            ChildProcess.WaitForInputIdle();
            Thread.Sleep(1000);

            // get window handle
            AppWinProcessHandle = WinAPI.FindWindow(WindowClassName, null);

            // set parent
            var mainWin = new WindowInteropHelper(Window.GetWindow(this.AppContainer));
            WinAPI.SetParent(AppWinProcessHandle, mainWin.Handle);

            // set width and height of process to the size of the UserControl
            WinAPI.MoveWindow(AppWinProcessHandle, 0, 0, (int)this.ActualWidth, (int)this.ActualHeight, true);

            // show child window
            WinAPI.ShowWindow(AppWinProcessHandle, WinAPI.SW_SHOW);
            WinAPI.EnableWindow(AppWinProcessHandle, true);

Da wird der IE erst außerhalb des Parents gestartet (als ob die Arguments im CreateProcess ignoriert werden).

Naja, solange die .NET-Variante funktioniert, bin ich zufrieden.

Nur das FindWindow() mit dem ClassName gefällt mir noch nicht ganz.
Falls es dafür eine schönere Lösung gibt, gerne her mit den Vorschlägen 😃

Die Grenzen meiner Sprache bedeuten die Grenzen meiner Welt - Tractatus 5.6, Ludwig Wittgenstein

771 Beiträge seit 2009
vor 7 Jahren

Das hattest du doch oben schon in deinem Code


AppWinProcessHandle = ChildProcess.MainWindowHandle;

🤔

A
adm1n Themenstarter:in
51 Beiträge seit 2014
vor 7 Jahren

Jo, hatte ich anfangs so, komischerweise ist der ChildProcess.MainWindowHandle aber immer 0 (siehe Anhang), sobald ich das Argument "-embedding" übergebe.

Irgend eine Idee, woran das liegen könnte?

Die Grenzen meiner Sprache bedeuten die Grenzen meiner Welt - Tractatus 5.6, Ludwig Wittgenstein

A
adm1n Themenstarter:in
51 Beiträge seit 2014
vor 7 Jahren

Neues Problem:
Das hosten eines Prozesses funktioniert soweit ganz gut, nun habe ich jedoch Probleme mit weiteren Kindprozessen des gehosteten Prozesses. Wenn z.B. der VNC-Viewer ein weiteres Fenster öffnet, ist dieses wieder "außerhalb".

Gibt es eine Möglichkeit, einen Prozess und alles was dieser an Kindprozessen öffnet in meiner Software zu hosten?

Die Grenzen meiner Sprache bedeuten die Grenzen meiner Welt - Tractatus 5.6, Ludwig Wittgenstein

16.806 Beiträge seit 2008
vor 7 Jahren

Neues Problem = neues Thema.
[Hinweis] Wie poste ich richtig?
Wir wollen hier keine Endlos-Themen. Danke.

A
adm1n Themenstarter:in
51 Beiträge seit 2014
vor 7 Jahren

Ok, dann eine Frage zum ursprünglichen Problem:

Das Hosten einer Software via SetParent() ist nicht das Gelbe vom Ei.
Hat jemand eine andere Idee, wie ich einen Bildschirm aufteilen kann?
Z.B. via Split Screen, virtuellen Desktops o.ä.?

Die Grenzen meiner Sprache bedeuten die Grenzen meiner Welt - Tractatus 5.6, Ludwig Wittgenstein