Autor |
Beitrag |
hestejue
Hält's aus hier
Beiträge: 11
|
Verfasst: Sa 15.11.08 17:04
Hi,
seit Jahren habe ich in meinen (Turbo/Virtual)Pascal-Programmen die Funktion "Blockwrite" verwendet und nie ein Problem damit gehabt. Anscheinend hat aber die Implementierung dieser Funktion in Delphi 7 einen krassen Fehler. In der Delphi Hilfe zu Blockwrite steht:
---------------------------------------------------------
procedure BlockWrite(var f: File; var Buf; Count: Integer [; var AmtTransferred: Integer]);
Ist AmtTransferred kleiner als Count, war der Datenträger vor Beendigung der Übertragung voll.
In diesem Fall gibt AmtTransferred die Anzahl der vollständig geschriebenen Blöcke zurück,
wenn die Datenblockgröße der Datei größer als 1 ist.
---------------------------------------------------------
Wenn ich in einem Programm mit "if Count<>AmtTransferred then..." abfragen will, ob ein Fehler aufgetreten ist, so funktioniert das nicht. AmtTransferred ist IMMER gleich Count, auch wenn die Platte voll ist und nichts mehr geschrieben werden konnte! Der gleiche Code in Virtual Pascal dagegen funktioniert einwandfrei.
Hat jemand das schon mal festgestellt? Ist das ein bekannter Bug?
|
|
alzaimar
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: Sa 15.11.08 21:06
Blockwrite ist veraltet und wird eigentlich nicht mehr verwendet. Daher ist es unwahrscheinlich, das man das mitbekommt.
Verwende doch einen TFileStream.
_________________ Na denn, dann. Bis dann, denn.
|
|
hestejue 
Hält's aus hier
Beiträge: 11
|
Verfasst: So 16.11.08 11:51
alzaimar hat folgendes geschrieben : | Blockwrite ist veraltet und wird eigentlich nicht mehr verwendet. Daher ist es unwahrscheinlich, das man das mitbekommt.
Verwende doch einen TFileStream. |
Das ist ja eine merkwürdige Antwort. Kannst du mir mal eine Liste machen, welche anderen fundamentalen Funktionen in Delphi noch alle fehlerhaft sind und deshalb nicht mehr verwendet werden dürfen? Anscheinend haben sich alle damit abgefunden, dass heutzutage nichts mehr richtig funktioniert und man deshalb nur noch mit dem Programmieren von Workarounds beschäftigt ist. Warum existiert die Funktion überhaupt noch, wenn sie nicht mehr verwendet werden soll? Für alles wo es auf Performance ankommt ist Blockwrite meines Erachtens unverzichtbar.
|
|
alzaimar
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: So 16.11.08 13:34
Na na, nun lass mal die Kirche im Dorf. Übrigens ruft 'BlockWrite' auch nur die Funktion 'WriteFile' aus der Microsoft Library 'Kernel.DLL' auf.
Hast Du eigentlich keine System.Pas bei deinem Delphi? Dort steht das Alles. Vielleicht wendest Du dich mit deinem Problem an Microsoft, anstatt so einen gesellschaftskritischen Rundumschlag zu starten. Zeugt nicht gerade von einer professionellen Vorgehensweise, wenn Du mich fragst.
Was deine Überlegungen bezüglich der Performance anbelangt, solltest Du vielleicht nochmals recherchieren. Soweit ich informiert bin, ist 'Blockwrite' nicht das schnellste Verfahren, da es synchron arbeitet. Weiterhin wird Speicher unnötig hin und her kopiert (innerhalb der Kernel-Routine). Arbeite lieber mit asynchronem IO und system pages als Puffer.
Abschließend könnte es sein, das der Fehler bei Dir liegt. Kann ich mir zwar nicht vorstellen, weil das Problem schnell einzugrenzen ist, aber schau doch nochmal. Das so eine fundamentale Geschichte von Microsoft unentdeckt bleibt, glaube ich nicht.
_________________ Na denn, dann. Bis dann, denn.
|
|
hestejue 
Hält's aus hier
Beiträge: 11
|
Verfasst: So 16.11.08 15:09
Nein, eine System.pas ist bei meinem Delphi 7 (personal edition) nicht dabei, nur eine System.dcu.
Ich frage mich nur, warum alle meine bisherigen Pascal-Versionen Blockwrite richtig umsetzen, nur Delphi nicht. Kann ja dann wohl nicht an Microsoft liegen, oder?
|
|
alzaimar
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: So 16.11.08 19:32
hestejue hat folgendes geschrieben : |
Ich frage mich nur, warum alle meine bisherigen Pascal-Versionen Blockwrite richtig umsetzen, nur Delphi nicht. Kann ja dann wohl nicht an Microsoft liegen, oder? |
Doch. BlockWrite ruft fast direkt 'WriteFile' aus der Kernel.dll auf, weswegen theoretische Zweifel an deinem Bug angebracht sind.
_________________ Na denn, dann. Bis dann, denn.
Zuletzt bearbeitet von alzaimar am So 16.11.08 20:02, insgesamt 1-mal bearbeitet
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: So 16.11.08 19:40
Zuerst: Du speicherst in eine Binärdatei (also file und nicht file of XY)??? Wenn ja, dann lies die Hilfe bitte noch einmal genau:
Die Delphi-Hilfe: | Delphi-Quelltext 1:
| procedure BlockWrite(var f: File; var Buf; Count: Integer [; var AmtTransferred: Integer]); |
Ist AmtTransferred kleiner als Count, war der Datenträger vor Beendigung der Übertragung voll.
In diesem Fall gibt AmtTransferred die Anzahl der vollständig geschriebenen Blöcke zurück,
wenn die Datenblockgröße der Datei größer als 1 ist. |
Ist also kein Bug, sondern as-designed  Ich weiß, das ist ärgerlich, aber wenn die Dokumentation mit dem verhalten übereinstimmt, muss man damit leben
Aber hier mal zur Erklärung etwas Source:
4418: 4419: 4420: 4421: 4422: 4423: 4424: 4425: 4426: 4427: 4428: 4429: 4430: 4431: 4432: 4433: 4434: 4435: 4436: 4437: 4438: 4439: 4440: 4441: 4442: 4443: 4444: 4445: 4446: 4447: 4448: 4449: 4450: 4451: 4452: 4453: 4454: 4455: 4456: 4457: 4458: 4459: 4460: 4461: 4462: 4463: 4464: 4465: 4466: 4467: 4468:
| { ... } function BlockIO(var f: TFileRec; buffer: Pointer; recCnt: Cardinal; var recsDone: Longint; ModeMask: Integer; IOProc: TIOProc; ErrorNo: Integer): Cardinal; begin if (f.Mode and ModeMask) = ModeMask then begin {$IFDEF LINUX} Result := IOProc(f.Handle, buffer, recCnt * f.RecSize); if Integer(Result) = -1 then {$ENDIF} {$IFDEF MSWINDOWS} if IOProc(f.Handle, buffer, recCnt * f.RecSize, Result, nil) = 0 then {$ENDIF} begin SetInOutRes(GetLastError); Result := 0; end else begin Result := Result div f.RecSize; if @RecsDone <> nil then RecsDone := Result else if Result <> recCnt then begin SetInOutRes(ErrorNo); Result := 0; end end; end else begin SetInOutRes(103); Result := 0; end; end;
function _BlockRead(var f: TFileRec; buffer: Pointer; recCnt: Longint; var recsRead: Longint): Longint; begin Result := BlockIO(f, buffer, recCnt, recsRead, fmInput, {$IFDEF MSWINDOWS} ReadFileX, {$ENDIF} {$IFDEF LINUX} __read, {$ENDIF} 100); end;
function _BlockWrite(var f: TFileRec; buffer: Pointer; recCnt: Longint; var recsWritten: Longint): Longint; begin Result := BlockIO(f, buffer, recCnt, recsWritten, fmOutput, {$IFDEF MSWINDOWS} WriteFileX, {$ENDIF} {$IFDEF LINUX} __write, {$ENDIF} 101); end; |
Wie du hier unschwer siehst, kann man durchaus erkennen, wenn ein Fehler aufgetreten ist:
- Am Rückgabewert: Ist dieser 0, ist ein Fehler aufgetreten
- An GetLastError (was in die Variable IOResult kopiert wird)
- An der IOResult-Variable.
Von nicht funktionieren kannhier also nicht die Rede sein
alzaimar hat folgendes geschrieben : | Blockwrite ist veraltet und wird eigentlich nicht mehr verwendet. Daher ist es unwahrscheinlich, das man das mitbekommt. |
Und außerdem ist es unflexibel, da ich den gesamten IO-Code ändern muss, nur um statt in eine Datei in den Arbeitsspeicher oder auf einen FTP-Server zu speichern  Mit Streams muss ich nur die Zeile, in der ich den Stream erzeuge ändern; das Speichern ändert sich dadurch gar nicht.
hestejue hat folgendes geschrieben : | alzaimar hat folgendes geschrieben : | Verwende doch einen TFileStream. |
Das ist ja eine merkwürdige Antwort. Kannst du mir mal eine Liste machen, welche anderen fundamentalen Funktionen in Delphi noch alle fehlerhaft sind und deshalb nicht mehr verwendet werden dürfen? |
Datei-Typen sind seit Delphi 4 als Obsoleted in der Hilfe gekennzeichnet und werden nur aus Legacy-Gründen weiter supported. Bereits seit Delphi 3 gab es ein funktionierendes Stream-System bei Delphi (hier sei TFileStream nur als einer der Vertreter erwähnt), das für heutige IO-Aufgaben wesentlich flexibler und leistungsfähiger ist, als die alten File-Typen.
hestejue hat folgendes geschrieben : | Anscheinend haben sich alle damit abgefunden, dass heutzutage nichts mehr richtig funktioniert und man deshalb nur noch mit dem Programmieren von Workarounds beschäftigt ist. |
Ich kann von A nach B laufen, mit dem Auto fahren oder mich Beamen. Je nach Entfernung der beiden hypothetischen Punkte A und B ist laufen, mit dem Auto fahren oder sich hinbeamen die effektivste Variante.
hestejue hat folgendes geschrieben : | Warum existiert die Funktion überhaupt noch, wenn sie nicht mehr verwendet werden soll? |
Weil ich z.B. letztens erst einen Quelltext von 1991(!!!) in der Hand hatte, der in TurboPascal 6 geschrieben war (so richtig schön mit objects und so) und dieser genauso Funktionieren muss, wie ein Programm, was auf dem Stand der Zeit™ geschrieben wurde.
hestejue hat folgendes geschrieben : | Für alles wo es auf Performance ankommt ist Blockwrite meines Erachtens unverzichtbar. |
Falsch: Nicht alles, was in ASM geschrieben ist, ist schnell. Genauso ist ein Programm nicht allein durch die Nutzung einer bestimmten Funktion schneller. Wenn der dahinterliegende Algorithmus nix taugt, kann die verwendete Funktionsbibliothek noch so schnell sein und es bringt dir rein gar nix.
2ct
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
hestejue 
Hält's aus hier
Beiträge: 11
|
Verfasst: So 16.11.08 20:14
Die Delphi-Hilfe: | Delphi-Quelltext 1:
| procedure BlockWrite(var f: File; var Buf; Count: Integer [; var AmtTransferred: Integer]); |
Ist AmtTransferred kleiner als Count, war der Datenträger vor Beendigung der Übertragung voll.
In diesem Fall gibt AmtTransferred die Anzahl der vollständig geschriebenen Blöcke zurück,
wenn die Datenblockgröße der Datei größer als 1 ist.
Ist also kein Bug, sondern as-designed Ich weiß, das ist ärgerlich, aber wenn die Dokumentation mit dem verhalten übereinstimmt, muss man damit leben  |
Was soll denn das heißen? Dass ich zwar den 4. Parameter übergeben kann, der aber nur Müll zurückliefert, wenn ich die "Datenblockgröße" auf 1 setze? Komischerweise funktioniert das bei allen anderen Pascal Implementierungen auch für Datenblockgröße=1 (Turbo Pascal, Virtual Pascal, Free Pascal,...). Gibt es noch mehr solche Fallen in Delphi?
Zitat: | Wie du hier unschwer siehst, kann man durchaus erkennen, wenn ein Fehler aufgetreten ist:
Von nicht funktionieren kannhier also nicht die Rede sein  |
Für mich sieht das eher wie ein fetter Bug aus: recDone wird nicht unter allen Umständen gesetzt! Damit ist es wertlos. Und ausserdem sehe ich im Code keine Abhängigkeit von der Datenblockgröße (wie in der Hilfe behauptet). Im Gegenteil: "Result := Result div f.RecSize;" wird für JEDE RecSize ausgeführt (nur nicht bei Fehlern!).
Du verweist ständig auf andere Möglichkeiten des Fehlerchecks (die ich natürlich auch schon verwende). Meine Frage war aber, ob Blockwrite in Delphi einen Bug hat und so wie es aussieht, ist das der Fall. Woher soll denn aber jemand wissen, WELCHE der möglichen Error Checks in Delphi funktionieren und welche nicht? Dann sollte man den 4. Parameter in BlockWrite komplett verbieten. So führt er ja nur zu unnötiger und nerviger Fehlersuche.
Zitat: | Datei-Typen sind seit Delphi 4 als Obsoleted in der Hilfe gekennzeichnet und werden nur aus Legacy-Gründen weiter supported. Bereits seit Delphi 3 gab es ein funktionierendes Stream-System bei Delphi (hier sei TFileStream nur als einer der Vertreter erwähnt), das für heutige IO-Aufgaben wesentlich flexibler und leistungsfähiger ist, als die alten File-Typen. |
Ob nun "obsolete" oder nicht, wenn es vorhanden ist, muß es auch korrekt funktionieren. Gerade bei einem Compiler sollte man da etwas sorgfältiger arbeiten.
So wie es aussieht, muß ich mich wohl doch woanders nach einem geeigneten Pascal-Compiler umsehen (z.B. Free-Pascal + Lazarus).
Zitat: | "Warum existiert die Funktion überhaupt noch, wenn sie nicht mehr verwendet werden soll?"
Weil ich z.B. letztens erst einen Quelltext von 1991(!!!) in der Hand hatte, der in TurboPascal 6 geschrieben war (so richtig schön mit objects und so) und dieser genauso Funktionieren muss, wie ein Programm, was auf dem Stand der Zeit™ geschrieben wurde.
|
Ja aber das ist es ja gerade: Er funktioniert ja NICHT mehr! (Jedenfalls nicht mehr für Blockwrite)
|
|
alzaimar
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: So 16.11.08 20:47
Manno, Du kannst dich aber auch aufregen,
Ben, ich sehe im Code nicht, das der zur Diskussion stehende Rückgabewert nur dann gültig ist, wenn die Recordgröße > 1 ist.
hestejue, allerdings sehe ich auch nicht, das 'Müll' geliefert wird. Auf welchen Wert sollte denn RecsDone im Fehlerfall gesetzt werden?
Wenn man sich den Code anschaut, sieht man doch, das der Wert (Result) der 'IOProc' nur in die Blockgröße umgerechnet wird, ergo sollte es auch für Blockgrößen=1 funktionieren. Wenn Du also mit dem Ergebnis nicht zufrieden bist, dann liegt das an der 'IOProc' und *das* ist der (indirekte) Aufruf der Kernel-Funktion. Also spinn nicht weiter rum und schieb das auf Delphi, denn das trifft hier wirklich nicht zu. Prüfe doch erstmal deinen Code nochmal, weil ich mir einfach nicht vorstellen kann, das das an der Windows-Routine liegt (möglich ist aber alles).
Ich würde das gerne selbst verifizieren, aber wo bekomm ich nun eine fast volle Pladde her?
Allerdings bleibt es Dir natürlich überlassen, dieses verbuggte Drecks-Delphi durch die wesentlich professionelleren Alternativen (Lazarus, FreePascal) zu ersetzen.
Edit: sooo, Test-Pladde gefunden (Ramdisk):
Ergebnis:
1. BlockWrite liefert nie unterschiedliche Werte. Oh, Delphi ist Müll
2. Writefile (Windows) aber auch nicht. Oh, Windows ist Müll.
Was das nun soll, muss mir mal einer erklären. Hier mein hingerotzter Testcode:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25:
| procedure TForm5.Button1Click(Sender: TObject); Var f : File of Word; p : PChar; n,r : Cardinal; x : LongBool;
begin assignFile(f,'R:\TEST'); Rewrite (f); n := 1029*393; GetMem(p, n); {$I-} randomize; repeat n := (123456+Random (100000)); r := 0; x := WriteFile(TFileRec(f).Handle, p^,n,r,nil); If x then Begin Memo1.Lines.add(Format('Error (%d): %d %d',[GetLastError, n,r])); Break; End else Memo1.Lines.add(Format('%d %d',[n,r])) until False; end; |
Ergebnis: n und r sind immer identisch (oder r=0, wenn x<>0);
_________________ Na denn, dann. Bis dann, denn.
Zuletzt bearbeitet von alzaimar am So 16.11.08 22:46, insgesamt 1-mal bearbeitet
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: So 16.11.08 22:18
hestejue hat folgendes geschrieben : | Zitat: | Wie du hier unschwer siehst, kann man durchaus erkennen, wenn ein Fehler aufgetreten ist:
Von nicht funktionieren kannhier also nicht die Rede sein  |
Für mich sieht das eher wie ein fetter Bug aus: recDone wird nicht unter allen Umständen gesetzt! Damit ist es wertlos. Und ausserdem sehe ich im Code keine Abhängigkeit von der Datenblockgröße (wie in der Hilfe behauptet). Im Gegenteil: "Result := Result div f.RecSize;" wird für JEDE RecSize ausgeführt (nur nicht bei Fehlern!). |
Was aber auch dem Vorgehen im Falle von Fehlern entspricht, oder siehst Du bei irgendeinem anderen Fall, dass recDone verändert wird?
Deine Aussage wäre dann richtig, wenn recDone ein out-Parameter wäre; da es aber ein var-Parameter ist, MUSS die Anwendung dafür sorgen, dass dort etwas Sinnvolles steht, BEVOR die Routine aufgerufen wird.
hestejue hat folgendes geschrieben : | Du verweist ständig auf andere Möglichkeiten des Fehlerchecks (die ich natürlich auch schon verwende). Meine Frage war aber, ob Blockwrite in Delphi einen Bug hat und so wie es aussieht, ist das der Fall. Woher soll denn aber jemand wissen, WELCHE der möglichen Error Checks in Delphi funktionieren und welche nicht? Dann sollte man den 4. Parameter in BlockWrite komplett verbieten. So führt er ja nur zu unnötiger und nerviger Fehlersuche. |
Gewöhne Dir an, Variablen vor deren Nutzung zu initialisieren (z.B. mit 0) und du bekommst Problemlos 0 = Count im Fehlerfalle --> Fehler
hestejue hat folgendes geschrieben : | Zitat: | "Warum existiert die Funktion überhaupt noch, wenn sie nicht mehr verwendet werden soll?"
Weil ich z.B. letztens erst einen Quelltext von 1991(!!!) in der Hand hatte, der in TurboPascal 6 geschrieben war (so richtig schön mit objects und so) und dieser genauso Funktionieren muss, wie ein Programm, was auf dem Stand der Zeit™ geschrieben wurde.
|
Ja aber das ist es ja gerade: Er funktioniert ja NICHT mehr! (Jedenfalls nicht mehr für Blockwrite) |
Ich hab oben das Gegenbeispiel gegeben, DASS er funktioniert, wenn man korrekt und sauber arbeitet. Man darf halt Warnungen und Hinweise vom Compiler nicht ignorieren und muss ggf. explizit darauf achten, seine Variablen sauber zu initialisieren.
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
hestejue 
Hält's aus hier
Beiträge: 11
|
Verfasst: Mo 17.11.08 08:16
Es wird hier anscheinend von unterschiedlichen Dingen geredet. Bei mir ist Blockwrite eine Prozedur und keine Funktion! Auch die Delphi-Hilfe beschreibt Blockwrite nur als Prozedur. Deren Rückgabewert ist (schon seit Pascal-Urzeiten) immer der 4. Übergabeparameter, in dem die Anzahl geschriebener Daten zurückgegeben wird. Nur eben nicht bei Delphi. Wenn ich mal davon ausgehe, dass die FUNKTION _Blockwrite (die wiederum BlockIO aufruft) irgendwie auch von der PROZEDUR Blockwrite verwendet wird, dann ergibt sich, dass recsDone im Falle dass IOProc=0 ist nicht gesetzt wird. In
BlockWrite(Datei,CopyArray,sizeof(CopyArray),geschrieben);
if geschrieben<>sizeof(CopyArray) then ...
ist geschrieben IMMER gleich sizeof(CopyArray), auch wenn ein Fehler aufgetreten ist. Und warum sollte ich eine Variable initialisieren, die sofort danach von einer Prozedur wieder überschrieben wird? Davon steht auch nichts in der Hilfe. Und es war auch noch nie bei irgendeiner der anderen Pascal-Versionen nötig, die ich verwendet habe.
Interessant wäre mal der Code der PROZEDUR Blockwrite (den ich leider nicht zur Verfügung habe).
|
|
Martok
      
Beiträge: 3661
Erhaltene Danke: 604
Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
|
Verfasst: Mo 17.11.08 09:02
BlockWrite(procedure) ist compiler-magic... da gibts keinen Source.
Hat überhaupt jemand alzaimars Test gesehen?
_________________ "The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
|
|
alzaimar
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: Mo 17.11.08 09:26
Martok, ich weiss jetzt nicht was daran Compiler Magic sein soll: "call @BlockWrite" und .. "Rechte Maustaste->Find Declaration: Delphi öffnet Systems.PAS und da gibt es komischerweise eine Routine namens '_Blockwrite'. Kann aber sein, das das nichts mit 'Blockwrite' zu tun hat. Und wenn ich mit 'Debug-DCU' kompiliere und per F7 durchsteppe, wird auch diese Routine in Systems.PAS aufgerufen. Also ich finde nicht, das das 'Compiler Magic' ist.
Und mein Testprogramm steht doch ein paar Posts weiter oben. Ist wohl noch dunkel gewesen, muste halt mal das Licht anmachen 
_________________ Na denn, dann. Bis dann, denn.
|
|
jaenicke
      
Beiträge: 19322
Erhaltene Danke: 1749
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mo 17.11.08 10:35
|
|
hestejue 
Hält's aus hier
Beiträge: 11
|
Verfasst: Mo 17.11.08 12:20
Zitat: | Er hat dort den Quelltext doch gepostet... |
Nein, hat er nicht. Das ist der Quelltext zu der FUNKTION _Blockwrite (man beachten den Unterstrich!). Was ich aber starte, ist die PROZEDUR Blockwrite (ohne Unterstrich!). Wahrscheinlich wird _Blockwrite von Blockwrite aufgerufen, aber man weiß es nicht genau. Und ob da Änderungen an den Übergabeparametern erfolgen, weiß man auch nicht.
Das Komische ist, dass Virtual Pascal auch die API Funktion WriteFile verwendet, und es da aber funktioniert. Intereesant ist auch die Doku von Microsoft zu WriteFile:
------------------------------
lpNumberOfBytesWritten
Points to the number of bytes written by this function call.
WriteFile sets this value to zero before doing any work or error checking.
If lpOverlapped is NULL, lpNumberOfBytesWritten cannot be NULL.
If lpOverlapped is not NULL, lpNumberOfBytesWritten can be NULL. If this is an overlapped write operation, you can get the number of bytes written by calling GetOverlappedResult. If hFile is associated with an I/O completion port, you can get the number of bytes written by calling GetQueuedCompletionStatus.
------------------------------
Da steht ja eigentlich eindeutig, dass lpNumberOfBytesWritten von WriteFile auf Null gesetzt wird. Und so funktioniert es auch bei Virtual Pascal. Aber warum nicht bei Delphi?
|
|
alzaimar
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: Mo 17.11.08 12:34
hestejue hat folgendes geschrieben : | Zitat: | Er hat dort den Quelltext doch gepostet... |
Nein, hat er nicht. Das ist der Quelltext zu der FUNKTION _Blockwrite (man beachten den Unterstrich!). Was ich aber starte, ist die PROZEDUR Blockwrite (ohne Unterstrich!). Wahrscheinlich wird _Blockwrite von Blockwrite aufgerufen, aber man weiß es nicht genau. |
Mann, bist du beratungsresistent. Ich habe doch eben geschrieben, das der Debugger direkt dort reinspringt. Glaub mir doch einfach, oder meinst, Du ich poste aus Spaß irgendwelchen Quatsch? Das ich mich hier selbst zitieren muss, spricht auch Bände:
alzaimar hat folgendes geschrieben : | ..."Rechte Maustaste->Find Declaration: Delphi öffnet Systems.PAS und da gibt es komischerweise eine Routine namens '_Blockwrite'. Kann aber sein, das das nichts mit 'Blockwrite' zu tun hat. Und wenn ich mit 'Debug-DCU' kompiliere und per F7 durchsteppe, wird auch diese Routine in Systems.PAS aufgerufen. Also ich finde nicht, das das 'Compiler Magic' ist. |
Du stellst hier Fragen über Fragen, beachtest nur das, was Du sehen willst, ignorierst die Hälfte der Anworten und probierst meinen Code überhaupt nicht aus. Dort beweise ich, das selbst WriteFile (bei mir) nicht richtig funktioniert. Hab ich vielleicht einen Fehler gemacht? Vielleicht klappt das mit einer RAMDISK nicht? Zeig doch mal deinen Code!
Ich finde das auch verdammt merkwürdig, aber immerhin suche ich den Fehler im Zweifelsfall bei mir bzw. poste reproduzierbaren Code und hoffe, das mir jemand zeigt, wo mein Fehler ist. Ich finde ihn nämlich nicht.
Übrigens, und da merkt man, das Du kein Praktiker bist, erklärt eine Beschreibung nur, wie es funktionieren sollte, aber nicht, wie es funktioniert. Das muss man im Zweifelsfall selbst herausbekommen. Bei meinem Testcode klappt das jedenfalls nicht.
Zeig doch mal, wie Virtual Pascal das macht...
_________________ Na denn, dann. Bis dann, denn.
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Mo 17.11.08 14:19
Zum Thema Compiler Magic mal noch eine kurze Anmerkung: Dass BlockWrite Compiler Magic ist, erkennt man nicht zuletzt daran, dass es diese Routine im Source der System.pas nicht gibt. Ferner ist die Signatur von BlockWrite nur MIT dem letzten Parameter vorhanden; laut Code-Vervollständigung darf dieser abger weggelassen werden. Dies ist nur mit einem Overload möglich, indem die Routine ein zweites Mal geschrieben wird, wird sie aber auch nicht. Und nicht zuletzt weist der Unterstrich auf Compiler-Magic hin. Ist in der System.pas nämlich Konvention, dass alles, was Compiler-Magic für den Aufruf benötigt mit einem _ im Namen gekennzeichnet wird.
Ganz witzig wird das übrigens bei der Routine Write, die es gar nicht gibt, auch nicht mit Unterstrich, dafür aber eine ganze Hand voll an Varianten mit einem Datentyü dahinter in teilweise 3 Varianten. Dass viele davon in ASM geschrieben sind, brauch ich glaube nicht erwähnen ...
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
hestejue 
Hält's aus hier
Beiträge: 11
|
Verfasst: Mo 17.11.08 17:28
Es scheint tatsächlich, als ob man das nicht so einfach herausbekommen könnte. Auch in Virtual Pascal gibt es in den Sourcen keine Prozedur BLOCKWRITE. Auch hier gibt es nur _BLOCKWRITE, allerdings ist dies bei Virtual Pascal eine Prozedur während es in Delphi eine Funktion zu sein scheint. Code von Virtual Pascal:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| procedure _BlockWrite(FileVar,Buffer: Pointer; Count: Longint; Result: Pointer); asm mov esi,RTE_Disk_Write_Error mov edi,FileVar mov ecx,Count mov edx,Buffer mov ebx,Result Call InOutBlock end; |
Das dort aufgerufene InOutBlock ist dann dies:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59:
|
procedure InOutBlock; asm test ebx,ebx jz @@1 and DWord Ptr [ebx],0 @@1: Call OpenCheck jne @@RET mov eax,ecx jecxz @@2 push edx mul [edi].TextRec.BufSize pop edx mov ecx,eax push ebx push ecx push 0 mov eax,esp push [edi].TextRec.Handle push edx push ecx push eax cmp esi,RTE_Disk_Write_Error je @@Write Call SysFileRead jmp @@Done @@Write: Call SysFileWrite @@Done: pop ecx pop edx pop ebx test eax,eax jnz @@ERROR mov eax,edx @@2: test ebx,ebx jz @@3 mov eax,ecx xor edx,edx div [edi].TextRec.BufSize mov [ebx],eax jmp @@RET @@3: cmp ecx,eax je @@RET xchg eax,esi @@ERROR: Call SetInOutRes @@RET: end; |
und SysFileWrite wiederum ist:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23:
| function SysFileWrite(Handle: Longint; const Buffer; Count: Longint; var Actual: Longint): Longint; asm sub eax,eax mov ah,$40 mov ebx,handle mov ecx,count mov edx,buffer int $21 jc @fehler
mov edx,actual mov [edx],eax sub eax,eax jmp @ret
@fehler: sub ecx,ecx mov edx,actual mov [edx],ecx
@ret: call teste_strg_c end; |
Leider kenne ich mich mit Assembler kaum aus. Wenn man aber mal davon ausgeht, dass BLOCKWRITE mit _BLOCKWRITE identisch ist und die Aufrufkette verfolgt, dann wird anscheinend nicht WriteFile benutzt, sondern der Interrupt 21 (der dann wohl wieder von Windows auf WriteFile umgebogen wird).
Auffällig finde ich, dass in Delphi alle BlockWrite Implementierungen als Funktion auftauchen, aber keine Prozedur (obwohl in der Online Hilfe nur von einer Prozedur die Rede ist) während in Virtual Pascal (und auch in FreePascal, siehe unten) auch Prozedur-Deklarationen existieren.
In Free Pascal sieht die Prozedur übrigens so aus:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16:
| Procedure BlockWrite(Var f:File;Const Buf;Count:Int64;var Result:Int64);[IOCheck];
Begin Result:=0; If InOutRes <> 0 then exit; case FileRec(f).Mode of fmInOut,fmOutput : Result:=Do_Write(FileRec(f).Handle,@Buf,Count*FileRec(f).RecSize) div FileRec(f).RecSize; fmInPut: inOutRes := 105; else InOutRes:=103; end; End; |
Moderiert von Gausi: Quote- durch Delphi-Tags ersetzt
|
|
|