Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

[C#] File inlezen gaat alleen goed met unsafe code

Pagina: 1
Acties:
  • 418 views sinds 30-01-2008
  • Reageer

  • Gehakt
  • Registratie: Juli 2002
  • Laatst online: 24-10 20:19
Ik ben tegen een probleem aangelopen met het inlezen van de Structure vanuit een file.
Alles leek goed te gaan maar ik ben erachter gekomen dat per variabele alleen de eerste byte word uitgelezen.

Dus stel dat er een 4byte structure waarde gevuld moet worden en in het bestand staat op die plaats 0x11223344 dan word de waarde in de structure 0x00000011.
Echter maak ik een unsafe structure met pointers dan werkt het wel correct en word er goed ingelezen.
Alleen ik zou het graag willen dat het werkt zonder gebruik te maken van unsafe code. Aangezien hij elders in de code dan gaat mekkeren als ik die waarde wil gebruiken.


Struct waarbij het fout gaat:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
[StructLayout(LayoutKind.Explicit, Size = 54)]
public struct ElfHeader
{
            [FieldOffset(0)]
            public byte e_ident;
            [FieldOffset(16)]
            public ushort e_type;
            [FieldOffset(18)]
            public ushort e_machine;
            [FieldOffset(20)]
            public uint e_version;
            (.........)
}


Struct waarbij het goed gaat:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
[StructLayout(LayoutKind.Explicit, Size = 54)]
unsafe public struct ElfHeaderStruct
{
        [FieldOffset(0)]
        public fixed byte e_ident[16];
        [FieldOffset(16)]
        public  fixed byte e_type[2];
        [FieldOffset(18)]
        public  fixed byte e_machine[2];
        [FieldOffset(20)]
        public fixed byte e_version[4];
        (.....)
}


Hier is de code voor het inlezen:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
BinaryReader reader = new BinaryReader(File.OpenRead(this.fileName));


// Lees de header van een executable 
byte[] buff = new byte[Marshal.SizeOf(elfHeader)];
reader.Read(buff, 0, Marshal.SizeOf(elfHeader));

reader.Close();

// Reserveer geheugen voor de ElfHeaderStruct 
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(elfHeader));

// Kopieer de ingelezen byte array naar het 
// gereserveerde geheugen 
Marshal.Copy(buff, 0, ptr, buff.Length);

// Marshal vanuit het geheugen naar de 
// managed struct 
elfHeader = (ElfHeaderStruct)Marshal.PtrToStructure(ptr, typeof(ElfHeaderStruct));

// Geef het geheugen weer vrij 
Marshal.FreeHGlobal(ptr);
return 0;


Doe ik iets fout of is er uberhaupt een betere manier om dit te doen?

  • bigbeng
  • Registratie: Augustus 2000
  • Laatst online: 26-11-2021
Het is toch logisch dat het niet goed gaat als je een byte definieert in de eerste struct (op offset 0)? Je krijgt dan 1 byte van de 16 tot je beschikking. Dus dat het een byte-array moet zijn lijkt me logisch. Misschien handig om eens naar de struct tutorial van Microsoft te kijken? Daarin wordt precies uitgelegd hoe een struct zich gedraagt.
Ik zou overigens verwachten dat je met de tweede versie van je struct meer succes boekt. Die moet het volgens mij ook met managed code ingelezen kunnen worden.

Zou het trouwens iets te maken kunnen hebben met byte-order? Als ik het mij goed herinner worden de bytes van een int in omgekeerde volgorde opgeslagen in het geheugen, dus ik kan mij zo voorstellen dat dit gedrag merkbaar is als jij een int in normale byte-order opslaat in je bestand.

[ Voor 4% gewijzigd door bigbeng op 03-12-2007 12:45 ]


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:22

.oisyn

Moderator Devschuur®

Demotivational Speaker

Waarom zijn de members van de safe struct geen arrays, terwijl die van de unsafe struct juist wel arrays zijn?
.edit: spuit 11

[ Voor 7% gewijzigd door .oisyn op 03-12-2007 12:51 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Gehakt
  • Registratie: Juli 2002
  • Laatst online: 24-10 20:19
(Hier ging even iets fout)

[ Voor 255% gewijzigd door Gehakt op 03-12-2007 16:21 ]


  • Gehakt
  • Registratie: Juli 2002
  • Laatst online: 24-10 20:19
bigbeng schreef op maandag 03 december 2007 @ 12:44:
Het is toch logisch dat het niet goed gaat als je een byte definieert in de eerste struct (op offset 0)? Je krijgt dan 1 byte van de 16 tot je beschikking. Dus dat het een byte-array moet zijn lijkt me logisch.
Ik wist niet wat ik voor een 16byte -> 64bit interger waarde moest nemen als datatype?
Waarschijnlijk moet ik daar wel een array voor gebruiken?
bigbeng schreef op maandag 03 december 2007 @ 12:44:
Misschien handig om eens naar de struct tutorial van Microsoft te kijken? Daarin wordt precies uitgelegd hoe een struct zich gedraagt.
Ik zou overigens verwachten dat je met de tweede versie van je struct meer succes boekt. Die moet het volgens mij ook met managed code ingelezen kunnen worden.
Die versie werkt ook gewoon in managed code maar leest dus niet goed in zoals je in mijn post hierboven nu kan lezen. :)
bigbeng schreef op maandag 03 december 2007 @ 12:44:
Zou het trouwens iets te maken kunnen hebben met byte-order? Als ik het mij goed herinner worden de bytes van een int in omgekeerde volgorde opgeslagen in het geheugen, dus ik kan mij zo voorstellen dat dit gedrag merkbaar is als jij een int in normale byte-order opslaat in je bestand.
Misschien dat iemand anders hier uitsluitsel over kan geven?

  • bigbeng
  • Registratie: Augustus 2000
  • Laatst online: 26-11-2021
http://www.builderau.com....39024590,320277904,00.htm
Different kinds of processors store integers differently. Intel processors typically store integers in little endian format, in other words, little end first. Most other processors store integers in big endian format. So when a binary file is read and written on a different platform, there’s the possibility that you’ll have to flip the bytes around to get the correct order.
Dus, Intel processors intern werken met little-endian, wat zoveel betekent als het kleinste gedeelte eerst. De eerste byte van een int representeert dan dus het laatste deel van het getal. M.a.w op een Intel worden integers omgekeerd opgeslagen. Hoe dit precies zijn weerslag heeft op ELF headers weet ik niet, maar als een ELF header big-endian opgeslagen wordt, dan zul je de byte array zelf moeten omzetten naar een int. Het kan zijn dat hier al convertors in het framework aanwezig zijn.

Endian-nes heeft ook invloed op string encodings, zoals UTF e.d, maar dit is zo te zien bij jou niet van toepassing. Heb je anders niet een bestand waarvan je de ELF header waardes uitgeschreven kent? Dan is het gewoon een kwestie van even een keertje proberen.

  • Gehakt
  • Registratie: Juli 2002
  • Laatst online: 24-10 20:19
Hmm ik zal eens opzoeken wat het ENDIAN format van de ARM7 processor is.
Maar er gaat meer fout dan dat.
Ik heb een ELF file waarvan ik de header heb uitgelezen met een HEX viewer:
Daar staat hetvolgende in:

code:
1
2
3
4
5
6
00000000  7F 45 4C 46 01 01 01 61 00 00 00 00 00 00 00 00    ELF...a........
00000010  02 00 28 00 01 00 00 00 3C 00 00 40 34 00 00 00    ..(.....<..@4...
00000020  00 D7 02 00 02 00 00 00 34 00 20 00 01 00 28 00    .×......4. ...(.
00000030  16 00 13 00 01 00 00 00 00 80 00 00 00 00 00 40    .........€.....@
00000040  00 00 00 40 4C 9F 00 00 58 BC 00 00 07 00 00 00    ...@LŸ..X&frac14;......
00000050  00 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00    .€..............


Dat betekend dat ik uiteindelijk het volgende in mijn structure wil hebben:
code:
1
2
3
4
5
6
e_ident = 0x7F 45 4C 46 01 01 01 61 00 00 00 00 00 00 00 00
e_type  = 0x02 00
e_machine = 0x28 00
e_version = 0x01 00 00 00 
e_entry = 0x3C 00 00 40
(...etc...)


Echter als ik met de Visual studio debugger kijk zie ik het volgende:
code:
1
2
3
4
5
6
elfHeader   {Programmer.ElfHeaderStruct}    Programmer.ElfHeaderStruct
e_ident 0x7f    byte
e_type  0x0002  ushort
e_machine   0x0028  ushort
e_version   0x00000001  uint
e_entry 0x4000003c  uint

Dus er gaat al duidelijk ergens iets fout in mijn code.
het correct inlezen van de headers is wel erg cruciaal voor het verdere behandelen van het bestand.

He nu ik er zelf even goed naar kijk kan het wel degelijk iets met de endianes van de processor te maken hebben. :X
Ik ga even zoeken of dat C# hier bepaalde functies voor heeft of ik kan aangeven wat de endianes van de structure is.
Meer info hierover is altijd welkom natuurlijk!

[ Voor 9% gewijzigd door Gehakt op 03-12-2007 16:24 ]


  • Sikkek
  • Registratie: Maart 2004
  • Laatst online: 29-11 09:55
Zoals al eerder aangegeven:
je e_ident is nu een byte. Hier kan dus niet meer in dan 1 byte. Er kan dus nooit 0x7F 45 4C 46 01 01 01 61 00 00 00 00 00 00 00 00 in. Dat moet in een byte array.

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Gehakt schreef op maandag 03 december 2007 @ 13:36:
[...]
Ik wist niet wat ik voor een 16byte -> 64bit interger waarde moest nemen als datatype?
Waarschijnlijk moet ik daar wel een array voor gebruiken?
Ehm, 16 bytes is toch 128 bits? anders kan je natuurlijk gewoon een Int64 gebruiken die zoals de naam al zegt 64 bits is

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:22

.oisyn

Moderator Devschuur®

Demotivational Speaker

Gehakt schreef op maandag 03 december 2007 @ 16:21:
Hmm ik zal eens opzoeken wat het ENDIAN format van de ARM7 processor is.
Maar er gaat meer fout dan dat.
Ik heb een ELF file waarvan ik de header heb uitgelezen met een HEX viewer:
Daar staat hetvolgende in:

code:
1
2
3
4
5
6
00000000  7F 45 4C 46 01 01 01 61 00 00 00 00 00 00 00 00    ELF...a........
00000010  02 00 28 00 01 00 00 00 3C 00 00 40 34 00 00 00    ..(.....<..@4...
00000020  00 D7 02 00 02 00 00 00 34 00 20 00 01 00 28 00    .×......4. ...(.
00000030  16 00 13 00 01 00 00 00 00 80 00 00 00 00 00 40    .........€.....@
00000040  00 00 00 40 4C 9F 00 00 58 BC 00 00 07 00 00 00    ...@LŸ..X&frac14;......
00000050  00 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00    .€..............


Dat betekend dat ik uiteindelijk het volgende in mijn structure wil hebben:
code:
1
2
3
4
5
6
e_ident = 0x7F 45 4C 46 01 01 01 61 00 00 00 00 00 00 00 00
e_type  = 0x02 00
e_machine = 0x28 00
e_version = 0x01 00 00 00 
e_entry = 0x3C 00 00 40
(...etc...)


Echter als ik met de Visual studio debugger kijk zie ik het volgende:
code:
1
2
3
4
5
6
elfHeader   {Programmer.ElfHeaderStruct}    Programmer.ElfHeaderStruct
e_ident 0x7f    byte
e_type  0x0002  ushort
e_machine   0x0028  ushort
e_version   0x00000001  uint
e_entry 0x4000003c  uint

Dus er gaat al duidelijk ergens iets fout in mijn code.
Ik zie anders niets fout gaan, afgezien van het feit dat e_ident maar 1 byte is, maar zo heb je 'm dan ook gedefinieerd. Je machine is little-endian, dus 0x02 00 wordt geinterpreteerd als de ushort '2'. Als ik kijk naar de documentatie van een elf header (http://www.sco.com/develo...98-04-29/ch4.eheader.html), dan zie ik dat een type van 2 staat voor een executable. Aangezien er geen code is gekoppeld aan 0x0200 (wat het zou moeten zijn als de ELF header in big-endian formaat was) lijkt me het dus prima zo. En ook e_machine lijkt te kloppen: 0x28 == 40 == ARM

Voor die e_ident zou je evt. nog 2 int64's of 4 int32's kunnen gebruiken

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Verwijderd

Noem me achtelijk, maar wat is er gebeurd met de standaard System.IO namespace en de aanverwante functies om bestanden te lezen/schrijven. Ik begrijp 100% dat dit veel leuker is, vind ik ook... maar... Hm?

Verwijderd

Het gaat om het inlezen van bestandsheader informatie, ik denk dat het wel zo moet, helaas voor TS :+

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:22

.oisyn

Moderator Devschuur®

Demotivational Speaker

Verwijderd schreef op maandag 03 december 2007 @ 17:44:
Het gaat om het inlezen van bestandsheader informatie, ik denk dat het wel zo moet, helaas voor TS :+
Onzin natuurlijk, je hoeft niet in een keer een chunk data uit te lezen en die chunk direct te accessen als een struct. Je zou ook de losse members kunnen serializen, waarbij het dus niet meer uitmaakt in welke volgorde ze staan en dan kan de e_ident gewoon een byte[] zijn.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Verwijderd

Ik heb toevallig een zelfde soort probleem gehad laatst (een C file met daarin een array van structs uitlezen, zo snel mogelijk).

Ik gebruik dit als read methode (voor 1 struct vanaf waar de pointer op dat moment in de FileStream zit, begin in dit geval):
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
string filename = ...; // << invullen
FileStream fs = new FileStream(filename, FileMode.Open);

//Create Buffer
byte[] buff = new byte[Marshal.SizeOf(typeof(ElfHeader))];
int amt = 0;
//Loop until we've read enough bytes (usually once) 
while (amt < buff.Length)
amt += fs.Read(buff, amt, buff.Length - amt); //Read bytes 
//Make sure that the Garbage Collector doesn't move our buffer 
GCHandle handle = GCHandle.Alloc(buff, GCHandleType.Pinned);
//Marshal the bytes
ElfHeader header = (ElfHeader)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(ElfHeader));
handle.Free();//Give control of the buffer back to the GC 

fs.Close();


Dan vervolgens de struct, zou ik zo doen:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
[StructLayout(LayoutKind.Sequential, Size = 54)]
public struct ElfHeader
{
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
        public byte[] e_ident;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
        public byte[] e_type;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
        public byte[] e_machine;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
        public byte[] e_version;
        (.....)
}


Met 'MarshalAs kan je aangeven wat 't eigenlijk is (kan er geen betere uitleg aan geven, sorry :P). 't is dus een array van waarden (i.t.t. een pointer array, wat je ook kunt aangeven), van lengte 16. De marshaler zorgt er zelf voor dat ie de goeie bytes pakt.

[ Voor 8% gewijzigd door Verwijderd op 03-12-2007 19:57 ]


  • Gehakt
  • Registratie: Juli 2002
  • Laatst online: 24-10 20:19
Hmm ik dacht dat ik nog had gepost maar blijkbaar is dr iets niet goed gegaan zie ik nu.
Ik wilde nog even laten weten dat het gelukt is allemaal.
Het was inderdaad gewoon de endianes die me verwarde samen met de fout van e_ident.

Daar heb ik nu op aanraden van .oisyn 2 Int64 voor gebruikt.
Thanx voor het meedenken en ik ben alweer stukken verder met mijn programma :D

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:22

.oisyn

Moderator Devschuur®

Demotivational Speaker

Verwijderd schreef op maandag 03 december 2007 @ 19:55:
Dan vervolgens de struct, zou ik zo doen:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
[StructLayout(LayoutKind.Sequential, Size = 54)]
public struct ElfHeader
{
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
        public byte[] e_ident;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
        public byte[] e_type;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
        public byte[] e_machine;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
        public byte[] e_version;
        (.....)
}
Waarom moet alles byte arrays zijn? Dat hoeft alleen voor e_ident, bij de overige 3 members wil je gewoon dat het native type zijn, zodat je ze makkelijk kunt interpreteren.

[ Voor 3% gewijzigd door .oisyn op 04-12-2007 14:21 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Verwijderd

.oisyn schreef op dinsdag 04 december 2007 @ 14:20:
[...]

Waarom moet alles byte arrays zijn? Dat hoeft alleen voor e_ident, bij de overige 3 members wil je gewoon dat het native type zijn, zodat je ze makkelijk kunt interpreteren.
Ik moet je eerlijk zeggen dat ik er niet op die manier naar heb gekeken :) 't ging mij meer om 't feit dattie fixed arrays uit las in z'n unsafe code, en bytes in de safe code...
Maar inderdaad, als je toch alleen de eerste byte wilt hebben is dat natuurlijk veel handiger op die manier :)
Pagina: 1