2016-05-25 34 views
8

Voglio trovare tutte le finestre di livello superiore (figli del desktop) sotto un determinato punto sul desktop. Non riesco a trovare un'API per questo.Trova tutte le finestre al di sotto di un punto

Il mio scenario è che sto trascinando una finestra sullo schermo e voglio rilasciarla in un'altra finestra (conosciuta). Posso colpire testare i limiti della finestra di destinazione ok, ma questo non mi dice se è occlusa da un'altra finestra (sconosciuta). L'utilizzo di WindowFromPoint e gli amici non funzioneranno, perché la finestra trascinata è necessariamente direttamente sotto il mouse. Quindi mi chiedo se posso ottenere tutte le finestre nella posizione del mouse e rivederle per vedere se una delle finestre che sto monitorando si trova direttamente sotto la finestra che sto trascinando.

C'è un modo per farlo senza ricorrere a EnumDesktopWindows/GetWindowRect su ogni trascinamento del mouse? O forse c'è un'altra soluzione che mi manca.

+1

Se si effettua l'ipotesi che non nuove finestre verranno creati mentre si sta trascinando, è possibile enumerare le finestre di primo livello Una volta a l'inizio dell'operazione di trascinamento, quindi utilizzare il risultato per verificare ogni mossa del mouse durante il trascinamento. –

+0

È un'ottimizzazione che vale la pena investigare, grazie. –

+0

Potresti riuscire a farla franca usando 'GetNextWindow' e guardando le finestre sopra il tuo obiettivo nell'ordine Z. – theB

risposta

4

Se si chiede gentilmente, WindowFromPoint ignorerà la finestra (quella attualmente trascinata) e restituirà la finestra successiva. Questo è ciò che fa Internet Explorer quando si trascina una scheda.

per farlo:

  1. Maniglia WM_NCHITTEST nella finestra trascinato
  2. ritorno HTTRANSPARENT durante il trascinamento. Chiama altrimenti la finestra predefinita proc.
  3. WindowFromPointwill ignoreHTTRANSPARENT windows, ma solo quelli che appartengono al thread chiamante. Questo non dovrebbe essere un problema per te, perché dovresti chiamare lo WindowFromPoint dal thread del proprietario della finestra.
  4. Assicurarsi che non vi siano finestre secondarie nel punto passato a WindowFromPoint o gestire WM_NCHITTEST anche per queste finestre secondarie.

guasti (se è ancora ottenere la finestra da WindowFromPoint)

  1. test GetCurrentThreadID() == GetWindowThreadProcessId(WindowFromPoint(), 0) per garantire che si sta chiamando dal thread corretto
  2. In WM_NCHITTEST, test che hwnd parametro è uguale a quello che si ottiene da WindowFromPoint()

Esempio (l'area all'interno rettangolo di restituisce la finestra sottostante da WindowFromPoint):

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 
{ 
    static const RECT s_TransparentRect = {100, 100, 200, 200}; 

    switch (message) 
    { 
    case WM_NCCREATE: 
     SetTimer(hWnd, 1, 100, 0); 
     break; 
    case WM_TIMER: 
     { 
      POINT cursorPos; 
      GetCursorPos(&cursorPos); 

      TCHAR buffer[256]; 
      _snwprintf_s(buffer, _countof(buffer), _TRUNCATE, _T("WindowFromPoint: %08X\n"), (int)WindowFromPoint(cursorPos)); 
      SetWindowText(hWnd, buffer); 
     } 
     break; 
    case WM_PAINT: 
     { 
      PAINTSTRUCT ps; 
      BeginPaint(hWnd, &ps); 
      Rectangle(ps.hdc, s_TransparentRect.left, s_TransparentRect.top, s_TransparentRect.right, s_TransparentRect.bottom); 
      EndPaint(hWnd, &ps); 
     } 
     break; 
    case WM_NCHITTEST: 
     { 
      POINT cursorPos; 
      GetCursorPos(&cursorPos); 
      MapWindowPoints(HWND_DESKTOP, hWnd, &cursorPos, 1); 

      if (PtInRect(&s_TransparentRect, cursorPos)) 
       return HTTRANSPARENT; 
     } 
     break; 
    } 

    return DefWindowProc(hWnd, message, wParam, lParam); 
} 
+0

Grazie mille. Ciò promette di evitare chiamate API ripetute (lineari in numero di finestre di primo livello). Sperimenterai e tornerai da te. –

+0

Grazie ancora. Con alcuni esperimenti questo si è rivelato abbastanza bene. –

+0

Hai bisogno di ulteriori passaggi che non ho menzionato? – Codeguard

3

Giusto, sai già cosa restituirà WindowFromPoint(), dovrebbe essere quello che stai trascinando. Quindi utilizzare GetWindow() con uCmd = GW_HWNDNEXT per ottenere quello sotto nell'ordine Z. GetWindowRect() per ottenere i suoi limiti, IntersectRect() per calcolare la sovrapposizione.

Continuare a chiamare GetWindow() per trovare più finestre che potrebbero essere sovrapposte. Fino a quando non restituisce NULL o la sovrapposizione è abbastanza buona. In caso contrario, normalmente preferirai quello che ha il rettangolo di risultato più grande da IntersectRect().

+0

Lo ha preso per un giro e ha scoperto che il successo del test del rettangolo non è sufficiente. Ci sono un sacco di finestre sul desktop che vengono rilevate, anche se non sono visibili dal punto di vista dell'utente. Ad esempio, appare una finestra con il titolo 'Finestra frame AXWIN', con stili (ex) abbastanza innocenti. C'è qualche altro mezzo per testare la visibilità? Anche il test del rettangolo può fallire per le finestre a più livelli. –

+0

Aggiungi un test su IsWindowVisible() –

+0

Grazie, sembra funzionare bene. La scansione sulla mia macchina funziona al di sotto del millisecondo, il che è fantastico. Ancora non riesce per le finestre a strati con regioni trasparenti. –