Newsgroup: it.comp.programmare.win32
Titolo: HWND --> CWnd

Salve a tutti!
Sto sviluppando un'applicazione per la selezione automatica delle e-mail
spam, basato un un algoritmo di IA abbastanza buono (accuratezza ca
90%), e ora sto cercando di integrarlo in Eudora.
Il codice dell'algoritmo è pronto ed è in C ANSI puro, ma purtroppo sono
costretto a lavorare in ambiente Visual C++ 6 per creare il plug-in per
Eudora.
Il lavoro procede piuttosto bene ma vista la mia inesperienza in questo
ambiente mi sono bloccato in un problemino piuttosto semplice (credo!):
Come (diavolo) si fa a creare un oggetto della classe CWnd partendo da
un HWND????
Il costruttore non riceve parametri, e c'è un attributo pubblico di CWnd
che è proprio di tipo HWND, ma a settarlo non cambia nulla!
Vi ringrazio per la collaborazione, e vi prometto che appena il soft
sarà prondo lo renderò disponibile free!
Bye!
Norman


Questa è un'operazione pericolosa, perché il distruttore della classe CWnd
tenterà di distruggere la finestra.
Hai  comunque diverse possibilità:
static CWnd* PASCAL FromHandle( HWND hWnd );
Questa funzione statica consente di ottenere un puntatore (che potrebbe
essere temporaneo) ad una classe CWnd dato l'handle. Questa funzione non
effettua il subclassing.
static CWnd* PASCAL FromHandlePermanent( HWND hWnd );
Questa funzione statica consente di ottenere un puntatore alla classe CWnd
già attacata all'handle hWnd. Se tale CWnd non è disponibile la funzione
ritorna NULL.
BOOL Attach( HWND hWndNew );
BOOL Detach();
La funzione Attach collega alla tua classe la finestra di handle hWndNew.
Quando non ne hai più bisogno ricordati di utilizzare il Detach() per
scollegarla.
Se non ricordo male neanche in questo modo viene effettuato il subclassing.
Se hai necessita di effettuare il subclassing e quindi gestire anche i
messaggi che arrivano alla finestra esiste la funzione SubclassWindow.
BOOL SubclassWindow( HWND hWnd );
Ricordati che non puoi effettuare il subclass di una finestra che non
appartiene al tuo processo.
Sono stato molto sintetico, perché a seconda di quello che vuoi fare ci sono
diverse possibilità.
Ti consiglio di approfondire l'argomento sull'help del Visual C++.
Prova ad iniziare con la funzione FromaHandle.
Porlock

 scollegarla.
La cosa non è molto semplice, perchè in realtà io non lavoro in una classe,
anche se la finestra di dialogo che vorrei aprire lo è. Ora mi spiego meglio:
Eudora offre delle funzioni di accesso per i plug-in che sono in c (con qualche
miscuglio c++!), e la funzione per aprire la configurazione ha questa struttura:
extern "C" long WINAPI ems_plugin_config(
    void FAR* globals,    /* Out: Return for allocated instance structure */
    emsMailConfigP mailConfig  /* In: Eudora mail info */
)
emsMailConfigP è fatta in questo modo:
typedef struct emsMailConfigS {
 long   size;   /* Size of this data structure */
 HWND   FAR*eudoraWnd; /* Eudora's main window */
    LPSTR   configDir;  /* Optional directory for config file */
 emsAddress  userAddr;  /* Users full name from Eudora config */
 unsigned long reserved;  /* V4! reserved */
} emsMailConfig;
quindi, l'unica cosa che io ho a disposizione è l'HWND della finestra principale
di Eudora.
Però io ho bisogno di aprire una finestra di dialogo (con il solito
ClasseFinestra.DoModal();), che però alla dichiarazione della sua classe ha
bisogno un CWnd...
Come posso fare???
Ti ringrazio per l'aiuto!
Bye!
Norman

 bisogno un CWnd...
Se usi la funzione FromHandle per avere un puntatorei a CWnd* poi cosa
succede ?
Che errore hai sulla DoModal  ?
Porlock

 Che errore hai sulla DoModal  ?
Allora, il codice nella funzione è questo:
//  --------------- Inizio Codice ---------------------------
// a quanto pare ci pensa lui ad allocare temporaneamente la memoria..
 CWnd *wnd=NULL;
 wnd = CWnd::FromHandle(*mailConfig->eudoraWnd);
/* ecco quello che ritorna.. hWnd è uguale a quello della finestra di
Eudora...
   - wnd 0x01c50050 {CTempWnd hWnd=0x00000754}
   + [CTempWnd] {CTempWnd hWnd=0x00000754}
   + CCmdTarget {CCmdTarget}
   + m_hWnd 0x00000754
   + m_hWndOwner 0x00000000
   m_nFlags 0
   m_pfnSuper 0x00000000
   m_nModalResult 0
   + m_pDropTarget 0x00000000 {COleDropTarget}
   m_pCtrlCont 0x00000000
   m_pCtrlSite 0x00000000
*/
// CConfig è la classe della mia finestra di dialogo
 CConfig dlgConfig(wnd);
// qui cominciano i guai.. In debugging mi ritorna un assertion fail :
//  { ASSERT(afxCurrentResourceHandle != NULL);
//  return afxCurrentResourceHandle; }
 int nRetCode = dlgConfig.DoModal();
//  --------------- Fine Codice ---------------------------
Non in debug mode nRetCode ritorna -1, e ovviamente non apre la finestra...
Attach() e Detach() non sono necessari con FromHandle perchè stando alla
documentazione:
<<If a CWnd object is not attached to the handle, a temporary CWnd object is
created and attached.>>
Non riesco proprio a capire dove è il problema, sic!
Bye!
Norman

 //  return afxCurrentResourceHandle; }
OK !
Credo di avere capito il problema.
Non ha nulla a che fare con il recupero di un puntatore CWnd dato l'handle.
Il problema risiede nelfatto che il costruttore non trova la risorsa della
dialog.
Questo perché la variabile afxCurrentResourceHandle è NULL.
Piccola digressione:
in un'applicazione interamente MFC (eseguible e varie DLL) le risorse
possono risiedere nell'applicazione ed anche nella DLL. Nelle MFC è
implementato un meccanismo (attraverso la classe CDynLinkLibrary) che qunado
viene richiesta una risorsa, questa viene cercata prima nel modulo corrente,
poi viene ricercata in tutti moduli caricati (non di sistema).
Questa ricerca viene effettuata da una funzione globale che si chiama
AfxFindResourceHandle(), che restituisce l'HINSTANCE della libreria che
possiede la risorsa, che può essere quindi caricata con una LoadResource.
Finedigressione.
Cosa è successo nel tuo caso ?
E' successo che l'handle del modulo corrente è a NULL e quindi il
caricamento della risorsa non può avvenire.
Soluzione.
Esiste la seguente funzione:
void AfxSetResourceHandle( HINSTANCE hInstResource );
che permette di settare l'handle del modulo corrente.
Se non vado errato l'handle del modulo corrente si può ottenere attraverso
la funzione
HINSTANCE AfxGetInstanceHandle( );
Quindi prima del costruttore e prima della DoModal della tua dialog prova a
mettere il seguente codice:
AfxSetResourceHandle(AfxGetInstanceHandle());
o se preferisci
HINSTANCE hinst = AfxGetInstanceHandle();
AfxSetResourceHandle(hinst);
Dovrebbe funzionare.
Ciao
Porlock.

 Dovrebbe funzionare.
Non funziona! :)
Mi risponde (in debugging) con un triste messaggio di assert:
 { ASSERT(afxCurrentInstanceHandle != NULL);
  return afxCurrentInstanceHandle; }
Comunque adesso ho trovato un codice di esempio che apre una finestra dialog, e
lo sto studiando... Pare usare qualche barbatrucco... mah!
Bye!
Norman


Mannaggia !!
Scusa se insisto, ma la cosa è alquanto interessante.
La seguente funzione cosa restituisce ?
HINSTANCE hinst = AfxGetInstanceHandle();
hinst è 0x00000000 o contiene qualcosa di significativo ?
Se invece metti la seguente istruzione nel codice:
HNSTANCE hinst= (HINSTANCE)::GetModuleHandle(NULL);
cosa trovi in hmod ?
Porlock.


[CUT]


HINSTANCE hinst = AfxGetInstanceHandle();


HNSTANCE hinst= (HINSTANCE)::GetModuleHandle(NULL);
cosa trovi in hinst?
Inoltre se non ho capito male stai realizzando una DLL in Visual C++ 6.0.
Dovrebbe essere quindi presente la funzione
extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
quando esegui in debug cosa contiene il parametro hInstance ?
Porlock.

 DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
Boh... Io non vedo nulla del genere, al massimo un InitInstance() nella
classe base.
Ad ogni modo ora il tutto funziona, grazie all'esempio che già ti citavo, e
la soluzione non è assolutamente ovvia... In pratica si cambia il costruttore
della classe di dialogo per accettare un intero al posto del CWnd della
finestra superiore! Mah!
Ad ogni modo ora ho una piccola domandina da newbie di VC6: ho associato una
variabile ad una casella di testo di una classe dialogo che viene chiamata
sotto un'altra classe dialogo. In questa casella di testo si inserisce (ad
esempio) un numero, che ovviamente andrà ad aggiornare la variabile associata
in precedenza. Il problema si pone quando voglio passare tale variabile alla
classe base di dialogo in modo abbastanza "elegante" (potrei farlo con una
serie di variabili globali, ma mi sembra un orrore!)... Qualche dritta?
Scusa la domanda stupida ma io di solito programmo in Java o in C-C++ sotto
UNIX.. :)
Bye!
Norman


Allora vediamo se ho capito:
tu hai una Dialog A, che può fare apparire un'ulteriore Dialog che
chiameremo B.
La Dialog B ha una casella di testo, e la classe della Dialog B ha come
membro una varibaile, diciamo m_sTesto, che contiene la stringa contenuta
nella casella di testo.
A questo punto vuoi far passare m_sTesto alla Dialog A in maniera elegante.
Se ho capito bene io ti suggerisco un'implementazione di questo tipo:
class CDialogB : public CDialog
{
public:
    CString m_sTesto;
...
... ecc. ecc.
...
}
class CDialogA : public CDialog
{
private: // o anche public vedi tu
    CDialogB m_dlgB;
pretected:
    afx_msg void OnEvento();
}
void CDialogA::OnEvento()
{
    m_dlgB.DoModal();
}
In questo modo da qualsiasi punto della classe CDialogA puoi accedere a
m_sTesto semplicemente scrivendo m_dlgB.m_sTesto.
Porlock.

 :-)))
[CUT]
Nella classe del tuo dialogo ridefinisci la DoModal() in questo modo
classe MyDialog : public CDialog
{
    ...
    int DoModal()
    {
        HINSTANCE hOld = AfxSetResourceHandle(AfxGetResourceHandle());
        int nValue = CDialog::DoModal();
       AfxSetResourceHandle(hOld);
        return nValue;
    }
    ...
};
Se AfxGetResourceHandle() ritorna nulla il valore dell'HINSTANCE lo devi
salvare in una variabile di appoggio quando viene chiamata la InitInstance()
della CWinApp dell'applicazione che contiene la classe.
Se il progetto è una DLL allora nella DLLEntryMain() e qualcosa di simile.
Spero di esserti stato d'aiuto.
Luca Basso Ricci.