Opmerking
Gebaseerd op deze code heb ik een unit geschreven die het makkelijk maakt om deze code te hergebruiken, en tevens de parameters van de 2e instantie door te geven aan de 1e. Zie deze post voor de uitleg en een link naar de unit...
Omschrijving
Het gebeurt vaak dat je een applicatie maar eenmalig wilt draaien, en zodra de applicatie een tweede keer wordt opgestart dat de vorige instantie dan naar de voorgrond komt.
Er zijn vele oplossingen, zoals FindWindow of een Mutex. Bij FindWindow is het probleem dat het te makkelijk is om conflicten te krijgen, en alhoewel ik een tijd een fan ben geweest van een Mutex zit de broadcast message die er vaak mee gepaard gaat me toch niet lekker. In plaats daarvan heb ik voor Pegasus gekozen voor een Memory Mapped File. Bij het aanmaken kan je controleren of de MMF al bestaat, je kan er data in opslaan (in dit geval de window handle van het form) en mocht je applicatie crashen dan zal Windows de MMF samen met je proces toch netjes afsluiten. Geen RegisterWindowMessage of een broadcast meer nodig, alleen jouw venster zal het doelwit worden van de message.
Code
Allereerst, ga naar Project -> View Source en knutsel erin totdat het er zo uit ziet (met hooguit een andere naam voor je main form uiteraard):
Code:
uses
Windows,
Forms,
FMain in 'Forms\FMain.pas';
const
// Zorg dat deze uniek is! Het maakt niet uit hoe je 'm noemt, maar
// als 2 applicaties dezelfde naam gebruiken kan er maar 1 tegelijk
// van starten... kies 'm dus zorgvuldig!
CFileMapping = 'MyApp.Instance';
var
hMapping: THandle;
wMap: ^THandle;
begin
// Controleer of er al een instantie van de applicatie draait
hMapping := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0,
SizeOf(THandle), CFileMapping);
if GetLastError() = ERROR_ALREADY_EXISTS then begin
wMap := MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
if wMap^ <> 0 then
PostMessage(wMap^, WM_INSTANCE, 0, 0);
UnmapViewOfFile(wMap);
CloseHandle(hMapping);
exit;
end else
wMap := MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, 0);
Application.Initialize;
Application.CreateForm(TfrmMain, frmMain);
// Doe dit na het creeren van het form en voor Application.Run
wMap^ := frmMain.Handle;
Application.Run;
// Sluit alles netjes af
UnmapViewOfFile(wMap);
CloseHandle(hMapping);
end;
Als laatste stap gaan we sleutelen aan het form welke de message binnen krijgt:
Code:
interface
uses
Messages,
Forms;
const
// Ervan uitgaande dat WM_USER + $1111 nog geen waarde heeft binnen
// je applicatie, hier kan je uiteraard elke waarde boven WM_USER van maken.
// Eronder is sterk afgeraden, daar zitten al standaard Windows messages.
WM_INSTANCE = WM_USER + $1111;
type
TfrmMain = class(TForm)
protected
procedure WMInstance(var Msg: TMessage); message WM_INSTANCE;
end;
implementation
procedure TfrmMain.WMInstance;
begin
// Hier kan je elke code uitvoeren die je wilt, in dit geval halen we
// de applicatie naar de voorgrond...
Application.Restore();
Application.BringToFront();
end;
En dat was 't!
Bookmarks