Forum: PC-Programmierung WriteFile und ReadFile


von Thomas H. (datatom)


Lesenswert?

Hallo zusammen,

ich sende Daten vom PC zum Controller RN-Mega 2560 und die gleichen 
Daten wieder zurück. Wenn ich den ersten Datensatz "127X1" mit WriteFile 
sende erhalte ich den korrekten Datensatz "127X1" wieder per ReadFile 
zurück. Beim zweiten Datensatz sende ich "127Y1" und erhalte 
"127X1ÌÌÌÌ“ŠupLñ*" anstatt "127Y1" zurück. Nach dem Senden sende ich 
noch "\r\n" per ReadFile, um das Ende des Datensatzes mitzuteilen.


Mein Bascom-Programm sieht folgendermaßen aus:
1
$prog , 255 , &B11011100 ,                                  '&B11011001 ,                                  'Quarz an / Teiler aus / Jtag aus
2
$regfile = "m2560def.dat"
3
$hwstack = 82                                               '80
4
$framesize = 68                                             ' 64
5
$swstack = 68                                               '44
6
$crystal = 16000000                                         'Quarzfrequenz
7
Config Pind.5 = Output
8
Led Alias Portd.5
9
$baud = 19200
10
11
12
'Config Com1 = 19200 , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0
13
'Config Com2 = 38400 , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0
14
Config Com3 = 19200 , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0
15
'Config Com4 = 38400 , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0
16
'Open "com1:" For Binary As #1                               'RS232
17
'Open "com2:" For Binary As #2                               'RS232
18
Open "com3:" For Binary As #3                               'RS232
19
'Open "com4:" For Binary As #4                               'RS232
20
21
22
23
Dim Eingabe As String * 50
24
Dim Ausgabe As String * 25
25
26
Dim String1 As String * 3
27
Dim String2 As String * 2
28
29
Dim X1_position As Integer
30
Dim Y1_position As Integer
31
Dim Z1_position As Integer
32
33
Config Porth = Output
34
Out1 Alias Porth.0
35
Out2 Alias Porth.1
36
Out3 Alias Porth.2
37
Out4 Alias Porth.3
38
Out5 Alias Porth.4
39
Out6 Alias Porth.5
40
Out7 Alias Porth.6
41
Out8 Alias Porth.7
42
43
Declare Sub Daten_empfangen()
44
Declare Sub Daten_senden()
45
46
Do
47
48
  Led = 0
49
  Call Daten_empfangen()
50
  Led = 1
51
  Call Daten_senden()
52
53
Loop
54
55
End
56
57
Sub Daten_empfangen()
58
59
  Input Eingabe
60
  String1 = Mid(eingabe , 1 , 3)
61
  String2 = Mid(eingabe , 4 , 2)
62
  Ausgabe = String1 + String2 + Chr(13)
63
64
  If String2 = "X1" Then
65
     X1_position = Val(string1)
66
  End If
67
68
  If String2 = "Y1" Then
69
     Y1_position = Val(string1)
70
  End If
71
72
  If String2 = "Z1" Then
73
     Z1_position = Val(string1)
74
  End If
75
76
End Sub
77
78
Sub Daten_senden()
79
80
  Print Ausgabe
81
82
End Sub

Meine ReadWrite-Funktion in C++ sieht so aus:
1
tbResult ReadAndWrite(HWND hwnd, char * char_cstr ) 
2
{
3
  //char write_buffer[10] = {' '};
4
  char read_buffer[8] = {' '};
5
  string string_parameter_read;
6
  string string_parameter_read2;
7
  string string_parameter_write;
8
  string string_parameter_write2;
9
  string string_fehlerfrei_gesendet;
10
  char *char_cstr2 = " ";
11
  char buffer_ende[] = {' '};
12
  char *angabe_achse = " ";
13
  size_t newsize = 0; // = strlen(write_buffer) + 1;
14
  ////////////////////////size_t origsize = wcslen(p_wchar_T) + 1;
15
  size_t origsize = 0;
16
  size_t convertedChars = 0;
17
  int i = 0;
18
  int Error = 1;
19
20
  if(board_angeschlossen == 1)
21
  {
22
    do // Senden bis der Achsenwert, etc. und das Endezeichen /r ordnungsmgemäß gesendet wurde
23
    {
24
      if(!WriteFile(hSerial, char_cstr, n, &dwBytesWrite, NULL))
25
      {
26
        //error occurred. Report to user
27
        msgboxIDss = MessageBox(NULL, (LPCWSTR)L"Fehler bei WriteFile!",
28
          (LPCWSTR)L"Fehler", MB_OK | MB_ICONEXCLAMATION);
29
        EndDialog(hwnd, 1);
30
        return TB_ERROR;
31
      }
32
      else
33
      {
34
        string_parameter_write = char_cstr;
35
        int laenge_string_write = string_parameter_write.length() - 2;
36
        string_parameter_write2 = string_parameter_write.substr(laenge_string_write,2);
37
        
38
        if(!WriteFile(hSerial, "\r\n", n, &dwBytesWrite, NULL))
39
        {
40
          //error occurred. Report to user
41
          msgboxIDss = MessageBox(NULL, (LPCWSTR)L"Fehler bei WriteFile!",
42
            (LPCWSTR)L"Fehler", MB_OK | MB_ICONEXCLAMATION);
43
          EndDialog(hwnd, 1);
44
          return TB_ERROR;
45
        }
46
        else
47
        {
48
          string_fehlerfrei_gesendet = "Ja";
49
        }
50
        
51
      }
52
    }while(string_fehlerfrei_gesendet == "Nein" );
53
54
    string_fehlerfrei_gesendet = "Nein";
55
56
    int do_zaehler = 1;
57
    do // Ausführen bis der empfangene Wert der Achse, etc. gleich des gesendeten Wertes der Achse ist
58
    {
59
      do_zaehler ++;
60
61
      //read_buffer[8] = ' ';
62
      if(!ReadFile(hSerial, &read_buffer, n, &dwBytesRead, NULL))
63
      {
64
        //error occurred. Report to user.
65
        msgboxIDss = MessageBox(NULL, (LPCWSTR)L"Fehler bei ReadFile!",
66
          (LPCWSTR)L"Fehler", MB_OK | MB_ICONEXCLAMATION);
67
        EndDialog(hwnd, 1);
68
        return TB_ERROR;
69
      }
70
71
      // Achsenwert übergeben
72
      string_parameter_read = read_buffer;
73
74
...
75
76
belangloses :)

Ich komm einfach nicht weiter:(

Kann mir bitte jemand helfen?

Vielen Dank.

Grüße

datatom

von ... (Gast)


Lesenswert?

Das hier ist schonmal etwas daneben!
1
if(!WriteFile(hSerial, "\r\n", n, &dwBytesWrite, NULL))
Schau Dir an, wozu der dritte Parameter dient! Wo kommt dieses "n" 
überhaupt her, in Deinem Codeschnipsel wird das nirgenwo gesetzt.

von Thomas H. (datatom)


Lesenswert?

N hat den wert 8. weil ich 8 bytes übertrage. Oder muß n den genauen 
Wert des zu übertragenen Characters haben? Bin gerade unterwegs und 
kanns nicht testen.

von ... (Gast)


Lesenswert?

Auweia.
Da muß drinstehen, wieviel Bytes aus dem angegebenen Buffer übertragen 
werden sollen! Und im nächsten Parameter bekommst Du zurück, wieviel 
tatsächlich übertragen wurden. Die beiden Werte müssen nicht 
notwendigerweise gleich sein.
Aber noch Schlimmer, in Deinem "Buffer" sind nur 3 Bytes ("\r\n")!
Wenn Du dem WriteFile sagst es soll 8 Bytes senden, sendest Du jede 
Menge Mist.

von Karl H. (kbuchegg)


Lesenswert?

Du solltest dir ganz schnell eine

WriteString

Funktion machen, die dir genau diese Details (Anzahl Bytes ist gleich 
der Stringlänge) abnimmt.

Solch kleine Hilfsfunktionen, die sich um 1 oder mehrere Details für 
einen bestimmten Datentypen kümmern, sind das wie man sich selber das 
Leben einfacher machen kann.

von Thomas H. (datatom)


Lesenswert?

Ich habe das Problem mit der falschen Längenangabe folgendermaßen 
gelöst:
1
...
2
3
laenge_write_char = sizeof(write_char);
4
      
5
      if(!WriteFile(hSerial, &write_char, laenge_write_char, &dwBytesWrite, NULL))
6
      {
7
        //error occurred. Report to user
8
        msgboxIDss = MessageBox(NULL, (LPCWSTR)L"Fehler bei WriteFile!",
9
          (LPCWSTR)L"Fehler", MB_OK | MB_ICONEXCLAMATION);
10
        EndDialog(hwnd, 1);
11
        return TB_ERROR;
12
      }
13
      else
14
      {
15
       
16
        ...         
17
   
18
        int laenge_write_r = strlen ("\r");
19
        
20
        if(!WriteFile(hSerial, "\r", laenge_write_r, &dwBytesWrite, NULL))
21
        {
22
          //error occurred. Report to user
23
          msgboxIDss = MessageBox(NULL, (LPCWSTR)L"Fehler bei WriteFile!",
24
            (LPCWSTR)L"Fehler", MB_OK | MB_ICONEXCLAMATION);
25
          EndDialog(hwnd, 1);
26
          return TB_ERROR;
27
        }
28
        else
29
        {
30
          string_fehlerfrei_gesendet = "Ja";
31
        }
32
        
33
      ...
34
35
      if(!ReadFile(hSerial, &read_buffer, laenge_write_char, &dwBytesRead, NULL))//n, &dwBytesRead, NULL))
36
      {
37
        //error occurred. Report to user.
38
        msgboxIDss = MessageBox(NULL, (LPCWSTR)L"Fehler bei ReadFile!",
39
          (LPCWSTR)L"Fehler", MB_OK | MB_ICONEXCLAMATION);
40
        EndDialog(hwnd, 1);
41
        return TB_ERROR;
42
      }
43
44
      // Achsenwert übergeben
45
      string_parameter_read = read_buffer;

Erster Durchlauf:
write_char hat den Wert 127X1
laenge_write_char hat den Wert 6

laenge_write_r hat den Wert 1

Senden klappt.

Lesen klappt auch.

read_buffer hat den Wert 127X1

So solls sein.

Aber beim zweiten Durchlauf klappts nicht mehr:-(

Senden klappt.

read_buffer hat den Wert 127XÌÌÌÌÌÌÕ©¦­ñ?
read_buffer ist so definiert:

char read_buffer[6];


Ich glaub ich bin blind.

von Karl H. (kbuchegg)


Lesenswert?

Du solltest zumindest dwBytesRead befragen, wieviele Zeichen empfangen 
wurden und nicht einfach annehmen, nur weil der ReadFile Call 
zurückkommt, hättest du 6 Zeichen gekriegt. ReadFile weiß nicht, auf 
wieviele Zeichen du wartest und kann auch vor Erreichen deiner magischen 
6 Zeichen Grenze schon w.o. geben.


wenn du also weißt, dass du eine bestimmte Zeichenanzahl abwarten musst, 
dann gehört das in eine Schleife, in der du die dwBytesRead aufsummierst 
und solange schleifst, bis du deine Anzahl beisammen hast.

Und das du hier

     string_parameter_read = read_buffer;

(string_parameter_read ist wohl eine String Klasse mit einem überladenen 
Zuweisungsoperator) sicherheitshalber den C-String auch noch sauber 
terminieren solltest, sollte eigentlich eine Selbstverständlichkeit 
sein. (*)


    read_buffer[ sizeof( read_buffer ) - 1 ] = '\0';
    string_parameter_read = read_buffer;


(*) ... die ich dir aber als C++ verwöhnter Programmierer nachsehe. 
C-Strings sind um einiges komplizierter zu benutzen als String-Klassen. 
Du musst wesentlich sorgfältiger damit umgehen und selbst dann sind sie 
immern noch wesentlich fehleranfälliger. Das rundum-sorglos Paket einer 
Klasse, die auf sich selbst aufpasst, existiert dort nicht.

von Thomas H. (datatom)


Lesenswert?

Wie frage ich denn dwBytesRead ab?

So?
1
int laenge_read_buffer = sizeof (dwBytesRead);

Bin gerade unterwegs und kanns nicht testen.

von Karl H. (kbuchegg)


Lesenswert?

Thomas Holländer schrieb:
> Wie frage ich denn dwBytesRead ab?

Das ist eine Variable.
Die hat einen Wert
1
    ....  ReadFile(hSerial, &read ........
2
3
    if( dwBytesRead == 5)
4
      MessageBox( ...  "Hurra, ich hab 5 Bytes bekommen" ... );
5
    else if( dwBytesRead == 4)
6
      MessageBox( ...  "Schmoll, ich hab bis jetzt nur 4 Bytes gekriegt" ... );

Oder eben in deinem Fall eine while Schleife aufziehen, die solange 
nicht verlassen wird, bis deine erwarteten 5 Bytes beisammen sind. Kein 
Mensch garantiert dir, dass du die Bytes in einem Stück bei einem 
einzigen Aufruf von ReadFile bekommst.


> So?
> int laenge_read_buffer = sizeof (dwBytesRead);

Duko lesen! In dwBytesRead informiert dich Windows darüber, wieviele 
Bytes es in deinen Buffer geschrieben hat. Dein Buffer kann 2 Kilobyte 
gross sein. Das heisst aber noch lange nicht, dass da dann auch 2 
Kilobytes reingeschrieben werden, wenn nur 5 Bytes daherkommen und der 
ReadFile wegen einem Timeout abgebrochen wird. Dann sagt dir eben 
dwbytesRead nach dem Aufruf, dass 5 Bytes in den Buffer geschrieben 
wurden.

von Thomas H. (datatom)


Lesenswert?

Versteh ich nich.

Ich habe jetzt die Längen 4 bis 8 durchprobiert. Beim zweiten Senden 
stimmt der Wert nicht mehr und die Doku sagt mir auch nichts.

von ... (Gast)


Lesenswert?

1
        int laenge_write_r = strlen ("\r");
2
        
3
        if(!WriteFile(hSerial, "\r", laenge_write_r, &dwBytesWrite, NULL))
4
        {
5
          //error occurred. Report to user
6
          msgboxIDss = MessageBox(NULL, (LPCWSTR)L"Fehler bei WriteFile!",
7
            (LPCWSTR)L"Fehler", MB_OK | MB_ICONEXCLAMATION);
8
          EndDialog(hwnd, 1);
9
          return TB_ERROR;
10
        }
11
        else
12
        {
13
          string_fehlerfrei_gesendet = "Ja";
14
        }
Der Ansatz ist ja schon mal gar nicht so verkehrt. Wenn Du jetzt noch 
dwBytesWrite überprüfst, passt das:
1
        int laenge_write_r = strlen ("\r");
2
        
3
        if(!WriteFile(hSerial, "\r", laenge_write_r, &dwBytesWrite, NULL))
4
        {
5
          //error occurred. Report to user
6
          msgboxIDss = MessageBox(NULL, (LPCWSTR)L"Fehler bei WriteFile!",
7
            (LPCWSTR)L"Fehler", MB_OK | MB_ICONEXCLAMATION);
8
          EndDialog(hwnd, 1);
9
          return TB_ERROR;
10
        }
11
        else if(laenge_write_r != dwBytesWrite)
12
        {
13
          string_fehlerfrei_gesendet = "Nein";
14
        }
15
        else
16
        {
17
          string_fehlerfrei_gesendet = "Ja";
18
        }
Beim ersten WriteFile und beim ReadFile analog

Thomas Holländer schrieb:
> Erster Durchlauf:
> write_char hat den Wert 127X1
> laenge_write_char hat den Wert 6
Was soll der Mist? Warum willst Du bei 5 Zeichen 6 Bytes senden?

> laenge_write_r hat den Wert 1
Das passt dann wieder.

Beim Lesen fehlt irgenwo noch das Stringendezeiche, mach nach dem 
ReadFile ein:
1
    read_buffer[dwBytesRead] = '\0';
Das ist vermutlich die Ursache für den ausgegebenen Müll.
Ein weiteres Problem ist, daß Du mehr Zeichen sendest als Du liest.
Beim ersten Lesen bekommst Du "127X1", beim zweiten "\r127X", beim 
dritten "1\r127" usw.

von Karl H. (kbuchegg)


Lesenswert?

Thomas Holländer schrieb:
> Versteh ich nich.
>
> Ich habe jetzt die Längen 4 bis 8 durchprobiert. Beim zweiten Senden
> stimmt der Wert nicht mehr und die Doku sagt mir auch nichts.

Du gehen mit Korb in Lager und sagen zu Chef:

Ich brauchen Schrauben M8. Da seien Korb, der fassen maximal 200 
Schrauben.

Chef verschwinden hinter Regale und holen Schrauben.

Wenn Cheffe wiederkommen, er geben dir Korb und sagen: Hab dir 10 
Schrauben reingegeben, mehr hab ich nicht.


Was gibt es da nicht zu verstehen?


1
   Chef_Read_Schrauben( Regal,  Korb,        200,                 &wieviele_wirklich, NULL );
2
                          ^       ^            ^                   ^                   ^
3
                          |       |            |                   |                   |
4
                          v       v            v                   v                   v
5
   ReadFile(           hSerial, read_buffer, sizeof(read_buffer), &dwBytesRead,       NULL)


Und wenn du 200 Schrauben brauchst, dann musst du halt wieder zum Chef 
gehen und dir die nächste Ladung holen. Dann kriegst du vielleicht 15 
(und damit hast du in Summe 25). Und beinm nächsten mal (eine halbe 
Stunde später) hat Cheffe wieder 23 neue Schrauben im Regal und gibt sie 
dir. Dann hast du in Summe 48. Und so geht das dahin, bis du deine 200 
Schrauben beisammen hast.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Prächtig!

von Thomas H. (datatom)


Lesenswert?

... schrieb:
> Thomas Holländer schrieb:
>> Erster Durchlauf:
>> write_char hat den Wert 127X1
>> laenge_write_char hat den Wert 6
> Was soll der Mist? Warum willst Du bei 5 Zeichen 6 Bytes senden?

Die Länge ermittle ich durch:
1
laenge_write_char = sizeof(write_char);

Siehe ganz oben. Warum der Wert 6 anstatt 5 ermittelt wird weis ich 
nicht. Werd ich gleich mal auf 5 ändern.

... schrieb:
> Beim Lesen fehlt irgenwo noch das Stringendezeiche, mach nach dem
> ReadFile ein:    read_buffer[dwBytesRead] = '\0';
> Das ist vermutlich die Ursache für den ausgegebenen Müll.

Wo und wie baue ich das denn mit dem Endezeichen ein?

von Karl H. (kbuchegg)


Lesenswert?

Thomas Holländer schrieb:
> ... schrieb:
>> Thomas Holländer schrieb:
>>> Erster Durchlauf:
>>> write_char hat den Wert 127X1
>>> laenge_write_char hat den Wert 6
>> Was soll der Mist? Warum willst Du bei 5 Zeichen 6 Bytes senden?
>
> Die Länge ermittle ich durch:
>
>
1
> laenge_write_char = sizeof(write_char);
2
>
>
> Siehe ganz oben. Warum der Wert 6 anstatt 5 ermittelt wird weis ich
> nicht. Werd ich gleich mal auf 5 ändern.
>
> ... schrieb:
>> Beim Lesen fehlt irgenwo noch das Stringendezeiche, mach nach dem
>> ReadFile ein:    read_buffer[dwBytesRead] = '\0';
>> Das ist vermutlich die Ursache für den ausgegebenen Müll.
>
> Wo und wie baue ich das denn mit dem Endezeichen ein?


Spätestens jetzt sollte klar sein:
Du brauchst ganz dringend Literatur. Wenn dein C++ Buch keinen Abschnitt 
über C-Strings hat, dann musst du eine kleine C++ Pause einlegen und dir 
mit einem C-Buch die Grundlagen der C-Programmierung beibringen. So hat 
das wenig Sinn.

von ... (Gast)


Lesenswert?

Thomas Holländer schrieb:
> Die Länge ermittle ich durch:
> laenge_write_char = sizeof(write_char);
>
> Siehe ganz oben. Warum der Wert 6 anstatt 5 ermittelt wird weis ich
> nicht. Werd ich gleich mal auf 5 ändern.

Autsch, das tut ja schon weh.
Du solltest Dir definitiv ein gutes Buch zu C und C++ zulegen und 
nachschauen was der sizeof Operator macht! Das was Du da erwartest macht 
er jedenfalls nicht. C-Strings sind anscheinend bisher ein Buch mit 
sieben Siegeln für Dich. Da hilft nur lesen, lesen und nochmals lesen!!!
Ein String in C mit 5 Zeichen benötigt 6 Bytes Speicherplatz, weil das 
Ende der Zeichenkette irgendwie gekennzeichnet werden muß. Und in C 
macht man das, indem man ein 0 (Null, '\0') Byte hinten dran hängt.
Desweiteren hast Du anscheinend das Ganze als Unicode Projekt angelegt. 
Dafür sollte man aber auf jeden Fall auch wissen, was Unicode ist, was 
der Unterschied zwischen ASCII und Unicode ist, welche verschiedenen 
Kodierungen es gibt und wie das Ganze in C/C++ realisiert ist und wie 
man diese Sachen ineinander umwandelt.
Hier mal ein Stück Code zum drüber nachdenken. Solange Du aber nicht 
verstehst was da wann, wie und warum passiert, brauchst Du gar nicht 
erst weitermachen.
1
int ReadAndWrite(HWND hwnd, char *char_cstr)
2
{
3
  if(board_angeschlossen == 1) {
4
    char *write_pointer = char_cstr;
5
    DWORD len = write_pointer ? (DWORD)strlen(write_pointer) : 0;
6
    DWORD dwBytesWritten;
7
    while(len) {
8
      if(!WriteFile(hSerial, write_pointer, len, &dwBytesWritten, NULL)) {
9
        //error occurred. Report to user
10
        return -1;
11
      }
12
      len -= dwBytesWritten;
13
      write_pointer += dwBytesWritten;
14
    }
15
    write_pointer = "\r";
16
    len = (DWORD)strlen(write_pointer);
17
    while(len) {
18
      if(!WriteFile(hSerial, write_pointer, len, &dwBytesWritten, NULL)) {
19
        //error occurred. Report to user
20
        return -1;
21
      }
22
      len -= dwBytesWritten;
23
      write_pointer += dwBytesWritten;
24
    }
25
26
    DWORD dwBytesRead;
27
    char read_buffer[256];
28
    int read_index = 0;
29
    while(read_index < sizeof(read_buffer)) {
30
      if(!ReadFile(hSerial, &read_buffer[read_index], 1, &dwBytesRead, NULL)) {
31
        //error occurred. Report to user
32
        return -1;
33
      }
34
      if(dwBytesRead != 1) {
35
         //error occurred. Report to user
36
        return -1;
37
      }
38
      if(read_buffer[read_index] == '\n') {
39
        // ignore
40
        continue;
41
      }
42
      if(read_buffer[read_index] == '\r') {
43
        // end of message
44
        break;
45
      }
46
      read_index++;
47
    }
48
    if(read_index == sizeof(read_buffer)) {
49
      //error occurred (buffer overflow). Report to user
50
      return -1;
51
    }
52
    read_buffer[read_index] = '\0';
53
54
    wchar_t print_buffer[sizeof(read_buffer)];
55
    mbtowc(print_buffer, read_buffer, MB_CUR_MAX);
56
    MessageBox(hwnd, print_buffer, L"ReadFile", MB_OK | MB_ICONINFORMATION);
57
58
    // ...

von Thomas H. (datatom)


Lesenswert?

Habe den Quelltext aus dem vorherigen Beitrag übernommen. Die Daten 
kommen jetzt. Vielen Dank.

Allerdings, verzögert. Wenn ich die Daten zum Controller sende kommen 
sie erst nach ein paar Sekunden zurück. Beim debuggen erhalte ich die 
Daten sofort, liegt wahrscheinlich an der Verzögerung, die der 
Haltepunkt auslöst.

Wer kann mir sagen, warum die verzögert kommen?

Vielen Dank.

Grüße

datatom

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.