Autor Beitrag
Silas
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 478

Windows XP Home
Delphi 2005, RAD Studio 2007, MASM32, FASM, SharpDevelop 3.0
BeitragVerfasst: Fr 13.06.08 18:37 
Hallo,

ich schreibe an einer kleinen (selbstezeichneten) Konsole und häng gerade an der Read/ReadLine-Methode fest.
Momentan habe ich es so gelöst, dass die Methode eine Busy-Waiting-Schleife startet und damit auf die Input-Events wartet:
ausblenden C#-Quelltext
1:
while (FWaitingForInput) Application.DoEvents();					

Während das unter Delphi keine Probleme bereitet (Application.ProcessMessages verwendet GetMessage, das erst bei einer neuen Message zurückkehrt), verursacht das Busy Waiting unter .NET eine CPU-Auslastung von 100%.

Im Endeffekt geht es mir nur darum, dass die Read-Methode(n) nicht zurückkehren, bevor die Eingabe beendet ist. Wie lässt sich das lösen?

Danke!
Silas

_________________
Religionskriege sind nur Streitigkeiten darüber, wer den cooleren imaginären Freund hat ;-)
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Mo 16.06.08 17:52 
user profile iconSilas hat folgendes geschrieben:
Im Endeffekt geht es mir nur darum, dass die Read-Methode(n) nicht zurückkehren, bevor die Eingabe beendet ist.
Für mich hört sich das nach einem merkwürdigen Design an: Entweder ist eine Methode non-blocking und die GUI läuft weiter oder sie blockiert und alles steht. ProcessMessages ist doch schonmal ein Hinweis auf eine Designschwäche :zwinker: .
Silas Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 478

Windows XP Home
Delphi 2005, RAD Studio 2007, MASM32, FASM, SharpDevelop 3.0
BeitragVerfasst: Di 17.06.08 13:44 
Das Problem ist ja, den momentanen Code anzuhalten ohne 100% CPU zu belegen und gleichzeitig auf GUI-Ereignisse zu reagieren. Ich kann ja den Thread schlecht einfach anhalten, weil er ja im Normalfall der GUI-Thread ist.
Bei der normalen Konsole funktioniert das (Mit Console.ReadLine) ja auch: Das Fenster wird gezeichnet, während der Prozess keine CPU-Last verursacht und auf eine Eingabe wartet.
Unter Win32 wartet ja GetMessage so lange, bis eine Message eintrifft und setzt das Programm dann fort, während PeekMessage immer fortsetzt. Deswegen verwendet man normalerweise auch GetMessage, weil so keine CPU-Last auftritt.
Genau diese Funktion bräuchte ich jetzt für .NET.

_________________
Religionskriege sind nur Streitigkeiten darüber, wer den cooleren imaginären Freund hat ;-)
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Di 17.06.08 14:11 
user profile iconSilas hat folgendes geschrieben:
(Application.ProcessMessages verwendet GetMessage, das erst bei einer neuen Message zurückkehrt)
Bei meinen Sourcen kommt das nicht so raus :gruebel: . Dort wird PeekMessage benutzt und ich bekomme in einer Endlosschleife auch 100% CPU.
Ich sehe sowohl für .Net als auch für Delphi nur die Lösung, eine eigene Nachrichtenschleife mit GetMessage zu benutzen, bis der Zeilenumbruch ankommt. Oder eben keine blockenden Methoden ;) (sondern zB Events).
Silas Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 478

Windows XP Home
Delphi 2005, RAD Studio 2007, MASM32, FASM, SharpDevelop 3.0
BeitragVerfasst: Di 17.06.08 14:23 
user profile iconKhabarakh hat folgendes geschrieben:
user profile iconSilas hat folgendes geschrieben:
(Application.ProcessMessages verwendet GetMessage, das erst bei einer neuen Message zurückkehrt)
Bei meinen Sourcen kommt das nicht so raus :gruebel: . Dort wird PeekMessage benutzt und ich bekomme in einer Endlosschleife auch 100% CPU.
:autsch: Das war meine eigene GetMessage-Prozedur, nicht ProcessMessages, sry.

user profile iconKhabarakh hat folgendes geschrieben:
Ich sehe sowohl für .Net als auch für Delphi nur die Lösung, eine eigene Nachrichtenschleife mit GetMessage zu benutzen, bis der Zeilenumbruch ankommt. Oder eben keine blockenden Methoden ;) (sondern zB Events).
Mit Callbacks/Events wäre das natürlich am saubersten, aber fürs Programmdesign ungünstig, weil Konsolenprogramme ja so gut wie immer linear ablaufen.

Eine eigene GetMessage-Methode hab ich schon versucht, das normale dispatchen scheint aber nicht zu genügen, die Events bleiben einfach auf der Strecke:
ausblenden Ein Versuch
1:
2:
3:
4:
5:
6:
MSG Message = new MSG();
if (GetMessage(out Message, Handle, 00))
{
    TranslateMessage(ref Message);
    DispatchMessage(ref Message);
else Application.Exit();

_________________
Religionskriege sind nur Streitigkeiten darüber, wer den cooleren imaginären Freund hat ;-)
Silas Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 478

Windows XP Home
Delphi 2005, RAD Studio 2007, MASM32, FASM, SharpDevelop 3.0
BeitragVerfasst: Do 24.07.08 15:58 
Ich konnte das Problem nun doch selber lösen, auch mit Win32-Methoden und GetMessage; die Sourcen des .NET-SDKs haben mir weitergeholfen.

Folgender Code (ApplicationExt.WaitForEvents()) tut genau das, was ich brauche: Er hält das Programm an, bis (mindestens) eine neue Message in der Message-Queue liegt.
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:
  /*  _______________________________________________________  *\
     |                                                       |
     |   Copyright © 2008, Silas (http://silas-online.de/)   |
     |   Erstellt am 18.07.2008 um 21:15                     |
     |                                                       |
     |   Dieser Code ist Lizenzfrei.                         |
     |_______________________________________________________|
  \*                                                           */

   

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
 

public static class ApplicationExt
{    

    private const uint WM_CHAR = 0x0102;
    private const uint WM_KEYDOWN = 0x0100;
    private const uint WM_KEYUP = 0x0101;
    private const uint WM_SYSCHAR = 0x0106;
    private const uint WM_SYSKEYDOWN = 0x0104;
    private const uint WM_SYSKEYUP = 0x0105;
    
    [StructLayout(LayoutKind.Sequential)]
    private struct MSG
    {
        public IntPtr hWnd;
        public uint message;
        public IntPtr wParam;
        public IntPtr lParam;
        public uint time;
        public POINT pt;
    }
    
    [StructLayout(LayoutKind.Sequential)]
    private struct POINT
    {
        public int X;
        public int Y;
    }

    
    [DllImport("user32.dll")] 
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool GetMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax);
    
    [DllImport("user32.dll")]
    private static extern bool TranslateMessage([In] ref MSG lpMsg);

    [DllImport("user32.dll")]
    private static extern IntPtr DispatchMessage([In] ref MSG lpmsg);
    
    
    public static void WaitForEvents()
    {
        MSG MessageStruct;
        GetMessage(out MessageStruct, IntPtr.Zero, 00);
        switch ((uint) MessageStruct.message)
        {
            case WM_KEYDOWN:
            case WM_SYSKEYDOWN:
            case WM_CHAR:
            case WM_WM.SYSCHAR:
            case WM_WM.KEYUP:
            case WM_SYSKEYUP:        
                Message ControlMessage;
                Control TargetControl;
                ControlMessage = Message.Create(MessageStruct.hWnd, (int)MessageStruct.message, MessageStruct.wParam, MessageStruct.lParam);
                TargetControl = Control.FromHandle(MessageStruct.hWnd);
                if ((TargetControl != null) && !TargetControl.PreProcessMessage(ref ControlMessage)) goto default;
                break;
            default
                TranslateMessage(ref MessageStruct);
                DispatchMessage(ref MessageStruct);
                break;
        }
    }

}

Vielleicht hilft er ja dem einen oder anderen auch weiter.

Grüße
Silas

_________________
Religionskriege sind nur Streitigkeiten darüber, wer den cooleren imaginären Freund hat ;-)