Tutorial zum Einlesen von Dateien gesucht



  • peak_me schrieb:

    Wie ist dann die Syntax nach "start"?
    Wie rufe ich die Funktionen auf?

    Normaler Asm code...

    Ein Beispielprogramm, das ich mal fuer NASM geschrieben habe:

    ;***************************** HELLO WORLD ******************************
    
    ;
    
    ; Hello World for Win32 msg box
    
    ; (c)2010 by Nobuo T.
    
    ;************************************************************************
    
    %macro codeseg 0
    
    segment .text
    
    align 4
    
    USE32
    
    %endmacro
    
    %macro dataseg 0
    
    segment .data
    
    align 4
    
    %endmacro
    
    %macro udataseg 0
    
    segment .bss
    
    alignb 4
    
    %endmacro
    
    ; **** Includes ****
    
    %include "..\..\inc\win32n.inc"
    
    EXTERN	ExitProcess
    
    EXTERN	MessageBoxA
    
    codeseg
    
    global start
    
    start:
    
    	; UINT uType
    
    	push	MB_OK | MB_ICONINFORMATION
    
    	; LPCSTR lpCaption
    
    	push	text1
    
    	; LPCSTR text
    
    	push	text0
    
    	; HWND hWnd
    
    	push	0
    
    	call	MessageBoxA
    
    	; error code
    
    	push	0
    
    	call	ExitProcess
    
    dataseg
    
    	text0 db "Hallo Welt!", 0
    
    	text1 db "->Titel<-", 0
    

    Die win32n.inc kannst du auch von meiner Homepage downloaden. Hab' da auch noch ein paar zusaetzliche Definitionen reingeschrieben.

    peak_me schrieb:

    Ich mache das zZ so:
    nasm -f coff xyz.asm
    gcc -o xyz xyz.o

    Zu wenig Ahnung vom Innenleben von gcc, um das naeher bewerten zu koennen...

    peak_me schrieb:

    nur ein Beispiel! Konkret musst du einzeln nachschauen, welche DLLs du fuer welche Funktionen brauchst.

    Und wo schaue ich das nach?

    Die msdn ist grundsaetzlich eine sehr gute Anlaufstelle fuer alle Fragen zur WinAPI, ihrer Funktionen und dlls...



  • erstmal danke für dein Beispiel
    Doch wie bringe ich das zum laufen?

    nasm -f coff b.asm
    gcc -o b b.o
    gibt ne Menge kryptische Errors aus

    nasm -f obj b.asm
    Golink b.obj

    "A source file is in OMF format which is not supported"

    Davon abgesehen hätte ich auch keine Ahnung, wie ich das auf die anderen Funktionen zum Dateiauslesen übertragen sollte.
    einfach

    pfad db "C:\bsp.txt"
    ...
    push pfad
    call readfile
    

    ?

    Gruß
    peak



  • So sah meine make.bat dazu aus:

    nasmw -d Win32 -O32 -f win32 w32hw.asm
    
    golink /entry start /fo "hello world.exe" w32hw.obj kernel32.dll user32.dll
    

    Wie gesagt: Such dir die WinAPI-Funktionen aus der MSDN und was die Aufrufkonvention angeht:
    https://secure.wikimedia.org/wikipedia/en/wiki/X86_calling_conventions
    und zB.
    http://msdn.microsoft.com/de-de/library/984x0h58.aspx



  • So sah meine make.bat dazu aus:

    Deine Befehle haben nicht ganz funktioniert, aber die hier haben es getan:

    nasm -d Win32 -f win32 b.asm
    

    Du hattest "nasmw" benutzt, das produziert errors ("operation size not specified")
    Die "o32"-Option brauchte ich nicht, was tut die? Auf der Seite von Nasm habe ich nichts dazu gefunden.

    golink b.obj kernel32.dll user32.dll

    Dieser Befehl tut es auch, die anderen Optionen habe ich weggelassen.
    Ich wusste erst nicht, wo ich die DLLs herbekommen soll, aber ich habe sie dann aus meinem system32-Ordner kopiert.

    Dein Programm läuft so wie gewünscht und das Fenster öffnet sich.
    Jetzt habe ich versucht, es auf mein ReadFile umzuschreiben.

    Ich habe den Wiki-Artikel zu den calling conventions und dem msdn-Artikel "Argument Passing and Naming Conventions" durchgelesen.
    https://secure.wikimedia.org/wikipedia/en/wiki/X86_calling_conventions
    http://msdn.microsoft.com/de-de/library/984x0h58.aspx

    Im msdn-Artikel werden ja mehrere unterstütze Calling-Contentions aufgelistet, doch welche wird in deinem Programm benutzt bzw wo legt man fest, welche man benutzen will?
    Ich vermute mal, dass du entweder "__cdecl" oder "__stdcall" verwendet hast, beide benutzen "Pushes parameters on the stack, in reverse order (right to left)".

    Die Parameter bei ReadFile sind:
    hFile [in]
    In welchem Format muss hier der Pfad vorliegen?
    Wäre das ok?
    pfad db "C:\xyz.txt", 0

    lpBuffer [out]
    "A pointer to the buffer that receives the data read from a file or device."
    Der Buffer ist das Label, in das der Input reinsoll?
    wenn ja:
    Was ist unter "Pointer" zu verstehen?
    sowas:?
    buffer db 0
    buffername db "buffer", 0

    nNumberOfBytesToRead [in]
    byteanzahl db 8
    ok?

    lpNumberOfBytesRead [out, optional]
    brauche ich wohl nicht

    lpOverlapped [in, out, optional]
    brauche ich wohl ebenfalls nicht

    Jetzt müssen alle Parameter in umgekehrter Reihenfolge also in den stack.
    Dazu habe ich dein Programm ab "; **** Includes ****" verändert, sonst ist alles gleich:

    ; **** Includes ****
    %include "C:\DJGPP\INC\win32n.inc"
    EXTERN    ReadFile
    codeseg
    global start
    start:
    	push 0              ;lpOverlapped
    	push 0              ;lpNumberOfBytesRead
    	push byteanzahl     ;nNumberOfBytesToRead
    	push buffername     ;lpBuffer
    	push pfad           ;hFile
    
    dataseg
    	pfad db "C:\xyz.txt", 0
    	buffer db 0
    	buffername db "buffer", 0
    	byteanzahl db 8
    

    Assemblieren und Linken laufen ohne Fehler, doch kommt bein Ausführen der b.exe dann die Windows-Meldung "b.exe funktioniert nicht mehr und muss beendet werden ..."

    Was mache ich falsch?

    Gruß
    Paul

    PS: danke für die tolle Hilfe, ich bin davon überzeugt, dass ich es Schritt für Schritt hinbekomme.
    Antworten kann ich wegen meiner Arbeitszeiten nicht immer sofort.



  • Die WinAPI Funktionen verwenden immer StdCall - Ausnahmen bilden hier nur Funktionen mit variabler Parameteranzahl, die dann cdecl benutzen (z.B. wsprintf).

    Ansonsten wurde ja schon zu Anfang des Threads gezeigt, wie man etwas einlesen kann. Zwingend notwendig ist auf jeden Fall CreateFile() -> ReadFile() -> CloseHandle().
    [Alternative dazu wäre die msvcrt zu verwenden(fopen,fread,...)]

    Zu Thema Pointers: google - das ist Basiswissen

    Ansonsten ist noch anzumerken, das NASM nicht *der* Assembler für Windows ist. Grad für Anfänger ist hier das masm32-SDK, das Microsofts macro Assembler enthält, besser geeignet (s.a. jwasm).



  • Die WinAPI Funktionen verwenden immer StdCall - Ausnahmen bilden hier nur Funktionen mit variabler Parameteranzahl

    alles klar

    Zwingend notwendig ist auf jeden Fall CreateFile() -> ReadFile() -> CloseHandle()

    ahhhh, jetzt habe ich erst die Pfeile bei dem Beitrag von WinAPI___- gesehen.
    Man muss also ALLE (bzw die drei) Funktionen nacheinander ausführen!

    Zu Thema Pointers: google - das ist Basiswissen

    aha, das ist also mit Pointer gemeint: einfach das Label ohne eckige Klammern

    mein Programm sieht jetzt so aus:

    ; **** Includes ****
    %include "C:\DJGPP\INC\win32n.inc"
    EXTERN CreateFile
    EXTERN ReadFile
    EXTERN CloseHandle
    codeseg
    global start
    start:
    	push 0 ;hTemplateFile
    	push FILE_ATTRIBUTE_NORMAL ;dwFlagsAndAttributes
    	push OPEN_EXISTING ;dwCreationDisposition
    	push 0 ;lpSecurityAttributes
    	push FILE_SHARE_READ ;dwShareMode
    	push GENERIC_READ ;dwDesiredAccess
    	push pfad ;lpFileName
    	call CreateFile
    
    	push 0 ;lpOverlapped
    	push byteanzahl ;lpNumberOfBytesRead
    	push 10 ;nNumberOfBytesToRead
    	push inhalt; lpBuffer
    	push pfad ;hFile
    	call ReadFile
    
    	push pfad
    	call CloseHandle
    
    dataseg
    	byteanzahl db 0
    	inhalt db 0
    	pfad db "C:\t.txt"
    

    Frage 1:
    Was ist der Datei-Handle, den man an ReadFile und CloseHandle übergeben muss?
    Ist das einfach der Dateipfad? Oder der Name, den CreateFile für die Datei erzeugt hat? Wenn ja: Wie würde dieser Name lauten?

    Frage 2:

    golink b.obj kernel32.dll

    wirft folgenden Error aus:
    "The follwoing symbol was not defined in the object file or files:
    CreateFile"

    "Requirements
    DLL Kernel32.dll"
    http://msdn.microsoft.com/en-us/library/aa363858(v=vs.85).aspx

    "ReadFile" und "CloseHandle" findet er in der kernel32.dll.
    Wieso findet er "CreateFile" nicht?

    Gruß
    Paul



  • Funktionen die mit Strings zu tun haben, gibt es meistens in zwei Varianten: ANSI/ASCII und Unicode. Dementsprechend erhält der Funktionsname das Suffix 'A' oder 'W' -> in deinem Fall CreateFileA (findet sich im Übrigen auch in der Dokumentation zu CreateFile).

    CreateFile gibt dir, wenn Erfolgreich, das entsprechende Handle zurück (durch eax).

    HANDLE hFile = CreateFileA("xyz.txt",...)
    if(hFile != INVALID_HANDLE_VALUE)
    {
        DWORD NumberOfBytesRead;
        if(ReadFile(hFile,&buffer,10,&NumberOfBytesRead,0) != 0)
        {
            if(NumberOfBytesRead == 10)
            {
    
            }
            else
            {
                //nicht so gelaufen wie gedacht;
            }
        }
        else
        {
            // Fehler
        }
    
    }
    else
    {
        // Fehler
    }
    

    (BTW: jedes Windows Programm endet mit einem ExitProcess())



  • peak_me schrieb:

    So sah meine make.bat dazu aus:

    Deine Befehle haben nicht ganz funktioniert, aber die hier haben es getan:

    nasm -d Win32 -f win32 b.asm
    

    Du hattest "nasmw" benutzt, das produziert errors ("operation size not specified")
    Die "o32"-Option brauchte ich nicht, was tut die? Auf der Seite von Nasm habe ich nichts dazu gefunden.

    Sollte nicht sein. nasmw ist bei mir die win32-Version von nasm. "nasm.exe" ist bei mir die DOS-Version. Vom Funktionsumfang sind beide gleich...
    Was die "O"-Option angeht, haette dir das auch ein fluechtiger Blick in die NASM-Hilfe oder ganz simpel ein "nasm -h" gesagt:

    nasm -h schrieb:

    -O<digit> optimize branch offsets
    -O0: No optimization (default)
    -O1: Minimal optimization
    -Ox: Multipass optimization (recommended)

    peak_me schrieb:

    golink b.obj kernel32.dll user32.dll

    Dieser Befehl tut es auch, die anderen Optionen habe ich weggelassen.
    Ich wusste erst nicht, wo ich die DLLs herbekommen soll, aber ich habe sie dann aus meinem system32-Ordner kopiert.

    Da liegen die dlls auch richtig. Alles, was golink im Prinzip tut, ist in den Programm-Container zu schreiben "lade beim starten kernel32.dll, user32.dll, ...", usw. Wo die Dateien zu finden sind, muss Windows dann selbst herausfinden und das kennt sich in seinen System-Ordnern idR. ganz gut aus. 😉

    peak_me schrieb:

    Jetzt müssen alle Parameter in umgekehrter Reihenfolge also in den stack.
    Dazu habe ich dein Programm ab "; **** Includes ****" verändert, sonst ist alles gleich:

    ; **** Includes ****
    %include "C:\DJGPP\INC\win32n.inc"
    EXTERN    ReadFile
    codeseg
    global start
    start:
    	push 0              ;lpOverlapped
    	push 0              ;lpNumberOfBytesRead
    	push byteanzahl     ;nNumberOfBytesToRead
    	push buffername     ;lpBuffer
    	push pfad           ;hFile
    
    dataseg
    	pfad db "C:\xyz.txt", 0
    	buffer db 0
    	buffername db "buffer", 0
    	byteanzahl db 8
    

    Assemblieren und Linken laufen ohne Fehler, doch kommt bein Ausführen der b.exe dann die Windows-Meldung "b.exe funktioniert nicht mehr und muss beendet werden ..."

    Was mache ich falsch?

    Dir ist schon klar, dass du am Ende deines Codes dein Programm immer explizit beenden musst - zB. durch einen Aufruf von "ExitProcess"?
    Tutst du das nicht, laeuft dein Programm einfach weiter - egal, ob der Quellcode zuende ist oder nicht. In diesem konkreten Fall werden dann halt die nach dem codesegment folgenden Daten ausgefuehrt. Das fuehrt dann idR. zum crash...

    peak_me schrieb:

    Zu Thema Pointers: google - das ist Basiswissen

    aha, das ist also mit Pointer gemeint: einfach das Label ohne eckige Klammern

    Vielleicht solltest du dir doch noch etwas mehr Detailwissen zum Thema Pointer (im Vergleich zu Adressen allgemein) anlesen... Das ist wichtig.
    Weil du den Begriff "Label" uebernommen hast, schreibe ich dazu vielleicht auch noch was:
    Ein Label ist in Assembler so etwas wie ein Platzhalter fuer eine Adresse.
    Wenn du irgendwo in deinem Quellcode ein Label zB. als "Variablennamen" oder Namen fuer eine Prozedur oder Sprungmarke schreibst, schaut der Assembler, welche Offsetadresse die Daten oder Befehle an dieser Stelle in deinem Programm haben und setzt dann an allen Stellen, an denen das Label als Verweis benutzt wird, diese Offset-Adresse ein.

    peak_me schrieb:

    Frage 1:
    Was ist der Datei-Handle, den man an ReadFile und CloseHandle übergeben muss?
    Ist das einfach der Dateipfad? Oder der Name, den CreateFile für die Datei erzeugt hat? Wenn ja: Wie würde dieser Name lauten?

    Haettest du den msdn-Artikel zu "CreateFile" etwas gruendlicher gelesen, wuesstest du, dass der File Handle etwas ist, das CreateFile bei Erfolg als Rueckgabewert liefert (ist ein int, also liegt er nach dem Aufruf in eax).
    Ein Handle ist idR. so etwas wie eine Identifikationsnummer. Beschleunigt das Arbeiten mit vielen eher abstrakten Objekten. Laesst sich zB. allemal wesentlich einfacher mit arbeiten, als jedes Mal beim Zugriff auf eine Datei den langen Dateinamen durchgehen zu muessen. 😉

    peak_me schrieb:

    wirft folgenden Error aus:
    "The follwoing symbol was not defined in the object file or files:
    CreateFile"

    "Requirements
    DLL Kernel32.dll"
    http://msdn.microsoft.com/en-us/library/aa363858(v=vs.85).aspx

    "ReadFile" und "CloseHandle" findet er in der kernel32.dll.
    Wieso findet er "CreateFile" nicht?

    Da warst du doch schon an der richtigen Stelle. Darunter steht, dass es 2 Versionen dieser Funktion gibt: Eine, die den Dateinamen als Unicode-String erwartet (CreateFileW) und eine, die den Dateinamen als normalen ANSI-Byte-String erwartet (CreateFileA). Mit diesen Namen stehen die Funktionen auch in der kernel32.dll. "CreateFile" gibt es nicht.
    Da die anderen Funktionen keine Strings als Parameter haben, gibt es diese Unterscheidung dort nicht...



  • Was die "O"-Option angeht, haette dir das auch ein fluechtiger Blick in die NASM-Hilfe oder ganz simpel ein "nasm -h" gesagt:

    Da habe ich schon nachgeschaut, ich habe aber nach "O32" gesucht und deswegen nichts gefunden.

    Da liegen die dlls auch richtig. [...] Wo die Dateien zu finden sind, muss Windows dann selbst herausfinden und das kennt sich in seinen System-Ordnern idR. ganz gut aus. 😉

    Ich dachte, die müssten im gleichen Ordner wie der Linker liegen, wenn man keinen extra Pfad angibt. Aber wenn das wie du beschrieben hast abläuft, lösche ich sie wieder aus meinem Ordner.

    Dir ist schon klar, dass du am Ende deines Codes dein Programm immer explizit beenden musst - zB. durch einen Aufruf von "ExitProcess"?

    Ja, das hatte ich vernachlässigt, weil die Programmenden in meinem Tutorial von C geregelt werden.
    Die hören immer so auf:

    popa
    mov eax, 0
    leave
    ret

    und laufen in diesem C-Programm ab:

    int PRE_CDECL asm_main( void ) POST_CDECL;
    int main()
    {
    int ret_status;
    ret_status = asm_main();
    return ret_status;
    }

    Ich werd aber mal stattdessen ExitProcess verwenden.

    Vielleicht solltest du dir doch noch etwas mehr Detailwissen zum Thema Pointer (im Vergleich zu Adressen allgemein) anlesen... Das ist wichtig.

    Wo könnte ich mir dazu was durchlesen?
    Ich habe das so verstanden:
    Ein Pointer ist eine Variable, die eine Adresse enthält.
    "pop Label" müsste dann doch die Adresse von "Label" in den stack schieben.
    Oder muss den Windowsfunktionen der Name des Pointers, also der Name des Labels in welchem die Adresse gespeichert ist, übergeben werden?
    also
    pop "Label"?

    Haettest du den msdn-Artikel zu "CreateFile" etwas gruendlicher gelesen, wuesstest du, dass der File Handle etwas ist, das CreateFile bei Erfolg als Rueckgabewert liefert (ist ein int, also liegt er nach dem Aufruf in eax).

    Ich hatte das schon gelesen:

    Return Value
    If the function succeeds, the return value is an open handle to the specified file, device, named pipe, or mail slot.

    konnte mir aber nichts darunter vorstellen, deswegen habe ich nachgefragt

    Ein Handle ist idR. so etwas wie eine Identifikationsnummer. Beschleunigt das Arbeiten mit vielen eher abstrakten Objekten. Laesst sich zB. allemal wesentlich einfacher mit arbeiten, als jedes Mal beim Zugriff auf eine Datei den langen Dateinamen durchgehen zu muessen. 😉

    verstehe

    call CreateFileA
    mov [handle],eax
    

    Dann habe ich in "handle" die Identifikationsnummer.

    Darunter steht, dass es 2 Versionen dieser Funktion gibt: Eine, die den Dateinamen als Unicode-String erwartet (CreateFileW) und eine, die den Dateinamen als normalen ANSI-Byte-String erwartet (CreateFileA).

    Ja, das hatte ich auch gelesen; ich dachte, dass das zwei Spezialfälle wären und wollte erstmal klären, wieso er CreateFile an sich nicht findet und dann auf dieses Thema eingehen.

    thx auch an InterfaceUnkowen!



  • So sieht es jetzt aus:

    %macro codeseg 0
    segment .text
    align 4
    USE32
    %endmacro
    
    %macro dataseg 0
    segment .data
    align 4
    %endmacro
    
    %macro udataseg 0
    segment .bss
    alignb 4
    %endmacro
    
    %include "C:\DJGPP\INC\win32n.inc"
    EXTERN CreateFileA
    EXTERN ReadFile
    EXTERN CloseHandle
    EXTERN    ExitProcess
    EXTERN    MessageBoxA
    codeseg
    global start
    
    start:
    	push 0 ;hTemplateFile
    	push FILE_ATTRIBUTE_NORMAL ;dwFlagsAndAttributes
    	push OPEN_EXISTING ;dwCreationDisposition
    	push 0 ;lpSecurityAttributes
    	push FILE_SHARE_READ ;dwShareMode
    	push GENERIC_READ ;dwDesiredAccess
    	push pfad ;lpFileName
    	call CreateFileA
    
    	mov [handle],eax
    
    	push 0 ;lpOverlapped
    	push byteanzahl ;lpNumberOfBytesRead
    	push 10 ;nNumberOfBytesToRead
    	push inhalt; lpBuffer
    	push handle ;hFile
    	call ReadFile
    
    	push handle
    	call CloseHandle
    
    	push    0
    	call    ExitProcess 
    
    dataseg
    	byteanzahl db 0
    	inhalt db 0
    	pfad db "C:\z.txt"
    	handle db 0
    

    Assemblieren, Linken und Ausführen laufen ohne Error durch.
    Da ich die Ausgabe immer über die Scripte meines Tutorial-Autors, die über C laufen, geregelt hatte, weiß ich nicht wie ich mir den Inhalt der Datei jetzt anzeigen lasse.

    Ich muss jetzt erstmal die äußeren Zwänge meiner Umwelt befriedigen (= Essen beschaffen und Möbel aufbauen).
    Vielleicht kann mir in der Zwischenzeit jemand einen Hinweis auf ein Tutorial geben, in dem ich etwas über die Ausgabe erfahren kann.
    Ich hatte kurz versucht, das Programm von Nobuo T als Ausgabe zu benutzen und hatte dies noch in das obenstehende Programm integriert:

    push    MB_OK | MB_ICONINFORMATION
        push    inhalt
        push    inhalt
        push    0
        call    MessageBoxA
    

    Eigentlich müsste im Fenstertitel und im Fenster ja nun der Inhalt der Datei stehen, aber beides ist leer.

    Sachdienliche Hinweise nehme ich gerne entgegen 🙂


Anmelden zum Antworten