Newsgroup: it.comp.programmare.win32
Titolo: [c++] oggetto finestra :-?

scusate il subject, non sapevo che scrivere.
sto scrivendo una classe C++ per le finestre, per rendere un po' orientata agli oggetti la programmazione delle mie applicazioni.
(praticamente qualcosa tipo il CWnd di MFC).
in principio avevo letto un articolo su come fare una thunk, cioè un qualcosa per permettere di chiamare un metodo della classe come
se fosse la procedura standard della finestra.
solo che era troppo macchinoso e non l'ho capito bene perchè ci stavano molti pezzi in ASM.
ho trovato un'altra soluzione:
memorizzare il puntatore della mia classe nella sezione USERDATA della finestra.
cerco di spiegarvi brevemente come faccio:
la WNDCLASS è una sola, e la WndProc che ho specificato alla registrazione è la DefWindowProc (si, proprio la window procedure
standard :-))
ecco il metodo della mia classe (woWindow) per la creazione della finestra:
HWND woWindow::Create(LPCTSTR lpszWindowTitle, DWORD dwStyle)
{
 m_hWnd = ::CreateWindowEx(0, WO_CLASS, lpszWindowTitle, dwStyle, CW_USEDEFAULT, 0, 500, 400, NULL, NULL,
MODULE.GetInstanceHandle(), (LPVOID)this); //MODULE è l'istanza di una classe globale
 ::SetWindowLong(m_hWnd, GWL_USERDATA, (LONG)this);
 ::SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)WO::StandardWndProc);
 return m_hWnd;
}
con il secondo SetWindowLong imposto la nuova WindowProcedure. eccola qua:
namespace WO
{
 LRESULT CALLBACK StandardWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
 {
  LONG p = ::GetWindowLong(hWnd, GWL_USERDATA);
  woWindow *pWnd = reinterpret_cast<woWindow *> (p);
  return pWnd->WindowsMessage(Msg, wParam, lParam);
 }
}
ogni volta che questa window procedure viene chiamata, si va a pescare il puntatore della classe woWindow con GetWindowLong (che è
stato precedentemente impostato (all'interno di woWindow::Create(...)) con SetWindowLong), fa il suo bel cast e richiama il metodo
woWindow::WindowsMessage(...), che è un metodo della mia classe, che riceve gli stessi parametri di una normale window procedure,
eccetto l'handle alla finestra (se no che cavolo di programmazione orientata agli oggetti sarebbe?).
ok, fin qui tutto funziona bene (certo, per ora ho scritto solo pochi metodi, tipo Show, Update, ecc...).
ora dovrei dedicarmi un attimo alla gestione degli eventi.
quello che ho in mente di fare è il classico eredita+override, ovvero:
(per ridefinire l'evento OnDestroy()):
class Window1 : public woWindow
{
public:
    LRESULT OnDestroy()
    {
        MessageBox(m_hWnd, "Ciao ciao!", "...", 0x00000040L);
        MODULE.OnExit();
        return 0;
    }
};
insomma, come con ATL, MFC, BeOS, ecc...
anche qui tutto ok, se non per il fatto che non posso gestire l'evento OnCreate (WM_CREATE) perchè quando creo la finestra
(CreateWindowEx in woWindow::Create) la window procedure è ancora quella standard, e solo all'istruzione successiva gli imposto
WO::StandardWndProc, che si preoccupa di indirizzare tutto alla classe giusta.
voi direte:
- registra la WNDCLASS con la procedura WO::StandardWndProc !!
se io faccio così quando creo la finestra (woWindow::Create) ancora non ho messo il puntatore della classe nello spazio riservato
della finestra (SetWindowLong... GWL_USERDATA), e mi genera un'eccezione (o un seg_fault, non ricordo... cmq in win2000 o fai il
debug, o termini l'applicazione, in win98 crash dopo pochi minuti ;-)) perchè al primo messaggio che arriva WO::StandardWndProc
cerca di recuperare il puntatore, ottiene un valore null, fa il cast, e prova a chiamare il metodo, e BUM!  :-)
sono riuscito a capirlo dopo 20 minuti che sono stato a debuggare il tutto!!
a questo punto voi direte:
- la funzione CreateWindowEx ti permette di passare come ultimo parametro un puntatore a qualsiasicosa. basta che passi quello e in
WM_CREATE (facento il casting in LPCREATESTRUCT di lParam, e prendendo il membro lpCreateParams) hai già il puntatore all'istanza
della classe, poi nei messaggi successivi lo ri-recuperi con GetWindowLong...
ho provato anche così (per forza, c'ho pensato io! ;-D)
però tra queste due linee di codice:
m_hWnd = ::CreateWindowEx(..., (LPVOID)this);
::SetWindowLong(m_hWnd, GWL_USERDATA, (LONG)this);
windows non manda solo il messaggio WM_CREATE alla mia window procedure, manda anche WM_GETMINMAXINFO e chissà quanti altri...
(questo l'ho scoperto in quei 20 minuti di debug ;-))))
quindi non posso fare così. ovvero, come faccio a ignorare i messaggi tra WM_CREATE e l'istruzione successiva a CreateWindowEx?
vi viene in mente qualche soluzione???
grazie di cuore e scusate per il post un po' lunghetto
ciao
xaero

Usa meno framework e piů le API.

Oltre ad aver spedito un post lunghissimo hai anche dimennticato che esiste
while ( GetMessage( &msg, 0, 0, 0 ) )
       {
             TranslateMessage( &msg ) ;
             if ( msg.message == WM_CREATE )
             {
                OnCreate(...);
             }
             DispatchMessage( &msg ) ;
       }

 Usa meno framework e più le API.
ho già programmato abbastanza con le API, ma anche con MFC.
ho bisogno di complicarmi la vita il più possibile.
realizzare un framework dovrebbe essere sufficiente
...quando raggiungerò l'apice della perfezione (quando?) sicuramente
mi sarò stancato di fabbricare framework e riprenderò a programmare
con le API o con MFC... anzi diciamo che aspettando C# e VS.Net
scrivo frameworks!  ;-))))))
ciao
xaero

 while ( GetMessage( &msg, 0, 0, 0 ) )
        [ cut ... ]
avrei preferito evitare il message-loop.
cmq ci ho provato, ho 'acchittato' una soluzione al volo e funziona,
ma adesso sono saltati fuori problemi ben più grandi con gli override...
cmq ho voluto la bicicletta... e ora pedalo  :o)
grazie
ciao
xaero

 ha scritto nel messaggio news:99qala$2c2jp$1...@ID-51253.news.dfncis.de...
[CUT (uff :D) ]
Io ho fatto cosi', spero ti possa essere utile...
Allora, ho definito nella WindowsClass della finestra:
* WndClass.lpfnWndProc = KWindow::StaticWinProc; *
che e' un metodo statico della mia classe.
Poi ho creato la finestra con CreateWindowEx e come ultimo parametro
ho messo (LPARAM)this , in modo che mi fosse spedito insieme al
WM_CREATE...
Infine, ho scrito la StaticWinProc cosi':
LRESULT WINAPI KWindow::StaticWinProc(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam){
 if (Msg==WM_CREATE){
  // Recupera l'indirizzo dell'istanza che e'
  // stato passato da CreateWindow e lo memorizza
  // assieme alla finestra
  SetWindowLong(hWnd,0,(LONG)lParam);
    }
    // Recupera l'istanza associata
    LPKWINDOW p=(LPKWINDOW)GetWindowLong(hWnd,0);
    // Chiama il metodo di gestione (non statico)
    return p->CurrWinProc(hWnd,Msg,wParam,lParam);
}
e cosi' la mia classe riesce a gestire tutti i mex, compreso il
WM_CREATE, e non mi ha dato nessun problema di
ereditarieta' (fin ora :D)
Spero di esserti stato utile
Bow-wow
Killer

> LRESULT WINAPI KWindow::StaticWinProc(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam){
caxxo!!
ti stavo per rispondere spiegando bene il problema, ma questa linea di codice mi ha illuminato!!!!!
il problema era che la mia WindowProcedure secondaria (quella non-statica chiamata dalla vera WndProc) non riceveva il parametro
HWND.
l'ho fatta così perchè pensavo fosse inutile.... e invece ora ho capito che in woWindow::Create, quando chiamo CreateWindowEx
succede un casino finchè non mi ritorna il valore... vabbè, è lunga da spiegare, ma se l'hai già fatta una cosa del genere lo sai
grazie mille
ciao
xaero