Delphi-PRAXiS
Seite 2 von 2     12   

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi 1. Instanz nach vorn bringen, wenn 2. geöffnet wird - fensterunabhängig! (https://www.delphipraxis.net/173567-1-instanz-nach-vorn-bringen-wenn-2-geoeffnet-wird-fensterunabhaengig.html)

RSE 6. Mär 2013 09:31

AW: 1. Instanz nach vorn bringen, wenn 2. geöffnet wird - fensterunabhängig!
 
Also auf zum nächsten Versuch:

Ich werde die Pipe wieder gegen einen Mutex ersetzen, um herauszubekommen, ob die aktuelle Instanz die einzige ist. Die Pipe ist, so wie ich sie momentan im Code habe, sowieso ziemlich zweckentfremdet. Wenn ich nun eine weitere Instanz finde (= der Mutex bereits reserviert/in Benutzung ist), dann sende ich mit BroadcastSystemMessage eine Message an alle Applications, die ein Antwort-Handle enthält. Die 1. Instanz reagiert darauf und schickt eine Message an das Antworthandle, welches die Prozess-ID enthält. Der Rest läuft dann wie bisher. Vielleicht finde ich auch noch Optimierungspotenzial - habe da eine Option BSF_ALLOWSFW gesehen.

Ich poste auf alle Fälle den finalen Code hier.

@ASM: Danke für die Idee mit den Broadcast-Messages!

@CCRDude: Das sind gute Hinweise, die du da bringst. In meinem Fall geht es allerdings nicht um das Öffnen von Dateien, sondern um ein reines Arbeitswerkzeug, das mit Daten aus einer Datenbank versorgt wird. Wenn dieses Programm mit dem selben Rechnernamen mehrfach ausgeführt wird, dann kommt die Organisation in diversen Tabellen durcheinander. Außerdem wird das Programm nur firmenintern eingesetzt, und da kann man schon einige Annahmen über den Einsatz machen ;-) Zumindest soweit, dass man keine Klimmzüge macht, um Fälle abzudecken, die sowieso nicht vorkommen werden. Der Admin wird sich z.B. nie im Remotezugriff auf einem Rechner selbst (zusätzlich) einloggen und dort dieses Programm starten. Bei Programmen, die die Firma verlassen würden, müsste man deine Hinweise natürlich beachten, keine Frage!

Uwe Raabe 6. Mär 2013 09:49

AW: 1. Instanz nach vorn bringen, wenn 2. geöffnet wird - fensterunabhängig!
 
Sidenote: Du kannst dir ja mal die madCollection und dort die Processes ansehen. Macht die Sache leichter.

RSE 6. Mär 2013 10:47

AW: 1. Instanz nach vorn bringen, wenn 2. geöffnet wird - fensterunabhängig!
 
@Uwe Raabe: Könntest du mir noch einen Hinweis geben, was genau ich mir dort ansehen sollte? Bei einem groben Überfliegen ist mir noch nichts speziell aufgefallen.

RSE 6. Mär 2013 12:20

AW: 1. Instanz nach vorn bringen, wenn 2. geöffnet wird - fensterunabhängig!
 
Es klappt! Endlich! Folgender Code hat gewonnen:
Delphi-Quellcode:
const
  ProgUID = 'geheim :-p';
  BSF_ALLOWSFW = $00000080; // fehlt in Unit Windows neben z.B. BSF_QUERY

var
  s: string;
  HMutex: THandle = 0;
  PBroadcastRecipients: PDWORD;
  WM_CCCSingleInstanceBroadcast: UINT = 0;

function TIrgendeineKlasse.SingleInstanceBroadcastReceiver(var m: TMessage): Boolean; // muss zwecks Akzeptanz durch Application.HookMainWindow als Methode einer Klasse implementiert sein
begin
  Result := False;
  if m.Msg = WM_CCCSingleInstanceBroadcast then
    Application.BringToFront;
end;

initialization

  WM_CCCSingleInstanceBroadcast := RegisterWindowMessage(ProgUID); // eindeutige Message-ID holen
  HMutex := CreateMutex(nil, True, ProgUID);
  if (HMutex = 0) or (GetLastError = ERROR_ALREADY_EXISTS) or
     (GetLastError = ERROR_ACCESS_DENIED) then
  begin
    // dieser Code läuft nur in der 2. Instanz des Programms
    New(PBroadcastRecipients);
    try
      PBroadcastRecipients^ := BSM_APPLICATIONS;
      BroadcastSystemMessage(BSF_ALLOWSFW or BSF_IGNORECURRENTTASK or
        BSF_POSTMESSAGE, PBroadcastRecipients, WM_CCCSingleInstanceBroadcast, 0, 0); // an alle: hier ist noch einer
      SwitchToThread; // Rest der Zeitscheibe verwerfen, damit die Instanz sich nach vorn bringen kann (falls das durch Race-Condition nicht klappt: Pech gehabt)
    finally
      Dispose(PBroadcastRecipients);
    end;
    MessageBox(0, 'Das Programm läuft bereits', '',
      MB_SYSTEMMODAL or MB_SETFOREGROUND or MB_TOPMOST); // über die erste Instanz legen
    Halt;
  end;
  Application.HookMainWindow(TIrgendeineKlasse.SingleInstanceBroadcastReceiver); // Klassenmethode sollte gehen - dieser Teil ist bei mir ganz anders umgesetzt

finalization

  if HMutex > 0 then
    CloseHandle(HMutex);

end.

Uwe Raabe 6. Mär 2013 12:31

AW: 1. Instanz nach vorn bringen, wenn 2. geöffnet wird - fensterunabhängig!
 
Zitat:

Zitat von RSE (Beitrag 1206080)
@Uwe Raabe: Könntest du mir noch einen Hinweis geben, was genau ich mir dort ansehen sollte? Bei einem groben Überfliegen ist mir noch nichts speziell aufgefallen.

Nichts Spezielles - es macht lediglich das Arbeiten mit den Kernel-Objekten leichter.

Uwe Raabe 6. Mär 2013 12:41

AW: 1. Instanz nach vorn bringen, wenn 2. geöffnet wird - fensterunabhängig!
 
Zitat:

Zitat von RSE (Beitrag 1206109)
Es klappt!

Hier nicht! Die Instanz kommt nicht in den Vordergrund - es blinkt lediglich das Taskbar-Icon.

Dieser Code funktioniert hier allerdings ganz gut:

Delphi-Quellcode:
uses
  madKernel;

procedure CheckFirstInstance;
var
  prcs: IProcesses;
begin
  prcs := Processes(CurrentProcess.ExeFile);
  if prcs.ItemCount > 1 then begin
    prcs[0].Windows_[0].BringToForeground();
    Halt;
  end;
end;

initialization
  CheckFirstInstance;
end.
Ich würde allerdings trotzdem den Mutex als Sentinel verwenden, und die Indizierung auf 0 in den Listen muss auch nicht immer richtig sein.

Der Knackpunkt ist hier, daß die zweite Instanz hier die erste in den Vordergrund bringt und nicht die erste sich selbst. Das Privileg, das ForegroundWindow zu ändern hat nämlich nicht jeder. Der gerade im Vordergrund liegende Prozess allerdings schon.

RSE 6. Mär 2013 14:20

AW: 1. Instanz nach vorn bringen, wenn 2. geöffnet wird - fensterunabhängig!
 
@Uwe Raabe: Ich war davon ausgegangen, dass das während der Verarbeitung der Message mit BSF_ALLOWSFW erlaubt sei, denn genau so ist diese Option definiert. Damit ist sie also komplett sinnfrei. Darf ich fragen, auf welcher Windows-Version du getestet hast? Bei mir läuft es unter Win7 32bit und 64bit gleichermaßen wie gewünscht.

Ich werde also nochmals umbauen (never ending story...) und der zweiten Instanz antworten, welches Fenster sie in den Vordergrund holen soll.

RSE 6. Mär 2013 15:11

AW: 1. Instanz nach vorn bringen, wenn 2. geöffnet wird - fensterunabhängig!
 
Letzter Stand (2. Instanz bringt die erste nach vorn):
Delphi-Quellcode:
const
  ProgUID = 'geheim :-p';
  WM_AnswerToSecondInstance = WM_USER + 1;

var
  HMutex: THandle = 0;
  PBroadcastRecipients: PDWORD;
  WM_CCCSingleInstanceBroadcast: UINT = 0;

function TIrgendeineKlasse.SingleInstanceBroadcastReceiver(var m: TMessage): Boolean; // muss zwecks Akzeptanz durch Application.HookMainWindow als Methode einer Klasse implementiert sein
var
  LHandle: HWnd;
  TopWindow: HWnd;
begin
  Result := False;
  if m.Msg = WM_CCCSingleInstanceBroadcast then
  begin
    // Logic copied from Application.BringToFront
    if Application.MainFormOnTaskBar and (Application.MainForm <> nil) then
      LHandle := Application.MainForm.Handle
    else
      LHandle := Application.Handle;
    if LHandle <> 0 then
    begin
      TopWindow := GetLastActivePopup(LHandle);
      if (TopWindow <> 0) and (TopWindow <> Application.Handle) and
        IsWindowVisible(TopWindow) and IsWindowEnabled(TopWindow) then
        PostMessage(m.WParam, m.LParam, TopWindow, 0);
    end;
  end;
end;

function TIrgendeineKlasse.SecondInstanceReceiver(var m: TMessage): Boolean; // muss zwecks Akzeptanz durch Application.HookMainWindow als Methode einer Klasse implementiert sein
begin
  Result := False;
  case m.Msg of
    WM_AnswerToSecondInstance:
      SetForegroundWindow(m.WParam);
  end;
end;

initialization

  WM_CCCSingleInstanceBroadcast := RegisterWindowMessage(ProgUID); // eindeutige Message-ID holen
  HMutex := CreateMutex(nil, True, ProgUID);
  if (HMutex = 0) or (GetLastError = ERROR_ALREADY_EXISTS) or
     (GetLastError = ERROR_ACCESS_DENIED) then
  begin
    // dieser Code läuft nur in der 2. Instanz des Programms
    Application.HookMainWindow(TIrgendeineKlasse.SecondInstanceReceiver); // Klassenmethode sollte gehen - dieser Teil ist bei mir ganz anders umgesetzt
    New(PBroadcastRecipients);
    try
      PBroadcastRecipients^ := BSM_APPLICATIONS;
      BroadcastSystemMessage(BSF_IGNORECURRENTTASK, PBroadcastRecipients,
        WM_CCCSingleInstanceBroadcast, MessageDistributor.Handle,
        WM_AnswerToSecondInstance); // an alle: hier ist noch einer
    finally
      Dispose(PBroadcastRecipients);
    end;
    SwitchToThread; // Rest der Zeitscheibe verwerfen, damit die 1. Instanz antworten kann (falls das durch Race-Condition nicht klappt: Pech gehabt)
    Application.ProcessMessages; // Antwort verarbeiten
    MessageBox(0, 'Das Programm läuft bereits', '',
      MB_SYSTEMMODAL or MB_SETFOREGROUND or MB_TOPMOST); // über die erste Instanz legen
    Halt;
  end;
  Application.HookMainWindow(TIrgendeineKlasse.SingleInstanceBroadcastReceiver); // Klassenmethode sollte gehen - dieser Teil ist bei mir ganz anders umgesetzt

finalization

  if HMutex > 0 then
    CloseHandle(HMutex);

end.
Mann, ist das viel Code geworden, aber letztlich steckt ja gar nicht so viel dahinter...

Uwe Raabe 6. Mär 2013 15:55

AW: 1. Instanz nach vorn bringen, wenn 2. geöffnet wird - fensterunabhängig!
 
Zitat:

Zitat von RSE (Beitrag 1206162)
@Uwe Raabe: Ich war davon ausgegangen, dass das während der Verarbeitung der Message mit BSF_ALLOWSFW erlaubt sei, denn genau so ist diese Option definiert. Damit ist sie also komplett sinnfrei. Darf ich fragen, auf welcher Windows-Version du getestet hast? Bei mir läuft es unter Win7 32bit und 64bit gleichermaßen wie gewünscht.

Win7 x64 - ganz einfach getestet:

- neue VCL-Anwendung
- deinen Code in die Unit aufgenommen
- noch 'ne Dummy-Klasse mit der entsprechenden Klassenmethode dazu
- compiliert
- aus dem Explorer 1. Instanz gestartet
- Delphi aktiviert (1. Instanz verliert den Focus und wird verdeckt)
- aus dem Explorer die 2. Instanz gestartet

exp: 1. Instanz kommt nach vorn
act: 1. Instanz bleibt verdeckt, aber Taskbar-Icon blinkt


Alle Zeitangaben in WEZ +1. Es ist jetzt 04:44 Uhr.
Seite 2 von 2     12   

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz