Entwickler-Ecke

Windows API - Fensterhandle finden / SingleInstance / SetForeGroundWindow


Singlepin - Do 14.08.14 19:15
Titel: Fensterhandle finden / SingleInstance / SetForeGroundWindow
Hallo Gemeinde,

mein Problem ist folgendes, um den doppelten Programmstart zu verhindern hatte ich bisher folgendes.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
var
  Flash: FLASHWINFO;
Initialization

hFMapping:=CreateFileMapping(INVALID_HANDLE_VALUE,NIL,PAGE_READWRITE,
                            0,sizeof(Application.Handle),
                            appName);
if hFMapping<>0
 then
  begin
  if GetLastError=ERROR_ALREADY_EXISTS
   then
    begin
    FSpeicher:=MapViewOfFile(hFMapping,FILE_MAP_READ,0,0,0);
    FHandle:=FSpeicher^;
    UnmapViewOfFile(FSpeicher);
    Windows.ShowWindow(FHandle,SW_Normal); // Fenster auf Normal
    Windows.SetForegroundWindow(FHandle);  // in den Vordergrund bringen
    halt;
    end
   else
    begin
    FSpeicher:=MapViewOfFile(hFMapping,FILE_MAP_WRITE,0,0,0);
    FSpeicher^:=Application.Handle;
    UnmapViewOfFile(FSpeicher);
    end;
  end
 else
  begin
  Application.MessageBox('Fehler beim Erstellen des Mapping','Abbruch',MB_OK);
  Halt;
  end;

Mit Win7 geht

Delphi-Quelltext
1:
2:
    Windows.ShowWindow(FHandle,SW_Normal); // Fenster auf Normal
    Windows.SetForegroundWindow(FHandle);  // in den Vordergrund bringen

nicht mehr.

Dafür möchte ich jetzt, daß das Programm in der Taskleiste blinkt.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
var
  Flash: FLASHWINFO;
Initialization

hFMapping:=CreateFileMapping(INVALID_HANDLE_VALUE,NIL,PAGE_READWRITE,
                            0,sizeof(Application.Handle),
                            appName);
if hFMapping<>0
 then
  begin
  if GetLastError=ERROR_ALREADY_EXISTS
   then
    begin
    FSpeicher:=MapViewOfFile(hFMapping,FILE_MAP_READ,0,0,0);
//    FHandle:=FSpeicher^;
    UnmapViewOfFile(FSpeicher);
    FHandle:=FindWindow(NIL,PChar('Programmname'));
    FillChar(Flash, SizeOf(Flash), 0);
    Flash.cbSize:=SizeOf(Flash);
    Flash.hwnd:=FHandle;
    Flash.uCount:=5;
    Flash.dwTimeOut:=2000;
    Flash.dwFlags:=FLASHW_ALL;
    FlashWindowEx(Flash);
//    Windows.ShowWindow(FHandle,SW_Normal); // Fenster auf Normal
//    Windows.SetForegroundWindow(FHandle);  // in den Vordergrund bringen
    halt;
    end
   else
    begin
    FSpeicher:=MapViewOfFile(hFMapping,FILE_MAP_WRITE,0,0,0);
    FSpeicher^:=Application.Handle;
    UnmapViewOfFile(FSpeicher);
    end;
  end
 else
  begin
  Application.MessageBox('Fehler beim Erstellen des Mapping','Abbruch',MB_OK);
  Halt;
  end;

Das geht zwar, aber mich stört

Delphi-Quelltext
1:
    FHandle:=FindWindow(NIL,PChar('Programmname'));                    


Wie komme ich zum richtigen Handle?

Moderiert von user profile iconNarses: Titel erweitert.
Moderiert von user profile iconNarses: Topic aus Sonstiges (Delphi) verschoben am Fr 15.08.2014 um 09:38


jaenicke - Do 14.08.14 19:45

Das einfachste ist ein Broadcast an eine von allen Instanzen mit RegisterWindowMessage erstellten Botschaft. Die kann dann die erste Instanz auswerten und sich selbst um die Anzeige kümmern. Oder diese könnte auch einfach z.B. einen Balloon Hint auf dem Tray Icon anzeigen oder so, sprich selbst viel mehr machen als nur blinken lassen.
Und du kannst auch weitere Infos übergeben (Parameter, ...).

An das Fensterhandle kannst du (wenn du es doch damit machen möchtest) kommen, indem du nicht anhand des Fenstertitels vorgehst, sondern über den Klassennamen gehst. Der sollte natürlich dann gut gewählt sein, sprich eindeutig.

Nebenbei:
Statt das in Initialization zu machen und dann mit Halt das Programm gegen einen Baum zu fahren kannst du auch einfach im Projektquelltext das Application.Run nur ausführen, wenn nicht schon eine Instanz lief...
Das ist viel eleganter und sauberer.


Singlepin - Do 14.08.14 20:18

Danke jaenicke
für die schnelle Antwort.
Die Anregungen werde ich mal durchgehen.
In diesem Fall dachte ich, das ich über das Applikatinshande das Fensterhandle ermitteln kann und fertig.
Die Frage ist wie.


Delete - Do 14.08.14 20:35

user profile iconSinglepin hat folgendes geschrieben Zum zitierten Posting springen:

...
Das geht zwar, aber mich stört

Delphi-Quelltext
1:
    FHandle:=FindWindow(NIL,PChar('Programmname'));                    


Wie komme ich zum richtigen Handle?



Delphi-Quelltext
1:
   FHandle:= FindWindow( Pchar(string(TForm2.Classname)), Nil );                    


jaenicke - Do 14.08.14 22:58

user profile iconhathor hat folgendes geschrieben Zum zitierten Posting springen:

Delphi-Quelltext
1:
   FHandle:=Windows.FindWindow(NIL,PChar(UpperCase(ExtractFileName(ParamStr(0)))));                    
Was hat der Dateiname der Exe mit dem Fenstertitel zu tun? :gruebel:


mandras - Fr 15.08.14 09:16

Ich habe da was aus meiner Mottenkiste ausgegraben um unter Win7 ein Fenster in den Vordergrund zu bekommen:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
procedure WinToTop (Handle: hWnd);
var ThreadID1, ThreadID2: integer;
begin
 if Handle = GetForeGroundWindow then exit;
 ThreadID1 := GetWindowThreadProcessID (GetForeGroundWindow, Nil);
 ThreadID2 := GetWindowThreadProcessID (Handle, Nil);
 if ThreadID1 <> ThreadID2 then begin
  AttachThreadInput (ThreadID1, ThreadID2, true);
  SetForeGroundWindow (Handle);
  AttachThreadInput (ThreadID1, ThreadID2, false);
 end else
  SetForeGroundWindow (Handle);
 if IsIconic (Handle) then ShowWindow (handle, sw_restore) else
  ShowWindow (handle, sw_Show);
end;



Vielleicht hilft es ja


Delete - So 17.08.14 00:17

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconhathor hat folgendes geschrieben Zum zitierten Posting springen:

Delphi-Quelltext
1:
   FHandle:=Windows.FindWindow(NIL,PChar(UpperCase(ExtractFileName(ParamStr(0)))));                    
Was hat der Dateiname der Exe mit dem Fenstertitel zu tun? :gruebel:


Ich habe es oben geändert in:


Delphi-Quelltext
1:
 FHandle:= FindWindow( Pchar(string(TForm1.Classname)), Nil );                    


Singlepin - Fr 22.08.14 15:28

Ich bin einen Schritt weiter.

mit

Delphi-Quelltext
1:
2:
3:
4:
    FHandle:=FindWindow(NIL,PChar('Artikel suche'));
    AllowSetForegroundWindow(FHandle);
    Windows.ShowWindow(FHandle,SW_Normal); // Fenster auf Normal
    Windows.SetForegroundWindow(FHandle);  // in den Vordergrund bringen


bekomme ich meine Anwendung in den Vordergrund.
Somit besteht mein Problem nur noch darin, daß ich mit

Delphi-Quelltext
1:
    FSpeicher^:=Application.Handle;                    

seit Windows 7 nicht mehr das richtige Handle in meinem MemoryMappedFile speichere.
Wie schon gesagt ich möchte nicht mit FindWindow nach dem "Falschen" suchen sondern das richtige Handle einfach auslesen.


jaenicke - Fr 22.08.14 16:00

Das Application-Handle ist (bis Delphi 2007 oder mit MainFormOnTaskbar auf False) das Fenster, das den Taskleistenknopf anzeigt. Das hat aber eigentlich nichts mit dem angezeigten Fenster zu tun. Meinst du vielleicht Application.MainForm.Handle?


Singlepin - Fr 22.08.14 16:47

Hallo Jaenicke,

bei Application.MainForm.Handle gibt es einen Laufzeitfehler, aber Application.MainFormHandle ist die Lösung.
Nun muß ich das Ganze nur noch richtig verstehen.

Danke für die Hilfe.


jaenicke - Fr 22.08.14 18:41

user profile iconSinglepin hat folgendes geschrieben Zum zitierten Posting springen:
bei Application.MainForm.Handle gibt es einen Laufzeitfehler
Dann gibt es in dem Moment noch kein Hauptformular, sprich es ist noch nicht fertig erzeugt.