Autor Beitrag
MCPC10
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 35

Win 10, Win Server 2016
C# (VS 2015, VS2017, VS2019), Assembler (Atmel Studio 7.0)
BeitragVerfasst: Do 15.06.17 17:38 
Guten Tag ;)

Mal wieder habe ich eine Frage die ich als Anfänger noch nicht so ganz lösen kann. Das Problem ist ich habe eine Listbox in der struct's drin sind, welche den Namen und den Pfad zu einer Datei enthalten. Jetzt will ich die datei mittels Process ausführen lassen, aber wie kann ich das erstellte process object speichern damit ich es später wieder nutzen kann um den Prozess zu stoppen. Vllt hat ja jemand ne bessere Idee um das zu realisieren.
N.B.: Die Process Klasse wird auch vom DaemonMasterService genutzt um den Process als Service in Session0 zu starten.

Der komplette Quellcode des Programmes ist Open Source: github.com/TWC-Software/DaemonMaster
(Jeder der Interesse hat an dem Projekt mitzumachen kann dies gerne tun, denn ich als Anfänger brauche jede Hilfe ;) ;) )

Die Teile worum es geht sind im DaemonMasterCore (ProcessManagement und Process)und in DaemonMaster (MainWindow.xaml.cs)

Stelle aus MainWindow.xaml.cs (ab Zeile 199):
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
 
private void MenuItemStartWS_OnClick(object sender, RoutedEventArgs e)
        {
            if (listBoxDaemons.SelectedItem == null)
                return;

            DaemonInfo daemonInfo = (DaemonInfo)listBoxDaemons.SelectedItem;
            Process process = ProcessManagement.CreateNewProcces(daemonInfo.ServiceName);
            process?.StartProcess();
        }

        private void MenuItemStopWS_OnClick(object sender, RoutedEventArgs e)
        {
            if (listBoxDaemons.SelectedItem == null)
                return;

            DaemonInfo daemonInfo = (DaemonInfo)listBoxDaemons.SelectedItem;
            Process process = ProcessManagement.GetProcessByName(daemonInfo.ServiceName);
            process?.StopProcess();
        }


ProcessManagement:

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:
49:
50:
51:
52:
53:
54:
using System.Collections.Generic;
using System.Linq;

namespace DaemonMasterCore
{
    public static class ProcessManagement
    {
        private static List<KeyValuePair<string, Process>> processes = new List<KeyValuePair<string, Process>>();

        /// <summary>
        /// Get the Process object of the given service name, if no process exists to the given service name the function return null
        /// </summary>
        /// <param name="serviceName"></param>
        /// <returns></returns>
        public static Process GetProcessByName(string serviceName)
        {
            if (IsProcessAlreadyThere(serviceName))
            {
                return processes.FirstOrDefault(x => x.Key == serviceName).Value;
            }

            return null;
        }

        /// <summary>
        /// Check if the Process with the given service name already exists
        /// </summary>
        /// <param name="serviceName"></param>
        /// <returns></returns>
        public static bool IsProcessAlreadyThere(string serviceName)
        {
            if (processes.FirstOrDefault(x => x.Key == serviceName).Equals(default(KeyValuePair<string, Process>)))
            {
                return false;
            }
            return true;
        }

        /// <summary>
        /// Create a new process with the service name (return the process object), if a process exists with the same service name, the function return null
        /// </summary>
        /// <param name="serviceName"></param>
        /// <returns></returns>
        public static Process CreateNewProcces(string serviceName)
        {
            if (IsProcessAlreadyThere(serviceName))
                return null;

            Process process = new Process(serviceName);
            processes.Add(new KeyValuePair<string, Process>(serviceName, process));
            return processes.FirstOrDefault(x => x.Key == serviceName).Value;
        }
    }
}


Process:

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:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DaemonMasterCore.Win32;
using System.Threading;

namespace DaemonMasterCore
{
    public class Process
    {
        private readonly Daemon _daemon = null;

        private System.Diagnostics.Process _process = null;
        private Timer _resetTimer = null;
        private uint _restartCounter = 0;

        public Process(Daemon daemon)
        {
            _daemon = daemon;
        }

        public Process(string serviceName)
        {
            _daemon = RegistryManagement.LoadDaemonFromRegistry(serviceName);
        }


        public Daemon GetDaemon => _daemon;

        public bool CloseConsoleApplication(bool useCtrlC)
        {
            if (_process == null)
                throw new NullReferenceException();


            if (useCtrlC)
            {
                return NativeMethods.GenerateConsoleCtrlEvent((uint)NativeMethods.CtrlEvent.CTRL_C_EVENT, (uint)_process.Id);
            }
            else
            {
                return NativeMethods.GenerateConsoleCtrlEvent((uint)NativeMethods.CtrlEvent.CTRL_BREAK_EVENT, (uint)_process.Id);
            }
        }

        public bool PauseProcess()
        {
            if (_process == null)
                throw new NullReferenceException();

            IntPtr processHandle = NativeMethods.OpenThread(NativeMethods.ThreadAccess.SUSPEND_RESUME, true, (uint)_process.Id);

            if (processHandle == IntPtr.Zero)
                return false;

            try
            {
                return NativeMethods.SuspendThread(processHandle);
            }
            finally
            {
                NativeMethods.CloseHandle(processHandle);
            }
        }

        public bool ResumeProcess()
        {
            if (_process == null)
                throw new NullReferenceException();

            IntPtr processHandle = NativeMethods.OpenThread(NativeMethods.ThreadAccess.SUSPEND_RESUME, true, (uint)_process.Id);

            if (processHandle == IntPtr.Zero)
                return false;

            try
            {
                return NativeMethods.ResumeThread(processHandle);
            }
            finally
            {
                NativeMethods.CloseHandle(processHandle);
            }
        }

        public bool StartProcess()
        {
            if (_daemon.FullPath == String.Empty)
                throw new Exception("Invalid filepath!");

            ProcessStartInfo startInfo = new ProcessStartInfo(_daemon.FullPath, _daemon.Parameter);
            startInfo.ErrorDialog = false;

            //UseShellExecute if the application is a shortcut
            string extension = Path.GetExtension(_daemon.FullPath);

            if (String.Equals(extension, ".lnk", StringComparison.OrdinalIgnoreCase))
            {
                startInfo.UseShellExecute = true;
            }
            else
            {
                startInfo.UseShellExecute = false;
            }

            //Create an _process object
            _process = new System.Diagnostics.Process();
            _process.StartInfo = startInfo;
            //Subscribe to the event
            _process.Exited += Process_Exited;
            //Required for that Exited event work
            _process.EnableRaisingEvents = true;
            _process.Start();
            return true;
        }

        public bool StopProcess()
        {
            if (_process == null || _process.HasExited)
                throw new NullReferenceException();

            //Disable Process_Exited event
            _process.EnableRaisingEvents = false;

            //IntPtr handle = _process.MainWindowHandle;
            //NativeMethods.PostMessage(handle, NativeMethods._SYSCOMMAND, (IntPtr)NativeMethods.wParam.SC_CLOSE, IntPtr.Zero);

            //Send Ctrl-C / Ctrl-Break command if the application is a console
            if (_daemon.ConsoleApplication)
            {
                CloseConsoleApplication(_daemon.UseCtrlC);
            }

            //Close the MainWindow of the application 
            _process.CloseMainWindow();

            //Waiting for the _process to close, after a ProcessKillTime the _process will be killed
            if (!_process.WaitForExit(_daemon.ProcessKillTime))
            {
                _process.Kill();
            }

            _process.Close();
            _process.Dispose();
            _process = null;
            return true;
        }




        private void Process_Exited(object sender, EventArgs args)
        {
            try
            {
                if (_daemon.MaxRestarts == 0 || _restartCounter < _daemon.MaxRestarts)
                {

                    Timer restartDelayTimer = new Timer(o =>
                    {
                        _process.Start();
                        _restartCounter++;

                        if (_resetTimer == null)
                        {
                            _resetTimer = new Timer(ResetTimerCallback, null, _daemon.CounterResetTime, Timeout.Infinite);
                        }
                        else
                        {
                            _resetTimer.Change(_daemon.CounterResetTime, Timeout.Infinite);
                        }

                        ((Timer)o).Dispose();

                    }, null, _daemon.ProcessRestartDelay, Timeout.Infinite);
                }
                else
                {
                    //Stop();
                }
            }
            catch (Exception)
            {
                //Stop();
            }
        }

        private void ResetTimerCallback(object state)
        {
            _restartCounter = 0;

            _resetTimer.Dispose();
            _resetTimer = null;
        }
    }
}


Ich Danke schonmal für jede hilfe und hoffe sehr das mir jemand helfen kann ;)

MfG
Mike Cr.
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Do 15.06.17 18:57 
- Nachträglich durch die Entwickler-Ecke gelöscht -
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 15.06.17 19:01 
Zitat:
Die Teile worum es geht sind im DaemonMasterCore (ProcessManagement und Process)und in DaemonMaster (MainWindow.xaml.cs)


Ich vermisse dazu eine konkretere Frage. Was ist das Problem in deinem gezeigten Code bzw. das Problem das du da vermutest? Funktioniert irgendwas nicht?

Zitat:
Das Problem ist ich habe eine Listbox in der struct's drin sind, welche den Namen und den Pfad zu einer Datei enthalten


Die Wahrscheinlichkeit das die Wahl eines structs falsch ist ist sehr hoch. Wenn du keine besonderen Gründe hast einen struct zu nehmen dann sollte das eine class sein.


Was deine Process Klasse macht sieht hochgefährlich aus. Ja es gibt eine Resume/SuspendThread API das heißt aber nicht das man die im Normalfall benutzen sollte. Schon gar nicht wenn dieses Tool das du da schreibst etwas für jeden sein soll der das Tool auf einen beliebigen Prozess anwenden soll. Threads sollten so geschrieben sein das man die dazu anweist sich selbst zu suspenden/resumen wenn man das von außen macht hat der Thread selbst kein Chance das in einem für sich und den Prozess in dem er lebt ungefährlichen Zustand zu tun. Im falschen Moment und der Thread landet in irgendwelchen Deadlock/Race Condition oder sonstigen Dauerschlaf Problemen. Wenn eine Anwendung nicht so geschrieben ist der den Thread sich selbst suspenden können sollte man ihn einfach nicht suspenden/resumen. Wenn bei dir Service tatsächlich Windows Services meint dann gibt es da einen offiziellen API Weg die zu pausieren. In .Net z.B. via der ServiceController Klasse.
MCPC10 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 35

Win 10, Win Server 2016
C# (VS 2015, VS2017, VS2019), Assembler (Atmel Studio 7.0)
BeitragVerfasst: Do 15.06.17 19:12 
Danke erstmal an euch beide :)

Also die Process Klasse von mir nutzt die Process Klasse von MS nur hat die, die Änderung das bei abstürzen des Prozesses dieser neugestartet wird. Das Resume, etc ist dafür da zum Beispiel einen Thread von Arma3 Server anzugehalten (nutze ich derzeit aber nicht / pausiert nicht den Dienst nur den Server!).
Das Problem liegt jetzt daran das ich in der Listbox nur den Namen von der Datei besitze und den Pfad. Mit disen daten geht mein Service und läd alle weiteren nötigen Infos aus der Registry, da Prozess der von einem Service ausgeführt wird in Session0 ist kann ich nicht auf ihn zugreifen. Jetzt will ich, das der Nutzer den Prozess auch auf dem normalen Desktop ausführen kann zum testen. Wenn ich jedoch jetzt ein neues Process Objekt erstelle wie/wo kann ich die Referenz speichern um später wieder drauf zuzugreifen (zum stoppen). Derzeit nutze ich eine Liste die in ProcessManagement die Objekte speichert, aber ich find das ich nicht die beste Idee.
Deshalb frage ich ob es eine bessere Methode gibt dafür.
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 15.06.17 22:34 
Zitat:
Deshalb frage ich ob es eine bessere Methode gibt dafür.


Für eine besser/schlechter Überlegung müsste man eigentlich mehr Details kennen. Ungesehen der Details könnte man das aber so machen wie du es machst.

Komisch ist die Verwendung einer List<KeyValuePair> so wie du die in der Klasse benutzt soll der Key der Liste scheinbar eindeutig sein. Dann erschließt sich mir nicht warum du nicht einfach ein Dictionary nimmst. Ist der Key nicht eindeutig weil du erlauben möchtest den gleichen Prozess mehrmals zu starten dann ist die Wahl des Keys falsch und der Key sollte etwas Prozess spezifisches sein wie zum Beispiel die ProzessId.

Wenn du mit einem solchen Key arbeiten möchtest sollte das auch das sein was die ProcessManagement rausgibt. Es fühlt sich falsch an die Prozesse über einen Key in einer Management Klasse zu verwalten diese Klasse aber die Process(e) rausgibt und nicht nur den Key.
MCPC10 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 35

Win 10, Win Server 2016
C# (VS 2015, VS2017, VS2019), Assembler (Atmel Studio 7.0)
BeitragVerfasst: Fr 16.06.17 13:23 
Zitat:
...der Key der Liste scheinbar eindeutig sein.

Ja das ist richtig, der Nutzer soll nur eine Instanz erstellen können (pro Listeneintrag)

Zitat:
Dann erschließt sich mir nicht warum du nicht einfach ein Dictionary nimmst.

Ich dachte man kann in ein Dictionary keine Klassen speichern.

Der Key stellt in meinem Fall der ServiceName dar da ich diesen in den Listen gespeichert habe. Der gibt das Process object zurück damit ich den Prozess dann mit diesem object starten/stoppen kann.

Hier ist das Object was pro Listeneintrag gespeichert wird.
Auf die frage wieso es ein struct ist, ich hab mal gelesen, dass wenn man viele kleine Sachen (string, int, etc) hat und will die speichern ist ein struct besser als eine class (wegen Speicher bla bla).
ausblenden 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:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;

namespace DaemonMasterCore
{
    public struct DaemonInfo
    {
        public override string ToString()
        {
            return DisplayName;
        }

        public string DisplayName { get; set; }
        public string ServiceName { get; set; }
        public string FullPath { get; set; }

        public ImageSource Icon => DaemonMasterUtils.GetIcon(FullPath);
    }
}


MfG
Mike Cremer
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: Sa 17.06.17 12:18 
Zitat:
Auf die frage wieso es ein struct ist, ich hab mal gelesen, dass wenn man viele kleine Sachen (string, int, etc) hat und will die speichern ist ein struct besser als eine class (wegen Speicher bla bla).

Ein struct wählt man wenn man einen Wertetyp darstellen will. Deutlicher, wenn man will das beim Zuweisen ein Kopie erstellt wird und nicht nur eine Referenz auf das gleiche Object. Ein Wertetyp Das macht üblicherweise nur Sinn wenn die Klasse auch in etwa genau einen Wert darstellt und sich nachträglich nicht mehr ändert. Eine bessere Beschreibung und weitere Faktoren wann struct die richtige Wahl ist findest du in den Framework Design Guidelines.

Wenn du die Setter der einzelnen Properties wegnimmst und die nur über den Constructor initialisierst sodass du einen unveränderlichen (immutable) Typen erhälst dann wäre struct ok. Da du diese Klasse aber scheinbar im UI Umfeld benutzt dessen Verhalten du nicht selbst kontrollierst hast du ein potentielles Problem mit dem Wertetyp verhalten. Jeder Zugriff erzeugt einen Kopie (potentiell aufwendig) anstatt nur eine Referenz. Wenn DaemonMasterUtils.GetIcon was aufwendiges ist würde ich mir auch überlegen das Icon zu cachen. Wenn du die Icon Property bindest ist eigentlich nicht genau vorhersagbar wie oft die unnötigerweise aufgerufen wird. Und die sollte ja nur erneut aufgerufen werden müssen wenn sich der FullPath ändert.

Ich persönlich mag deine ToString Implementierung nicht ;) Wenn du irgendwo den DisplayName sehen willst dann binde doch einfach an DisplayName. Der Sinn von ToString ist den Inhalt des Objects zu beschreiben und DisplayName ist nicht mal Näherungsweise der Inhalt deines Objects. Es scheint nicht mal zwingend eindeutig im Context deiner Anwendung zu sein. Das ToString bei vielen DataBinding Operationen als Fallback zum anzeigen genutzt wird muss man nicht mißbrauchen. Das macht Sinn bei ~echten~ structs. Also solche die ziemlich genau einen Wert darstellen und keine Property haben um etwas sinnvolles über sich anzuzeigen (sowas wie Byte, Integer etc.)

Für diesen Beitrag haben gedankt: MCPC10
MCPC10 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 35

Win 10, Win Server 2016
C# (VS 2015, VS2017, VS2019), Assembler (Atmel Studio 7.0)
BeitragVerfasst: Sa 17.06.17 19:55 
Danke erstmal,
So hab mal wie du es mir geraten hast die Klasse geändert. Wenn du noch Fehler findest wäre ich dir dankbar ;)

ausblenden 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:
using System.Windows.Media;

namespace DaemonMasterCore
{
    public class DaemonInfo
    {
        private ImageSource _icon = null;
        private string _fullPath = null;

        public string DisplayName { get; set; }
        public string ServiceName { get; set; }

        public string FullPath
        {
            get => _fullPath;
            set
            {
                _fullPath = value;
                //Get the new Icon
                _icon = DaemonMasterUtils.GetIcon(value);
            }
        }

        public ImageSource Icon => _icon;

    }
}


MfG
Mike Cremer
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 19.06.17 11:01 
Zitat:
Wenn du noch Fehler findest wäre ich dir dankbar


Es geht nur um potentiell besser machen von Fehlern war nie die Rede ;)
Sieht für mich für den Context von dem ich Annehme in dem du die Klasse nutzen willst ok aus.
MCPC10 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 35

Win 10, Win Server 2016
C# (VS 2015, VS2017, VS2019), Assembler (Atmel Studio 7.0)
BeitragVerfasst: Mi 21.06.17 15:35 
Ok Danke ;)

Ist es eigentlich schlauer den DaemonProcess (oder ehemals Process) in die Klasse DaemonInfo zu tun oder ProcessManagement zu nutzen?

MfG
Mike Cremer
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: Mi 21.06.17 20:32 
Die Antwort ist schwierig. Da du die Aufgaben der Management Klasse und der Process Klasse anders verteilt hast als ich es tun würde bzw. ohne weiteres Detailwissen erwartet hätte.

Deine Management Klasse ist eigentlich ausgesprochen dumm und verwaltet nur eine Liste. Die ganze Intelligenz steckt in der Process Klasse. So wäre es definitiv falsch die in der DaemonInfo Klasse zu verwenden. DaemonInfo scheint eine UI spezifische Sache zu sein. Da sollte man sowenig Logik wie möglich hin transportieren und deine Process Klasse ist voll davon. Bei der jetzigen Verteilung der Logik würde ich dafür plädieren das Process zu einer private Klasse der Management Klasse zu machen damit niemand anderes damit arbeiten kann.

Jemand anderes müsste sich dann also mit einer Art Referenz z.b. dem Servicenamen an die ProcessManagement Klasse wenden damit diese die nötigen Operation auf dem Process ausführt. Ob diese Logik dann in ProcessMangement steckt oder in der privaten Process Klasse ist dann nicht mehr so relevant. So hast du aber eine sauberere Trennung zwischen UI und Logik da du nur eine Schnittstelle zur ProcessManagement Klasse hast (die ein Singelton darstellt) anstatt von der UI zu vielen Process Instanzen und zur ProcessManagement Klasse wobei du dann potentiell beide nacheinander ansprechen musst wo es definitiv schöner wäre wenn es eine atomarere Aktion wäre. Theoretisch würdest du möglicherweise sowas machen wie den Process an der Process Klasse stoppen und dann aus der Liste in ProcessManagement entfernen oder ähnliches.
MCPC10 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 35

Win 10, Win Server 2016
C# (VS 2015, VS2017, VS2019), Assembler (Atmel Studio 7.0)
BeitragVerfasst: Mi 21.06.17 21:55 
Das Problem ist, dass der Service die reine Process Klasse braucht und nicht den Manager da es nur einen Process ausführt und es unnötig wäre das zu managen (deshalb public). Und wie realisiert man es am besten beim stoppen des Process im ProcessManagement den zu löschen. Event?

MfG
Mike Cremer
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 22.06.17 20:31 
Zitat:
Das Problem ist, dass der Service die reine Process Klasse braucht und nicht den Manager da es nur einen Process ausführt und es unnötig wäre das zu managen (deshalb public)


Ich würde da widersprechen. Einen eindeutigen Manager/Koordinator zu haben der sowas übernimmt ist leichter zu handhaben. Das was rausgegeben wird und zum Beispiel in der UI landet sollten nur Daten sein aber keine bereits daran gekoppelte Funktionalität. Ganz klassisches trennen von Logik und UI. Die Funktionalität sollte da stecken wo man die Daten hergeholt hat und um Aktionen mit den Daten anzustellen sollte man sich wieder an die Stelle wenden woher man die hat. Jedes übliche Anwendungsmuster der letzten Jahrzehnte würde genau das so vorsehen. Mein Erfahrungswert ist also das das was du tust zu Problemen führen wird. Das Urteil ist aber ungesehen der Details deiner konkreten Aufgabe sondern Kategorie "üblicherweise".

Für diesen Beitrag haben gedankt: MCPC10
MCPC10 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 35

Win 10, Win Server 2016
C# (VS 2015, VS2017, VS2019), Assembler (Atmel Studio 7.0)
BeitragVerfasst: Do 22.06.17 20:49 
Und wie deiner Meinung nach ist es am bestem möglich dem ProcessManagement mitzuteilen das der Process sich selbst "gelöscht" hat.

MfG
Mike Cremer
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 22.06.17 21:22 
Sehr spekulative Antwort da ich wie gesagt nicht alle Details kenne. Aber ich hau mal meine Gedankengänge raus ;)

Die Process Kapselung aus dem Framework hat da Events für die sollten von der Management Klasse gefangen werden. Jemand der dann von außen an diesen Process will um eine Aktion daran auszuführen kann dann vom der Management Klasse gesagt bekommen das das keine Zweck mehr hat. Würde das deine Process Kapsel Klasse machen die im System frei verfügbar ist ginge das auch aber die Mangement Klasse würde deine Process Kapsel aber weiter managen was dann sinnlos ist da dieser Process tot ist. Natürlich könntest du dir jetzt einen Art Callback ausdenken damit die Process Klasse der Management Klasse bescheid sagt das es keinen Sinn mehr macht ihn zu managen und diese entfernt. Aber da du die Process Kapsel rausgibst könnten viele verschiedene Nutzer eine Referenz auf diese Klasse haben und die müssten jetzt irgendwie auch alle mitbekommen das ihr Referenz auf die Process Klasse nutzlos ist. Jetzt könntest du sagen ich habe aber immer nur einen Nutzer einer Process Instanz darum geht das problemlos. Deine Architektur lässt aber anderes zu. Architektur einer Anwendung sollte aber falsche Nutzung durch Programmierer bestmöglichst verhindern. Und nicht darauf bauen das sie nur richtig, wie gedacht, benutzt wird. Das klappt ja nicht mal wenn man alleine programmiert, geschweige denn wenn weitere Programmierer hinzukommen ;)

Wenn ich das aber zwischenzeitlich richtig verstanden habe und es nicht irgendwelche Prozesse sind sondern Windows Services gäbe es da noch den ServiceManager aus dem Framework denn man überwachen müsste. Aus der Diensteverwaltung könnte man ja Services starten/stoppen/pausieren/automatische Restarts einbauen usw.. Mir wäre nicht bewusst das man alle Aktionen eines Windows services über die Process Klasse überwachen könnte.

Für diesen Beitrag haben gedankt: MCPC10
MCPC10 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 35

Win 10, Win Server 2016
C# (VS 2015, VS2017, VS2019), Assembler (Atmel Studio 7.0)
BeitragVerfasst: Do 22.06.17 22:47 
Habe nach deinem Tipp die DaemonProcess Klasse internal gemacht, so das nur noch per ProcessManagement darauf zugegriffen werden kann. Da die ProcessManagement Klasse jetzt sowohl den Service, als auch das Manuelle starten (in Session1/Desktop) steuert müsste ich nur irgendwie dem ProcessManagement mitteilen das wenn ein Objekt gelöscht wird er es auch entfernt. Nur mit Events bin ich mir nicht sicher ob/wie man die dann wieder deabboniert nachdem die Klasse "gekillt" worden ist (oder brauch man das nicht bzw. wie macht man das am ehesten).

P.S.: Ich aktualisiere morgen den Code auf Github dann sieht man wie ich das jetzt handhabe (nach deinem Tipp ;))

MfG
Mike Cremer