Problem mit RichEdit StreamCallback
-
Hallo,
Ich habe eine Problem mit der StreamCallback Funktion die über die Nachricht EM_STREAMOUT angesprochen wird.Vorweg:
Ich habe aus den RichEdit Nachrichten eine Klasse gemacht anlehnend an die von MFC CRichEdit. Generell Funktioniert die StreamCallback/EM_STREAMOUT Funktion, wenn ich mit ihr in einer Nachrichtenschleife einer Anwendung arbeite.Jetzt bin ich in den letzten Tagen über ein Problem gestolpert, was so noch nicht aufgetreten ist. Ich muss mit meiner RichEdit Klasse in einem Thread arbeiten, der mit der CreateThread Funktion erstellt wurde.
Wenn ich mit meiner RichEdit Klasse in einem Thread arbeite, funktioniert die _GetStreamCallback Funktion nicht mehr richtig
der Fehler passiert in der Zeile 113 im nachfolgendem Code. die Funktion fwrite gibt mir exakt die gleiche Anzahl von geschriebenen Zeichen zurück wie die _GetStreamCallback Funktion im Parameter cb vorschreibt. ABER die Datei hFile und der damit verbundene Zeichenfolgenpuffer lpBuffer, enthalten keine Werte.
Wie gesagt, der Code funktioniert,... nur nicht wenn er in einem Thread läuft.
Warum werden da keine Werte in die Datei bzw. in den Puffer geschrieben?
Weis da jemand Rat? Bin über jede Hilfe dankbar...[EDIT]
Ich vergas, was ich vielleicht noch erwähnen sollte, Das komische an der Sache ist das Gegenstück zu meiner _GetStreamCallback Funktion, also _SetStreamCallback funktioniert in dem Thread. Ich kann also werte in das Steuerelement schreiben, aber keine auslesen.long CRichEdit::GetTextStream (LPSTR &lpBuffer, UINT uiFormat /*= SF_TEXT*/) { /************************************************************************************************************************************** GET TEXT STREAM liest den Text aus dem Stream des Rich Edit Steuerelementes aus - in - uiFormat Spezifiziert das Datenformat des einlesens und die Ersetzungsoptionen - out - lpBuffer Zeiger auf einen Zeichenfolgenpuffer in dem die Daten aus dem Steuerelement übertragen werden. - return - Integer Wert der die Länge des Zeichenfolgenpuffers bestimmt. **************************************************************************************************************************************/ long lReturn = 0; //-------------------------------------------------------------------------------------------------------------- // Dateistream erstellen und den Zeichenfolgenpuffer mit dem Stream verbinden FILE *hFile = SetStreamBuffer(lpBuffer); //-------------------------------------------------------------------------------------------------------------- // EditStream Strucktur erstellen und Rückruffunktion die den Dateistream befüllt übergeben m_es.dwCookie = (DWORD)hFile; m_es.pfnCallback = _GetStreamCallback; //-------------------------------------------------------------------------------------------------------------- // Daten auslesen Starten lReturn = StreamOut(SF_TEXT,m_es); //-------------------------------------------------------------------------------------------------------------- // Willkürliche Zeichen im Zeichenfolgenpuffer abschneiden und Werte vom Dateipuffer in die Datei schreiben lpBuffer[lReturn]=0; fflush (hFile ); fclose(hFile);//Datei Schliesen //-------------------------------------------------------------------------------------------------------------- // Anzahl der Zeichenfolgen im Puffer zurück geben. return lReturn; } FILE* CRichEdit::SetStreamBuffer (LPSTR &lpszBuffer) { /************************************************************************************************************************************** SET STREAM BUFFER erstellt einen Temporäre Datei für das ein o. auslesen von Daten in/aus dem Ritch Edit Steuerelement. - in/out - lpszBuffer Zeiger auf einen Zeichenfolgenpuffer, der für das auslesen von Daten aus dem Steuerelement mit dem Dateihandel verbunden wird. In diesen Puffer werden die Daten Zeitgleich wie zu der Temporären Datei geschrieben. - return - Zeiger auf ein Temporäres Dateihandel, in das Daten ein oder aus dem Steuerelement gelesen werden können. - Merke: - Der Zeichenfolgenpuffer MUSS gelöscht werden, nachdem er nicht mehr gebraucht wird um Memory Leaks zu vermeiden. **************************************************************************************************************************************/ int iCtrlSize = ((lpszBuffer == NULL) ? (GetTextLength()+1) : strlen(lpszBuffer)); const int iSize = (iCtrlSize <= 1) ? MAX_TEXT : iCtrlSize; BOOL bNewBuffer = FALSE; //-------------------------------------------------------------------------------------------------------------- // Zeichenfolgenpuffer für das auslesen der Daten aus dem Steuerelement if(lpszBuffer == NULL) { lpszBuffer = new char [iSize]; memset(lpszBuffer,0,iSize); bNewBuffer = TRUE; } //-------------------------------------------------------------------------------------------------------------- // Datei Stream erstellen FILE* hFile = NULL; hFile = tmpfile (); fseek (hFile,0,SEEK_SET); //Cursor an den Anfang der Datei setzen //-------------------------------------------------------------------------------------------------------------- // Vom Steuerelement lesen // Zeichenfolgen Puffer mit der Temporären Datei verbinden, Daten werden direkt in den Puffer geschrieben if(bNewBuffer) setvbuf ( hFile , lpszBuffer , _IOFBF , iSize ); //Datei an die größe anpassen //-------------------------------------------------------------------------------------------------------------- // Ins Steuerelement schreiben // Datei auf die größe des Zeichenfolgenpuffers setzten und Daten in die temporäre Datei schreiben else { setvbuf ( hFile , NULL , _IOFBF , iSize ); //Datei an die größe anpassen fwrite(lpszBuffer,sizeof(char),iSize,hFile); fflush (hFile ); } fseek (hFile,0,SEEK_SET); //Cursor an den Anfang der Datei setzen return hFile; } DWORD CALLBACK CRichEdit::_GetStreamCallback (DWORD dwCookie, LPBYTE lpBuff,LONG cb, LONG *pcb) { /************************************************************************************************************************************** GET STREAM CALL BACK liest Daten aus dem Rich Edit Steuerelement in einen Zeichenfolgenpuffer, der mit der Datei verbunden ist. - in - dwCookie Zeiger auf ein Datei Handel, in das die Daten aus dem Steuerelement geschrieben werden. - pbBuff Zeiger auf einen Zeichenfolgen Puffer, der die Daten aus dem Steuerelement übergiebt. - cb Integer wert der die Anzahl an Zeichenfolgen in dem Parameter pbBuff bestimmt. - pcb Zeiger zu einer Integer Variable, die die Anzahl der geschrieben Daten in die Datei bestimmt. Ist diese Zahl kleiner o. NULL, als der Wert im Parameter cb, wird die Funktion nicht mehr aufgerufen. - return - Integer Wert der bestimmt, ob die Funktion erfolgreich ausgeführt worden ist. Wird eine 0 zurück gegeben wurde die Funktion erfolgreich ausgeführt, ein Wert ungleich 0 Bedeutet das ein Fehler aufgetreten ist und die Funktion nicht erneut aufgerufen wird. - Merke: - Die GetStreamCallback Funktion ist eine Anwendungs-definierte Rückruffunktion, die weiterführend mit den EM_STREAMOUT Mitteilungen verwendet wird. - Wird die Klasse CRichEdit in einem Thread gestartet, werden keine Werte aus dem Puffer lpBuff in die Datei(hFile) geschrieben, obwohl der Rückgabewert pcb die länge der geschriebenen Bytes enthält. ***************************************************************************************************************************************/ FILE *hFile = (FILE*)dwCookie; lpBuff[cb] = 0;//Die Zeichenfolge abschneiden um keine willkürlichen Zeichen zu bekommen *pcb = fwrite(lpBuff,sizeof(BYTE),cb,hFile); //-------------------------------------------------------------------------------------------------------------- // Alles im Puffer auf NULL setzten um keine vorherigen Werte zu bekommen. memset(lpBuff,0,cb); //-------------------------------------------------------------------------------------------------------------- // Wenn die Anzahl der geschriebenen Zeichen mit der Anzahl der Zeichen im übergebene lpBuff übereinstimmt, // wird NULL zurück gegeben. Jeglichen anderen Rückgabewerte bewirken das die Funktion nicht erneut aufgerufen wird. return (*pcb - cb); } long CRichEdit::StreamOut (UINT uiFormat,EDITSTREAM& es ) { /************************************************************************************************************************************** STREAM OUT Die EM_STREAMOUT Mitteilung veranlast ein RichEdit Steuerungselement, seinen Inhalt zu einer Anwendungsdefinierten EditStreamCallback Funktion zu senden. Die Wiederholungsfunktion kann dann den Datenstrom zu einer Datei oder zu jeder anderen möglicher Position schreiben. - in - uiFormat Spezifiziert das Datenformat- und -Ersetzungs optionen - es Zeiger auf eine EDITSTREAM Struktur. **************************************************************************************************************************************/ HWND hWndRichEdit = GetDlgItem(); return (long)::SendMessage(hWndRichEdit,EM_STREAMOUT,(WPARAM)uiFormat,(LPARAM)&es); }
-
Text from RichEdit:
http://stackoverflow.com/questions/20700343/how-to-get-text-from-rich-edit-control-as-a-cstringThreads kannst erstellen, wenn RTB Inhalt nach bestimmten zeit aktualisiren willst.
Vermutlich beim RTB auslesen blockirst du GUI Thread, du mus Thread fur schreiben stoppen, dann kannst auslesen.
-
Sorry hab mich verschrieben....mit Thread "schreiben" blockierst den GUI Thread,darum kannst nichts in RTB auslesen. Hast Möglichkeiten, schmeise Threads raus oder bende/stoppe Thread "schreiben".
-
Hallo,
erst mal danke für deine Antwort. Ich versteh was du meinst, mit deiner Hilfestellung, aber es ist definitiv nicht so, dass der Schreibvorgang den Lesevorgang behindert oder diese sich irgendwo kreuzen.
der Fehler passiert in folgender Funktion. Wie du dort siehst, wird GetStreamCallback (Zeile 12) vor der Funktion SetStreamCallback (Zeile 21) angesprochen. Also der Lesevorgang, kann nicht durch den Schreibvorgang beeinflusst werden. Es gibt auch keine andere Funktion in der Klasse, die auf das RichEdit Steuerelement zugreift.void CEMailSendStatusDlg::SetStatusText () { /************************************************************************************************************************************** SET STATUS TEXT übergibt den Text aus der m_sStatus Variable an das RichEdit Steuerelement **************************************************************************************************************************************/ //-------------------------------------------------------------------------------------------------------------- // Bisherigen Text aus dem Steuerelement auslesen // Merke: // - GetTextStream gibt hier keine Werte zurück, weil in der Funktion CRichEdit::_GetStreamCallback // die übergebenen werte nicht in die Temporäre Datei geschrieben werden. LPSTR lpszBuffer = NULL; m_CR_Status.GetTextStream(lpszBuffer); //-------------------------------------------------------------------------------------------------------------- // Bisherigen und neuen Text zusammenführen string sBuffer = lpszBuffer; sBuffer += (::lstrlen(lpszBuffer) > 0) ? CRLF : _T(""); sBuffer += m_sStatus; _DELETE(lpszBuffer); //-------------------------------------------------------------------------------------------------------------- // Text in das Steuerelement setzen m_CR_Status.SetTextStream((LPSTR)sBuffer.c_str()); m_CR_Status.LineScroll(m_CR_Status.GetLineCount()); m_sStatus.clear(); }
Ich Beschreibe mal das ganze Scenario...
CEMailSendStatusDlg Ist ein kleiner Dialog mit zwei Steuerelementen. Einem Progressbar- und einem RichEdit Steuerelement. Der Dialog dient dazu, mir den Versendestatus von E-Mails anzuzeigen. Das RichEdit empfängt dabei solche Nachrichten wie "Start SSL" "Login Ok" usw. Die Progressbar zeigt an wie viele Bytes bisher gesendet wurde.In der Klasse CEMailSendStatusDlg starten 2 Threads die via CreateThread erstellt werden und die in einer while Schleife mit WaitOnAddress Angehalten werden.
In dem EMailDialog in dem ich die E-Mail erstelle, anzeige und versende, wird der EMailStatus Dialog, vor dem versenden der E-Mail und dem Thread der die E-Mail versendet, gestartet.
Aus einer Klasse die in der Hierarchie etwas unterhalb der EMailDialog Klasse liegt, werden dann die Wert für den EMailStatus Dialog gesetzt und die beiden WaitOnAddress Funktionen aufgeweckt, woraufhin die Progressbar und das RichEdit Steuerelement aktualisiert wird.
Das Ganze funktioniert soweit ganz gut, ich komme auch über WM_GETTEXT Nachricht an die bisherigen Werte des RichEdit Steuerelementes. Generell würde ich aber dennoch gerne verstehen, warum ausgerechnet in dem Moment GetStreamCallback versagt.
-
Mir ist Dein Code zu kompliziert. Für was benötigst Du die Datei, wenn Du nur die Daten kopieren willst?
Wir kommst Du darauf, dass Du den Puffer, den Die die Callback Funktion gibt, löschen zu dürfen?
-
Die Datei hFile benötige ich um mit dieser Funktion werte in Dateien (C:\test.txt) zu schreiben oder aus ihr zu lesen.
das gegenstück zu Get- & SetTextStream ist Get- &SetFileStreamWir kommst Du darauf, dass Du den Puffer, den Die die Callback Funktion gibt, löschen zu dürfen
welchen puffer meinst du?
-
Zeile 117
memset(lpBuff,0,cb);
-
weil in dem puffer, alte werte stehen bleiben, wenn die vorhergehende Zeichenfolge länge war, als die aktuelle. deshalb kam ich auf die Idee den zu löschen. ansonsten gibt es keinen Grund dafür.
steht ja auch im Kommentar darüber.
-
Und was interessiert dich das bitte?
Der Callback sagt Dir genau wie weit Du den Speicher zu benutzen hast. Du hast da gar nichts selbst zu ermitteln.
-
LowFly so wie du dir vorgestellst hast, der Code aus deinem ersten Post einfach in Thread packen und ausfuhren, so wird nicht funz.
Du versuchst aus dem Thread auf RichEdit Steuerelement mit Funktion "StreamOut" zugreifen(Threadübergreifend), dein richtige Lösung ist "Threadübergreifende programmierung",schau im netz ob wad findest, ansonsten kann ich dir paar links zu Dokus & Beispiele posten. Kannst dir sicher sein, dass du in deinem Projekt/Code wirst einiges ändern müssen.
-
@MR C
Ah ok,
ja dann poste doch mal die links bitte
-
Mal Grundsätzlich: Du hast also einen anderen Thread der auf das RTF Control zugreift?
Wenn dem so ist, kannst Du den gleich wieder vergessen, weil jede Interaktion letzten Endes durch die Messagequeue synchronisiert wird.Zudem besteht ja sogar die Möglichkeit, dass sich das Control in der Größe bereits verändert hat und entsprechend der Buffer gar nicht passt.
Weiterhin ist nach wie vor, Dein Code ein Witz.
Wir kommst Darauf über strlen auf die Größe eines allokierten Buffers zu schließen? Wenn also im letzten Zyklus nur noch ein Byte übertragen wurde wir dim nächsten Durchgang alles byteweise durchlaufen obwohl der Buffer ursprünglich mal größer war.Dein ganzer Konstrukt mit dem Buffer der irgendwie erhalten bleibt und wieder verwendet wird hat viel zu viele Seiteneffekte.
Ich sehe auch keinen Schutz, dass dieser Buffer nicht aus zwei Threads verwendet werden könnte.Ich habe das mal durchgespielt und bekomme Deine Probleme nicht.
Allerdings kollekiert mein Program einfach alle Daten in einem Puffer.Ich würde davon ausgehen, dass Du einfach einen Fehler in Deinem Code hast.
-
LowFly schau hier rein...
Einführung in Threads:
https://msdn.microsoft.com/de-de/library/ms171728(v=vs.110).aspx?cs-save-lang=1&cs-lang=cpp#code-snippet-1
https://www.labri.fr/perso/betrema/winnt/frogfly1.html
(+SourceCode):
http://www.ucancode.net/Visual_C_MFC_Samples/WaitForSingleObject-_beginthreadex-SetEvent-Start-Stop-Thread-VC-MFC-Example.htm
http://flylib.com/books/en/4.348.1.78/1/
http://www.tenouk.com/visualcplusmfc/visualcplusmfc22.html
https://www.tutorialcup.com/cplusplus/multithreading.htmBeispiele:
http://www.codeproject.com/Articles/14746/Multithreading-Tutorial
http://www.codeproject.com/Articles/43994/The-Practical-Guide-to-Multithreading-Part
http://www.cppfun.com/win32-multi-threads-programming.htm
-
Martin er kann fehler suchen solange es geht. Er hat doch geschrieben, dass der Code ohne Thread funz tadenlos. Ohne "Threadübergreifende programmierung" kommt er kein Schritt weiter. Er greift nicht mit Thread auf RichEdit, sondern aus dem Thread auf RichEdit und so einfach geht das nicht, in NET mus er mit Ivnoke arbeiten.
-
Mit einem Thread ist das doch sowieso quatsch. Er verwendet SendMessage. Also gibt es eine Threadsynchronisation. Da muss man in der Windows API gar nichts machen. Aber ein Thread bringt hier auch keinerlei Vorteile.
-
...fur die hintergrund events ohne die GUI Threads oder andere events zu blockieren wie z.b Emails in Dateien archivieren oder Email versenden und empafngen,usw.... bringen Threads seine voteile.
-
Klar bringen Threads was, aber nur dann wenn sie hinreichend von der UI entkoppelt werden.
Und hier nicht, wenn er SendMessage verwendet und ausgerechnet, dass für eine Operation die länger dauern kann.
-
Martin des habe ich schon erwähnt...wiederhole nicht, dass was ich schon geschrieben habe.