| auteur : Farscape | En parcourant les fenêtres sur le bureau :
Voici un exemple permettant de trouver le handle de fenêtre de Word et de fermer Word :
char szIdentite[]= " Microsoft Word " ;
CWnd* pWnd = CWnd:: GetDesktopWindow ( )- > GetTopWindow ( );
CWnd * pWndWord = 0 ;
CString s,strWord;
while ( 1 )
{
pWnd = pWnd- > GetNextWindow ();
if ( pWnd = = NULL ) break ;
pWnd- > GetWindowText ( s );
strWord= s;
if (strWord.Find (szIdentite)! = - 1 )
{
pWndWord = pWnd;
break ;
}
}
if (pWndWord)
:: PostMessage (pWndWord- > GetSafeHwnd (),WM_SYSCOMMAND, SC_CLOSE, 0L );
|
Note: Si le nom de l'application est fixe on pourra utiliser la commande :
CWnd:: FindWindow
static CWnd* PASCAL FindWindow (
LPCTSTR lpszClassName,
LPCTSTR lpszWindowName );
|
CWnd * pWnd= CWnd:: FindWindow ( NULL ," MonProg " );
|
|
| auteur : Farscape | Les classes MFC proposent des objets permettant de maintenir des collections d'objets en mémoire.
Classiquement on retrouvera des objets gérant des tableaux ,des listes ,et des maps .
On trouve dans les MFC deux générations de ces objets :
CObArray , CObList, CMapPtrToWord
Déclinés avec des string des word ,int etc..
La seconde génération utilise des classes templates :
CArray, CList ,CMap
il est préférable d'utiliser ces classes en remplacement des précédentes.
L'exemple qui suit déclare un objet à sérialiser .
Un tableau de cet objet.
Son remplissage, sa sauvegarde et sa lecture avec la classe CArchive.
class CItem : public CObject
{
public :
DECLARE_SERIAL ( CItem )
CItem (){ Clear ();}
~ CItem (){ }
CItem (const CItem & rItem)
{
CopyFrom (rItem);
}
const CItem& operator = (const CItem& Src)
{
CopyFrom (Src);
return * this ;
}
void Clear ()
{
m_strNom= " " ;
m_strPrenom= " " ;
m_strCdp= " " ;
m_strVille= " " ;
}
void Serialize (CArchive& ar)
{
if (ar.IsStoring ())
ar < < m_strNom < < m_strPrenom < < m_strCdp < < m_strVille;
else
ar > > m_strNom > > m_strPrenom > > m_strCdp > > m_strVille;
}
void CopyFrom (const CItem & Src )
{
if (this = = & Src) return ;
Clear ();
m_strNom = Src.m_strNom;
m_strPrenom = Src.m_strPrenom;
m_strCdp = Src.m_strCdp;
m_strVille = Src.m_strVille;
}
CString GetStrDump ()
{
return ( m_strNom + " / " + m_strPrenom + " / " + m_strCdp + " / " + m_strVille);
}
CString m_strNom;
CString m_strPrenom;
CString m_strCdp;
CString m_strVille;
} ;
template < > void AFXAPI SerializeElements< CItem> (CArchive& ar, CItem* pElements, int nCount);
typedef CArray< CItem,CItem& > CArrayItem;
template < > void AFXAPI SerializeElements < CItem> ( CArchive& ar,
CItem* pItem, int nCount )
{
for ( int i = 0 ; i < nCount; i+ + , pItem+ + )
pItem- > Serialize ( ar );
}
IMPLEMENT_SERIAL ( CItem, CObject, 0 )
void Test ()
{
CItem item;
item.m_strCdp= " 06800 " ;
item.m_strNom= " farscape " ;
item.m_strPrenom= " ??? " ;
item.m_strVille= " Nice " ;
CArrayItem arItem;
arItem.Add (item);
for (int i= 0 ;i< 10 ;i+ + )
{
item.m_strNom.SetAt (0 ,' A ' + i);
arItem.Add (item);
}
{
CFile File;
if (File.Open (" MyArchive.arc " , CFile:: modeCreate | CFile:: modeWrite ))
{
CArchive ar ( & File, CArchive:: store);
arItem.Serialize (ar);
}
}
arItem.RemoveAll ();
CFile File;
if (File.Open (" MyArchive.arc " , CFile:: modeRead ))
{
CArchive ar ( & File, CArchive:: load);
arItem.Serialize (ar);
}
for (i= 0 ;i< arItem.GetSize ();i+ + )
{
AfxMessageBox (arItem[i].GetStrDump ());
}
}
|
|
| auteur : Nourdine Falola | En complément de Comment sérialiser des données avec les MFC ?
Pour ceux qui n'ont pas envie d'implémenter eux-même la fonction SerializeElements<CItem> chaque fois qu'ils veulent sérialiser le contenu d'un CArray ou d'un autre conteneur template MFC, il est possible de définir une classe template, héritant du conteneur en question, implémentant déjà ce service.
Exemple du CArray
# include <string>
# include <sstream>
# include <afxtempl.h>
template < class T,class U>
class CArraySer: public CArray< T,U>
{
public :
CArraySer (int line,bool b= true );
virtual ~ CArraySer ();
virtual void Serialize (CArchive& ar);
void AFXAPI SerializeElts (CArchive& ar, T* pItem, int nCount);
private :
bool isSerializable;
std:: string msg;
} ;
template < class T,class U>
CArraySer< T,U> :: CArraySer (int line,bool b)
{
isSerializable = b;
std:: ostringstream oss;
oss < < " Erreur ligne " < < line < < " : CArraySer contient des objets non-sérialisables " ;
msg = oss.str ();
}
template < class T,class U>
CArraySer< T,U> :: ~ CArraySer ()
{
}
template < class T,class U>
void AFXAPI CArraySer< T,U> :: SerializeElts ( CArchive& ar,T* pItem, int nCount )
{
for ( int i = 0 ; i < nCount; i+ + , pItem+ + )
pItem- > Serialize ( ar );
}
template < class T, class U>
void CArraySer< T, U> :: Serialize (CArchive& ar)
{
ASSERT_VALID (this );
if (isSerializable)
{
CObject:: Serialize (ar);
if (ar.IsStoring ())
{
ar.WriteCount (GetSize ());
}
else
{
DWORD nOldSize = ar.ReadCount ();
SetSize (nOldSize, - 1 );
}
SerializeElts (ar, m_pData, GetSize ());
}
else
{
AfxMessageBox (msg.c_str ());
}
}
|
L'utilisation de CArraySer est similaire à celle de CArray, cependant il n'est plus nécessaire d'implémenter la fonction statique SerializeElements dans le code appelant. Le constructeur comporte 2 arguments : le 1er correspond à la ligne à laquelle vous déclarerez le CArraySer, le 2è est un booléen qui indique si les objets contenus par le CArraySer sont sérialisables ou non. Reprenons l'exemple du CArray en le remplaçant par CArraySer :
IMPLEMENT_SERIAL ( CItem, CObject, 0 )
void Test ()
{
CItem item;
item.m_strCdp= " 35200 " ;
item.m_strNom= " Shakala " ;
item.m_strPrenom= " Bigboom " ;
item.m_strVille= " Rennes " ;
CArraySer< CItem,CItem& > arItem (__LINE__);
arItem.Add (item);
for (int i= 0 ;i< 10 ;i+ + )
{
item.m_strNom.SetAt (0 ,' A ' + i);
arItem.Add (item);
}
{
CFile File;
if (File.Open (" MyArchive.arc " , CFile:: modeCreate | CFile:: modeWrite ))
{
CArchive ar ( & File, CArchive:: store);
arItem.Serialize (ar);
}
}
arItem.RemoveAll ();
CFile File;
if (File.Open (" MyArchive.arc " , CFile:: modeRead ))
{
CArchive ar ( & File, CArchive:: load);
arItem.Serialize (ar);
}
for (i= 0 ;i< arItem.GetSize ();i+ + )
{
AfxMessageBox (arItem[i].GetStrDump ());
}
}
|
Si vous déclarez le contenu du CArraySer comme non-sérialisable (isSerializable = false) et que vous tentez tout de même la sérialisation, un AfxMessageBox vous délivre un message vous indiquant votre erreur et la ligne à laquelle vous avez déclarez le CArraySer incriminé.
|
| auteur : Farscape | Les collections à base de template MFC : CArray ,CMap ,CList
disposent de fonctions particulières pour compléter des traitements de construction, de sérialisation ou de destruction d'éléments.
Voici la problématique :
Je déclare un CMap sur dont les valeurs sont des pointeurs sur objets
Donc l'insertion dans la CMap nécessitera l'allocation de l'objet au préalable.
Exemple :
la déclaration :
CMap< CString ,const char * ,_InfosFolder* ,_InfosFolder * > m_mapFilter;
|
l'insertion:
m_mapFilter.SetAt (" clef " , new _InfosFolder);
|
La destruction d'un élément par la fonction RemoveKey ou RemoveAll
provoquera une fuite de mémoire puisque la CMap appelle le destructeur de l'élement type ici un pointeur sur objet ,et donc ne libérera pas l'objet lui même.
pour résoudre ce problème on implémentera la fonction DestructElements :
voici son prototype:
template < class TYPE >
void AFXAPI DestructElements ( TYPE* pElements,
int nCount );
|
implémentation pour le type de donnée _InfosFolder *
template < > void AFXAPI DestructElements < _InfosFolder * > ( _InfosFolder* * pItem, int nCount )
{
for ( int i = 0 ; i < nCount; i+ + , pItem+ + ) delete * pItem;
}
|
Dans ce contexte comment être sûr de ne pas faire un delete sur un élément non alloué par un new ?
je propose l'utilisation d'une macro à rajouter dans la définition du type de donnée.
# ifdef _DEBUG
# define TRACK_ALLOC ( _class ) void * m_pNew ; \
void * operator new (size_t nSize,LPCSTR lpszFileName,int nLine)\
{ \
_class * p= static_cast < _class * > (:: operator new (nSize, lpszFileName, nLine));\
p- > m_pNew= p;return p;\
} \
bool IsAlloc (){ return m_pNew= = this ;}
# else
# define TRACK_ALLOC ( _class ) void * m_pNew ; \
void * operator new (size_t nSize)\
{ \
_class * p= static_cast < _class * > (:: operator new (nSize));\
p- > m_pNew= p;return p;\
} \
bool IsAlloc (){ return m_pNew= = this ;}
# endif
|
ce qui donnera :
struct _InfosFolder
{
TRACK_ALLOC (_InfosFolder)
} ;
template < > void AFXAPI DestructElements < _InfosFolder * > ( _InfosFolder* * pItem, int nCount )
{
for ( int i = 0 ; i < nCount; i+ + , pItem+ + )
{
if (* pItem- > IsAlloc ()) delete * pItem;
}
}
|
ces macros doivent etre inserées avant le bloc de code:
# ifdef _DEBUG
# define new DEBUG_NEW
# undef THIS_FILE
static char THIS_FILE[] = __FILE__;
# endif
|
Note:le même traitement peut être appliqué sur la clé de la CMap.
en effet la suppression d'un élement invoque la fonction DestructElements
sur la clef et sur l'élément associé.
Extrait de AfxTempl.h
void CMap< KEY, ARG_KEY, VALUE, ARG_VALUE> :: RemoveAll ()
{
ASSERT_VALID (this );
if (m_pHashTable ! = NULL )
{
for (UINT nHash = 0 ; nHash < m_nHashTableSize; nHash+ + )
{
CAssoc* pAssoc;
for (pAssoc = m_pHashTable[nHash]; pAssoc ! = NULL ; pAssoc = pAssoc- > pNext)
{
DestructElements< VALUE> (& pAssoc- > value, 1 );
DestructElements< KEY> (& pAssoc- > key, 1 );
}
}
}
delete [] m_pHashTable;
m_pHashTable = NULL ;
m_nCount = 0 ;
m_pFreeList = NULL ;
m_pBlocks- > FreeDataChain ();
m_pBlocks = NULL ;
}
|
|
| auteur : Farscape | Principe de fonctionnement général d'une édition à partir d'une CView CScrollView :
Le traitement normal d'affichage se fait dans la fonction virtuelle OnDraw utilisée aussi pour le dessin écran .
Elle reçoit en argument un objet CDC (contexte de périphérique) .
Si on imprime, la fonction OnDraw est appelée par une autre fonction virtuelle, OnPrint ;le dc sera alors un contexte de périphérique pour imprimante, et CDC::IsPrinting() renvoie TRUE .
Dans le cas de la prévisualisation écran on aura un objet CPreviewDC; OnPrint et OnDraw seront appelées.
Les fonctions disponibles pour le système d'édition sont:
- OnPreparePrinting : définition de la plage d'édition voir CPrintInfo
- OnBeginPrinting : créations d'objets gdi personnels.
- OnPrepareDC : appelée pour chaque page, c'est l'emplacement convenu pour définir le système de coordonnées (autre que MM_TEXT) .
- OnPrint : Appelée pour chaque page ,traitements spécifiques pour compléter éventuellement le dessin, entêtes, pieds de page, date et heure d'édition. Appelle OnDraw.
- OnEndPrinting: suppression des objets GDI.
dans une view on trouve généralement les messages suivants:
ON_COMMAND (ID_FILE_PRINT, CFormView:: OnFilePrint)
ON_COMMAND (ID_FILE_PRINT_DIRECT, CFormView:: OnFilePrint)
ON_COMMAND (ID_FILE_PRINT_PREVIEW, CFormView:: OnFilePrintPreview)
|
Correspondant aux différents appels pour l'édition de la fenêtre en cours.
- ID_FILE_PRINT : impression avec sélection de l'imprimante.
- ID_FILE_PRINT_DIRECT : impression directe sur l'imprimante en cours.
- ID_FILE_PRINT_PREVIEW : prévisualisation écran.
Note : le système MFC associe l'édition/prévisualisation à la fenêtre
en cours on ne dispose pas en standard d'un objet indépendant pour gérer
l'édition comme ça existait chez Borland avec les OWL .
|
| auteur : Nourdine Falola | On a pu déjà remarquer que beaucoup d'applications proposent à l'utilisateur d'enregistrer le document en cours de fermeture si celui-ci a été modifié depuis son ouverture. Les MFC proposent également ce mécanisme.
L'architecture d'application gère ce comportement grâce à la donnée membre BOOL m_bModified de la classe CDocument (dirty flag : TRUE si le document a été modifié, FALSE sinon). Cet indicateur est initialisé à false à la création, lecture, enregistrement du document. C'est au programmeur de le mettre à TRUE lorsque le document a été modifié.
Sa valeur peut-être consultée avec la fonction BOOL CDocument::IsModified() et modifiée avec la fonction void SetModifiedFlag(BOOL bModified = TRUE).
Lorsque l'utilisateur ferme un document, l'architecture d'application appelle la fonction virtuelle virtual BOOL SaveModified( ) qui ouvre une boîte de dialogue si m_bModified = TRUE. On peut redéfinir le comportement de la fonction SaveModified( ).
Exemple d'indication de modification du document :
void CMyDoc:: ModifData (CString strName,CString strLevel,CString strLoc,CTime tDate)
{
m_pData- > SetName (strName);
m_pData- > SetLevel (strLevel);
m_pData- > SetPlace (strLoc);
m_pData- > SetDate (tDate);
SetModifiedFlag ();
UpdateAllViews (NULL );
}
|
void CMyView:: MyFunction ()
{
CMyDoc * pDoc = static_cast < CMyDoc* > (GetDocument ());
pDoc- > ModifData (" Nom " ," Niveau " ," Localisation " ,CTime:: GetCurrentTime ());
}
|
|
| auteur : Nourdine Falola | Un splashscreen est une boîte de dialogue qui apparaît au lancement d'une application pendant que celle-ci se charge. Lorsque l'application est prête, le splashscreen disparaît et la fenêtre principale de l'application apparaît.
Pour réaliser un splashscreen il suffit de décocher l'option "Title bar" dans les propriétés de la boîte de dialogue, et d'intercepter le message WM_WINDOWPOSCHANGING (à la main) :
Dans le .h de la dialog
...
afx_msg void OnWindowPosChanging ( WINDOWPOS* lpwndpos );
|
Dans le .cpp de la dialog
BEGIN_MESSAGE_MAP (CPasBougerDlg, CDialog)
...
ON_WM_WINDOWPOSCHANGING ()
END_MESSAGE_MAP ()
...
void CPasBougerDlg:: OnWindowPosChanging (WINDOWPOS* pWndPos)
{
if (IsWindowVisible ())
pWndPos- > flags | = SWP_NOMOVE;
}
|
NOTE : On peut bien sûr utiliser ce principe pour empêcher le redimensionnement et le déplacement d'une fenêtre dérivée de CWnd (view, dialog, framewnd...).
Pour une boîte de dialogue, on procèdera de la même façon en laissant éventuellement la barre de titre.
Dans un contexte SDI, on interceptera le message sur le CMainFrame et on interdira le redimensionnement :
void CMainFrame:: OnWindowPosChanging (WINDOWPOS* pWndPos)
{
if (IsWindowVisible ())
{
pWndPos- > flags | = SWP_NOMOVE;
pWndPos- > flags | = SWP_NOSIZE;
}
}
|
|
| auteur : Farscape | Dans le cas d'une CFormView, CView les ascenseurs apparaissent automatiquement dès que la surface cliente de la fenêtre devient trop petite par rapport à la surface réelle.
Comment faire dans ce contexte pour supprimer les ascenseurs ?, hé bien une CFormView ou CView hérite de la classe CSCrollView qui dispose d'une fonction spécifique pour adapter la surface de la fenêtre à la taille fenêtrée: SetScaleToFitSize
Exemple réalisé dans la méthode OnInitialUpdate d'une CFormView:
CRect Rect;
GetParentFrame ()- > GetWindowRect (& Rect);
GetParentFrame ()- > SetWindowPos ( NULL ,0 ,0 ,Rect.Width (),Rect.Height ()/ 2 ,SWP_NOMOVE | SWP_NOZORDER);
SIZE size;
size.cx= Rect.Width ();
size.cy= Rect.Height ()/ 2 ;
SetScaleToFitSize (size);
|
J'ai diminué la taille de la fenêtre parent (MDIChild) par deux et ajusté la taille réelle avec SetScalToFitSize.
|
Consultez les autres F.A.Q.
|
|