Autor Beitrag
lord2k3
Hält's aus hier
Beiträge: 6



BeitragVerfasst: Sa 12.03.16 14:51 
Hallo Entwickler,

bin neu im Forum und auch recht neu in der C# Programmierung, versuche mir viel über die MSDN (schön das es sowas gibt) und auch über die openBooks / Forums anzueignen. Betreibe dies gerade ein wenig in meiner Freizeit und finde C# ist echt ganz schick :)

Nun habe aktuell das Problem, dass mein WinForm welches ein Process startet sich nicht beendet lässt, wenn der gestartete Process geschlossen wird und das Exited Event gefeuerert wird.
In dem WinForm habe ich ein Close Button, welcher den Prozess beendet (Anhand der übergebenen ID) das funktioniert.
Ich möchte aber auch darauf regaieren, der Externe Process geschlossen wird (rotes X :)) - so dass sich das WinForm ebenfalls beendet. Das WinForm hier wird mittels ShowDialog() von meiner Form1 (Main) aufgerufen.

Als Fehlermeldung kommt immer:
Zitat:
Eine nicht behandelte Ausnahme des Typs "System.InvalidOperationException" ist in System.Windows.Forms.dll aufgetreten.

Zusätzliche Informationen: Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement fConsole erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde.


So genug gequatscht hier der Code:
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:
public partial class fConsole : Form
{
  public int ID { get; set; }
  private bool exited = false;
  
  private void start()
  {
    Process myProcess = new Process();
    ProcessStartInfo processInfo = new ProcessStartInfo();

    processInfo.FileName = "Notepad.exe";
    processInfo.UseShellExecute = false;
    processInfo.RedirectStandardOutput = true;
    processInfo.CreateNoWindow = true;

    myProcess.EnableRaisingEvents = true;
    myProcess.OutputDataReceived += new DataReceivedEventHandler(myProcess_OutputDataReceived);
    myProcess.Exited += new EventHandler(myProcess_Exited);
    myProcess.StartInfo = processInfo;
    myProcess.Start();
    ID = myProcess.Id;
    myProcess.BeginOutputReadLine();
  }
      
  private void myProcess_Exited(object sender, System.EventArgs e)
  {
    if (!exited)
    {
      exited = true;
      this.Close();
    }
  }
  private void btClose_Click(object sender, EventArgs e)
    {
    kill();
    DialogResult = DialogResult.Cancel;
    this.Close();
    }
  
  private void kill()
  {
    try
    {
      exited = true;
      Process p = Process.GetProcessById(ID);
      p.Kill();
    }
    catch (Exception ex)
    {
      cMessage.showError("Process Error", ex.ToString());
     }
  }
}


Vielleicht hat jemand ja einen Tip für mich, Danke! :)
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4708
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Sa 12.03.16 15:17 
Der Process.Exited Event wird in einem anderen Thread ausgeführt und threadübergreifende Zugriffe auf Winforms Elemente ist nicht erlaubt. So wie die Fehlermeldung das auch besagt. fConsole gehört zum dem Hauptthread und nicht zum Thread in dem dein myProcess_Exited EventHandler ausgeführt wird.

Um Close erfolgreich aufzurufen musst du den im Hauptthread ausführen dazu hat jedes Winform Control eine Invoke Methode um Dinge in dem Thread auszuführen zu dem das konkrete Control gehört. Z.B.

ausblenden C#-Quelltext
1:
this.Invoke((MethodInvoker) delegate { this.Close(); });					

Für diesen Beitrag haben gedankt: lord2k3
lord2k3 Threadstarter
Hält's aus hier
Beiträge: 6



BeitragVerfasst: Sa 12.03.16 15:36 
user profile iconRalf Jansen hat folgendes geschrieben Zum zitierten Posting springen:

ausblenden C#-Quelltext
1:
this.Invoke((MethodInvoker) delegate { this.Close(); });					


Hallo Ralf,

vielen Dank für den Hinweis und das Beispiel!
Habs geändert und es funktioniert wunderbar :) :) :)

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
private void myProcess_Exited(object sender, System.EventArgs e)
  {
    if (!exited)
    {
      exited = true;
      this.Invoke((MethodInvoker) delegate { this.Close(); });
    }
  }
lord2k3 Threadstarter
Hält's aus hier
Beiträge: 6



BeitragVerfasst: Sa 12.03.16 15:48 
Eine Frage noch, ich nutze auch die ausgabe meines Prozesses mit dem OutputDataReceived Event. Warum wird da kein Threadübergreifender Zugriff angemeckert? Die Rückgabewerte funktionieren nämlich einwandfrei.
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4708
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Sa 12.03.16 16:15 
Ich denke da sollte das gleiche Problem bestehen. Was machst du den in dem EventHandler bei dir?
lord2k3 Threadstarter
Hält's aus hier
Beiträge: 6



BeitragVerfasst: Sa 12.03.16 19:55 
Okay hat sich erledigt, bei genauerer Betrachtung hätte ich vorher wahrscheinlich auf die Lösung kommen können.
Die Ausführung gestaltet sich genau so :)

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
private void myProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            if (e.Data != null)
            {
                if (rtbConsole.InvokeRequired)
                {
                    MethodInvoker textBoxInvoke = delegate { rtbConsole.Text += e.Data + Environment.NewLine; };
                    rtbConsole.Invoke(textBoxInvoke);
                }
            }
        }


Muss mir das mit dem Invoke nochmal genauer zu Gemüte führen, Danke!