Tutorial zum Einlesen von Dateien gesucht
-
hallo!
Ich möchte den Inhalt einer Variable (oder heiß es "Label"? Jedenfalls ein 'Ding' aus "segment .data") aus einer Textdatei auslesen.
Ich habe eine Textdatei mit dem Inhalt
"a", "b", "c"
und daraus soll
var dd "a", "b", "c"
werden.
In meinem Tutorial habe ich dazu nichts gefunden.
Nach kurzer Suche im Internet habe ich einige Hinweise dazu aufspüren können und wahrscheinlich könnte ich mir daraus auch das gewünschte als Formalismus zusammenbasteln, aber wirklich verstehen würde ich es nicht.Kennt ihr also ein Tutorial o.ä., in dem erklärt wird, wie man in NASM-Scripten Dateien einliest?
Ihr könnt es mir natürlich auch erklären, aber das erwarte ich nicht.
Gruß
peak
-
Anmerkung:
Das Ganze soll unter Windows laufen, falls das OS einen Unterschied macht.
-
-
...
GlobalFree()
-
Wie kann ich auf diese Funktionen unter NASM zugreifen?
-
Eine include file, die die benötigten Deklarationen enthält, gibt es hier: Win32.inc. Zudem must du beim linken die benötigte Import-library(s) (kernel32.lib) angeben. (z.B. enthalten im Windows SDK)
-
Eine include file, die die benötigten Deklarationen enthält, gibt es hier: Win32.inc. Zudem must du beim linken die benötigte Import-library(s) (kernel32.lib) angeben. (z.B. enthalten im Windows SDK)
Und wie benutze ich die?
-
Alternativ kannst du dir auch den GoLink Linker besorgen - da kannst du die benutzten dlls beim linken einfach mit angeben und brauchst nicht mit irgendwelchen frickeligen Libs herumwursten.
Beispiel:
So deklarierst du WinAPI-Funktionen in deinem Quellcode:EXTERN FindNextFileA EXTERN CopyFileA EXTERN GetCurrentDirectoryA EXTERN FindClose ;... segment .text global start start: ;code hier, usw...
So assemblierst und linkst du den Kram:
nasmw -d Win32 -O32 -f win32 example.asm golink /console /entry start /fo example.exe example.obj kernel32.dll user32.dll shell32.dll comdlg32.dll example.res
(nur ein Beispiel! Konkret musst du einzeln nachschauen, welche DLLs du fuer welche Funktionen brauchst. example.res ist eine Ressources-Datei, um Ressourcen einzubinden. Das koennen Dialoge, Icons, diese nette Beschreibung im Eigenschaftendialog oder was auch immer sein... Musst du aber selbst erstellen.)
-
Beispiel:
So deklarierst du WinAPI-Funktionen in deinem Quellcode:
Assembler Code:
EXTERN FindNextFileAEXTERN CopyFileA
EXTERN GetCurrentDirectoryA
EXTERN FindClose
;...
segment .text
global startstart:
;code hier, usw...Wie ist dann die Syntax nach "start"?
Wie rufe ich die Funktionen auf?So assemblierst und linkst du den Kram:
Ich mache das zZ so:
nasm -f coff xyz.asm
gcc -o xyz xyz.onur ein Beispiel! Konkret musst du einzeln nachschauen, welche DLLs du fuer welche Funktionen brauchst.
Und wo schaue ich das nach?
Ich kann zwar tiviale Assemblerprogramme schreiben, aber darüber habe ich keinen Plan von irgendwas.
Gibt es vielleicht irgendwo ein Beispielprogramm?Gruß
peak
-
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.oZu 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 ausnasm -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.
einfachpfad 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.aspxIm 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", 0lpBuffer [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", 0nNumberOfBytesToRead [in]
byteanzahl db 8
ok?lpNumberOfBytesRead [out, optional]
brauche ich wohl nichtlpOverlapped [in, out, optional]
brauche ich wohl ebenfalls nichtJetzt 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ß
PaulPS: 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
retund 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