Discussion:
WaitForSingleObject
(zu alt für eine Antwort)
Jens Köhler
2012-02-24 16:15:37 UTC
Permalink
Hallo,

um zu verhindern, das ein Programm mehrfach gestartet wird, habe ich
im Internet ein funktionierende Lösung gefunden. Ein Ausschnitt davon
steht unten. Meine Frage ist, was macht das WaitForSingleObject?
Ich habe zwar die Hilfe gelesen, geholfen hat mir das aber nicht.

Jens

Mutex := CreateMutex(nil, True, MutexName);
if GetLastError = Error_Already_Exists then
begin
if WaitForSingleObject(Mutex, 5000) = Wait_Object_0 then
begin
Soeren Muehlbauer
2012-02-24 16:35:15 UTC
Permalink
Hi,
Post by Jens Köhler
um zu verhindern, das ein Programm mehrfach gestartet wird, habe ich
im Internet ein funktionierende Lösung gefunden. Ein Ausschnitt davon
steht unten. Meine Frage ist, was macht das WaitForSingleObject?
Ich habe zwar die Hilfe gelesen, geholfen hat mir das aber nicht.
Jens
Mutex := CreateMutex(nil, True, MutexName);
if GetLastError = Error_Already_Exists then
begin
if WaitForSingleObject(Mutex, 5000) = Wait_Object_0 then
begin
Ein Mutex stellt ein Synchronisierungsobjekt dar. Derlei gibt es unter
Windows mehrere. Weitere sind beispielsweise Kritische Sektionen
(CriticalSection), Events, Semaphoren oder Waitable Timer.

Kritische Sektionen sind immer nur in einer Programminstanz gültig.
Weiterhin kann man eine Kritische Sektion nicht mehrfach erzeugen. Ein
Thread, welcher einen geschützen Quelltextabschnitt betreten möchte,
muss sich das Eigentum an der Kritischen Sektion sichern
(EnterCriticalSection). Nach seiner Arbeit gibt er die Kritische Sektion
wieder frei (LeaveCriticalSection). Kritische Sektionen stellen die
leichtgewichtigste aller Synchronisationsobjekte dar.

Mutexe, Semaphoren, Events und Waitable Timer hingegen können mit einem
Namen versehen werden. Diese sind dann prozessübergreifend verfügbar.
Wenn dem Namen ein "Global\" vorangestellt ist, gilt das
Synchronisationsobjekt auch über Sessiongrenzen hinweg. Stellt man ein
"Local\" davor so gilt das nur für die aktuelle Session.

Um sich das Eigentum an so einem Objekt zu sichern ruft man eine der
WaitForXXX Methoden auf. Gibt diese Funktion WAIT_OBJECT_0 (oder bei
WaitForMultipleObjects WAIT_OBJECT_X) zurück, so hast Du das Eigentum am
Objekt. Um das Eigentum wieder abzugeben ruft man bei Mutexen
ReleaseMutex und bei Semaphoren ReleaseSemaphore auf.

Das Eigentum an einem Mutex ist immer exklusiv. Nur ein
Prozess/Codeabschnitt kann den Mutex besitzen (im Gegensatz zu einer
kritischen Sektion bei der ein Thread auch mehrfach EnterCriticalSection
aufrufen kann).
Semaphoren wird beim Erzeugen ein Zähler mitgegeben. Genau diese Anzahl
kann dann per WaitForXXX betreten werden.

In Deinem Fall oben, wird versucht den Mutex zu erzeugen. Wenn der Mutex
existiert, wird versucht eine bestimmte Zeit darauf zu warten, denn ein
anderes Programm besitzt diesen Mutex schon. Wenn das Warten nicht
erfolgreich war (wahrscheinlich ein else des If's oben), bricht das
Program ab. CreateMutex wie es oben geschrieben ist, übernimmt sofort
den Besitz (InitialOwner = true).

Sören
Jens Köhler
2012-02-24 18:37:56 UTC
Permalink
Post by Soeren Muehlbauer
Hi,
Post by Jens Köhler
um zu verhindern, das ein Programm mehrfach gestartet wird, habe ich
im Internet ein funktionierende Lösung gefunden. Ein Ausschnitt davon
steht unten. Meine Frage ist, was macht das WaitForSingleObject?
Ich habe zwar die Hilfe gelesen, geholfen hat mir das aber nicht.
Jens
Mutex := CreateMutex(nil, True, MutexName);
if GetLastError = Error_Already_Exists then
begin
if WaitForSingleObject(Mutex, 5000) = Wait_Object_0 then
begin
In Deinem Fall oben, wird versucht den Mutex zu erzeugen. Wenn der Mutex
existiert, wird versucht eine bestimmte Zeit darauf zu warten, denn ein
anderes Programm besitzt diesen Mutex schon. Wenn das Warten nicht
erfolgreich war (wahrscheinlich ein else des If's oben), bricht das
Program ab. CreateMutex wie es oben geschrieben ist, übernimmt sofort
den Besitz (InitialOwner = true).
Sören
Hallo,

danke für die ausführliche Erklärung.

Die Frage bleibt allerdings, was macht bzw. soll das WaitForSingleObject
machen?
Das es eine vorherige Instanz des Programmes gibt, verrät mir die Zeile
if GetLastError = Error_Already_Exists then

Wenn ich keine weite Instanz zulassen möchte, muß ich die startende Instanz
gleich wieder beenden. Ggf. muß ich vorher noch das schon laufende Programm
benachrichtigen.
Weshalb sollte ich dann auf den Mutex warten?

Grüße
Jens
Soeren Muehlbauer
2012-02-24 19:59:12 UTC
Permalink
Hi,
Post by Jens Köhler
Die Frage bleibt allerdings, was macht bzw. soll das WaitForSingleObject
machen?
Das ist eventuell dafür gedacht, dass ein Nutzer ein Programm schließt.
Das Programm braucht allerdings eine kleines bischen Zeit, und zwar in
der Art, dass keine GUI mehr sichtbar ist. Jetzt will der Nutzer das
Programm gleich wieder starten. Vielleicht ist die Wartezeit genau dafür
da, der sich beendenden Instanz noch ein wenig Zeit zu geben.

Sören
Sven Lanoster
2012-02-24 20:05:26 UTC
Permalink
Moin, moin.
Post by Jens Köhler
Das es eine vorherige Instanz des Programmes gibt, verrät mir die
Zeile if GetLastError = Error_Already_Exists then
Wenn ich keine weite Instanz zulassen möchte, muß ich die startende
Instanz gleich wieder beenden. Ggf. muß ich vorher noch das schon
laufende Programm benachrichtigen.
Weshalb sollte ich dann auf den Mutex warten?
Stell dir vor, du hast ein Programm, welches unbedingt laufen soll und
deswegen von einem Watchdog überwacht wird. Der Watchdog startet das
Programm neu, sobald es nicht mehr reagiert. Jetzt kann es aber sein,
dass das Programm noch gar nicht ganz tot ist, sondern noch ein paar
Sekunden zuckt. Da ist es dann hilfreich, wenn die vom Watchdog
gestartete neue Instanz einen Augenblick wartet, bis sie aufgibt.

Ob du das brauchst, must du selbst entscheiden.

MfG,
Sven.
--
Prokrastinierer alle Länder vereinigt euch - morgen!
Heiko Nocon
2012-02-24 19:21:11 UTC
Permalink
Post by Jens Köhler
um zu verhindern, das ein Programm mehrfach gestartet wird, habe ich
im Internet ein funktionierende Lösung gefunden. Ein Ausschnitt davon
steht unten. Meine Frage ist, was macht das WaitForSingleObject?
Das wartet 5 Sekunden lang darauf, daß der bisherige Besitzer des Mutex
diesen freigibt, also ReleaseMutex() aufruft.

Möglicherweise, um auch die Situation zu behandeln, daß zwar eine
Instanz bereits läuft, diese aber gerade dabei ist, sich zu beenden
(also Fenster zu schließen, Resourcen freizugeben usw.) und nur noch
nicht dazu gekommen ist, auch das Mutex freizugeben.

Ohne Berücksichtigung dieser Situation würde am Ende keine der beiden
Instanzen mehr laufen.
--
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.
Jens Köhler
2012-02-25 07:02:52 UTC
Permalink
Post by Heiko Nocon
Das wartet 5 Sekunden lang darauf, daß der bisherige Besitzer des Mutex
diesen freigibt, also ReleaseMutex() aufruft.
Hallo,

das warten scheint so aber nicht zu funktionieren.
Wenn ich eine Kopie der Exe starte, in der IDE den Cursor auf das Halt
hinter der Wait... stelle und mit F4 das Pragramm starte, bin ich
verzögerungsfrei an der Stelle. 5 Sekunden sollten doch deutlich spürbar
sein.

Jens
Heiko Nocon
2012-02-25 10:30:32 UTC
Permalink
Post by Jens Köhler
das warten scheint so aber nicht zu funktionieren.
Wenn ich eine Kopie der Exe starte, in der IDE den Cursor auf das Halt
hinter der Wait... stelle und mit F4 das Pragramm starte, bin ich
verzögerungsfrei an der Stelle. 5 Sekunden sollten doch deutlich spürbar
sein.
Er wartet _maximal_ 5 Sekunden. Wenn das Mutex beim Aufruf der
Wartefunktion bereits signalisiert ist, kehrt sie natürlich sofort
zurück.
--
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.
Jens Köhler
2012-02-26 07:39:36 UTC
Permalink
Post by Heiko Nocon
Post by Jens Köhler
das warten scheint so aber nicht zu funktionieren.
Er wartet _maximal_ 5 Sekunden. Wenn das Mutex beim Aufruf der
Wartefunktion bereits signalisiert ist, kehrt sie natürlich sofort
zurück.
so wie ich es verstanden habe, müßte doch folgendes passieren:

da die erste Instanz kein ReleaseMutex aufruft, sollte die 2. Instanz
doch eigentlich garnicht in den Besitz des Mutex gelangen können.
WaitForSingleObject müßte die 5 Sekunden warten und danach WAIT_TIMEOUT
zurückgeben. Damit würde der Code zwischen begin und end von
WaitForSingleObject (benachrichtigen der 1. Instanz) nie ausgeführt werden.

Da die Benachrichtigung aber erfolgt und sich das Programm
verzögerungsfrei beendet, verstehe ich den Sinn des Aufrufs
immer noch nicht.
Läßt sich das warten irgendwie provozieren? Ich habe schon alles
ausprobiert, was mir eingefallen ist, das Ergebnis ist immer
das gleiche.

Jens
Heiko Nocon
2012-02-26 10:36:13 UTC
Permalink
Post by Jens Köhler
da die erste Instanz kein ReleaseMutex aufruft, sollte die 2. Instanz
doch eigentlich garnicht in den Besitz des Mutex gelangen können.
Das gilt nur unter der Voraussetzung, daß die existierende Instanz
Besitzer des Mutex ist, es also mit InitialOwner=true erzeugt hat. Und
natürlich müssen beide Instanzen denselben Mutexnamen verwenden.
Post by Jens Köhler
WaitForSingleObject müßte die 5 Sekunden warten und danach WAIT_TIMEOUT
zurückgeben. Damit würde der Code zwischen begin und end von
WaitForSingleObject (benachrichtigen der 1. Instanz) nie ausgeführt werden.
Richtig, so ist das gedacht.
Post by Jens Köhler
Da die Benachrichtigung aber erfolgt und sich das Programm
verzögerungsfrei beendet
Wenn das wirklich so ist, dann stimmt irgendwas nicht am Code. Der
gepostete Code ist aber soweit korrekt (es fehlt nur die Prüfung, ob das
Mutex überhaupt erzeugt bzw. geöffnet werden konnte), ergo:
Es gibt Unterschiede zwischen gepostetem Code und tatsächlichem Code.
Und irgendwo in diesen Unterschieden muß der Fehler liegen.
--
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.
Jens Köhler
2012-02-26 20:20:03 UTC
Permalink
Post by Heiko Nocon
Wenn das wirklich so ist, dann stimmt irgendwas nicht am Code. Der
gepostete Code ist aber soweit korrekt (es fehlt nur die Prüfung, ob das
Es gibt Unterschiede zwischen gepostetem Code und tatsächlichem Code.
Und irgendwo in diesen Unterschieden muß der Fehler liegen.
hier die radikal reduzierte startfähige Unit, die das Verhalten zeigt.
Per Copy u. Paste aus Delphi übernommen.

unit Unit1;

interface

uses
Windows, Forms;

type
TForm1 = class(TForm)
private
public
end;

var
Form1 : TForm1;

implementation

{$R *.DFM}

const
MutexName = 'MutexTest';
var
Mutex : HWND;
x : integer;
initialization;

Mutex := CreateMutex(nil, True, MutexName);
if GetLastError = Error_Already_Exists then
begin
if WaitForSingleObject(Mutex, 5000) = Wait_Object_0 then
begin
x := 1;
end;
ReleaseMutex(Mutex);
Halt;
end;

finalization

end.

anstelle von x := 1; stand der Code zum suchen und benachrichtigen der
ersten Instanz. Ich erzeuge die Exe, kopiere sie im Explorer und starte
sie. Dann setze ich den Cursor auf x := 1 und starte mit F4 das Programm
zum 2. mal und lande verzögerungsfrei an der CursorPosition.

System: D5 und WinXP

Jens
Heiko Nocon
2012-02-26 22:23:22 UTC
Permalink
Post by Jens Köhler
hier die radikal reduzierte startfähige Unit, die das Verhalten zeigt.
[...]

Ich hab's hier gepasted und kann das Verhalten bestätigen, so wie du es
beschrieben hast.

Das einzige, was ich dazu auf die schnelle gefunden habe, war folgender
Satz aus dem MSDN:

"When using this technique(*), you should set the bInitialOwner flag to
FALSE; otherwise, it can be difficult to be certain which process has
initial ownership."

(*) Genau die hier verwendete Nutzung eines Mutex

Der Satz klingt für mich wie ein ziemlich hilfloses und verharmlosendes
Gestammel, welches eigentlich meint: Wie haben mal wieder Scheiße
gebaut, das Zeug funktioniert einfach nicht so, wie es sollte.

Das entspricht auch der Tatsache, daß frühere Versionen des MSDN die
fragliche Passage nicht enthalten, ganz sicher noch nicht die bei D5
mitgelieferte...

Sprich: Der Code muß umkonstruiert werden, um mal wieder einen
Workaround um einen erneuten Ausdruck von Microsofts grenzenloser
Unfähigkeit zu basteln. Etwa so:

Mutex := CreateMutex(nil, false, MutexName);
if Mutex<>0 then begin
case WaitForSingleObject(Mutex, 5000)
Wait_Object_0:
//"Mutex-Besitzer"->Jetzt kann's losgehen
//"und beim Beenden des Programmes irgendwann:
//ReleaseMutex(Mutex);
//CloseHandle(Mutex);
Wait_timeout:
begin
//"zweiter im Rennen"
x:=1;
//kein ReleaseMutex()
CloseHandle(Mutex);
end;
else
//Fehler
end;
end else
//Fehler

In der Hoffnung, daß wenigstens der Rest der Mutexfunktionalität auch
tatsächlich vorhanden ist und nicht nur von Microsoft behauptet wird...
--
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.
Jens Köhler
2012-02-27 14:50:29 UTC
Permalink
Post by Heiko Nocon
Sprich: Der Code muß umkonstruiert werden
Hallo,

mit Deiner Konstruktion funktioniert es.

Vielen Dank, auch den anderen, für die aufschlußreichen Antworten.

Viele Grüße
Jens

Loading...