Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Thread blockiert Hauptprogramm

49 views
Skip to first unread message

Matthias Hanft

unread,
Aug 26, 2007, 5:47:42 AM8/26/07
to
Hallo,

eigentlich habe ich ja schon oft und viel (durchaus erfolg-
reich :-) ) mit Threads gearbeitet, aber jetzt habe ich mal
einen, der mit "WaitCommEvent" auf die serielle Schnitt-
stelle warten soll - was er auch tut, aber leider so, daß
auch das Fenster des Hauptprogramms nicht mehr reagiert!

Jetzt ist mein bisheriges Thread-Verständnis ganz schön
ins Wanken geraten: Bisher dachte ich eigentlich, daß man
gerade solche Dinge, die lange brauchen und/oder von irgend-
was event-getriggert sind, in einen Thread auslagert, damit
man seine Ruhe vor ihnen hat, sie sich melden, wenn sie was
zu sagen haben, und das Hauptprogramm derweil ganz normal
weiterarbeitet.

Aber offensichtlich stimmt das nicht - oder nicht ganz,
oder nicht immer? Ist das wirklich so (und auch so gedacht),
daß "Windows-Waits" den gesamten Prozeß warten lassen (und
nicht nur den Thread)? Oder nur WaitCommEvent? (Es steht
ja auch in der Doku "WaitCommEvent does not return until
one of the specified events or an error occurs.", aber
bisher war IIRC sowas immer threadbezogen...?!)

Und dann noch im speziellen: Könnte man den vorliegenden
Fall durch FILE_FLAG_OVERLAPPED lösen? Bin halt bisher
davor zurückgeschreckt, weil man da noch CreateEvent
und die OVERLAPPED-Structure anlegen und verwenden muß...

Danke & Gruß Matthias.

Arno Garrels

unread,
Aug 26, 2007, 7:39:57 AM8/26/07
to
Matthias Hanft wrote:
> Aber offensichtlich stimmt das nicht - oder nicht ganz,
> oder nicht immer? Ist das wirklich so (und auch so gedacht),
> daß "Windows-Waits" den gesamten Prozeß warten lassen (und
> nicht nur den Thread)?

Nein, immer den aufrufenden Thread.

> Oder nur WaitCommEvent?

Glaub ich nicht.

> (Es steht
> ja auch in der Doku "WaitCommEvent does not return until
> one of the specified events or an error occurs.", aber
> bisher war IIRC sowas immer threadbezogen...?!)

Wo rufst Du denn WaitCommEvent auf, in der Execute-Methode?

Arno Garrels

Matthias Hanft

unread,
Aug 26, 2007, 8:43:40 AM8/26/07
to
Arno Garrels schrieb:

>
> Nein, immer den aufrufenden Thread.

Aufschnauf... Du hast mein Weltbild wieder geradegerückt :-)

> Wo rufst Du denn WaitCommEvent auf, in der Execute-Methode?

Ja, klar - wo sonst? Ist das falsch? Copy & Paste:

procedure TSerialThread.Execute;
var
myError: SerialErr;
begin
FMessage:='Thread Start';
Synchronize(DisplayMessage);
while not Terminated do begin
myError:=SerialWait(FSerialPtr); // da drunter dann WaitCommEvent
FMessage:='Thread Wait Event: '+IntToStr(myError);
Synchronize(DisplayMessage)
end; (* while not Terminated *)
FMessage:='Thread Stop';
Synchronize(DisplayMessage)
end;

Wobei mir ein Punkt gerade einfällt: Die Schnittstelle wird
(mit "CreateFile('COM1')") im Haupt-Thread geöffnet, d.h.
der dabei entstehende Handle stammt auch von dort. Wird evtl.
dadurch dann auch im Haupt-Thread drauf gewartet?

Gruß Matthias.

Arno Garrels

unread,
Aug 26, 2007, 9:41:09 AM8/26/07
to
> Wobei mir ein Punkt gerade einfällt: Die Schnittstelle wird
> (mit "CreateFile('COM1')") im Haupt-Thread geöffnet, d.h.
> der dabei entstehende Handle stammt auch von dort. Wird evtl.
> dadurch dann auch im Haupt-Thread drauf gewartet?

Das spielt keine Rolle.

Ich würde mal prüfen, was myError sagt. Die While-Schleife in Execute
könnte die Ursache sein.

Bei mir klappt das Warten jedenfalls prima, schalte ich das Modem ein
wird das Handle signalisiert :)

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
TMyThread = class(TThread)
protected
procedure Execute; override;
end;

TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
Thread : TMyThread;
public
{ Public-Deklarationen }
end;

var
Form1 : TForm1;
hCom1 : THandle = INVALID_HANDLE_VALUE;

implementation

{$R *.dfm}

procedure OpenCom1;
begin
if hCom1 = INVALID_HANDLE_VALUE then
begin
hCom1 := CreateFile('COM1:',
GENERIC_READ or GENERIC_WRITE,
0, nil, OPEN_EXISTING, 0, 0);

if hCom1 = INVALID_HANDLE_VALUE then
RaiseLastOSError;
end;
end;


procedure TForm1.Button1Click(Sender: TObject);
begin
OpenCom1;
Button1.Enabled := FALSE;
Thread := TMyThread.Create(true);
Thread.FreeOnTerminate := TRUE;
Thread.Resume;
end;

{ TMyThread }

procedure TMyThread.Execute;
var
dwCom1Status : DWORD;
begin
SetCommMask(hCom1, EV_BREAK or EV_CTS or EV_DSR or EV_ERR or EV_RLSD or EV_RXCHAR);
WaitCommEvent(hCom1, dwCom1Status, 0);
Closehandle(hCom1);
hCom1 := INVALID_HANDLE_VALUE;
Beep;
end;

end.


Matthias Hanft wrote:
> Arno Garrels schrieb:
>>
>> Nein, immer den aufrufenden Thread.
>
> Aufschnauf... Du hast mein Weltbild wieder geradegerückt :-)
>
>> Wo rufst Du denn WaitCommEvent auf, in der Execute-Methode?
>
> Ja, klar - wo sonst? Ist das falsch? Copy & Paste:
>
> procedure TSerialThread.Execute;
> var
> myError: SerialErr;
> begin
> FMessage:='Thread Start';
> Synchronize(DisplayMessage);
> while not Terminated do begin
> myError:=SerialWait(FSerialPtr); // da drunter dann WaitCommEvent
> FMessage:='Thread Wait Event: '+IntToStr(myError);
> Synchronize(DisplayMessage)
> end; (* while not Terminated *)
> FMessage:='Thread Stop';
> Synchronize(DisplayMessage)
> end;
>
>

> Gruß Matthias.

Arno Garrels

unread,
Aug 26, 2007, 10:00:54 AM8/26/07
to
Arno Garrels wrote:
>> Wobei mir ein Punkt gerade einfällt: Die Schnittstelle wird
>> (mit "CreateFile('COM1')") im Haupt-Thread geöffnet, d.h.
>> der dabei entstehende Handle stammt auch von dort. Wird evtl.
>> dadurch dann auch im Haupt-Thread drauf gewartet?
>
> Das spielt keine Rolle.
>
> Ich würde mal prüfen, was myError sagt. Die While-Schleife in Execute
> könnte die Ursache sein.

Also genauer gefragt, wie oft wird DisplayMessage aufgerufen und was
macht diese Prozedur? Warscheinlich hast Du ein gutes Beispiel dafür
geliefert, wie man den Haupt-Thread 'tot-synchronisiert'.

Arno Garrels

Jens Spallek

unread,
Aug 26, 2007, 10:06:22 AM8/26/07
to
Matthias Hanft wrote:

Bau in diese Schleife ein Sleep($irgendwas) ein:

> while not Terminated do begin
> myError:=SerialWait(FSerialPtr); // da drunter dann WaitCommEvent
> FMessage:='Thread Wait Event: '+IntToStr(myError);
> Synchronize(DisplayMessage)
> end; (* while not Terminated *)

Dann bekommt auch Dein Hauptthread ausreichend Zeit.

JS

Arno Garrels

unread,
Aug 26, 2007, 11:30:16 AM8/26/07
to

Naja, es ist bestimmt schneller, wenn der Thread die Strings z.B. in
einen Puffer schreibt, und den Haupt-Thread via PostMessage() benachrichtigt
wenn etwas im Puffer liegt und mindestens 1 Sekunde seit der letzten
Benachrichtigung vergangen ist. Der Haupt-Thread holt sich dann die
Strings ab. Der Puffer muss natürlich in diesem Fall mit einer
CriticalSection geschützt werden. So muss der Thread nicht unnötig
warten und der Benutzer wird häufig genug über den Fortgang der
Dinge informiert.

Arno Garrels


Matthias Hanft

unread,
Aug 26, 2007, 11:40:43 AM8/26/07
to
Arno Garrels schrieb:

>
> Ich würde mal prüfen, was myError sagt. Die While-Schleife in Execute
> könnte die Ursache sein.

myError sagt 8 (=EV_CTS). Das ist auch in Ordnung, weil ich aufs Ändern
der CTS-Leitung warte.

> Bei mir klappt das Warten jedenfalls prima, schalte ich das Modem ein
> wird das Handle signalisiert :)

Jaaa... _das_ klappt bei mir auch prima. Aber hast Du mal versucht,
das Hauptfenster herumzuschieben, solange der Thread wartet? Geht
das bei Dir? Ja, sicher (s.u.).

> hCom1 := CreateFile('COM1:',

BTW: Geht da eigentlich wirklich kein 'COM17:' (USB-Adapter), sondern
nur "echte" Hardware? Warum?

> Thread.Resume;

Da hatte ich idiotischerweise Thread.Execute stehen. Frohgemut habe
ich das durch Resume ausgetauscht - keine Änderung!

Der Rest ist gleich (außer daß ich die while-Schleife habe, ich will
halt _alle_ künftigen CTS-Änderungen mitkriegen, nicht nur die erste).
Spielt aber keine Rolle - auch ohne while blockiert bei mir alles auch
schon beim "ersten" Warten.

Jetzt hab ich mal den kompletten Unit-Text durch Deinen ausgetauscht
- und schon geht das Hauptfensterrumschieben. Argh! Vermutlich ein
weiterer Brett-vorm-Kopf-Effekt?! Ich suche mal rum und melde mich
dann wieder...

Gruß Matthias.

Matthias Hanft

unread,
Aug 26, 2007, 11:43:09 AM8/26/07
to
Arno Garrels schrieb:

>
> Also genauer gefragt, wie oft wird DisplayMessage aufgerufen und was
> macht diese Prozedur? Warscheinlich hast Du ein gutes Beispiel dafür
> geliefert, wie man den Haupt-Thread 'tot-synchronisiert'.

procedure TSerialThread.DisplayMessage;
begin
Form1.Memo1.Lines.Append(FMessage);
Application.ProcessMessages
end;

(auch ohne Application.ProcessMessages keine Änderung)

IMHO macht man's so, wenn man in einem Thread die VCL benutzen will?

Hab aber auch schon _alle_ "Synchronize" grad mal rausgeschmissen,
hilft auch nix. Wo ist jetzt noch der Unterschied zu Deinem Programm?!

Ich suche weiter...

Gruß Matthias.

Matthias Hanft

unread,
Aug 26, 2007, 12:05:35 PM8/26/07
to
Matthias Hanft schrieb:
>
> Ich suche weiter...

Also, ich habs gefunden. Wobei sich dann schon gleich das nächste
Problem stellt... aber der Reihe nach:

Ich hatte, während der Thread am WaitCommEvent wartet, im Haupt-
thread auch mal irgendwo GetCommModemStatus aufgerufen. Wenn man
das rausmacht, gehts. Anscheinend darf man also während einer
solchen Warterei ansonsten _nichts_ mit der Schnittstelle machen,
noch nicht mal die Modemleitungen einlesen wollen. Kann das sein?

Das führt dann gleich zum nächsten Problem: Wie beende ich das
Programm, während im Thread noch an WaitCommEvent gewartet wird?

Eigentlich hätte ich beim Beenden erst mal

EscapeCommFunction(hFile, CLRRTS); // setzt RTS=0

gemacht -> Programm hängt.

Danach z.B. noch

SetCommMask(hFile, 0); // auf keine Events mehr warten

-> Programm hängt.

Aber selbst wenn man das alles wegläßt und nur

CloseHandle(hFile);

macht -> Programm hängt.

Eine Möglichkeit, einen Delphi-Thread zwangsweise abzuschießen,
habe ich auch nicht gefunden. Klar, man kann halt das gesamte
Programm beenden, und der Handle wird dann sicher automagisch
geschlossen (samt Thread-Abschuß). Aber die feine englische Art
ist das ja nicht. Was kann man da "sauber" machen? Evtl. doch
Richtung FILE_FLAG_OVERLAPPED/CreateEvent etc. gehen? In der
Beschreibung zum Flag steht ja auch "If this flag is not specified,
then I/O operations are serialized", was wohl bedeutet, daß
alles andere (inkl. CloseHandle) erst nach (erfolgreichem)
Abschluß von WaitCommEvent passieren wird. Und da letzteres
nie passiert...?!

Gruß Matthias.

Matthias Hanft

unread,
Aug 26, 2007, 1:13:45 PM8/26/07
to
Matthias "Ingrid" Hanft schrieb:
>
> Richtung FILE_FLAG_OVERLAPPED/CreateEvent etc. gehen?

Hab ich grad mal probiert, aber das ufert dann irgendwie aus...

Wenn das File "overlapping" ist, kommt "WaitCommEvent" sofort
zurück mit "Fehler" 997 "IO pending" (ist korrekt gemäß Doku)
und feuert dann, wenn was passiert, anscheinend den Event, den
man in der OVERLAPPED-Struktur angegeben hat.

Auf den kann man wohl auch warten, aber das verschiebt das
Problem ja nur auf eine andere Ebene: Wenn man das Programm
beenden will, hängts dann eben nicht am WaitCommEvent, sondern
am Wait für den Event...

Momentan fällt mir dazu nur ein, ein WaitForMultipleObjects
zu machen, von denen einer der OVERLAPPED-Event ist und der
andere ein manuell getriggerter "Programmende"-Event ist.
Aber das ist dann alles schon irgendwie arg umständlich...

Ein "GetOverlappedResult", das (wenn man will) auf den Event
wartet, hab ich auch noch gefunden. Wenn das zurückkehren würde,
wenn man die Schnittstelle schließt (was dann ja vermutlich
geht, wenns overlapped ist), wärs ja auch schon gegessen.

Argh. Warum kommt mir das alles "von hinten durch die Brust
ins Auge" vor? Kann man sowas nicht auch einfacher realisieren?!

Ich forsche weiter...

Gruß Matthias.

Arno Garrels

unread,
Aug 26, 2007, 1:30:43 PM8/26/07
to
Matthias Hanft wrote:
> SetCommMask(hFile, 0); // auf keine Events mehr warten
>
> -> Programm hängt.
>
> Aber selbst wenn man das alles wegläßt und nur
>
> CloseHandle(hFile);
>
> macht -> Programm hängt.

> Was kann man da "sauber" machen? Evtl. doch


> Richtung FILE_FLAG_OVERLAPPED/CreateEvent etc. gehen? In der
> Beschreibung zum Flag steht ja auch "If this flag is not specified,
> then I/O operations are serialized", was wohl bedeutet, daß
> alles andere (inkl. CloseHandle) erst nach (erfolgreichem)
> Abschluß von WaitCommEvent passieren wird. Und da letzteres
> nie passiert...?!

Das sehe/lese ich auch so, also Overlapped IO probieren, da kann
man dann zusätzlich auf ein eigenes Event-Handle warten, wenn man
denn möchte.

Arno Garrels

Arno Garrels

unread,
Aug 26, 2007, 1:39:46 PM8/26/07
to
Matthias Hanft wrote:
> Matthias "Ingrid" Hanft schrieb:
>>
>> Richtung FILE_FLAG_OVERLAPPED/CreateEvent etc. gehen?
>
> Hab ich grad mal probiert, aber das ufert dann irgendwie aus...
>
> Wenn das File "overlapping" ist, kommt "WaitCommEvent" sofort
> zurück mit "Fehler" 997 "IO pending" (ist korrekt gemäß Doku)
> und feuert dann, wenn was passiert, anscheinend den Event, den
> man in der OVERLAPPED-Struktur angegeben hat.
>
> Auf den kann man wohl auch warten, aber das verschiebt das
> Problem ja nur auf eine andere Ebene: Wenn man das Programm
> beenden will, hängts dann eben nicht am WaitCommEvent, sondern
> am Wait für den Event...
>
> Momentan fällt mir dazu nur ein, ein WaitForMultipleObjects
> zu machen, von denen einer der OVERLAPPED-Event ist und der
> andere ein manuell getriggerter "Programmende"-Event ist.
> Aber das ist dann alles schon irgendwie arg umständlich...

So hab ich das mal mit overlapped sockets gemacht, ist gar nicht
so schwer, mit der Behandlung möglicher Fehler aber schon eine
etwas länglichere Execute-Routine.

Arno Garrels

Matthias Hanft

unread,
Aug 26, 2007, 2:12:06 PM8/26/07
to
Arno Garrels schrieb:
>
[WaitForMultipleEvents]

> So hab ich das mal mit overlapped sockets gemacht, ist gar nicht
> so schwer, mit der Behandlung möglicher Fehler aber schon eine
> etwas länglichere Execute-Routine.

Ja, das erinnert mich an Programme, die zu 96% aus Fehlerabfangen
bestanden... hab ich auch mal geschrieben :-)

Ich habs mittlerweile mit "GetOverlappedResult" probiert. Mit
"FALSE" lieferts korrekt Error 996 zurück (Operation in progress),
aber mit "TRUE" (also wenns drauf warten soll), gibts (wenn
das Ereignis eintritt) eine EAccessViolation ("Lesen von
Adresse 00000008" bzw. "00000000") bei Thread.Execute (näher
will ers mir nicht sagen, auch madExcept greift anscheinend
nicht ein). Wenn ich die noch finde, könnte es so funktionieren,
da GetOverlappedResult offensichtlich auch dann zurückkehrt, wenn
die Schnittstelle geschlossen wird. Dann würde "ein einziges
Warten" ja genügen.

BTW, die Overlapped-Struktur ist laut Windows-Hilfe immer ein
"LPOVERLAPPED", wird aber in diesen beiden Delphi-Headern
unterschiedlich definiert:

function WaitCommEvent(hFile: THandle; var lpEvtMask: DWORD; lpOverlapped: POverlapped): BOOL; stdcall;
function GetOverlappedResult(hFile: THandle; const lpOverlapped: TOverlapped;
var lpNumberOfBytesTransferred: DWORD; bWait: BOOL): BOOL; stdcall;

was IMHO aufs gleiche rauskommen müßte (ja?), aber eigenartig
ists schon...?! (Ich hab auch nix gefunden, wie Strukturen
bei stdcall übergeben werden, aber eine andere Möglichkeit
als einen Zeiger drauf gibts ja wohl nicht?!)

Habs auch mal testweise geändert in

function GetOverlappedResult2(hFile: THandle; lpOverlapped: POverlapped;
var lpNumberOfBytesTransferred: DWORD; bWait: BOOL): BOOL; stdcall;

ist aber auch nix anderes passiert.

Gruß Matthias.

Hans-Peter Diettrich

unread,
Aug 26, 2007, 2:48:12 PM8/26/07
to
Matthias Hanft wrote:

> Ein "GetOverlappedResult", das (wenn man will) auf den Event
> wartet, hab ich auch noch gefunden. Wenn das zurückkehren würde,
> wenn man die Schnittstelle schließt (was dann ja vermutlich
> geht, wenns overlapped ist), wärs ja auch schon gegessen.

Und dafür gibt's dann WaitForMultipleObjects...

Das WaitCommEvent dürfte IMO zu einer veralteten Schnittstelle gehören.
Richtig sollte es so funktionieren, daß man dem Absender signalisiert,
daß man über Ereignisse informiert werden möchte, d.h. dann soll eine
Message abgeschickt werden. Wenn sich der Empfänger dann verabschiedet,
verpufft die angeforderte Benachrichtigung einfach, mangels Empfänger.


Mich hat schon beim Programmieren für meinen Atari ST gestört, daß man
dort ständig angeben muß, auf welche Ereignisse das Programm nun wieder
warten soll. Könnte es sein, daß "ereignisgesteuert" (event driven) nur
ein beschränkter Sonderfall von "meldungsbasiert" (message based/driven)
ist?

DoDi

Matthias Hanft

unread,
Aug 27, 2007, 5:09:11 AM8/27/07
to
Matthias Hanft schrieb:

>
> Ich habs mittlerweile mit "GetOverlappedResult" probiert. Mit
> "FALSE" lieferts korrekt Error 996 zurück (Operation in progress),
> aber mit "TRUE" (also wenns drauf warten soll), gibts (wenn
> das Ereignis eintritt) eine EAccessViolation ("Lesen von
> Adresse 00000008" bzw. "00000000") bei Thread.Execute (näher
> will ers mir nicht sagen, auch madExcept greift anscheinend
> nicht ein). Wenn ich die noch finde, könnte es so funktionieren,
> da GetOverlappedResult offensichtlich auch dann zurückkehrt, wenn
> die Schnittstelle geschlossen wird. Dann würde "ein einziges
> Warten" ja genügen.

So, jetzt geht alles. Falls die Nachwelt mal irgendwann diesen
Thread ergooglet, hier die Auflösung des Rätsels:

GetOverlappedResult stürzt nur dann nicht ab, wenn man vorher
mit WaitForSingleObject drauf wartet. Dann ist GetOverlapped-
Result aber ohnehin überflüssig, weil man ja schon benachrich-
tigt wurde. Und auf MultipleObjects (zum Beenden) braucht man
auch nicht warten, weil das Ändern der CommMask auch schon das
Warten beendet. Und irgendwelche "Sleep" im Thread sind ebenso
unnötig. Also eigentlich alles ganz einfach :-)

Die richtige Reihenfolge ist daher die hier:

procedure TSerialThread.Execute;
begin
hFile:=CreateFile(...FILE_FLAG_OVERLAPPED...); // sonst kommt man aus
// dem Warten nicht
// anständig wieder raus
hEvent:=CreateEvent(nil, TRUE, FALSE, nil); // der, auf den man nachher wartet
SetCommMask(hFile, EV_CTS); // z.B. - auf was man halt wartet


while not Terminated do begin

FillChar(Overlapped, SizeOf(Overlapped), 0); // lieber vor jedem
Overlapped.hEvent:=hEvent; // Warten neu initialisieren?
WaitCommEvent(hFile, EvtMask, @Overlapped); // kommt sofort zurück
WaitForSingleObject(hEvent, INFINITE); // wartet bis was passiert
// hier z.B. GetCommModemStatus einlesen, um CTS auszuwerten
ResetEvent(hEvent) // Signalisierung zurücksetzen


end; (* while not Terminated *)

CloseHandle(hEvent);
CloseHandle(hFile)
end;

und zum anständigen Beenden vom Hauptprogramm aus dann einfach

SerialThread.Terminate; // setzt erst mal "Terminated"
SetCommMask(hFile, 0); // triggert den Event im Thread

und das war's dann auch schon.

Einfach, nicht? :-)

Danke nochmal für alle Antworten!

Gruß Matthias.

Heiko Nocon

unread,
Aug 26, 2007, 8:15:10 AM8/26/07
to
Matthias Hanft wrote:

>einen, der mit "WaitCommEvent" auf die serielle Schnitt-
>stelle warten soll - was er auch tut, aber leider so, daß
>auch das Fenster des Hauptprogramms nicht mehr reagiert!

Dann hast du etwas falsch gemacht. Der Thread wird natürlich von
WaitComEvent blockiert (wenn nicht mit overlapping IO gearbeitet wird),
aber der Mainthread läuft weiter.

Es sei denn, du machst im Mainthread irgendwas, was implizit oder
explizit wiederum auf den zusätzlichen Thread wartet.

Was uns zu der Frage bringt, wie genau du eigentlich deinen Mainthread
mit dem Warte-Thread synchronisierst...

>Und dann noch im speziellen: Könnte man den vorliegenden
>Fall durch FILE_FLAG_OVERLAPPED lösen?

Nö, überhaupt nicht. Der zusätzliche Thread würde dann nur anstatt durch
den Aufruf von WaitCommEvent selber durch das nachfolgende Warten auf
das Event in der Overlapped-Struktur blockiert. Ansosnten ändert sich
nix.

--
Wer Komponenten ohne Quelltext oder richtig miese Komponenten
oder gute Komponenten mit Quelltext, ohne die Source zu verstehen, sich verschafft,
um sie in Form "eigener" Programme in Verkehr zu bringen,
der wird mit Gefängnis nicht unter 5 Jahren bestraft.

Heiko Nocon

unread,
Aug 26, 2007, 4:21:01 PM8/26/07
to
Matthias Hanft wrote:

>BTW, die Overlapped-Struktur ist laut Windows-Hilfe immer ein
>"LPOVERLAPPED", wird aber in diesen beiden Delphi-Headern
>unterschiedlich definiert:
>
>function WaitCommEvent(hFile: THandle; var lpEvtMask: DWORD; lpOverlapped: POverlapped): BOOL; stdcall;
>function GetOverlappedResult(hFile: THandle; const lpOverlapped: TOverlapped;
> var lpNumberOfBytesTransferred: DWORD; bWait: BOOL): BOOL; stdcall;

Das ist völlig korrekt. Während man WaitCommEvent auch ohne einen
Verweis auf eine Overlapped-Struktur verwenden kann (nämlich wenn man
kein Overlapped IO verwendet), macht GetOverlappedResult nur Sinn, wenn
man eine solche Struktur verwendet.

Der Unterschied im Win-API besteht darin, daß bei WaitCommEvent die
Übergabe eines NULL-Zeigers zulässig ist, bei GetOverlappedResult
hingegen nicht. Die Delphi-Deklaration _erzwingt_ die korrekte
Verwendung, denn sie läßt die Übergabe eines Nullzeigers bei
GetOverlappedResult schon zur Compilezeit erst garnicht zu, während die
C'ler den Fehler erst zur Laufzeit bemerken, wenn sie nämlich Error Nr.
sowieso (illegaler Parameter) von der Funktion zurückbekommen.

Naja, mit Typecasts oder Neudeklaration geht's dann auch mit Delphi
wieder, da einen Nullzeiger reinzuwürgen, aber das ist eine andere
Geschichte...

Heiko Nocon

unread,
Aug 26, 2007, 12:28:12 PM8/26/07
to
Matthias Hanft wrote:

>BTW: Geht da eigentlich wirklich kein 'COM17:' (USB-Adapter), sondern
>nur "echte" Hardware?

Doch, natürlich geht das. Allerdings sind aus historischen Gründen nur
COM1[:] bis COM9[:] direkt über diese Namen zu öffnen. Für alle höheren
Nummern mußt du Dateinamen der Form '\\.\COMx' (x=Nummer der Ports)
verwenden. Diese Form funktioniert allerdings auch bei den kleinen
Nummern, also ist es am sinnvollsten, sie gleich generell zu verwenden.

Arno Garrels

unread,
Aug 27, 2007, 5:21:03 AM8/27/07
to
Matthias Hanft wrote:
> Arno Garrels schrieb:
>>
> [WaitForMultipleEvents]
>> So hab ich das mal mit overlapped sockets gemacht, ist gar nicht
>> so schwer, mit der Behandlung möglicher Fehler aber schon eine
>> etwas länglichere Execute-Routine.
>
> Ja, das erinnert mich an Programme, die zu 96% aus Fehlerabfangen
> bestanden... hab ich auch mal geschrieben :-)
>
> Ich habs mittlerweile mit "GetOverlappedResult" probiert.

Du kannst bestimmt GetOverlappedResult() weglassen und
stattdessen gleich WaitForMultipleObjects() aufrufen.


> Mit
> "FALSE" lieferts korrekt Error 996 zurück (Operation in progress),
> aber mit "TRUE" (also wenns drauf warten soll), gibts (wenn
> das Ereignis eintritt) eine EAccessViolation ("Lesen von
> Adresse 00000008" bzw. "00000000") bei Thread.Execute (näher
> will ers mir nicht sagen, auch madExcept greift anscheinend
> nicht ein). Wenn ich die noch finde, könnte es so funktionieren,
> da GetOverlappedResult offensichtlich auch dann zurückkehrt, wenn
> die Schnittstelle geschlossen wird. Dann würde "ein einziges
> Warten" ja genügen.
>
> BTW, die Overlapped-Struktur ist laut Windows-Hilfe immer ein
> "LPOVERLAPPED", wird aber in diesen beiden Delphi-Headern
> unterschiedlich definiert:
>
> function WaitCommEvent(hFile: THandle; var lpEvtMask: DWORD;
> lpOverlapped: POverlapped): BOOL; stdcall;

Hier Pointer auf Variable vom Typ TOverlapped.

var
Ov: TOverlapped;
begin
FillChar(Ov, SizeOf(TOverLapped), #0);
Ov.hEvent := CreateEvent(nil, True, True, nil);

Übergebe @Ov

>function
> GetOverlappedResult(hFile: THandle; const lpOverlapped: TOverlapped;
> var lpNumberOfBytesTransferred: DWORD; bWait: BOOL): BOOL; stdcall;

Hier Variable vom Typ TOverlapped

Übergebe Ov.

Arno Garrels

Matthias Hanft

unread,
Aug 27, 2007, 5:43:22 AM8/27/07
to
Heiko Nocon schrieb:

>
> Der Unterschied im Win-API besteht darin, daß bei WaitCommEvent die
> Übergabe eines NULL-Zeigers zulässig ist, bei GetOverlappedResult
> hingegen nicht. Die Delphi-Deklaration _erzwingt_ die korrekte
> Verwendung, denn sie läßt die Übergabe eines Nullzeigers bei
> GetOverlappedResult schon zur Compilezeit erst garnicht zu,

Ach, _das_ ist der Unterschied ("NOT NULL" oder nicht) - hatte mich
schon gewundert. Wieder was dazugelernt :-) Danke!

Gruß Matthias.

Matthias Hanft

unread,
Aug 27, 2007, 5:47:26 AM8/27/07
to
Heiko Nocon schrieb:

>
> Doch, natürlich geht das. Allerdings sind aus historischen Gründen nur
> COM1[:] bis COM9[:] direkt über diese Namen zu öffnen. Für alle höheren
> Nummern mußt du Dateinamen der Form '\\.\COMx' (x=Nummer der Ports)
> verwenden. Diese Form funktioniert allerdings auch bei den kleinen
> Nummern, also ist es am sinnvollsten, sie gleich generell zu verwenden.

Argh. Danke. Woher weiß man sowas? :-)

Hab grad nochmal in MSHELP bei CreateFile nachgeschaut: Da stehen solche
Formen bei "Physical Disks and Volumes" und "Tape Drives" drin (z.B.
"\\.\A:" oder "\\.\TAPE0"), aber bei "Communications Resources" haben
sie sich leider ausgeschwiegen :-(

Gruß Matthias.

Matthias Hanft

unread,
Aug 27, 2007, 5:53:59 AM8/27/07
to
Heiko Nocon schrieb:

>
>>Und dann noch im speziellen: Könnte man den vorliegenden
>>Fall durch FILE_FLAG_OVERLAPPED lösen?
>
> Nö, überhaupt nicht. Der zusätzliche Thread würde dann nur anstatt durch
> den Aufruf von WaitCommEvent selber durch das nachfolgende Warten auf
> das Event in der Overlapped-Struktur blockiert. Ansosnten ändert sich
> nix.

Wie sich ja mittlerweile herausgestellt hat, stimmt das so nicht ganz:

Wenn CreateFile...
- ... _ohne_ FILE_FLAG_OVERLAPPED geöffnet wurde, blockieren während
des Wartens auf WaitCommEvent _alle_ anderen Aufrufe dieser Schnitt-
stelle (insbesondere auch SetCommMask und sogar CloseHandle, so daß
man das ganze zum Beenden nur "abwürgen" kann);
- ... _mit_ FILE_FLAG_OVERLAPPED geöffnet wurde, kann man während
WaitForSingleObject das Triggern des Events (auch) mit SetCommMask
auslösen, so daß man "geordnet" wieder aus dem Programm rauskommt.

Ist also doch ein ganz schöner Unterschied...

Gruß Matthias.

Hans-Peter Diettrich

unread,
Aug 27, 2007, 8:54:28 AM8/27/07
to
Heiko Nocon wrote:

> Die Delphi-Deklaration _erzwingt_ die korrekte
> Verwendung, denn sie läßt die Übergabe eines Nullzeigers bei
> GetOverlappedResult schon zur Compilezeit erst garnicht zu, während die
> C'ler den Fehler erst zur Laufzeit bemerken, wenn sie nämlich Error Nr.
> sowieso (illegaler Parameter) von der Funktion zurückbekommen.

Und das ist auch einer der Gründe, warum in C++ Referenzen eingeführt
wurden. Die dürfen, im Gegensatz zu Pointern, auch nie NULL sein. Aber
C++ ist für "echte" C Programmierer genauso inakzeptabel wie Delphi ;-)

DoDi

Hans-Peter Diettrich

unread,
Aug 27, 2007, 9:01:55 AM8/27/07
to
Matthias Hanft wrote:

> Wie sich ja mittlerweile herausgestellt hat, stimmt das so nicht ganz:
>
> Wenn CreateFile...
> - ... _ohne_ FILE_FLAG_OVERLAPPED geöffnet wurde, blockieren während
> des Wartens auf WaitCommEvent _alle_ anderen Aufrufe dieser Schnitt-
> stelle (insbesondere auch SetCommMask und sogar CloseHandle, so daß
> man das ganze zum Beenden nur "abwürgen" kann);

Gilt das auch dann, wenn CreateFile innerhalb von Thread.Execute
aufgerufen wird?

DoDi

Matthias Hanft

unread,
Aug 27, 2007, 12:09:49 PM8/27/07
to
Hans-Peter Diettrich schrieb:

>>
>> Wenn CreateFile...
>> - ... _ohne_ FILE_FLAG_OVERLAPPED geöffnet wurde, blockieren während
>> des Wartens auf WaitCommEvent _alle_ anderen Aufrufe dieser Schnitt-
>> stelle (insbesondere auch SetCommMask und sogar CloseHandle, so daß
>> man das ganze zum Beenden nur "abwürgen" kann);
>
> Gilt das auch dann, wenn CreateFile innerhalb von Thread.Execute
> aufgerufen wird?

Gerade eben extra für Dich ausprobiert: Ja. Anscheinend merkt sich
Windows bei Filehandles nicht, welchem Thread sie "zugehörig" sind.

Bzw. in MSHELP steht ja auch "If this flag is not specified, then
I/O operations are serialized, even if the calls to the read and
write functions specify an OVERLAPPED structure." - das klingt
schon ziemlich global für das jeweilige File...

Gruß Matthias.

Heiko Nocon

unread,
Aug 27, 2007, 12:26:36 PM8/27/07
to
Matthias Hanft wrote:

>Wie sich ja mittlerweile herausgestellt hat, stimmt das so nicht ganz:
>
>Wenn CreateFile...
>- ... _ohne_ FILE_FLAG_OVERLAPPED geöffnet wurde, blockieren während
> des Wartens auf WaitCommEvent _alle_ anderen Aufrufe dieser Schnitt-
> stelle (insbesondere auch SetCommMask und sogar CloseHandle, so daß
> man das ganze zum Beenden nur "abwürgen" kann);

Naja...

Das ist schon korrekt, allerdings hattest du nicht erwähnt, daß du in
deinem Hauptthread weitere Funktionen aufrufst, die mit demselben
FileHandle arbeiten.

Damit hast du das erfüllt, was ich in meinem Posting mit:

>Es sei denn, du machst im Mainthread irgendwas, was implizit oder
>explizit wiederum auf den zusätzlichen Thread wartet.

umschrieben hatte...

Matthias Hanft

unread,
Aug 27, 2007, 12:46:11 PM8/27/07
to
Heiko Nocon schrieb:

>
> Das ist schon korrekt, allerdings hattest du nicht erwähnt, daß du in
> deinem Hauptthread weitere Funktionen aufrufst, die mit demselben
> FileHandle arbeiten.

Stimmt. Aber wüßtest Du denn sonst eine Lösung ohne FILE_FLAG_
OVERLAPPED, mit der man den wartenden Thread "sauber" beenden
könnte? (Nur aus wissenschaftlichem Interesse - es geht ja jetzt,
also lang' ich da nimmer hin :-) .)

Gruß Matthias.

Heiko Nocon

unread,
Aug 27, 2007, 2:49:40 PM8/27/07
to
Matthias Hanft wrote:

>Aber wüßtest Du denn sonst eine Lösung ohne FILE_FLAG_
>OVERLAPPED, mit der man den wartenden Thread "sauber" beenden
>könnte?

Sollte eigentlich auch gehen. Jedem Thread seine eigene Kopie des
Filehandles spendieren. Siehe DuplicateHandle().

Hans-Peter Diettrich

unread,
Aug 27, 2007, 8:27:23 PM8/27/07
to
Heiko Nocon wrote:

>>Aber wüßtest Du denn sonst eine Lösung ohne FILE_FLAG_
>>OVERLAPPED, mit der man den wartenden Thread "sauber" beenden
>>könnte?
>
>
> Sollte eigentlich auch gehen. Jedem Thread seine eigene Kopie des
> Filehandles spendieren. Siehe DuplicateHandle().

Das dürfte auch nicht gehen, wenn die einzelnen Operationen serialisiert
werden. Ein serielles Gerät ist was anderes als ein random-access Gerät,
es hat ja nicht einmal eine Fileposition, die für verschiedene Handles
einen unterschiedlichen Wert haben könnte. Aber Probieren geht über
Studieren ;-)

DoDi

Hans-Peter Diettrich

unread,
Aug 27, 2007, 8:14:33 PM8/27/07
to
Matthias Hanft wrote:

>>>Wenn CreateFile...
>>>- ... _ohne_ FILE_FLAG_OVERLAPPED geöffnet wurde, blockieren während
>>> des Wartens auf WaitCommEvent _alle_ anderen Aufrufe dieser Schnitt-
>>> stelle (insbesondere auch SetCommMask und sogar CloseHandle, so daß
>>> man das ganze zum Beenden nur "abwürgen" kann);
>>
>>Gilt das auch dann, wenn CreateFile innerhalb von Thread.Execute
>>aufgerufen wird?
>
>
> Gerade eben extra für Dich ausprobiert: Ja. Anscheinend merkt sich
> Windows bei Filehandles nicht, welchem Thread sie "zugehörig" sind.

Danke für Deine Bemühungen :-)

> Bzw. in MSHELP steht ja auch "If this flag is not specified, then
> I/O operations are serialized, even if the calls to the read and
> write functions specify an OVERLAPPED structure." - das klingt
> schon ziemlich global für das jeweilige File...

Ja, da wird wohl das ganze Gerät blockiert, solange noch eine Funktion
ansteht.

DoDi

Matthias Hanft

unread,
Aug 28, 2007, 3:17:30 AM8/28/07
to
Hans-Peter Diettrich schrieb:

>
> Das dürfte auch nicht gehen, wenn die einzelnen Operationen serialisiert
> werden. Ein serielles Gerät ist was anderes als ein random-access Gerät,
> es hat ja nicht einmal eine Fileposition, die für verschiedene Handles
> einen unterschiedlichen Wert haben könnte. Aber Probieren geht über
> Studieren ;-)

Stimmt. Ergebnis: Geht auch nicht. Habe nach dem Öffnen (ohne FILE_FLAG_
OVERLAPPED)

myBool:=DuplicateHandle(GetCurrentProcess, FSerialPtr^.hFile,
GetCurrentProcess, @Form1.FDupHandle, 0, TRUE, DUPLICATE_SAME_ACCESS);

gemacht und dann mal SetCommMask(Form1.FDupHandle, 0) - blockiert genauso.

Vermutlich kann man sowas wirklich nur mit TerminateThread abwürgen? Aber
"TerminateThread is a dangerous function that should only be used in the
most extreme cases." :-)

Gruß Matthias.

kosst amojan

unread,
Aug 30, 2007, 12:35:39 AM8/30/07
to

"Matthias Hanft"

> Jetzt ist mein bisheriges Thread-Verständnis ganz schön
> ins Wanken geraten: Bisher dachte ich eigentlich, daß man
> gerade solche Dinge, die lange brauchen und/oder von irgend-
> was event-getriggert sind, in einen Thread auslagert, damit
> man seine Ruhe vor ihnen hat, sie sich melden, wenn sie was
> zu sagen haben, und das Hauptprogramm derweil ganz normal
> weiterarbeitet.

Versuche doch einmal dem Thread explizit eine niedrigere
Priorität zuzuweisen als dem übergeordnetem Thread. Ich habe
bei einem Projekt mal festgestellt, dass Delphi bei _echten_
Threads, also die die vom Betriebssystem verwaltet werden
und nicht von Delphi, ein untergeordneter durchaus eine höhere
Priorität bekommen kann als ein übergeordneter.

Stefan Meisner

unread,
Aug 30, 2007, 7:12:19 AM8/30/07
to
> bei einem Projekt mal festgestellt, dass Delphi bei _echten_
> Threads, also die die vom Betriebssystem verwaltet werden
> und nicht von Delphi,

Was bitte sind "echte" Threads? Delphis TThread kapselt
diese vom Betriebssystem verwalteten Threads: Delphi
verwaltet da gar nichts.

Grüsse
Stefan

kosst amojan

unread,
Aug 30, 2007, 11:57:02 AM8/30/07
to

"Stefan Meisner"

Das kommt auf die Implementierung an. Normalerweise macht man es
so, dass du Recht hast. Aber es geht auch anders.

Stefan Meisner

unread,
Aug 30, 2007, 12:49:11 PM8/30/07
to
> Das kommt auf die Implementierung an. Normalerweise macht man es
> so, dass du Recht hast. Aber es geht auch anders.

Dann sprechen wir aber nicht mehr von einem TThread
sondern etwas ganz anderem?

Grüsse
Stefan

Jens Spallek

unread,
Aug 31, 2007, 5:55:41 AM8/31/07
to
Arno Garrels wrote:

> Naja, es ist bestimmt schneller, wenn der Thread die Strings z.B. in
> einen Puffer schreibt, und den Haupt-Thread via PostMessage() benachrichtigt
> wenn etwas im Puffer liegt und mindestens 1 Sekunde seit der letzten
> Benachrichtigung vergangen ist. Der Haupt-Thread holt sich dann die
> Strings ab. Der Puffer muss natürlich in diesem Fall mit einer
> CriticalSection geschützt werden. So muss der Thread nicht unnötig
> warten und der Benutzer wird häufig genug über den Fortgang der
> Dinge informiert.

Das ist natürlich richtig und auch wesentlich eleganter, gut geeignet
für asynchrones Logging. Mein Vorschlag zielte eher direkt auf die
Problemursache und unmittelbare Abhilfe. Mit Deiner Variante läßt
sich auch die Puffergröße konfigurieren und so das Threadverhalten
steuern.

JS

0 new messages