Autor Beitrag
_mk_
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 23



BeitragVerfasst: Mo 22.08.22 15:51 
Hallo zusammen.

Folgende Code funktioniert bereits, aber ich empfinde meine Lösung sehr plump und unschön. Besonders die letzte Endlosschleife gefällt mir nicht. Das muss doch irgendwie schöner gehen? Für einen Denkanstoß wäre ich Euch sehr dankbar.

ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
        public static void ShadowRemoteSession(string remoteServer, int sessionID)
        {
            Process process = new Process();
            process.StartInfo.FileName = "mstsc";
            process.StartInfo.Arguments = $"/v:{remoteServer} /shadow:{sessionID} /multimon /control";
            process.Start();
            process.WaitForInputIdle();

            int processID = 0;
            do
            {
                Process[] processes = Process.GetProcessesByName("mstsc");
                foreach (Process p in processes)
                {
                    string mainTitle = p.MainWindowTitle.ToLower();

                    if (mainTitle.Contains($"(sitzungs-id {sessionID}) auf \"{remoteServer}\""))
                    {
                        processID = p.Id;
                        break;
                    }
                
                }
            } while (true);

            do
            {
                try
                {
                    Process processByID = Process.GetProcessById(processID);

                    Thread.Sleep(10);
                }
                catch (Exception ex)
                {
                    SendRemoteMessage(remoteServer, sessionID, "Ihre Sitzung wird nicht mehr gespiegelt.");
                    break;
                }
            } while (true);
        }
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mo 22.08.22 16:16 
Die Process Klasse hat einen Exited Event der entsprechend gefeuert wird. Aber je nach Anwendung mußt du auch irgendwie warten bis der gefeuert wird.
Process.Exited

Moderiert von user profile iconTh69: URL-Titel hinzugefügt

Für diesen Beitrag haben gedankt: _mk_
_mk_ Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 23



BeitragVerfasst: Di 23.08.22 08:51 
user profile iconRalf Jansen hat folgendes geschrieben Zum zitierten Posting springen:
Die Process Klasse hat einen Exited Event der entsprechend gefeuert wird. Aber je nach Anwendung mußt du auch irgendwie warten bis der gefeuert wird.
docs.microsoft.com/e....exited?view=net-6.0


Vielen Dank. Mit Ereignissen habe ich noch keinerlei Erfahrung gemacht. Da ich ein Process-Objekt habe, kann ich so lange warten, bis dieser beendet wird. Zeitgleich wird nur eine Sitzung gespiegelt. Das sollte so reichen.

ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
        public static void ShadowRemoteSession(string remoteServer, int sessionID)
        {
            using (Process process = new Process())
            {
                process.StartInfo.FileName = "mstsc";
                process.StartInfo.Arguments = $"/v:{remoteServer} /shadow:{sessionID} /multimon /control";

                process.Start();
                process.WaitForInputIdle();
            }

            Process mirrorProcess = new Process();
            bool detected = false;
            do
            {
                Process[] processes = Process.GetProcessesByName("mstsc");
                foreach (Process p in processes)
                {
                    string mainTitle = p.MainWindowTitle.ToLower();
                    if (mainTitle.Contains($"(sitzungs-id {sessionID}) auf \"{remoteServer}\"") || mainTitle.Contains($"(sessionid {sessionID}) on {remoteServer}"))
                    {
                        mirrorProcess = p;
                        detected = true;
                    }

                }
            } while (detected == false);

            mirrorProcess.WaitForExit();
            SendRemoteMessage(remoteServer, sessionID, "Ihre Sitzung wird nicht mehr gespiegelt.");
        }
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Di 23.08.22 09:35 
user profile icon_mk_ hat folgendes geschrieben Zum zitierten Posting springen:
Mit Ereignissen habe ich noch keinerlei Erfahrung gemacht.

Dann solltest du dich mal damit beschäftigen!

Dein Schleife erzeugt 100% Kernauslastung (nicht gut für den Rechner und die Umwelt)!!!

PS: Außerdem ist da noch ein möglicher Fehler in deinem Code bzgl. der Contains-Abfrage.

Für diesen Beitrag haben gedankt: _mk_
_mk_ Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 23



BeitragVerfasst: Di 23.08.22 10:06 
user profile iconTh69 hat folgendes geschrieben Zum zitierten Posting springen:
user profile icon_mk_ hat folgendes geschrieben Zum zitierten Posting springen:
Mit Ereignissen habe ich noch keinerlei Erfahrung gemacht.

Dann solltest du dich mal damit beschäftigen!

Dein Schleife erzeugt 100% Kernauslastung (nicht gut für den Rechner und die Umwelt)!!!

Das habe ich nicht bedacht. Der Umwelt zu Liebe werde ich mich damit näher beschäftigen. Meine Klasse teste erst einmal in einer Konsoleanwendung. Ich wüsste gerade nicht, wie und an welcher Stelle ich auf Ereignis reagieren sollte. Die Anwendung läuft doch synchron. Muss ich einer weiteren Ausführungsstrang erstellen? Und wenn ja? Welche Klasse soll ich verwenden; Thread oder Task? Ich hatte gelesen, dass die Thread-Klasse quasi veraltet ist.

user profile iconTh69 hat folgendes geschrieben Zum zitierten Posting springen:
PS: Außerdem ist da noch ein möglicher Fehler in deinem Code bzgl. der Contains-Abfrage.

Den Fehler kann ich gerade nicht erkennen.
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Di 23.08.22 10:30 
Manchmal ist schlafen ganz gut, um einer Ermüdung vorzubeugen. ;-)

Was passiert, wenn remoteSever Großbuchstaben enthält?

Aber ich verstehe den Sinn deiner Schleife sowieso nicht, denn mit mirrorProcess.WaitForExit() wartest du doch auf das Ende dieses Prozesses - warum nicht gleich process.WaitForExit() verwenden? Oder sind process und mirrorProcess zwei verschiedene Prozesse?

Für diesen Beitrag haben gedankt: _mk_
_mk_ Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 23



BeitragVerfasst: Di 23.08.22 11:01 
user profile iconTh69 hat folgendes geschrieben Zum zitierten Posting springen:
Was passiert, wenn remoteSever Großbuchstaben enthält?

Stimmt. Da habe ich etwas übersehen und werde den Variableninhalt gleich in Kleinbuchstaben umwandeln.

user profile iconTh69 hat folgendes geschrieben Zum zitierten Posting springen:
Aber ich verstehe den Sinn deiner Schleife sowieso nicht, denn mit mirrorProcess.WaitForExit() wartest du doch auf das Ende dieses Prozesses - warum nicht gleich process.WaitForExit() verwenden? Oder sind process und mirrorProcess zwei verschiedene Prozesse?

Sobald der Benutzer die Zustimmmung auf die Sitzungsspiegelung bestätigt hat, wird ein neuer mstsc-Prozeß gestartet. Dafür benötige ich die Schleife, um ein Process-Object zu erhalten. Mir fällt keine einfachere und sinnvollere Lösung ein, um an das Process-Object zu kommen. Vielleicht gibt es auch eine Windowsmeldung, die erzeugt wird, wenn ein neuer Prozeß gestartet wurde. Aber so weit bin ich noch lange nicht ....
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Di 23.08.22 11:21 
Zitat:
Mir fällt keine einfachere und sinnvollere Lösung ein, um an das Process-Object zu kommen.

In Windows kennt eigentlich jeder Prozess den Prozess der ihn gestartet hat. Darüber wäre es relativ leicht den zusammenhang festzustellen ohne auf Fenstertitles zu prüfen.
Auf solchen Text zu testen ist natürlich immer ein Problem. Niemand garantiert das jede Remote Desktop Version genau diesen Text als Titel benutzt und wenn verschiedene Clientsprachen ins Spiel kommen wirds erst recht schwierig. Du versuchst ja schon deutsch und englisch abzudecken.

Leider ist das in der Process Klasse nicht veröffentlicht(warum auch immer). Per WMI kann man an die ProcessId des Parents kommen um rauszufinden ob die zusammengehören.
Ist aber möglicherweise zuviel komplexität für zu wenig gewinn.


Kannst du in der Schleife mal prüfen ob du an die StartInfo des Prozesses kommst und ob da was hilfreiches drin ist? Die enthält vermutlich die Parameter mit dem der SubProzess gestartet wurde. Diese Info ist dann eventuell eindeutiger als der Fenstertitel. Zumindest ist eine Kommandozeile nie lokalisiert ;)

Für diesen Beitrag haben gedankt: _mk_
_mk_ Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 23



BeitragVerfasst: Di 23.08.22 13:02 
user profile iconRalf Jansen hat folgendes geschrieben Zum zitierten Posting springen:
Kannst du in der Schleife mal prüfen ob du an die StartInfo des Prozesses kommst und ob da was hilfreiches drin ist? Die enthält vermutlich die Parameter mit dem der SubProzess gestartet wurde. Diese Info ist dann eventuell eindeutiger als der Fenstertitel. Zumindest ist eine Kommandozeile nie lokalisiert ;)

Leider liefert die StartInfo-Klasse nichts brauchbares.

user profile iconRalf Jansen hat folgendes geschrieben Zum zitierten Posting springen:
Per WMI kann man an die ProcessId des Parents kommen um rauszufinden ob die zusammengehören.
Ist aber möglicherweise zuviel komplexität für zu wenig gewinn.

In der Tat hat die WMI-Klasse Win32_Process eine Eigenschaft namens ParentProcessID.

Moderiert von user profile iconTh69: C#-Tags hinzugefügt
_mk_ Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 23



BeitragVerfasst: Di 23.08.22 15:12 
Meine Methode habe überarbeitet. Es kann ja sein, dass die Spiegelung bereits gestartet ist. Da bekomme ich den PID von ParentProcess nicht mehr raus. Deshalb bleibe erst einmal beim Fenstertitel bis ich etwas besseres gefunden habe.

Meine kleine Funktion innerhalb meiner Methode liefert mir ein Process-Object. Kann ich schon bereits beim Rückgabewert zwischen angenommener und abgelehnter Sitzungsspiegelung unterscheiden. Ich möchte nicht noch mal außerhalb der Funktion auf den Fenstertitel prüfen. Ich würde eine boolsche Variable außerhalb meiner Funktion definieren und entsprechend in meiner Funktion setzen. Ich weiß nicht, ob das ein schöner Programmierstil ist. Nach Möglichkeit möchte ich mir gerne gleich einen guten Programmierstil angewöhnen.

ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
        public static void ShadowRemoteSession(string remoteServer, int sessionID)
        {
            Process mirrorProcess = GetMirrorProcess();

            if (mirrorProcess == null)
            {
                using (Process process = new Process())
                {
                    process.StartInfo.FileName = "mstsc";
                    process.StartInfo.Arguments = $"/v:{remoteServer} /shadow:{sessionID} /multimon /control";
                    process.Start();
                    process.WaitForInputIdle();
                }
            }

            do
            {
                mirrorProcess = GetMirrorProcess();
                System.Threading.Thread.Sleep(50);
            } while (mirrorProcess == null);

            mirrorProcess.WaitForExit();
            SendRemoteMessage(remoteServer, sessionID, "Ihre Sitzung wird nicht mehr gespiegelt.");
            
            Process GetMirrorProcess()
            {
                Process[] processes = Process.GetProcessesByName("mstsc");

                foreach (Process process in processes)
                {
                    string mainTitle = process.MainWindowTitle.ToLower();

                    if (mainTitle.Contains($"(sitzungs-id {sessionID}) auf \"{remoteServer.ToLower()}\"") || 
                        mainTitle.Contains($"(sessionid {sessionID}) on {remoteServer.ToLower()}") || 
                        mainTitle.Contains("spiegelungsfehler") || 
                        mainTitle.Contains("shadow error"))

                        return process;
                }

                return null;
            }
        }
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Mi 24.08.22 11:32 
user profile icon_mk_ hat folgendes geschrieben Zum zitierten Posting springen:
Kann ich schon bereits beim Rückgabewert zwischen angenommener und abgelehnter Sitzungsspiegelung unterscheiden.

Soll das ein Frage sein?

Gib deiner Methode doch einen bool-Wert als Rückgabe.

PS: Persönlich halte ich lokale Methoden für nicht so toll, da sie m.E. den Lesefluß beeinträchtigen.

Für diesen Beitrag haben gedankt: _mk_
_mk_ Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 23



BeitragVerfasst: Do 25.08.22 11:04 
user profile iconTh69 hat folgendes geschrieben Zum zitierten Posting springen:
user profile icon_mk_ hat folgendes geschrieben Zum zitierten Posting springen:
Kann ich schon bereits beim Rückgabewert zwischen angenommener und abgelehnter Sitzungsspiegelung unterscheiden.

Soll das ein Frage sein?

Ja. Eigentlich war es als Frage formuliert.

user profile iconTh69 hat folgendes geschrieben Zum zitierten Posting springen:
Gib deiner Methode doch einen bool-Wert als Rückgabe.

Wie mache es? Ich benötige zusätzlich das Process-Objekt. Von Skriptsprachen kenne ich es, dass einfach ein Array zurückgegeben wird, da alle Variablen vom Typ Variant sind. Da alle im enthaltenden Member definierten Variablen in der lokalen Funktion zugänglich sind, habe ich es erst einmal über das Deklarieren und das Auswerten einer boolschen Variable in der "umschließenden" Methode gelöst.

ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
        public static void ShadowRemoteSession(string remoteServer, int sessionID)
        {
            Process mirrorProcess = GetMirrorProcess();

            if (mirrorProcess == null)
            {
                using (Process process = new Process())
                {
                    process.StartInfo.FileName = "mstsc";
                    process.StartInfo.Arguments = $"/v:{remoteServer} /shadow:{sessionID} /multimon /control";
                    process.Start();
                    process.WaitForInputIdle();
                }
            }

            bool isAborted = false;
            do
            {
                mirrorProcess = GetMirrorProcess();
                System.Threading.Thread.Sleep(50);
            } while (mirrorProcess == null);

            mirrorProcess.WaitForExit();
            if (isAborted == false)
                SendRemoteMessage(remoteServer, sessionID, "Ihre Sitzung wird nicht mehr gespiegelt.");
            
            Process GetMirrorProcess()
            {
                Process[] processes = Process.GetProcessesByName("mstsc");

                foreach (Process process in processes)
                {
                    string mainTitle = process.MainWindowTitle.ToLower();

                    if (mainTitle.Contains($"(sitzungs-id {sessionID}) auf \"{remoteServer.ToLower()}\"") || mainTitle.Contains($"(sessionid {sessionID}) on {remoteServer.ToLower()}"))
                    {
                        return process;
                    }
                    else if (mainTitle.Contains("spiegelungsfehler") || mainTitle.Contains("shadow error"))
                    {
                        isAborted = true;
                        return process;
                    }
                }

                return null;
            }
        }
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Do 25.08.22 11:48 
user profile icon_mk_ hat folgendes geschrieben Zum zitierten Posting springen:
Ja. Eigentlich war es als Frage formuliert.
Dafür gibt es aber dann ein bestimmtes Satzeichen. ;-)

Und wegen der bool-Rückgabe: ich dachte, du meinst die Hauptmethode ShadowRemoteSession.
Bei der lokalen Methode kannst du entweder ein Tupel zurückgeben oder aber einen out-Parameter hinzufügen.

Für diesen Beitrag haben gedankt: _mk_
_mk_ Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 23



BeitragVerfasst: Do 25.08.22 12:59 
user profile iconTh69 hat folgendes geschrieben Zum zitierten Posting springen:
Bei der lokalen Methode kannst du entweder ein Tupel zurückgeben oder aber einen out-Parameter hinzufügen.

Ich hatte gelesen, dass man die Verwendung des out-Parameters vermeiden sollte. Tupel hatte ich nicht wirklich auf dem Schirm, weil ich auch nicht wußte, wofür ich sie einsetzen soll.

Jetzt sollten wir es endlich haben.

ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
        public static void ShadowRemoteSession(string remoteServer, int sessionID)
        {
            (Process mirrorProcess, bool isAborted)result = GetMirrorProcess();

            if (result.mirrorProcess == null)
            {
                using (Process process = new Process())
                {
                    process.StartInfo.FileName = "mstsc";
                    process.StartInfo.Arguments = $"/v:{remoteServer} /shadow:{sessionID} /multimon /control";
                    process.Start();
                    process.WaitForInputIdle();
                }
            }

            do
            {
                result = GetMirrorProcess();
                System.Threading.Thread.Sleep(50);
            } while (result.mirrorProcess == null);

            result.mirrorProcess.WaitForExit();
            if (result.isAborted == false)
                SendRemoteMessage(remoteServer, sessionID, "Ihre Sitzung wird nicht mehr gespiegelt.");

            (Process, bool)GetMirrorProcess()
            {
                Process[] processes = Process.GetProcessesByName("mstsc");

                foreach (Process process in processes)
                {
                    string mainTitle = process.MainWindowTitle.ToLower();

                    if (mainTitle.Contains($"(sitzungs-id {sessionID}) auf \"{remoteServer.ToLower()}\"") || mainTitle.Contains($"(sessionid {sessionID}) on {remoteServer.ToLower()}"))
                    {
                        return (process, false);
                    }
                    else if (mainTitle.Contains("spiegelungsfehler") || mainTitle.Contains("shadow error"))
                    {
                        return (process, true);
                    }
                }

                return (nulltrue);
            }
        }
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Do 25.08.22 13:24 
Zitat:
Ich hatte gelesen, dass man die Verwendung des out-Parameters vermeiden sollte.

Wir leben in Zeiten von asynchronem Code also das Ding mit Task(s) und async/await Schlüsselwörtern.
Da funktioniert out nicht. Das hast du vermutlich gelesen. In klassischem Code ist das völlig ok.

Moderiert von user profile iconTh69: C#- durch Quote-Tags ersetzt
Moderiert von user profile iconTh69: C#-Tags hinzugefügt

Für diesen Beitrag haben gedankt: _mk_
_mk_ Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 23



BeitragVerfasst: Do 25.08.22 13:43 
Danke für den Hinweis. In dem Zusammenhang mit Tasks hatte ich es nicht gelesen. Mit den Klassen der Task Parallel Library muss ich mich sowieso noch tiefergehender beschäftigen.

Moderiert von user profile iconTh69: Voll-Zitat entfernt
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Do 25.08.22 19:45 
Wo ich da gerade nochmal auf deinen Code gucke.
ausblenden C#-Quelltext
1:
(Process mirrorProcess, bool isAborted)result = GetMirrorProcess();					

Du brauchst hier result nicht.
ausblenden C#-Quelltext
1:
(Process mirrorProcess, bool isAborted) = GetMirrorProcess();					

reicht. Bei Tuplen gibt es eine sogenannte Deconstruction. Das Tuple wird aufgelöst und einfach lokalen Variablen zugeordnet. In ShadowRemoteSession gibts den Tuple dann also nicht sondern einfach 2 lokale Variablen die genauso benutzt werden können wie lokale Variablen.
Der nächste Aufruf von GetMirrorProcess wäre dann auch einfach
ausblenden C#-Quelltext
1:
(mirrorProcess, isAborted) = GetMirrorProcess();					

denn die beiden lokalen Variablen gibts dann ja schon.

Für diesen Beitrag haben gedankt: _mk_
_mk_ Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 23



BeitragVerfasst: Fr 26.08.22 08:32 
user profile iconRalf Jansen Vielen Dank, dass Du Dir meinen Code noch einmal angeschaut und vereinfach hast. Tuple gefallen mir immer mehr. Jetzt, wo ich einen konkreten Anwendungsfall, kenne.