Noch ein paar weitere Infos für die technisch interessierten und eine mögliche Lösung des Problems:
Im Prinzip ist das Problem mit dem ungültigen Handle ein Folgefehler aus einer Speicherseite im Datensegment, die mit 00 gefüllt ist und daher kommt es zu einer falschen Zeigerdereferenzierung. Hier wird der oben fehlgeschlagene lseek-Vorgang durchgeführt:
Code:
02a8:27b8 E88800 call 2843 ($+88)
02a8:27bb 8B5D0A mov bx,[di+0A]
02a8:27be 368B5F08 mov bx,ss:[bx+08]
02a8:27c2 8B5508 mov dx,[di+08]
02a8:27c5 8B4D0C mov cx,[di+0C]
02a8:27c8 D1E2 shl dx,1
02a8:27ca D1D1 rcl cx,1
02a8:27cc D1E2 shl dx,1
02a8:27ce D1D1 rcl cx,1
02a8:27d0 D1E2 shl dx,1
02a8:27d2 D1D1 rcl cx,1
02a8:27d4 D1E2 shl dx,1
02a8:27d6 D1D1 rcl cx,1
02a8:27d8 D1E2 shl dx,1
02a8:27da D1D1 rcl cx,1
02a8:27dc D1E2 shl dx,1
02a8:27de D1D1 rcl cx,1
02a8:27e0 D1E2 shl dx,1
02a8:27e2 D1D1 rcl cx,1
02a8:27e4 D1E2 shl dx,1
02a8:27e6 D1D1 rcl cx,1
02a8:27e8 D1E2 shl dx,1
02a8:27ea D1D1 rcl cx,1
02a8:27ec B80042 mov ax,4200
02a8:27ef CD21 int 21
Zum Zeitpunkt, wo der Fehler auftritt ergibt sich dann folgendes Bild:
Code:
Trap 3, system state: stopped
AX=0000 BX=284b CX=0000 DX=ffff SI=06f8 DI=0000 SP=fbfa BP=0001
DS=7922 ES=371a FS=0298 GS=c253 FL=3246
CS:IP=02a8:27bb SS:SP=05ca:fbfa
d ds:di+0a
7922:0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
7922:0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
7922:0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
7922:0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
7922:0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
7922:0050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
7922:0060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
7922:0070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
Trap 1, system state: stopped
AX=0000 BX=0000 CX=0000 DX=ffff SI=06f8 DI=0000 SP=fbfa BP=0001
DS=7922 ES=371a FS=0298 GS=c253 FL=3346
CS:IP=02a8:27be SS:SP=05ca:fbfa
02a8:27be 368B5F08 mov bx,ss:[bx+08]
d ss:[bx+08]
05ca:ffffffff 00 E4 58 00 00 A9 07 A8 02 A8 02 CA 05 AF 07 06 .dX..).(.(.J./..
05ca:000f FC FE FB 84 09 34 19 3C 19 92 09 68 09 44 46 16 |~{..4.<...h.DF.
05ca:001f 00 C3 2F 30 00 00 00 00 00 84 09 00 00 00 00 0A .C/0............
05ca:002f 00 0A 00 CF 0E 00 00 00 00 01 00 00 00 00 00 00 ...O............
05ca:003f 00 00 00 00 00 42 78 FA 33 0A 00 0A 00 0A 00 00 .....Bxz3.......
05ca:004f 00 00 00 00 00 00 00 00 00 52 2A FF 9F 80 00 03 .........R....
05ca:005f 00 A0 71 0E 01 28 0B 00 00 00 00 52 2B 00 00 00 . q..(.....R+...
05ca:006f 00 00 00 05 00 02 3D 00 00 00 00 00 00 00 00 00 ......=......... Das Programm lädt also was aus Seite 0 des Stacksegments, da steht natürlich nicht das drin, was es gerne hätte, nämlich das zu verwendende Filehandle. Also habe ich mich einmal mithilfe eines erweiterten Debuggers auf die Suche gemacht und mittels memory-Breakpoint die Stelle gesucht, an welcher die Seite mit 0 überschrieben wird. Hier landen wir scheinbar, wenn ich das richtig erkennen kann, in der Heapverwaltung der Runtime:
Code:
000027CC: F7450AFFFF test w,[di][0000A],0FFFF
000027D1: 7404 je 0000027D7
000027D3: 33DB xor bx,bx
000027D5: EB03 jmps 0000027DA
000027D7: 8B5D0C mov bx,[di][0000C]
000027DA: 8B4D0E mov cx,[di][0000E]
000027DD: 0BDB or bx,bx
000027DF: 7410 je 0000027F1 ; Wenn BX 0 ist, dann leeren:
000027E1: 51 push cx ; Sichern von cx Register
000027E2: B90080 mov cx,08000 ;0x8000 Words (=64k)...
000027E5: F3AB repe stosw ;...auf ES:DI schreiben
000027E7: 81C20010 add dx,01000 ; Nächstes Segment
000027EB: 8EC2 mov es,dx
000027ED: 4B dec bx ; ggf. noch ein Segment leeren
000027EE: 75F2 jne 0000027E2
000027F0: 59 pop cx ; CX rücksichern
000027F1: D1E9 shr cx,1
000027F3: 7301 jae 0000027F6
000027F5: AA stosb
In der Löschschleife (repe stosw) wird also unser Segment mit Nullern überschrieben. Jede Speicherseite des Heapmanagers hat eine 16byte große Verwaltungsstruktur, welche verschiedene Informationen zur Speicherseite enthält, die vom Heapmanager verwendet werden. Wenn auf einer Seite auf Offset C eine 0 steht, dann wird also recht ordentlich aufgeräumt, indem die ganzen 64k, die ein Segment nunmal hat, mit 0 überschrieben werden. Dummerweise werden damit auch Daten aus dem Weg geräumt, die OA scheinbar noch braucht. Jetzt wäre es natürlich noch interessant, wie oft das auftritt. Scheinbar ziemlich selten, ein Breakpoint zeigt nämlich, dass diese Aufräumaktion nur unmittelbar vor unserem Fehler ausgeführt wird. Die Netzwerkversion überlebt das irgendwie nicht wirklich, aber die Standalone-Version scheinbar schon.. Warum das ?
Meine Vermutung: Es hat was mit dem geringeren Speicherbedarf der Standalone Version zu tun.
Ein Haltepunkt an derselben Stelle bei der Standalone-Version offenbart nämlich, dass die Bedingung dort scheinbar bei der Indexreparatur einfach nicht auftritt.
Nun kommt einem unweigerlich die Idee, wie man das Problem lösen könnte: Man treibt dem Programm die Aufräumaktion einfach ein für Allemal aus, indem man den conditional Jump auf 27DF einfach in einen absoluten Jump verwandelt, sodass der Aufräumtrupp nie gerufen wird. Siehe da, patcht man den Jump im Programmspeicher, so rennt auch die Netzwerkversion bei der Indexerstellung brav durch und der Index scheint nichtmal Fehler zu haben.
Jetzt kommt das abschließende Problem: Wie patcht man das Ganze?
Meine Vermutung was ja zunächst, dass sich das Ganze in form von Pcode in der oa4.spi befindet, aber nachdem es sich hier um "echten" Code handelt und nicht irgendwelche interpretierten Werte, scheint der Heapmanager Teil der Runtime zu sein und befindet sich - man glaubt es kaum - in oa4.exe. Nimmt man die Hex-Werte von oben als Suchzeichenkette für die OA4.EXE, so erhält man dann
Die 74 (JE) ändern wir einfach in EB (JMP), und schon sollte das Problem aus der Welt sein. Allerdings möchte ich vor unvorhergesehenen Konsequenzen warnen, denn das Löschen der Seiten wird wohl schon irgendeinen Sinn haben und der alte Speicher bleibt nun ja schließlich stehen. Nachdem diese Funktion allerdings scheinbar sogut wie nie aufgerufen wird, und bei Aufruf ja scheinbar zu Problemen führt, könnte diese Funktion evtl. auch die Ursache für andere kuriose OA4-Fehler (Systemdiskette einlegen, Ende des Speichers, etc.) sein.
Wer also wagemutig ist, kann mal ausprobieren, OA4.EXE zu patchen.
Damits einfacher geht, hab ich wieder mal nen Patcher geschrieben und beigelegt.
Hoffentlich hilft es was und ich hab mein Wochenende nicht umsonst verplempert