Laden...

Kann mit SetFilePointer() aus kernel32.dll den Pointer eines Dateihandles nicht positionieren

Erstellt von barzefutz vor 14 Jahren Letzter Beitrag vor 14 Jahren 3.722 Views
B
barzefutz Themenstarter:in
95 Beiträge seit 2007
vor 14 Jahren
Kann mit SetFilePointer() aus kernel32.dll den Pointer eines Dateihandles nicht positionieren

Hallo,

ich versuche, auf einen physikalischen Datenträger zu schreiben, und zwar mit den importierten Windows-API-Methoden aus der kernel32.dll-Bibliothek. Mit CreateFile() bekomme ich ein Handle auf den Datenträger, mit WriteFile() kann ich Daten in Form von Bytes in dieses Handle schreiben.

Das funktioniert soweit auch, aber nur wenn ich den ersten Sektor des Datenträgers beschreibe. Wenn ich mit SetFilePointer() versuche, den Pointer auf den zweiten Sektor zu setzen, dann wird die Methode zwar fehlerfrei ausgeführt (daher ist der Threadtitel vielleicht ein wenig irreführend), aber wenn ich danach mit WriteFile() Daten schreibe, werden diese bloß wieder in den ersten Sektor geschrieben, obwohl der Pointer am Anfang des zweiten Sektors steht.

Nach dem Wälzen der Microsoft-Dokumentation habe ich auch noch kontrolliert, ob der Pointer wirklich da steht, wo ich ihn haben will:


uint position = SetFilePointer(diskHandle, 0, 0, EMoveMethod.Current);

Und er steht auf 512 Bytes, also da, wo er sein soll. Trotzdem wird ab Sektor 0 geschrieben, obwohl der Pointer auf Sektor 1 steht.

In diesem Forumsthread hier:

unable to write at the specified file pointer

Hat anscheinend jemand dasselbe Problem:

hey guys i m trying to write at a specified sector on a fat32 drive..
i m able to read from any sector by setfilepointer but wheen i write after moviing the pointer it still writes on the starting of the drive at 0 sector how can i write at any sectorr can any on help me plzzzzzz its very important very very ....

Leider bin ich absoluter Neuling auf dem Gebiet, daher bin ich nun etwas ratlos. Hat das von euch schon mal einer gemacht und kann mir weiterhelfen? Vielen Dank im Voraus 😃

2.760 Beiträge seit 2006
vor 14 Jahren

Leider bin ich absoluter Neuling auf dem Gebiet, daher bin ich nun etwas ratlos.

Hmm.. und dann gleich an PInvoke von Kernel-Methoden wagen...
Datei schreiben geht auch mit File.Create() und einem Stream...

B
barzefutz Themenstarter:in
95 Beiträge seit 2007
vor 14 Jahren

Hallo,

irgendwann ist immer das erste Mal. File.Create hilft mir leider nicht, ich will ja keine Datei erstellen, sondern direkt Sektor für Sektor auf den physikalischen Datenträger schreiben. Oder geht das damit auch?

2.760 Beiträge seit 2006
vor 14 Jahren

Achso, nö. Das geht damit nicht. Deine Frage war ein bisschen verwirrend da du abwechselnd von Dateien und Sektoren gesprochen hast, da dachte ich mir ich poste das einfach mal.

B
barzefutz Themenstarter:in
95 Beiträge seit 2007
vor 14 Jahren

Nein, von Dateien war nie die Rede. Leider sind die Namen der kernel32-Methoden CreateFile() und WriteFile() etwas irreführend, weil man damit nicht nur Dateien, sondern auch physikalische Datenträger öffnen und beschreiben kann, wie ich auch erst nach längerer Recherche herausgefunden habe 😉

Man übergibt anstelle eines Pfades im Dateisystem einfach einen Gerätenamen als Dateipfad, beispielsweise \.\PhysicalDrive2

P
67 Beiträge seit 2008
vor 14 Jahren

Bist du dir sicher das die Sektor-Größe des Datenträgers (CD?) 512bytes groß ist?

Religionskriege sind Konflikte zwischen erwachsenen Menschen, bei denen es darum geht, wer den cooleren, imaginaeren Freund hat

B
barzefutz Themenstarter:in
95 Beiträge seit 2007
vor 14 Jahren

Nein, bin mir da leider nicht sicher. Es handelt sich ganz konkret um einen USB-Stick. Haben die andere Sektorgrößen? Ich habe jetzt einfach mal 512 Byte angenommen und gar nicht weiter drüber nachgedacht. Wie kann ich denn die Sektorgröße eines Datenträgers herausfinden?

Nachtrag: Ich habe gerade noch etwas komisches festgestellt, unabhängig vom Positionieren des Pointers. Ich kann anscheinend nur den ersten Sektor beschreiben, egal was ich tue. Wenn der Pointer am Anfang des Datenträgers steht und ich 1024 Byte, also zwei Sektoren, schreibe, dann wird trotzdem nur der erste Sektor beschrieben, der zweite bleibt unberührt. Ich versteh das nicht...

Weil das alles so abstrakt ist, hier mal ein Bild, damit man es sich besser vorstellen kann. Was ich ausführe, ist:


Byte[] b1 = new Byte[1024];

for (int i = 0; i < b1.Length; i++)
{
       b1[i] = 254;   // Hex: FE
}

uint written = 0;
System.Threading.NativeOverlapped g = new System.Threading.NativeOverlapped();

WriteFile(diskHandle, b1, 1024, out written, ref g);

Ich will also die ersten 2 Sektoren (die ersten 1024 Byte) des Datenträgers beschreiben, aber er beschreibt nur die ersten 512 Byte...

1.361 Beiträge seit 2007
vor 14 Jahren

Hi,

vielleicht hilft dir auch Direkter Datenträgerzugriff.

beste Grüße
zommi

B
barzefutz Themenstarter:in
95 Beiträge seit 2007
vor 14 Jahren

Hallo zommi,

danke für den Link.
Hat mir leider nicht weitergeholfen, sofern ich nicht etwas wichtiges übersehen habe. Da geht es ja nur um den Lesevorgang, der funktioniert bei mir einwandfrei soweit ich das getestet habe. Aus dem Support-Artikel, den herbivore in diesem Thread verlinkt hat, werde ich leider nicht schlau. Der gilt doch nur für Zugriffe, die vom Dateisystem ausgehen, oder?

1.361 Beiträge seit 2007
vor 14 Jahren

Hi,

Da geht es ja nur um den Lesevorgang

Dass man FileStream.ReadByte durch FileStream.WriteByte ersetzen kann, sollte man aber erkennen 😉

Ich wollte jedenfalls darauf hinaus, dass du vielleicht auch mal probierst, das Laufwerk nur mit CreateFile zu öffnen und anschließend aus dem Handle einen FileStream erzeugst. (wie im verlinkten Artikel)
Dann solltest du doch genauso mit FileStream.Write usw. schreiben können.

Das macht es vielleicht einfach einfacher 😃

beste Grüße
zommi

B
barzefutz Themenstarter:in
95 Beiträge seit 2007
vor 14 Jahren

Hallo zommi,

super, es hat funktioniert! Ich habe, wie du gesagt hast, über einen FileStream auf den Datenträger schreiben können, auch über den ersten Sektor hinweg! Klasse, vielen Dank.

Kannst du dir vielleicht erklären, warum es mit WriteFile() nicht ging?

Glaubst du, es wäre performancetechnisch günstiger, direkt WriteFile() anstatt den C#-Umweg über den Stream zu benutzen? Ruft der FileStream in letzter Konsequenz auch eine API-Funktion wie WriteFile() auf? Oder meinst du, das ist performancetechnisch egal? Dann bräuchte ich mich mit WriteFile() ja nicht mehr herumzuschlagen 🙂

1.361 Beiträge seit 2007
vor 14 Jahren

Bei Arbeit mit dem Datenträger wird wohl immer dieser der Flaschenhals sein. Egal wie viele Wrapper um die API-Aufrufe drum sind. (Und natürlich wird auch das .Net-Framework irgendwie WriteFile aufrufen)

Der Vorteil von WriteFile wäre aber, dass du wahlfreien Zugriff hast (bei diesem FileStream kannst du glaub ich nicht die Position neu setzen).
Insofern wäre ja WriteFile auch nicht schlecht. (kannst es ja selbst nochmal kapseln)

Probier mal WriteFile ohne die OverlappedStruktur aufzurufen (also die methodendeklaration anpassen und als letzten Parameter 0 oder IntPtr.Zero übergeben).
Vielleicht ändert das ja was ?!

beste Grüße
zommi

B
barzefutz Themenstarter:in
95 Beiträge seit 2007
vor 14 Jahren

Ich habe beim Herumstöbern diese Wrapperklasse hier gefunden:

http://buiba.blogspot.com/2009/06/using-winapi-createfile-readfile.html

Es ist eine Wrapperklasse für die Dateiein- und Ausgabeoperationen von kernel32. Mit der habe ich es eben auch nochmal probiert, die benutzt intern IntPtr.Zero für das Overlapped-Dings (ehrlich gesagt weiss ich gar nicht was das ist 😃 ), aber das Resultat ist genau dasselbe. Scheint also nicht am Programmcode zu liegen. Vielleicht liegt es daran, dass ich Windows 7 benutze, ich werde es später nochmal an einem XP-Rechner testen.

Aber wie dem auch sei, hauptsache es funktioniert nun, vielen Dank nochmal!

Eine Frage noch, was ist eigentlich die optimale Puffergröße für so einen FileStream? Nach meinen Beobachtungen ist das schreiben umso schneller, je größer der Puffer ist. Hat es irgendwelche Nachteile, den Puffer auf astronomische Werte zu setzen (sagen wir mal 5 MB)?