WindowMessage wird unbeabsichtigt ausgelöst



  • Hallo,

    ich habe mir ein kleines Programm zur Kommunikation mit der seriellen Schnittstelle geschrieben. Die Kommunikation selbst funktioniert prima, nur die GUI ist noch fehlerbehaftet.

    Die GUI besitzt folgende Schaltflächen: "Verbinden", "Trennen", "Senden" und "Beenden".
    Darüber hinaus noch zwei List-Boxes und eine Edit-Box. Die List-Boxes sind zum Anzeigen der gesendeten/empfangenen Zeichenketten und die Edit-Box zur Eingabe.

    Hier mal der Quelltext für das Hauptprogramm:

    // *****	Resourcendatei includieren	*****
    #include <windows.h>
    #include "resource.h"
    #include "SerCom.h"
    
    // *****	Globale Variablen	*****
    HINSTANCE hInst = 0;
    DCB sDCB;
    char Recv_Buffer[255];	
    
    // *****	Funktionsprototypen	*****
    BOOL CALLBACK DialogProc (HWND, UINT, WPARAM, LPARAM);
    
    // *****	Hauptfunktion mit Dialogaufruf	*****	
    int  WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
                        LPSTR lpCmdLine, int nCmdShow)
    {
    	hInst = hInstance;
       return  DialogBox (hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DialogProc);
    }
    
    // *****	Dialogprozedur	*****
    BOOL CALLBACK DialogProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	HWND hEdit;		// Handle Eingabefeld
    	int cTxtLen;	// Stringlaenge speichern
    	LPSTR lpszMem;	// Eingabepuffer
    
    	switch(message)
    	{
    	  case  WM_INITDIALOG:
    		  {
    			  // Dialogicon setzen
    			  SendMessage(hDlg,WM_SETICON,TRUE,(LONG) LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON1)));
    			  return  TRUE;
    		  }
          case  WM_CLOSE:
    		  {
    			  EndDialog(hDlg,0);
    			  return  TRUE;
    		  }
          case  WM_COMMAND:
    		  {
    			  switch(LOWORD(wParam))
    			  {
    				  case  IDEXIT:
    					  {
    						  ClosePort();
    						  EndDialog(hDlg,0);
    						  return  TRUE;
    					  }
    				  case IDC_BTCNCT: // "Verbinden"-Knopf
    					  {
    						  InitDCB(&sDCB);
    						  if(!OpenPort(2, &sDCB))
    							  MessageBox(hDlg, "Verbindung konnte nicht hergestellt werden!", "Fehler!", MB_ICONERROR | MB_OK);
    						  else
    							  MessageBox(hDlg, "Verbindung auf COM2 hergestellt!", "Nachricht", MB_ICONWARNING | MB_OK);
    
    						  return TRUE;
    					  }
    				  case IDC_BTDISCNCT: // "Trennen"-Knopf
    					  {
    						  if(!ClosePort())
    							  MessageBox(hDlg, "Verbindung konnte nicht getrennt werden!", "Fehler!", MB_ICONERROR | MB_OK);
    						  else
    							  MessageBox(hDlg, "Verbindung getrennt!", "Nachricht", MB_ICONWARNING | MB_OK);
    					  }
    				  case IDC_BTSEND: // "Senden"-Knopf
    					  {
    						  hEdit = GetDlgItem(hDlg, IDC_EDIT1);
    						  cTxtLen = GetWindowTextLength(hEdit);
    						  lpszMem = (LPSTR) VirtualAlloc((LPVOID) NULL, (DWORD) (cTxtLen + 1), MEM_COMMIT, PAGE_READWRITE);
    
    						  GetWindowText(hEdit, lpszMem, cTxtLen + 1);
    						  if (lpszMem != NULL)
    						  {
    								SendDlgItemMessage(hDlg, IDC_LIST1, LB_ADDSTRING, 0, (DWORD) ((LPSTR) lpszMem)); 
    								SetWindowText(hEdit, (LPSTR) NULL);
    								WritePort(lpszMem);
    						  };				
    					  }
    
    				  default:
    					  break;
    			  }
             }
    	  case WM_USER:
    		  {
    			  SendDlgItemMessage(hDlg, IDC_LIST2, LB_ADDSTRING, 0, (DWORD) &Recv_Buffer[0]);
    			  return TRUE;
    		  }
    
             return  FALSE;
       }
       return  FALSE;
    }
    

    OpenPort erstellt ein zusätzlichen Thread, mit dem die eingehenden Zeichen per ReadPort abgefragt werden. In ReadPort wird per SendMessage ein Rundruf gestartet, um den Lesepuffer an eine der List-Boxes zu übergeben.

    SendMessage( HWND_BROADCAST, WM_USER, NULL, NULL );
    
    case WM_USER:
    		  {
    			  SendDlgItemMessage(hDlg, IDC_LIST2, LB_ADDSTRING, 0, (DWORD) &Recv_Buffer[0]);
    			  return TRUE;
    		  }
    

    Jetzt wird nur jedes mal, wenn ich einen Klick auf ein Steuerelement oder einen Tastendruck, mit Eingabefokus auf das Eingabefeld, durchführe die WM_USER ausgeführt und der Lesepuffer geschrieben. Das ist offensichtlich sehr unschön.

    Wie kann ich dieses Verhalten dahingehend ändern, dass wirklich nur beim Eintreffen eines Zeichens die WindowMessage ausgelöst wird?

    MfG

    Fried


  • Mod

    Schau Dir mal bitte die Dokumentation zu dem break statement an und lies mal was über switch/case!



  • Ja, gut. Habe ich gemacht. Kannst du es bitte etwas genauer formulieren. Ich seh' noch nicht, wo das Problem liegen soll.



  • Fried schrieb:

    Ja, gut. Habe ich gemacht.

    Offenbar nicht, sonst hättest du erkannt, wo das Problem ist.

    Wenn du am Ende eines case-Blocks kein return oder break einfügst, macht das Programm mit dem nachfolgenden case-Block weiter.



  • Hm, ja. Das habe ich mal geändert. Das Problem besteht weiterhin.

    Das Entfernen der break-Anweisung hat nichts gebracht.



  • Fried schrieb:

    Das Entfernen der break-Anweisung hat nichts gebracht.

    Du sollst auch nichts entfernen. In deinem Program fehlt etwas!

    Vielleicht schaust du dir das Kapitel über switch nochmal an.



  • Hier meine ersten Auffälligkeiten:

    Zwischen den case-Zweigen WM_COMMAND und WM_USER fehlt vielleicht ein break ?

    Zwischen den case-Zweigen IDC_BTDISCNCT und IDC_BTSEND fehlt vielleicht ein break ?

    Meine persönliche Empfehlung: Schließe jeden case-Zweig mit einem break-Befehl ab, auch dann wenn der Zweig vorzeitig durch return verlassen wird. Nur so kannst Du Dir einen unbewußten Automatismus beim Programmieren "trainieren" und solche sehr gemeine Stolperfallen vermeiden.

    Die Nachricht (den "Rundruf")

    SendMessage( HWND_BROADCAST, WM_USER, NULL, NULL );
    

    an ALLE Prozesse zu senden ist wohl ein wenig oversized 😮 es soll ja NUR Dein eigener Thread in Deinem eigenen Prozess benachrichtigt werden, oder nicht?

    Martin



  • Okay, die Nachrichtenschleife des Dialoges sieht jetzt wie folgt aus:

    switch(message)
    	{
    	  case  WM_INITDIALOG:
    		  {
    			  // Dialogicon setzen
    			  SendMessage(hDlg,WM_SETICON,TRUE,(LONG) LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON1)));
    			  return  TRUE;
    		  }
          case  WM_CLOSE:
    		  {
    			  EndDialog(hDlg,0);
    			  return  TRUE;
    		  }
          case  WM_COMMAND:
    		  {
    			  switch(LOWORD(wParam))
    			  {
    				  case  IDEXIT:
    					  {
    						  ClosePort();
    						  EndDialog(hDlg,0);
    						  return  TRUE;
    					  }
    				  case IDC_BTCNCT: // "Verbinden"-Knopf
    					  {
    						  InitDCB(&sDCB);
    						  if(!OpenPort(2, &sDCB))
    							  MessageBox(hDlg, "Verbindung konnte nicht hergestellt werden!", "Fehler!", MB_ICONERROR | MB_OK);
    						  else
    							  MessageBox(hDlg, "Verbindung auf COM2 hergestellt!", "Nachricht", MB_ICONWARNING | MB_OK);
    
    						  return TRUE;
    					  }
    				  case IDC_BTDISCNCT: // "Trennen"-Knopf
    					  {
    						  if(!ClosePort())
    							  MessageBox(hDlg, "Verbindung konnte nicht getrennt werden!", "Fehler!", MB_ICONERROR | MB_OK);
    						  else
    							  MessageBox(hDlg, "Verbindung getrennt!", "Nachricht", MB_ICONWARNING | MB_OK);
    						  return TRUE;
    					  }
    				  case IDC_BTSEND: // "Senden"-Knopf
    					  {
    						  hEdit = GetDlgItem(hDlg, IDC_EDIT1);
    						  cTxtLen = GetWindowTextLength(hEdit);
    						  lpszMem = (LPSTR) VirtualAlloc((LPVOID) NULL, (DWORD) (cTxtLen + 1), MEM_COMMIT, PAGE_READWRITE);
    
    						  GetWindowText(hEdit, lpszMem, cTxtLen + 1);
    						  if (lpszMem != NULL)
    						  {
    								SendDlgItemMessage(hDlg, IDC_LIST1, LB_ADDSTRING, 0, (DWORD) ((LPSTR) lpszMem)); 
    								SetWindowText(hEdit, (LPSTR) NULL);
    								WritePort(lpszMem);
    						  };
    						  return TRUE;						  
    					  }
    				  default:
    					  break;
    			  }
             }
    	  case WM_USER:
    		  {
    			  SendDlgItemMessage(hDlg, IDC_LIST2, LB_ADDSTRING, 0, (DWORD) &Recv_Buffer[0]);
    			  return TRUE;
    		  }
    	  default:
             break;
       }
    

    Die einzelnen Case-Statements werden mit return TRUE abgeschlossen, da die Nachricht ja bearbeitet wurde (Zumindest habe ich das so verstanden.).

    Das ein Rundruf problematisch ist, habe ich mir auch schon gedacht. Das Einzige, was da helfen könnte, wäre ein Zugriff auf das Handle des Dialoges. CreateThread mault aber rum, wenn ich der Funktion ReadPort dieses Handle als Parameter übergebe. Bleibt nur globaler Zugriff.

    Gibt's eine bessere Möglichkeit?



  • Entweder via RegisterWindowMessage () sich eine "eigene" Botschaft besorgen oder dafür sorgen, dass CreateThread () nicht mehr "herummault".
    Schliesslich kannst Du nicht ahnen, was ein WM_USER bei anderen Anwendungen alles auslöst.



  • Ich habe jetzt die switch-case-Geschichte in Ordnung gebracht.
    Daran hat's allerdings nicht gelegen.

    Es wird jedes Mal, wenn ich einen Button oder ein Feld klicke die Nachricht WM_USER geschickt.
    Wie kann ich das abstellen?



  • Fried schrieb:

    Ich habe jetzt die switch-case-Geschichte in Ordnung gebracht.
    Daran hat's allerdings nicht gelegen.

    Au weia 🙄

    Fried schrieb:

    Wie kann ich das abstellen?

    Indem du am Ende deines case-Blocks für WM_COMMAND ein break einfügst!



  • Die Lösung des Problems liegt im korrekten Ansprechen des Handles für die Listbox. Wenn die global verfügbar ist, kann ich das Ganze ohne WindowMessage durchspielen. Die restlichen Komponenten des Dialogs, die offensichtlich störend wirken, werden somit umgangen.

    Danke alle, die mitgeholfen haben.


  • Mod

    Fried schrieb:

    Ja, das habe ich schon gemacht. Der Fall WM_USER müßte jetzt eigentlich separat behandelt werden, wird er aber nicht.

    👎 Du hast es nicht verstanden!

    Lass bitte den Broadcast bleiben. Warum sollen andere Prozesse Deine WM_USER Nachricht bekommen? Was ist wenn die unter WM_USER was ganz anderes verstehen...



  • Aye, aye! Broadcast abgestellt und stattdessen LB_ADDSTRING direkt an das Handle der Box geschickt.

    SendMessage erwartet ja als ersten Parameter ein HWND. Das gebe ich ihm jetzt als HWND der Listbox mit. Damit gibt's keine WM_USER an die anderen Boxes.

    Vorrangig ist, dass es läuft. Es ist auch kein Freizeit-Projekt, ich steh' unter Termindruck. 😃


  • Mod

    Fried schrieb:

    Aye, aye! Broadcast abgestellt und stattdessen LB_ADDSTRING direkt an das Handle der Box geschickt.

    SendMessage erwartet ja als ersten Parameter ein HWND. Das gebe ich ihm jetzt als HWND der Listbox mit. Damit gibt's keine WM_USER an die anderen Boxes.

    Vorrangig ist, dass es läuft. Es ist auch kein Freizeit-Projekt, ich steh' unter Termindruck. 😃

    Und übergibst Du auch den lParam richtig? Ansonsten WM_USER senden und dabei Das Handle Deines Hauptfensters verwenden, dass eben auch die WM_USER Nachricht behandelt.



  • Die GUI funktioniert jetzt so, wie sie soll.

    Der Aufruf für die Listbox sieht nun so aus:

    SendMessage( hWnd, LB_ADDSTRING, 0, (LPARAM)(LPSTR)Recv_Buffer );


Anmelden zum Antworten