Blog literacki, portal erotyczny - seks i humor nie z tej ziemi
API
19.08.1999
Blokowanie możliwości kilkukrotnego uruchomienia programu w tym samym czasie.
Jest kilka sposobów na zablokowanie możliwości kilkukrotnego uruchomienia programu w tym samym czasie. Jednym z nich (najprostszym) jest użycie funkcji API FindWindow. Bardziej skomplikowane to użycie muteksów /mutex/ i semaforów /semaphore/. Wszystkie metody mają wspólną cechę - wykrywają obecność uruchomionego programu w funkcji WinMain i jeżeli znajdą go, to powracają z metody WinMain bez wywołania funkcji Run TApplication.
FindWindow
Funkcja ta poszukuje uchwytu okna bazując na tekście z paska tytułowego, bądź zarejestrowanej klasy WndClass.
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
//poszukuje uruchomień programu poprzez sprawdzenie
//tytułów aplikacji. aby tego dokonać musimy
//tymczasowo zmienić tytuł, tak, żeby funkcja FindWindow
//nie znalazła programu
Application->Title = "";
HWND hPrevApp = ::FindWindow(NULL, "single");
// FindWindow zwraca niezerowy HWND jeżeli znajdzie okno
// jeżeli okno znaleziono, to przywraca je
// i nie uruchami ponownie
if(hPrevApp)
{
PostMessage(hPrevApp, WM_SYSCOMMAND, SC_RESTORE, 0);
return 0;
}
// jeżeli nie - przywraca tytuł i uruchamia aplikację
else
Application->Title = "single";
try
{
Application->Initialize();
Application->CreateForm(__classid(TForm1), &Form1);
Application->Run();
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
return 0;
}
CreateMutex
Funkcja FindWindow jest prosta, ale ma kilka wad. Po pierwsze, zakłada ona, że nie zmienisz tytułu programu. Po drugie, jeżeli użytkownik kliknie kilkukrotnie szybko na ikonę programu, to może się on uruchomić dwa razy, ponieważ program nie zdąży sprawdzić czy został już uruchomiony drugi.
CreateMutex zapewnia o wiele lepszy sposób detekcji programu. Muteksy to widoczne w całym systemie obiekty. Jeżeli program A stworzy muteks, to program B może go zobaczyć. Muteksy mają swe nazwy; może istnieć tylko jeden muteks o danej nazwie. Jeżeli stworzyłem muteks "Mój wyjątkowy Muteks" to żaden inny program nie może już takiego stworzyć.
W celu użycia CreateMutex musisz spróbować utworzyć muteks po rozpoczęciu procedury WinMain. Jeżeli to się nie uda, oznacza to, że program został już raz uruchomiony. Oto przykładowy kod wykorzystujący CreateMutex:
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
// Próba tworzenia muteksu. Jeśli ten istnieje,
// GetLastError zwróci ERROR_ALREADY_EXISTS
HANDLE hInstanceMutex = ::CreateMutex(NULL,TRUE,
"PROJECT1");
if(GetLastError() == ERROR_ALREADY_EXISTS)
{
if(hInstanceMutex)
CloseHandle(hInstanceMutex);
return 0;
}
try
{
Application->Initialize();
Application->CreateForm(__classid(TForm1), &Form1);
Application->Run();
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
ReleaseMutex(hInstanceMutex);
CloseHandle(hInstanceMutex);
return 0;
}
System operacyjny serializuje wywołania CreateMutex, więc muteks nie może "umknąć" programowi, tak jak to było w przypadku FindWindow. System dba także oto, żeby w przypadku zawieszenia programu został zwolniony muteks.
Może się zdarzyć, że będziesz chciał przywrócić okno programu po jego ponownym wywołaniu. Poniższy kod demonstruje w jaki sposób dokonać tego używając CreateMutex i FindWindow. CreateMutex dba o to, by nie uruchamiać programu ponownie, a FindWindow daje uchwyt do okna, do którego wysyłamy komunikat powrotu.
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
HANDLE hInstanceMutex = ::CreateMutex(NULL,TRUE,
"SINGLE.MUTEX");
if(GetLastError() == ERROR_ALREADY_EXISTS)
{
if(hInstanceMutex)
CloseHandle(hInstanceMutex);
Application->Title = "";
HWND hPrevApp = ::FindWindow(NULL, "Form1");
if(hPrevApp)
::ShowWindow(hPrevApp, SW_RESTORE);
SetForegroundWindow(hPrevApp);
return 0;
}
try
{
Application->Initialize();
Application->CreateForm(__classid(TForm1), &Form1);
Application->Run();
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
ReleaseMutex(hInstanceMutex);
CloseHandle(hInstanceMutex);
return 0;
}
CreateSemaphore
Poniższy kod pokazuje jak użyć semaforów w celu limitowania jednoczesnych uruchomień programu. Ten kod limituje uruchomienia do pięciu na raz.
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
// Próbuje stworzyć semafor, jeżeli już jest
// system zwróci poprzedni, bez tworzenia nowego
// Uwaga: Wartość semafora zostanie zmniejszona
// przy każdym wywołaniu WaitForSingeObject
// trzeci argument określa
// maksymalną liczbę uruchomień. Drugi argument
// jest początkową wartością semafora.
HANDLE hSemaphore = ::CreateSemaphore(NULL,5,5,
"SINGLE.SEMAPHORE");
// kiedy WaitForSingleObject zwróci WAIT_OBJECT_O,
// oznacza to, że wartość semafora wyzerowała się
if(hSemaphore && WaitForSingleObject(hSemaphore, 0)
!= WAIT_OBJECT_0)
{
CloseHandle(hSemaphore);
ShowMessage("Szef mówi, że możesz uruchomić tylko 5 na raz!");
return 0;
}
try
{
Application->Initialize();
Application->CreateForm(__classid(TForm1), &Form1);
Application->Run();
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
ReleaseSemaphore(hSemaphore,1,NULL);
CloseHandle(hSemaphore);
return 0;
}