[WIN32] SendMessage qui bloque. **new** code minimal qui buggue

SendMessage qui bloque. **new** code minimal qui buggue [WIN32] - C - Programmation

Marsh Posté le 09-10-2003 à 09:50:14    

salut,
 
 
j'ai un probleme que je n arrive pas a resoudre. J'ai une fonction qui me permet de logguer dans un ListCtrl ce que je veux. Ma fonction qui insere dans le ListCtrl est la suivante :
 

Code :
  1. void LogToWindow(DWORD dwLevel) {
  2. if (logWindow != NULL) {
  3.  if (plogLast) {
  4.   if (plogLast->dwLevel & logLevelFilter) {
  5.    char szBuffer[64];
  6.    LVITEM lvi;
  7.                                                           sprintf(szBuffer,"%d",plogLast->dwID);
  8.    lvi.mask = LVIF_TEXT;
  9.    lvi.iItem = 0;
  10.    lvi.iSubItem = 0;
  11.    lvi.pszText = szBuffer;
  12.    lvi.cchTextMax = 64;
  13.    SendMessage(logWindow,LVM_INSERTITEM,0,(LPARAM)&lvi);
  14.   }
  15.  }
  16. }
  17. }


 
 
cela marche tres bien a 99% du temps. Mais sur un cas bien precis la fonction SendMessage bloque indefiniment.
 
 
Ce cas est le suivant :
 
J'ai un bouton de type ON/OFF , j appuie une fois dessus ca declenche une action, une fois a nouveau ca la stoppe.  
     - ON : je cree un thread, je mets une variable V a TRUE et celui ci m'envoie a intervalle regulier (200 ms)par le biais d une boucle sur V un caractere sur le port serie, puis un 2eme (20  mn apres le premier). Chaque ecriture sur le port serie est logguée, et ca apparait tres bien dans la fenetre de log.
     - OFF : Je mets ma variable V a FALSE ce qui a pour but d arreter la boucle d envoi des caractere, puis envoie un caractere  different sur le port (caractere de stop). Sachant que toutes les ecritures sont logguées , je loggue egalement celle ci, le log fichier, memoire fonctionne bien mais des que je loggue dans la fenetre de log mon SendMessage d insertion de ligne bloque. C est aussi simple que ca.
 
 
Pendant le ON, je peux envoyer autant de caractere que necessaire, ca ne bloque pas. Mais des que je stoppe ,ca bloque.
 
 
Quelqu un aurait il une idee ?
 
 
merci :)


Message édité par xilebo le 16-10-2003 à 13:42:15
Reply

Marsh Posté le 09-10-2003 à 09:50:14   

Reply

Marsh Posté le 09-10-2003 à 09:55:52    

Essaye de faire un PostMessage à la place

Reply

Marsh Posté le 09-10-2003 à 10:05:27    

SendMessage est une méthode bloquante (ne sort que quand le message a été traité)
Tu peux effectivement utiliser PostMessage qui n'est pas bloquant.

Reply

Marsh Posté le 09-10-2003 à 10:10:12    

Kyle_Katarn a écrit :

Essaye de faire un PostMessage à la place


+1
SendMessage est synchrone, tant que le message que tu envoies n'est pas traité, ton programme est bloqué. Pour peu que tu aies bcp de messages en attente, ça le fera pas.
 
PostMessage est asynchrone, il n'attend pas que le message soit traité.
 
edit: [:benou_grilled]


Message édité par Harkonnen le 09-10-2003 à 10:10:49

---------------
J'ai un string dans l'array (Paris Hilton)
Reply

Marsh Posté le 09-10-2003 à 10:18:49    

De plus, faut faire gaffe avec PostMessage:
Tu passes un pointeur vers ta structure lvi, quand ton message sera traité, cette structure n'existera peut etre plus...

Reply

Marsh Posté le 09-10-2003 à 10:25:29    

oui mais si je fais un postmessage, le message ne sera alors pas traité ? ou il sera mis a la queue ? Et ca peut bloquer meme si seulement 3 ou 4 message (enfin moins de 20 c sur) sont envoyés.

Reply

Marsh Posté le 09-10-2003 à 11:01:32    

Bon ca ne fonctionne pas du tout avec PostMessage et ca plante meme (surement a cause du fait que ma structure LVITEM n existe plus en memoire, mais je n ai pas le choix)
 
 
De toute facon, si le SendMessage bloque a l infini c est qu il y a un probleme mais je ne vois pas lequel.  
 
Peut etre du a l'appel SendMessage dans un thread ? en tout cas j ai vérifié la valeur du Handle de fenetre, il est correct. Je ne comprends vraiment pas ce qu il se passe. Et vu que je n ai pas le code source de SendMessage (merci  ms) je ne peux pas voir quel est le probleme et pourquoi ca boucle.
 

Reply

Marsh Posté le 14-10-2003 à 16:06:33    

bon j ai tenté de faire un code minimal qui fait la meme chose et ca plante de la meme facon.
 
C est tres simple: j'ai une boite de dialogue avec un bouton (check box) et un ListCtrl (MFC)
 
le button :  
lorsqu on appuie sur le bouton , il met une variable a 1 et cree un thread .
lorsqu on réappuie sur le bouton, il met cette meme variable a 0 et attend que le thread stoppe.
 
 
le thread :
 
il boucle sur cette variable, tant qu elle est a un, il insere une ligne dans le listctrl PUIS s endort 500 ms. Lorsqu il sort de la boucle il insere a nouveau une ligne (que je differencie par le texte inséré) et la il bloque sur cette insertion.
 
 
 
 
Comment ca se fait ? y a t il un mecanisme que j ai pas compris ?
 
 
 
je vous mets egalement le code de cette application minimale.
 
 

Code :
  1. // TESTDlg.cpp : implementation file
  2. //
  3. #include "stdafx.h"
  4. #include "TEST.h"
  5. #include "TESTDlg.h"
  6. #ifdef _DEBUG
  7. #define new DEBUG_NEW
  8. #undef THIS_FILE
  9. static char THIS_FILE[] = __FILE__;
  10. #endif
  11. /////////////////////////////////////////////////////////////////////////////
  12. // CAboutDlg dialog used for App About
  13. class CAboutDlg : public CDialog
  14. {
  15. public:
  16. CAboutDlg();
  17. // Dialog Data
  18. //{{AFX_DATA(CAboutDlg)
  19. enum { IDD = IDD_ABOUTBOX };
  20. //}}AFX_DATA
  21. // ClassWizard generated virtual function overrides
  22. //{{AFX_VIRTUAL(CAboutDlg)
  23. protected:
  24. virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
  25. //}}AFX_VIRTUAL
  26. // Implementation
  27. protected:
  28. //{{AFX_MSG(CAboutDlg)
  29. //}}AFX_MSG
  30. DECLARE_MESSAGE_MAP()
  31. };
  32. CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
  33. {
  34. //{{AFX_DATA_INIT(CAboutDlg)
  35. //}}AFX_DATA_INIT
  36. }
  37. void CAboutDlg::DoDataExchange(CDataExchange* pDX)
  38. {
  39. CDialog::DoDataExchange(pDX);
  40. //{{AFX_DATA_MAP(CAboutDlg)
  41. //}}AFX_DATA_MAP
  42. }
  43. BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
  44. //{{AFX_MSG_MAP(CAboutDlg)
  45.  // No message handlers
  46. //}}AFX_MSG_MAP
  47. END_MESSAGE_MAP()
  48. /////////////////////////////////////////////////////////////////////////////
  49. // CTESTDlg dialog
  50. CTESTDlg::CTESTDlg(CWnd* pParent /*=NULL*/)
  51. : CDialog(CTESTDlg::IDD, pParent)
  52. {
  53. //{{AFX_DATA_INIT(CTESTDlg)
  54.  m_dwOnSend = 0;
  55. //}}AFX_DATA_INIT
  56. // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
  57. m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
  58. }
  59. void CTESTDlg::DoDataExchange(CDataExchange* pDX)
  60. {
  61. CDialog::DoDataExchange(pDX);
  62. //{{AFX_DATA_MAP(CTESTDlg)
  63.  // NOTE: the ClassWizard will add DDX and DDV calls here
  64. //}}AFX_DATA_MAP
  65. }
  66. BEGIN_MESSAGE_MAP(CTESTDlg, CDialog)
  67. //{{AFX_MSG_MAP(CTESTDlg)
  68. ON_WM_SYSCOMMAND()
  69. ON_WM_PAINT()
  70. ON_WM_QUERYDRAGICON()
  71. ON_BN_CLICKED(IDC_ON, OnOn)
  72. //}}AFX_MSG_MAP
  73. END_MESSAGE_MAP()
  74. /////////////////////////////////////////////////////////////////////////////
  75. // CTESTDlg message handlers
  76. BOOL CTESTDlg::OnInitDialog()
  77. {
  78. CDialog::OnInitDialog();
  79. // Add "About..." menu item to system menu.
  80. // IDM_ABOUTBOX must be in the system command range.
  81. ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
  82. ASSERT(IDM_ABOUTBOX < 0xF000);
  83. CMenu* pSysMenu = GetSystemMenu(FALSE);
  84. if (pSysMenu != NULL)
  85. {
  86.  CString strAboutMenu;
  87.  strAboutMenu.LoadString(IDS_ABOUTBOX);
  88.  if (!strAboutMenu.IsEmpty())
  89.  {
  90.   pSysMenu->AppendMenu(MF_SEPARATOR);
  91.   pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
  92.  }
  93. }
  94. // Set the icon for this dialog.  The framework does this automatically
  95. //  when the application's main window is not a dialog
  96. SetIcon(m_hIcon, TRUE);   // Set big icon
  97. SetIcon(m_hIcon, FALSE);  // Set small icon
  98. CListCtrl *pCtrl ;
  99. pCtrl = (CListCtrl *)GetDlgItem(IDC_LIST);
  100. pCtrl->InsertColumn(0,"message",LVCFMT_LEFT,200);
  101. return TRUE// return TRUE  unless you set the focus to a control
  102. }
  103. void CTESTDlg::OnSysCommand(UINT nID, LPARAM lParam)
  104. {
  105. if ((nID & 0xFFF0) == IDM_ABOUTBOX)
  106. {
  107.  CAboutDlg dlgAbout;
  108.  dlgAbout.DoModal();
  109. }
  110. else
  111. {
  112.  CDialog::OnSysCommand(nID, lParam);
  113. }
  114. }
  115. // If you add a minimize button to your dialog, you will need the code below
  116. //  to draw the icon.  For MFC applications using the document/view model,
  117. //  this is automatically done for you by the framework.
  118. void CTESTDlg::OnPaint()
  119. {
  120. if (IsIconic())
  121. {
  122.  CPaintDC dc(this); // device context for painting
  123.  SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
  124.  // Center icon in client rectangle
  125.  int cxIcon = GetSystemMetrics(SM_CXICON);
  126.  int cyIcon = GetSystemMetrics(SM_CYICON);
  127.  CRect rect;
  128.  GetClientRect(&rect);
  129.  int x = (rect.Width() - cxIcon + 1) / 2;
  130.  int y = (rect.Height() - cyIcon + 1) / 2;
  131.  // Draw the icon
  132.  dc.DrawIcon(x, y, m_hIcon);
  133. }
  134. else
  135. {
  136.  CDialog::OnPaint();
  137. }
  138. }
  139. // The system calls this to obtain the cursor to display while the user drags
  140. //  the minimized window.
  141. HCURSOR CTESTDlg::OnQueryDragIcon()
  142. {
  143. return (HCURSOR) m_hIcon;
  144. }
  145. void CTESTDlg::OnOn()
  146. {
  147. if ( ((CButton *)GetDlgItem(IDC_ON))->GetCheck() ) { // stopper le thread
  148.   m_dwOnSend = 1; // redemarrer le thread
  149.   hSendThread = CreateThread(NULL, 0, SendThreadFunc,this, 0, &SendThreadID);
  150. }
  151. else {
  152.   m_dwOnSend = 0;
  153.   WaitForSingleObject(hSendThread,INFINITE);
  154. }
  155. }
  156. DWORD WINAPI SendThreadFunc(LPVOID pParam) {
  157. CTESTDlg *pDlg = (CTESTDlg *)pParam;
  158. static int i = 0;
  159. CString tmp;
  160. CListCtrl *pCtrl = (CListCtrl *)pDlg->GetDlgItem(IDC_LIST);
  161. while (pDlg->m_dwOnSend) {
  162.  tmp.Format("send : %d",i++);
  163.  pCtrl->InsertItem(0,tmp,0);
  164.  Sleep(500);
  165. }
  166. tmp.Format(" stop : %d",i++);
  167. pCtrl->InsertItem(0,tmp,0);
  168. return 0;
  169. }


 

Reply

Marsh Posté le 14-10-2003 à 17:33:53    

j'ai pas tout lu (trop de mfc), et je vois rien dans le thread qui peut bloquer indéfiniment, mais essaye d'éviter le "WaitForSingleObject(hSendThread,INFINITE);" et remplace le INFINITE par disons 1 ou 2 sec. ensuite tu testes la valeur de retour : par ex si ca te renvoie WAIT_TIMEOUT (timeout écoulé) bah t'as plus qu'a killer le thread (TerminateThread), mais franchement ca serait bizarre que ca soit le thread qui bloque comme ca...

Reply

Marsh Posté le 14-10-2003 à 17:41:04    

bah mfc j ai pas trop le choix mais ca change rien , effectivement les 2 fonctions interessantes sont OnOn() et SendThreadFunc() (le reste on s en fout). ET si je vire le WaitSingleObject ca passe (normal vu que j attends pas). Effectivement c est mon thread qui bloque parceque tout simplement , l appel a InsertItem (qui est en fait un sendmessage) bloque. donc thread bloqué -> processus principal egalement bloqué.
 
 
Mon réel problème est : pourquoi mon InsertItem(stop) bloque a ce moment la alors que si je le mets en commentaire, AUCUN des autres InsertItem ne bloque.
 
 
 
 
Je vous mets au défi : essayer de faire une appli minimale (meme sans MFC) qui insere des lignes dans un ListCtrl tous les X millisecondes par l'intermediaire d'un thread, et bien ca bloquera .

Reply

Marsh Posté le 14-10-2003 à 17:41:04   

Reply

Marsh Posté le 14-10-2003 à 17:58:15    

moui pourquoi pas... pas grand chose a faire la.
 
sinon pour resoudre ton pb, tu fais un beau TerminateThread et puis voila...

Reply

Marsh Posté le 14-10-2003 à 18:13:15    

bon... a priori tu aurais raison, ca bloque sur la derniere insertion, apres la boucle while du thread.
pour infos : proj en win32, en C, et insertion dans une listbox.
 
vais regarder ca de plus près... y a pas de raison pour que ca marche pas.

Reply

Marsh Posté le 14-10-2003 à 18:23:45    

je viens de comprendre pourquoi :  (attention c'est complexe a expliquer)
 
apres la mise a 0 de la variable qui permet de dire au thread de se finir, tu fais un WaitForSingleObject. Donc la boucle while du thread se termine, et l'appel a SendMessage s'effectue. Or le WaitForSingleObject (qui est un peu l'équivalent d'un Sleep) se trouve dans la DlgProc, et bloque donc tous les messages en queue, ce qui bloque le SendMessage de la listbox, et donc tu te retrouves dans une position ou le SendMessage du thread attend que le msg soit traité, et ta DlgProc attend que le thread se termine => on appelle ca un deadlock je crois : les 2 threads attendent un évenement de la part de l'autre thread, et sont donc bloqués...
 
remplacer le dernier SendMessage par un PostMessage résoud la situation.
 
edit:
en fait remplacer le Send par un Post c'est pas forcèment la bonne solution, pasque bloquer la DlgProc c'est jamais bon. tu devrais donc eviter le WaitForSingleObject, et trouver un autre moyen, selon tes besoins.
En mettant juste la variable a 0 ca suffirait pas ?


Message édité par Konar le 14-10-2003 à 18:33:03
Reply

Marsh Posté le 14-10-2003 à 18:39:26    

Deux suggestions :

  • Pourquoi ne pas utiliser un évenement HEVENT plutôt qu'une variable pour indiquer à la thread de se terminer.

Ton code dans ta thread serait du genre :

Code :
  1. while( WaitForSingleObject( pDlg->m_hStopThread, 500 ) == WAIT_TIMEOUT )
  2. {
  3. /*... mise à jour de la liste ... */
  4. }


 

  • De même, je crois qu'il est déconseillé de mettre à jour des contrôles avec des PostMessage (dans la documentation, Microsoft parle systématiquement de SendMessage). Donc ce que tu peut faire, c'est faire un message privé que ta thread va envoyer via PostMessage à ta boîte de dialogue et dans le traîtement de ce message ta boîte de dialogue ajoute un élement à ta liste.


---------------
each day I don't die is cheating
Reply

Marsh Posté le 14-10-2003 à 18:48:15    

Ca change pas le pb de la DlgProc bloquée.
ca revient au même de virer le SendMessage (ou le InsertItem dans son cas) situé après le while du thread, pour le mettre apres le WaitForSingleObject de l'évenement du clic de bouton. La DlgProc sera bloquée au max 500 ms (quoique c'est pas critique non plus...)

Reply

Marsh Posté le 14-10-2003 à 18:55:03    

merci beaucoup , je n avais pas pensé que le WaitForSingleObject bloquait le traitement des messages. Je vais donc essayer de trouver une solution a ce probleme.
 
eh beh , c est pas facile a détecter ce genre de probleme.
 
Encore merci de votre aide , ca m'aide beaucoup :)... je vous dirai demain si j ai reussi a trouver une autre solution.
 
edit : konar, ton explication etait tres claire ;)


Message édité par xilebo le 14-10-2003 à 18:57:23
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed