2010-08-01 15 views
7

Voglio creare la mia classe per gestire la creazione di finestre e la procedura della finestra, ma ho notato che la procedura della finestra deve essere statica! Ora mi chiedo se è possibile rendere la procedura della finestra orientata agli oggetti? Ho letto alcuni tutorial su finestre orientate agli oggetti, ma rendono sempre la procedura statica -.- che cosa è l'uso in questo? :/Object oriented C++ win32?

Tutti i link o informazioni su come ovviare a questo problema sarebbe apprezzato,

grazie

+0

Vedere [ Metodo migliore per l'archiviazione di questo puntatore per l'utilizzo in WndProc ] (http://stackoverflow.com/questions/117792/best-method-for-storing-this-pointer-for-use-in-wndproc). –

+2

È per questo motivo che ho sempre desiderato che 'WndProc' avesse un parametro' void * user_data'. Renderebbe più semplice la creazione di un wrapper basato su oggetti. –

+0

@Evan: sì, ma avrebbe anche richiesto a qualcuno * sane * di occuparsi della progettazione dell'API ... L'API Win32 sarebbe stata una bestia molto diversa, se così fosse stato. – jalf

risposta

11

Si può andare in giro che facendo la statica WndProc delegare tutto ai membri:

// Forward declarations 
class MyWindowClass; 
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 

std::map<HWND, MyWindowClass *> windowMap; 

// Your class 
class MyWindowClass { 
private: 
    HWND m_handle; 

    // The member WndProc 
    LRESULT MyWndProc(UINT message, WPARAM wParam, LPARAM lParam) { /* ... */ } 

public: 
    MyWindowClass() 
    { 
    /* TODO: Create the window here and assign its handle to m_handle */ 
    /* Pass &WndProc as the pointer to the Window procedure */ 

    // Register the window 
    windowMap[m_handle] = this; 
    } 
}; 

// The delegating WndProc 
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 
{ 
    std::map<HWND, MyWindowClass *>::iterator it = windowMap.find(hWnd); 
    if (it != windowMap.end()) 
    return it->second->MyWndProc(message, wParam, lParam); 
    return 0; 
} 
+1

+1, ma potresti anche voler controllare WM_DESTROY e rimuovere l'handle dalla mappa. – SoapBox

+0

bel esempio, sembra fantastico. Cercherò di implementarlo nel mio, buon uso di std :: map per trovare la maniglia corrispondente – Kaije

+0

@SoapBox È incompleto in molti modi, grazie per aver notato questo però. –

3

Se siete alla ricerca di object oriented API Win32, allora si dovrebbe cercare di MFC e/o WTL.

+0

Soluzioni di overkill per molti scopi. MFC può essere * relativamente * sottile, ma è comunque una grande API a sé stante, e il passaggio da una API all'altra richiede molto lavoro. È anche completamente inutile se tutto ciò che si vuole fare è utilizzare alcune classi C++ proprie durante la codifica per Win32. Il mio vecchio "object framework" per Win32 era probabilmente su 2 o 3 lati di codice: poco più di una classe base, un'inizializzazione e un ciclo principale di GetMessage/etc. – Steve314

+0

Materia di opinione –

+0

MFC è un framework sgradevole, ma è ben supportato e relativamente ben noto nel mondo Win32. – seand

1

È possibile utilizzare l'handle di finestra passato a WindowProc per afferrare un oggetto creato per quella finestra specifica e delegare la gestione degli eventi a tale oggetto.

ad es.

IMyWindowInterface* pWnd = getMyWindowObject(hWnd); 
pWnd->ProcessMessage(uMsg, wParam, lParam); 
+0

Sembra buono, ho notato che l'unica cosa unica nella procedura è il manico, ma non ero sicuro di come trovare la mia finestra attraverso di essa. come nel tuo esempio, il getMyWindowObject (hwnd), la funzione consisterebbe nel scorrere le finestre aperte per vedere se l'handle corrisponde? perché se così fosse, se gestissi un WM_MOUSEMOVE o WM_TIMER, non sarebbe noioso sul processore? – Kaije

+0

La cosa migliore da fare è usare una qualche forma di hash da hash da HWND a pointers-to-your-window-object: la ricerca è veloce. A meno che tu non abbia un numero piuttosto elevato di finestre aperte, mi aspetterei che un ciclo attraverso tutte le coppie (HWND, object *) sia abbastanza veloce per te '. –

6

La tecnica generale di permettere un'istanza della finestra per essere rappresentato da un'istanza di classe è quello di fare uso di SetWindowLongPtr e GetWindowLongPtr per associare il puntatore dell'istanza della classe all'handle della finestra. Di seguito è riportato un codice di esempio per iniziare. Potrebbe non essere compilato senza alcune modifiche. È solo pensato per essere un riferimento.

Personalmente, ho smesso di aggiornare le mie classi di finestre alcuni anni fa quando ho scoperto la classe di template CWindow e CWindowImpl di ATL. Si prendono cura di fare tutto questo codice banale per te, così possono concentrarsi solo sulla scrittura di metodi che gestiscono i messaggi della finestra. Vedere il codice di esempio che ho scritto su here.

Spero che questo aiuti.

class CYourWindowClass 
{ 
private: 
    HWND m_hwnd; 

public: 
    LRESULT WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam) 
    { 
     switch (uMsg) 
     { 
      case WM_CREATE: return OnCreate(wParam, lParam); 
      case wM_PAINT: return OnPaint(wParam, lParam); 
      case WM_DESTROY: 
      { 
       SetWindowLongPtr(m_hwnd, GWLP_USERDATA, NULL); 
       m_hwnd = NULL; 
       return 0; 
      } 
     } 
     return DefWindowProc(m_hwnd, uMsg, wParam, lParam); 

    } 

    CYourWindowClass() 
    { 
     m_hwnd = NULL; 
    } 

    ~CYourWindowClass() 
    { 
     ASSERT(m_hwnd == NULL && "You forgot to destroy your window!"); 
     if (m_hwnd) 
     { 
      SetWindowLong(m_hwnd, GWLP_USERDATA, 0); 
     } 
    } 

    bool Create(...) // add whatever parameters you want 
    { 
     HWND hwnd = CreateWindow("Your Window Class Name", "Your Window title", dwStyle, x, y, width, height, NULL, hMenu, g_hInstance, (LPARAM)this); 
     if (hwnd == NULL) 
      return false; 

     ASSERT(m_hwnd == hwnd); 
     return true; 
    } 


    static LRESULT __stdcall StaticWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
    { 
     CYourWindowClass* pWindow = (CYourWindowClass*)GetWindowLongPtr(hwnd, GWLP_USERDATA); 

     if (uMsg == WM_CREATE) 
     { 
      pWindow = ((CREATESTRUCT*)lParam)->lpCreateParams; 
      SetWindowLongPtr(hwnd, GWLP_USERDATA, (void*)pWindow); 
      m_hWnd = hwnd; 
     } 

     if (pWindow != NULL) 
     { 
      return pWindow->WndProc(uMsg, wParam, lParam); 
     } 

     return DefWindowProc(hwnd, uMsg, wParam, lParam); 
    }; 


}; 
+0

Mi chiedevo sempre che cosa fosse lpParam per la funzione CreateWindowEx, ma suppongo che questo potrebbe essere il modo in cui lo rende vulnerabile, dato che qualcuno potrebbe utilizzare GetWindowLong per ottenere i dati dell'utente: P – Kaije

2

solo per aggiungere alla risposta di Brian, ma di un quadro win32 che è più principiante amichevole dare un'occhiata a Win32++. La libreria stessa non è così completa nelle funzionalità rispetto a MFC o QT, ma questo è un compromesso che il progettista ha fatto all'inizio per mantenere la libreria facile da capire e semplice da usare.

Se sei ancora interessato a questo argomento, ti incoraggio vivamente a dare un'occhiata a questo poiché utilizza ancora un'altra tecnica per salvare il puntatore "this" utilizzando la memoria locale del thread.