[Win32] Find Dialog Window?

Pagina: 1
Acties:
  • 113 views sinds 30-01-2008
  • Reageer

  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
Ik heb een applicatie waar maar één instance van hoort te draaien. Als een tweede instance wordt gestart, moet het window van de eerste instance geactiveerd worden.
Nu is het probleem het vinden van dit window. Omdat het een dialoogvenster is, kan ik niet zoeken op class. Ik zoek nu op title, maar ik zou graag extra informatie in de title zetten, dus daar kan ik ook niet meer op zoeken.
Wat is de juiste manier om dit op te lossen?

C++:
1
2
3
4
5
6
7
8
CreateMutex(NULL, true, "9a6bfda6-7733-4b7d-92b0-3046c9191830");
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
    HWND hWnd = FindWindow(NULL, "XBT Client");
    ShowWindow(hWnd, SW_SHOWMAXIMIZED);
    SetForegroundWindow(hWnd);
    return false;
}

Verwijderd

Een mogelijke oplossing is het volgende:

C++:
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#define STR_MESSAGE L"OlafApp101"

//
//  Deze is idd global, je kan vast wel een mooiere oplossing verzinnen
//  
UINT 
    g_uiMyMessage = NULL;

// 
//  WinMain
//
MSG
    msgCurrent;
    
HANDLE
    hMutex = INVALID_HANDLE_VALUE;


g_uiMyMessage = RegisterWindowMessage( STR_MESSAGE );

hMutex = CreateMutex(NULL, true, "9a6bfda6-7733-4b7d-92b0-3046c9191830");
if( GetLastError() == ERROR_ALREADY_EXISTS )
{
    CloseHandle( hMutex ); // dit moest ook als CreateMutex failed met error_exists, dacht ik
    SendMessage( HWND_BROADCAST, uiMyMessage, NULL, NULL );
    return 1;
}

// 
//  .. andere code ...
//

while( GetMessage( &msgCurrent, NULL, NULL, NULL ) )
{
    TranslateMessage( &msgCurrent );
    DispatchMessage( &msgCurrent );
}

//  
//  Cleanup
//
CloseHandle( hMutex );
UnregisterWindowMessage( g_uiMyMessage );


//
//  In de WindowProc van je *toplevel* hoofdvenster  
//
switch( uMsg )
{
case WM_ ...:
break;

default:
    if( uMsg == g_uiMyMessage )
    {
        //
        //  Let op: onder win2K en hoger wordt het venster niet echt on top gezet maar de taskbarknop en titlebar zullen flashen om de aandacht te trekken
        //  Je eigen app on top 'forcen' vanuit dezelfde instance zelf gaat niet zonder meer
        //
        SetForegroundWindow( hWnd );
    }
}


Een andere mogelijkheid is een verborgen venster aan te maken met een unieke class en caption en daar dan je findwindow op te doen, hoewel ik die methode vrij vies vind. (er zijn al meer dan genoeg hidden windows op je systeem, je hoeft daar zelf geen bijdrage aan te leveren imo) Als je dan je window hebt, stuur je er een WM_USER (oid) naartoe, waarna de hidden window je dialog activeert vanuit de app zelf.

[ Voor 81% gewijzigd door Verwijderd op 02-01-2005 20:42 . Reden: Sample code ipv tekst ]


  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
Verwijderd schreef op zondag 02 januari 2005 @ 19:48:
Een mogelijke oplossing is het volgende:
Bedankt. Maar geeft dat onder Windows XP geen problemen omdat niet actieve apps niet zomaar zelf de focus op mogen eisen?

  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 10:16
Jawel, maar het nieuwe scherm hééft de focus en wanneer je dus die aan het hidden window geeft kan die toch weer het hoofdscherm van z'n eigen app activeren? Een andere mogelijkheid is, dunkt me, de handle van je mainwindow in shared memory te plaatsen waarmee je de setfocus vanuit de nieuwe instance aan kunt roepen.

Zie nu dat je niet de focus, maar een eigen message naar die hidden window wilde sturen. Weet ook niet of je wel de focus naar een hidden window kunt sturen...

[ Voor 23% gewijzigd door riezebosch op 03-01-2005 22:15 ]

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
riezebosch schreef op maandag 03 januari 2005 @ 22:13:
Jawel, maar het nieuwe scherm hééft de focus en wanneer je dus die aan het hidden window geeft kan die toch weer het hoofdscherm van z'n eigen app activeren? Een andere mogelijkheid is, dunkt me, de handle van je mainwindow in shared memory te plaatsen waarmee je de setfocus vanuit de nieuwe instance aan kunt roepen.

Zie nu dat je niet de focus, maar een eigen message naar die hidden window wilde sturen. Weet ook niet of je wel de focus naar een hidden window kunt sturen...
Dat bericht wordt verstuurd zodat het hidden window zelf de dialog app de focus kan geven.

Maar zo'n handle is toch alleen geldig binnen een process? Die kan ik toch niet zomaar in een ander proces gebruiken?

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 23:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

Je kunt toch gewoon een named event aanmaken vanuit de app? Als ie nog niet bestaat dan draait je app nog niet, en kun je naar die event gaan luisteren. Als ie wel bestaat dan draait je app dus al, en kun je de geopende event vanuit de nieuwe instance raisen zodat de al bestaande instance weet dat ie geactiveerd moet worden.

[ Voor 3% gewijzigd door .oisyn op 04-01-2005 11:53 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 10:16
OlafvdSpek schreef op maandag 03 januari 2005 @ 22:17:
[...]

Dat bericht wordt verstuurd zodat het hidden window zelf de dialog app de focus kan geven.

Maar zo'n handle is toch alleen geldig binnen een process? Die kan ik toch niet zomaar in een ander proces gebruiken?
Ja, maar het probleem in WinXP is dus juist dat alleen het actieve process windows naar de foreground mag duwen. Anders begint ie alleen maar te knipperen onderin de balk (zoals MSN). Of je moet weer truuks uit gaan halen met AttachThreadInput, maar dat lijkt me beetje overdone voor dit probleem.

Handles kan je ook zeer zeker vanuit andere processen gebruiken icm de API's van Windows. Niet alles is toegestaan als je niet in hetzelfde process zit (maar daar kan je dus wel weer in komen met AttachThreadInput), maar SetForeground is er één die wel kan.

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 23:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

OlafvdSpek schreef op maandag 03 januari 2005 @ 22:17:
Maar zo'n handle is toch alleen geldig binnen een process? Die kan ik toch niet zomaar in een ander proces gebruiken?
Window handles zijn global, itt de andere handles (je kunt wel handles transformeren naar een ander proces, dat doe je met (uit mijn hoofd) DuplicateHandle)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
.oisyn schreef op dinsdag 04 januari 2005 @ 11:52:
Je kunt toch gewoon een named event aanmaken vanuit de app? Als ie nog niet bestaat dan draait je app nog niet, en kun je naar die event gaan luisteren. Als ie wel bestaat dan draait je app dus al, en kun je de geopende event vanuit de nieuwe instance raisen zodat de al bestaande instance weet dat ie geactiveerd moet worden.
Dat is inderdaad een goede oplossing, bedankt.
Is het wachten op die event te combineren met de main event loop of heb ik daar een aparte thread voor nodig?

  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 10:16
OlafvdSpek schreef op dinsdag 04 januari 2005 @ 15:45:
[...]
oplossing van .oisyn
[...]

Dat is inderdaad een goede oplossing, bedankt.
Is het wachten op die event te combineren met de main event loop of heb ik daar een aparte thread voor nodig?
Ik kende 'm nog niet, maar vond 'm inderdaad ook als de beste oplossing klinken :)

[ Voor 9% gewijzigd door riezebosch op 05-01-2005 09:40 ]

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
.oisyn schreef op dinsdag 04 januari 2005 @ 11:52:
Je kunt toch gewoon een named event aanmaken vanuit de app? Als ie nog niet bestaat dan draait je app nog niet, en kun je naar die event gaan luisteren. Als ie wel bestaat dan draait je app dus al, en kun je de geopende event vanuit de nieuwe instance raisen zodat de al bestaande instance weet dat ie geactiveerd moet worden.
Hmm, nu ik dat nog een keer lees, twijfel ik eraan of het werkt. De bestaande instance weet dan wel dat die geactiveerd moet worden, maar verhindert Windows XP niet dat het venster op de voorgrond kotm?

  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 10:16
OlafvdSpek schreef op donderdag 27 januari 2005 @ 17:54:
[...]

Hmm, nu ik dat nog een keer lees, twijfel ik eraan of het werkt. De bestaande instance weet dan wel dat die geactiveerd moet worden, maar verhindert Windows XP niet dat het venster op de voorgrond kotm?
Uit je eerdere post maakte ik op dat het werkte... Anders moet je misschien toch mijn oplossing proberen riezebosch in "[Win32] Find Dialog Window?" Omdat dan je nieuwe, nog niet geïnitialiseerde, instance de foreground-thread is en dus wel een setForegroundWindow mag uitvoeren (ook op andermans handles). Uit eigen ervaring :)

Was idd ook wat direct in mij opkwam dat WinXP dat wel is kon gaan verhinderen, omdat met die named events het programma wat je naar de voorgrond wilt halen daar zelf helemaal geen rechten toe heeft.

Misschien dat een hidden window met een constante titel dan nog niet eens zo'n slecht idee is. In ieder geval wel stuk minder omslachtig.

[ Voor 24% gewijzigd door riezebosch op 27-01-2005 23:48 ]

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


Verwijderd

OlafvdSpek schreef op zondag 02 januari 2005 @ 16:15:
Ik heb een applicatie waar maar één instance van hoort te draaien. Als een tweede instance wordt gestart, moet het window van de eerste instance geactiveerd worden.
Nu is het probleem het vinden van dit window. Omdat het een dialoogvenster is, kan ik niet zoeken op class. Ik zoek nu op title, maar ik zou graag extra informatie in de title zetten, dus daar kan ik ook niet meer op zoeken.
Wat is de juiste manier om dit op te lossen?

C++:
1
2
3
4
5
6
7
8
CreateMutex(NULL, true, "9a6bfda6-7733-4b7d-92b0-3046c9191830");
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
    HWND hWnd = FindWindow(NULL, "XBT Client");
    ShowWindow(hWnd, SW_SHOWMAXIMIZED);
    SetForegroundWindow(hWnd);
    return false;
}
Kan je niet alle windows enumeraten en via het daarbij behorende process, de bijbehorende executable zoeken? Ik doe zoiets maar dan in Delphi (sorry voor dat :) ), zal eens ff een
linkje zoeken. Voor mij werkt het perfect, ik kan zelfs parameters etc doorsturen naar de 1e instance. hmz, zie net dat ik toch ook de findwindow gebruik, maar ik heb goede code gezien die op de door mij (boven) beschreven manier goed functioneert.

[ Voor 10% gewijzigd door Verwijderd op 27-01-2005 23:56 ]


  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 10:16
Ik moet ook nog steeds is te weten komen hoe je de bijbehorende executable van een gegeven process/thread achterhaald B) Maar klinkt idd ook als een goeie oplossing.

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
Nee, een check van de executable is ook geen (mooie) optie in verband met een andere executable na een upgrade (oude wordt hernoemd), of met debug/release/installed executables.

  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 10:16
Kan jij me wel aan de functie helpen dan die dit kan? Ik heb namelijk geen idee welke uit de user32 of kernel32 dit zou kunnen zijn.

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
Als je hProcess en hModule hebt kun je GetModuleFileNameEx() gebruiken. Maar ik weet niet waar je hModule vandaan kunt halen.

  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 10:16
hModule
[in] Handle to the module. If this parameter is NULL, GetModuleFileNameEx returns the path of the executable file of the process specified in hProcess.
;)

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 10:16
Misschien heb je er nog wat aan. Bedacht me dat de oplossing met shared memory helemaal niet zo ingewikkeld hoeft te zijn. Volgens mij moet dit ook prima op WinXP werken, omdat je vanuit de nieuwe foreground-thread de functie SetForegroundWindow aanroept.

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#pragma data_seg(".shared")
HWND hwnd=0;
#pragma data_seg()

int WINAPI _tWinMain(HINSTANCE hinstExe, HINSTANCE, PTSTR pszCmdLine, int)
{
   if (hwnd == 0)
   {
      // Create your new instance here and asign the handle to hwnd
      hwnd = ...
   }
   else
   {
      // Put running instance on the foreground
      SetForegroundWindow(hwnd);
   }
}

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 07-05 22:49

curry684

left part of the evil twins

Wat een moeilijke oplossingen allemaal....
C++:
1
2
3
4
5
6
7
int msg = RegisterWindowMessage("9a6bfda6-7733-4b7d-92b0-3046c9191830");
CreateMutex(NULL, true, "9a6bfda6-7733-4b7d-92b0-3046c9191830");
if (GetLastError() == ERROR_ALREADY_EXISTS)
{ 
  PostMessage(HWND_BROADCAST, msg, 0, 0);
  return false;
}

En later in je message loop dat ding netjes opvangen en jezelf naar de foreground kicken.

Al die onzin met events regelmatig moeten checken of shared memory is veel te complex :)

Professionele website nodig?


  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 10:16
Dat is dus het probleem in WinXP: alleen de foregroundthread mag zichzelf naar de foreground kicken... Anders krijg je, net als MSN, alleen maar een knipperende waarschuwing in de balk: "ik wil naar de foreground".

edit:

Hebben ze ook niet voor niks gedaan hoor. Het zou knap vervelend zijn als iedere applicatie zich op elk moment naar de foreground kon kicken. Dit is wel weer te omzeilen door met AttachThreadInput te werken, waarmee je net doet of je ook tot de foreground-thread behoort, maar dat is ingewikkelder dan de shared-mem oplossing.

[ Voor 46% gewijzigd door riezebosch op 01-02-2005 03:41 ]

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 10:16
Misschien dat het met LockSetForegroundWindow( LSFW_UNLOCK ) lukt. Dan kan een scherm zichzelf naar de voorgrond pushen?

Wel net zo netjes om ook daarna de LSFW_LOCK weer aan te roepen :P

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
riezebosch schreef op donderdag 10 maart 2005 @ 13:35:
Misschien dat het met LockSetForegroundWindow( LSFW_UNLOCK ) lukt. Dan kan een scherm zichzelf naar de voorgrond pushen?

Wel net zo netjes om ook daarna de LSFW_LOCK weer aan te roepen :P
> The foreground process can call the LockSetForegroundWindow

Daarmee kun je dus verhinderen dat background processes dat doen. Maar dat lijkt in XP automatisch aan te staan.

Ik heb het nu zo opgelost:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const extern UINT g_are_you_me_message_id = RegisterWindowMessage("XBT Client Are You Me Message");

BOOL CALLBACK CXBTClientApp::enumerator(HWND hWnd, LPARAM lParam)
{
    DWORD result;
    if (!SendMessageTimeout(hWnd, g_are_you_me_message_id, 0, 0, SMTO_BLOCK, 200, &result)
        || result != g_are_you_me_message_id)
        return true;
    *reinterpret_cast<HWND*>(lParam) = hWnd;
    return false;
}

long CXBTClientDlg::OnAreYouMe(WPARAM, LPARAM)
{
    return g_are_you_me_message_id;
}

HWND hWnd = NULL;
EnumWindows(enumerator, reinterpret_cast<LPARAM>(&hWnd));
ShowWindow(hWnd, SW_SHOWMAXIMIZED);
SetForegroundWindow(hWnd);

[ Voor 36% gewijzigd door Olaf van der Spek op 10-03-2005 14:49 ]

Pagina: 1