2010-05-21 28 views
16

Sto implementando una piccola applicazione (observer) che deve "allegare" sé stessa alla fine di un'altra finestra (osservata). Quest'ultimo non è una finestra all'interno dell'applicazione.Ascoltare un altro evento di ridimensionamento della finestra in C#

In questo momento ho risolto ottenendo l'hWnd della finestra e interrogando periodicamente in un thread la posizione della finestra osservata, spostando di conseguenza la finestra dell'osservatore.

Tuttavia questa è una soluzione molto poco elegante. Quello che mi piacerebbe fare è ascoltare l'evento di ridimensionamento della finestra osservata in modo che l'osservatore reagisca solo quando necessario.

Suppongo che dovrei usare un hook e ho trovato molti modi per farlo, ma la mia mancanza di conoscenza del C WinAPI mi sta bloccando nel capire quale hook ho bisogno di creare e come (pinvoke/parameters/etc).

Sono abbastanza sicuro che questo è abbastanza banale, e alcuni di voi familiarità con C/C++ e WinAPI avrà la risposta a portata di mano;)

Grazie

+0

Solo menzione di C# è nel titolo e nei tag. Stai usando C# o C? – Yuvi

+0

@Yuvi, La mia ipotesi è che vuole ascoltare un "evento" in C# che viene attivato in un'altra applicazione non nel suo "dominio". –

+0

@Yuvi Cito C# come il mio programma di base è scritto in C#. Non proprio comodo usare le app C++ for Business;) –

risposta

7

Espansione sulla risposta di Chris Taylor: Invece di eseguire autonomamente l'interazione nativa, è possibile utilizzare ManagedWinApi, che contiene una classe .

MODIFICA: per utilizzare ManagedWinApi. Da qualche parte nel codice:

Hook MyHook = new Hook(HookType.WH_CALLWNDPROC, false, false); 
MyHook.Callback += MyHookCallback; 
MyHook StartHook(); 

Per la richiamata, fare riferimento CallWndProc e CWPSTRUCT:

private static int MyHookCallback(int code, IntPtr wParam, IntPtr lParam, ref bool callNext) 
{ 
    if (code >= 0) 
    { 
     // You will need to define the struct 
     var message = (CWPSTRUCT)Marshal.PtrToStructure(lParam, typeof(CWPSTRUCT)); 
     // Do something with the data 
    } 
    return 0; // Return value is ignored unless you set callNext to false 
} 
+1

In effetti mi sono imbattuto in ManagedWinAPi, ma i documenti non sono così esplicativi quanto mi piacerebbe molto, visto la mia incompetenza in WinAPI. Sto esaminando gli esempi per ottenerlo. Per quanto ho capito, non ho davvero bisogno di un sistema globale come un aggancio diretto alla finestra è sufficiente. Devo solo essere informato degli eventi di ridimensionamento/spostamento. Idealmente questo dovrebbe accadere in un evento C#. Quello che devo capire è come collegare i punti, quindi ogni suggerimento è più che benvenuto, specialmente da parte di persone che hanno familiarità con ManagedWinApi. –

+0

Grazie! Questo è molto chiarificatore. Lo testerò al più presto. Per riferimento, CWPSTRUCT è qui http://www.pinvoke.net/default.aspx/Structures/CWPSTRUCT.html –

+0

il codice suggerito si aggancia solo alle finestre all'interno del progetto C#. Devo collegarmi a una finestra esterna. Cercando su Google ho capito che questo non sembra possibile in C#, a meno che Magadewinapi non abbia un modo per farlo. Ho provato a impostare il parametro globale nel costruttore Hook su true, ma ho sbattuto la mia sessione. Qualche suggerimento? –

4

Un gancio WH_CALLWNDPROC sarebbe probabilmente sufficiente, questo ti permetterà di monitorare tutti i messaggi destinati alla finestra di interesse.

Stai chiedendo come creare un hook globale utilizzando C# o sei felice di creare l'hook in C++ e quindi di interoperarlo con .NET? La seconda opzione è la strada che vorrei percorrere.

In pratica la parte superiore della mia testa, quello che vorrei fare è la seguente

1- Creare hook globale in C, e le funzioni di esportazione in InstallHook e UninstallHook, che può essere chiamato dalla tua app C# utilizzando Interop. InstallHook prendi un po 'di finestra nella tua applicazione C#.

2- Fare in modo che la funzione di gancio installata invii un messaggio personalizzato alla finestra C# fornita nella chiamata a InstallHook quando, nel proprio caso, c'è un messaggio a cui si è interessati come WM_SIZE.

3- Nell'applicazione C# la finestra che riceve i messaggi inviati dal gancio annullerà WndProc per gestire il messaggio personalizzato.

Questo è uno schema di un approccio.

4

vi suggerisco di utilizzare WinEvents:

public delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime); 

    [DllImport("user32.dll")] 
    public static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags); 

Vedi anche: event hooks

+0

Sono sicuro che questo funziona. Ho appena scritto un programma sfruttando SetWinEventHook. –

1

I Ho incontrato la stessa cosa in un codice con cui stavo lavorando e ho scoperto che non potevo iniettare una DLL gestita nel processo.

Non volendo scrivere un DLL C++ non gestita che ha usato SetWindowsHook, sono andato con una combinazione di SetWinEventHook, che segnala quando una finestra inizia e finisce un evento di movimento e un Timer per interrogare la finestra mentre si muove per dare l'apparenza di seguire la finestra mentre si muove.

Ecco una (molto semplificata) versione di una classe che può farlo (basta sostituire il TODO con il codice per spostare la finestra o generare un evento).

public class DockingHelper 
{ 
    private readonly uint m_processId, m_threadId; 

    private readonly IntPtr m_target; 

    // Needed to prevent the GC from sweeping up our callback 
    private readonly WinEventDelegate m_winEventDelegate; 
    private IntPtr m_hook; 

    private Timer m_timer; 

    public DockingHelper(string windowName, string className) 
    { 
     if (windowName == null && className == null) throw new ArgumentException("Either windowName or className must have a value"); 

     m_target = FindWindow(className, windowName); 
     ThrowOnWin32Error("Failed to get target window"); 

     m_threadId = GetWindowThreadProcessId(m_target, out m_processId); 
     ThrowOnWin32Error("Failed to get process id"); 

     m_winEventDelegate = WhenWindowMoveStartsOrEnds; 
    } 

    [DllImport("user32.dll", SetLastError = true)] 
    private static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect); 

    [DllImport("user32.dll", SetLastError = true)] 
    private static extern bool UnhookWinEvent(IntPtr hWinEventHook); 

    [DllImport("user32.dll")] 
    private static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags); 

    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] 
    private static extern IntPtr FindWindow(string lpClassName, string lpWindowName); 

    [DllImport("user32.dll", SetLastError = true)] 
    private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); 

    private void ThrowOnWin32Error(string message) 
    { 
     int err = Marshal.GetLastWin32Error(); 
     if (err != 0) 
      throw new Win32Exception(err, message); 
    } 

    private RECT GetWindowLocation() 
    { 
     RECT loc; 
     GetWindowRect(m_target, out loc); 
     if (Marshal.GetLastWin32Error() != 0) 
     { 
      // Do something useful with this to handle if the target window closes, etc. 
     } 
     return loc; 
    } 

    public void Subscribe() 
    { 
     // 10 = window move start, 11 = window move end, 0 = fire out of context 
     m_hook = SetWinEventHook(10, 11, m_target, m_winEventDelegate, m_processId, m_threadId, 0); 
    } 

    private void PollWindowLocation(object state) 
    { 
     var location = GetWindowLocation(); 
     // TODO: Reposition your window with the values from location (or fire an event with it attached) 
    } 

    public void Unsubscribe() 
    { 
     UnhookWinEvent(m_hook); 
    } 

    private void WhenWindowMoveStartsOrEnds(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime) 
    { 
     if (hwnd != m_target) // We only want events from our target window, not other windows owned by the thread. 
      return; 

     if (eventType == 10) // Starts 
     { 
      m_timer = new Timer(PollWindowLocation, null, 10, Timeout.Infinite); 
      // This is always the original position of the window, so we don't need to do anything, yet. 
     } 
     else if (eventType == 11) 
     { 
      m_timer.Dispose(); 
      m_timer = null; 
      var location = GetWindowLocation(); 
      // TODO: Reposition your window with the values from location (or fire an event with it attached) 
     } 
    } 

    [StructLayout(LayoutKind.Sequential)] 
    private struct RECT 
    { 
     public int Left, Top, Right, Bottom; 
    } 

    private delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime); 
} 
Problemi correlati