Im Hex-Editor habe ich dann die "falschen" Funktionsnamen gesehen
Ich habe jetzt mal gesucht, Ursache ist der Linker, der hier die Namen vergibt. Aber ich habe noch keine Möglichkeit gefunden das zu ändern.
Im Hex-Editor habe ich dann die "falschen" Funktionsnamen gesehen
Ich habe jetzt mal gesucht, Ursache ist der Linker, der hier die Namen vergibt. Aber ich habe noch keine Möglichkeit gefunden das zu ändern.
c:\Users\<USER>\.choosenim\toolchains\nim-1.2.0\dist\
Woher stammen diese Pfade, selbst erstellt?
In meiner Installation liegt nim-1.2.0\dist\ im Ordner C:\Users\USER\scoop\apps\nim
Ich spiele gerade mit der Möglichkeit Daten mittels Dll-Aufruf von AutoIt an Nim zu leiten.
In pur AutoIt würde ich das so machen, um Strings zu übertragen:
; ===== V E R S E N D E R =====
; == ByteArray erstellen, mit Daten füllen
Local $byteArray = _ByteArray_Create("wchar[128]", 3)
DllStructSetData ($byteArray[1], 1, "Daten #1")
DllStructSetData ($byteArray[2], 1, "Daten #2")
DllStructSetData ($byteArray[3], 1, "Daten #3")
; == Pointer für DllCall
Local $ptrData = DllStructGetPtr($byteArray[0])
; == fiktiver Aufruf
; DllCall('MyCommunicate.dll', 'int', 'sendData', 'ptr', $ptrData)
; ===== E M P F Ä N G E R =====
; == ByteArray aus Pointer erstellen
Local $aReceived = _ByteArray_Create("wchar[128]", 3, $ptrData)
ConsoleWrite(DllStructGetData($aReceived[1], 1) & @CRLF)
ConsoleWrite(DllStructGetData($aReceived[2], 1) & @CRLF)
ConsoleWrite(DllStructGetData($aReceived[3], 1) & @CRLF)
Func _ByteArray_Create($_sStruct, $_iElements=2, $_ptrArray0=Null)
Local $iSize = DllStructGetSize(DllStructCreate($_sStruct, 1))
Local $aByte[$_iElements +1]
If $_ptrArray0 <> Null Then
$aByte[0] = DllStructCreate('byte[' & $iSize *$_iElements & "]", $_ptrArray0)
Else
$aByte[0] = DllStructCreate('byte[' & $iSize *$_iElements & "]")
EndIf
Local $ptr = DllStructGetPtr($aByte[0])
For $i = 0 To $_iElements -1
$aByte[$i+1] = DllStructCreate($_sStruct, $ptr + $i*$iSize)
Next
Return $aByte
EndFunc ;==>_ByteArray_Create
Alles anzeigen
Nun ist die Frage, wie kann ich in Nim die Daten aus dem Pointer in eine Variable entpacken? Ich erstelle dazu den passenden Datentyp ByteArray, der so dimensioniert ist, dass er exakt die Daten aus dem Pointer aufnehmen kann - aber wie gehts?
Sind alle 0 Bytes groß. Wieso sind die da drin? Habe doch nichts von loggen gesagt...
Keine Ahnung, ist bei mir nicht vorhanden. ![]()
Und die Dateinamen dann zu übersetzen ist kompletter Humbug, da die Dateien selbst anders sortiert werden als mit ihrem originalen Unicode-Titel.
Du bist lustig - schon mal was von Ziffern gehört? Da auch ANSI konforme Daten nicht in alphabetischer Reihenfolge auf der CD/DVD gespeichert sind, wird doch sowieso nummeriert. ![]()
Lass von der Progressbar die prozentuale Auftragsabarbeitung anzeigen, unabhängig von der Dauer des jeweiligen Vorgangs.
Du hast 10 Vorgänge, also 10% je Vorgang. Nach jedem fertigen Vorgang erhöhst du den Wert der Progressbar also um 10. Sind alle durch bist du bei 100 und die Progressbar ist "voll". ![]()
Als Möglichkeit sehe ich hier, die Datei temporär umzubenennen
Ich würde eher dazu tendieren, Nicht-ANSI Zeichen in Dateinamen generell durch ANSI zu ersetzen.
Also statt
Louisa Johnson - Who’s Loving You.m4a
besser
Louisa Johnson - Who's Loving You.m4a
Ist vielleicht Geschmackssache, aber ich halte absolut nichts von Nicht-ANSI Zeichen (und Leerzeichen!) in Pfad-/Dateinamen. In meiner Musikbibliothek würde dein Bsp. so aussehen:
Louisa_Johnson__Who's_Loving_You.m4a
In den Tags kann drinstehen was will, das wird ja auch entsprechend für die Anzeige genutzt, also kann doch der Dateiname "sauber" bleiben. ![]()
Wenn ich von hier die Versionen für x86 (nim-1.2.0_x32.zip, mingw32.7z) verwende, dann kann ich die dll mit deinen Parametern zwar erstellen, ohne dass gemeckert wird, aber es funktioniert dann trotzdem nicht in AutoIt, wenn #AutoIt3Wrapper_UseX64=n.
Ja, ich hatte dazu in der VM nim und mingw schon als 32bit installiert um so eine eigene Umgebung zu haben. Da funktioniert auch das Kompilieren, aber die Dll ist nicht nutzbar (@error=1).
Hinzu kommt das Problem des automatisch erstellten Funktions-Namens-Suffix (@8).
DllStructGetData($tBitmapData, "Scan0")
Es wäre erst mal nicht ganz unwichtig, wenn du uns den Aufbau deiner Struktur $tBitmapData mitteilst (lauffähige Minimalskripte werden von uns überhaupt sehr begrüßt).
Laut deiner Befehlszeile ..., DllStructGetData($tBitmapData, "Scan0") ist "Scan0" ein Pointer, der auf ein DWORD-Array der Größe $iWidth * $iHeight verweist. Das ist alles, was man deinem Post entnehmen kann. Alles andere.... - wie Bitnugger bereits sagte: Glaskugel
Diesen Thread hast Du sicher bereits selbst gefunden, oder ?
Ja, du brauchst aber keine Threads vor ~2018 ansehen. 90% von dem dort geschriebenen ist inzwischen inhaltlich überholt.
Das, was dort zusätzlich als Einzelparameter übergeben wird, ist inzwischen schon so in der Konfiguration enthalten.
Ich habe mal etwas probiert, um Programme sowohl für 32bit, als auch für 64bit zu erstellen.
x64 ist Standard und somit kein Problem.
Laut Beschreibung "Use --cpu:i386 or --cpu:amd64 to switch the CPU architecture." sollte also mit --cpu:i386 eine 32bit Version erstellt werden können. (Wobei der Schalter ja auf die CPU Architektur ausgerichtet ist. x86-CPU habe ich schon seit Jahren nicht mehr gesehen, selbst die 8 Jahre alten PC in der Firma haben x64 CPU - Aber: Dort laufen x86-OS!)
Wie muss ich denn ein Programm erstellen, dass auf CPU(x64) mit OS(x86) laufen soll?
Ich wollte jetzt meine Dll-Datei mal als x86 erstellen: nim c --app:lib --cpu:i386 -o:math32.dll math.nim
Das haut mir der Compiler um die Ohren.
In file included from C:\Users\BugFix\nimcache\math_d\stdlib_system.nim.c:9:0:
C:\Users\BugFix\scoop\apps\nim\1.2.0\lib/nimbase.h:522:13: error: size of array 'Nim_and_C_compiler_disagree_on_target_architecture' is negative
typedef int Nim_and_C_compiler_disagree_on_target_architecture[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8 ? 1 : -1];
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from C:\Users\BugFix\nimcache\math_d\stdlib_io.nim.c:9:0:
C:\Users\BugFix\scoop\apps\nim\1.2.0\lib/nimbase.h:522:13: error: size of array 'Nim_and_C_compiler_disagree_on_target_architecture' is negative
typedef int Nim_and_C_compiler_disagree_on_target_architecture[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8 ? 1 : -1];
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from C:\Users\BugFix\nimcache\math_d\@mmath.nim.c:9:0:
C:\Users\BugFix\scoop\apps\nim\1.2.0\lib/nimbase.h:522:13: error: size of array 'Nim_and_C_compiler_disagree_on_target_architecture' is negative
typedef int Nim_and_C_compiler_disagree_on_target_architecture[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8 ? 1 : -1];
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Error: execution of an external compiler program 'gcc.exe -c -w -mno-ms-bitfields -IC:\Users\BugFix\scoop\apps\nim\1.2.0\lib -IC:\CODE\nim\_myExamples_ -o C:\Users\BugFix\nimcache\math_d\stdlib_io.nim.c.o C:\Users\BugFix\nimcache\math_d\stdlib_io.nim.c' failed with exit code: 1
Alles anzeigen
Wieso wird denn "NimMain" exportiert?
Nim Main (Ordinal #1) ist die Ansprungadresse und verwaltet den internen Zugriff auf die Funktionen der Dll.
Die obige Dll sieht kpl. so aus (zu finden unter "..USER\nimcache"):
/* Generated by Nim Compiler v1.2.0 */
/* (c) 2020 Andreas Rumpf */
/* The generated code is subject to the original license. */
/* Compiled for: Windows, amd64, gcc */
/* Command for C compiler:
gcc.exe -c -w -mno-ms-bitfields -IC:\Users\BugFix\scoop\apps\nim\1.2.0\lib -IC:\CODE\nim\_myExamples_ -o C:\Users\BugFix\nimcache\math_d\@mmath.nim.c.o C:\Users\BugFix\nimcache\math_d\@mmath.nim.c */
#define NIM_INTBITS 64
#include "nimbase.h"
#include <windows.h>
#undef LANGUAGE_C
#undef MIPSEB
#undef MIPSEL
#undef PPC
#undef R3000
#undef R4000
#undef i386
#undef linux
#undef mips
#undef near
#undef far
#undef powerpc
#undef unix
# define nimfr_(proc, file) \
TFrame FR_; \
FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = 0; nimFrame(&FR_);
# define nimfrs_(proc, file, slots, length) \
struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename; NI len; VarSlot s[slots];} FR_; \
FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = length; nimFrame((TFrame*)&FR_);
# define nimln_(n, file) \
FR_.line = n; FR_.filename = file;
N_LIB_EXPORT N_STDCALL(NI, addiere)(NI p1, NI p2);
N_LIB_PRIVATE N_NOINLINE(void, raiseOverflow)(void);
static N_INLINE(void, nimFrame)(TFrame* s);
N_LIB_PRIVATE N_NOINLINE(void, callDepthLimitReached__mMRdr4sgmnykA9aWeM9aDZlw)(void);
static N_INLINE(void, popFrame)(void);
N_LIB_EXPORT N_STDCALL(NI, subtrahiere)(NI p1, NI p2);
N_LIB_EXPORT N_STDCALL(NI, multipliziere)(NI p1, NI p2);
N_LIB_EXPORT N_STDCALL(NF, dividiere)(NF p1, NF p2);
static N_INLINE(void, initStackBottomWith)(void* locals);
N_LIB_PRIVATE N_NOINLINE(void, nimGC_setStackBottom)(void* theStackBottom);
N_LIB_PRIVATE N_NIMCALL(void, stdlib_ioInit000)(void);
N_LIB_PRIVATE N_NIMCALL(void, systemDatInit000)(void);
N_LIB_PRIVATE N_NIMCALL(void, systemInit000)(void);
N_LIB_PRIVATE N_NIMCALL(void, NimMainModule)(void);
extern TFrame* framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw;
extern TFrame* framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw;
extern TFrame* framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw;
extern TFrame* framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw;
extern TFrame* framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw;
extern TFrame* framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw;
static N_INLINE(void, nimFrame)(TFrame* s) {
{
if (!(framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw == NIM_NIL)) goto LA3_;
(*s).calldepth = ((NI16) 0);
}
goto LA1_;
LA3_: ;
{
(*s).calldepth = (NI16)((*framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw).calldepth + ((NI16) 1));
}
LA1_: ;
(*s).prev = framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw;
framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw = s;
{
if (!((*s).calldepth == ((NI16) (((NI) 2000))))) goto LA8_;
callDepthLimitReached__mMRdr4sgmnykA9aWeM9aDZlw();
}
LA8_: ;
}
static N_INLINE(void, popFrame)(void) {
framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw = (*framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw).prev;
}
N_LIB_EXPORT N_STDCALL(NI, addiere)(NI p1, NI p2) {
NI result;
NI TM__Jx2ojdvqxHFcjCMWQrg9aRg_2;
nimfr_("addiere", "C:\\CODE\\nim\\_myExamples_\\math.nim");
result = (NI)0;
nimln_(7, "C:\\CODE\\nim\\_myExamples_\\math.nim");
if (nimAddInt(p1, p2, &TM__Jx2ojdvqxHFcjCMWQrg9aRg_2)) { raiseOverflow(); };
result = (NI)(TM__Jx2ojdvqxHFcjCMWQrg9aRg_2);
popFrame();
return result;
}
N_LIB_EXPORT N_STDCALL(NI, subtrahiere)(NI p1, NI p2) {
NI result;
NI TM__Jx2ojdvqxHFcjCMWQrg9aRg_3;
nimfr_("subtrahiere", "C:\\CODE\\nim\\_myExamples_\\math.nim");
result = (NI)0;
nimln_(10, "C:\\CODE\\nim\\_myExamples_\\math.nim");
if (nimSubInt(p1, p2, &TM__Jx2ojdvqxHFcjCMWQrg9aRg_3)) { raiseOverflow(); };
result = (NI)(TM__Jx2ojdvqxHFcjCMWQrg9aRg_3);
popFrame();
return result;
}
N_LIB_EXPORT N_STDCALL(NI, multipliziere)(NI p1, NI p2) {
NI result;
NI TM__Jx2ojdvqxHFcjCMWQrg9aRg_4;
nimfr_("multipliziere", "C:\\CODE\\nim\\_myExamples_\\math.nim");
result = (NI)0;
nimln_(13, "C:\\CODE\\nim\\_myExamples_\\math.nim");
if (nimMulInt(p1, p2, &TM__Jx2ojdvqxHFcjCMWQrg9aRg_4)) { raiseOverflow(); };
result = (NI)(TM__Jx2ojdvqxHFcjCMWQrg9aRg_4);
popFrame();
return result;
}
N_LIB_EXPORT N_STDCALL(NF, dividiere)(NF p1, NF p2) {
NF result;
nimfr_("dividiere", "C:\\CODE\\nim\\_myExamples_\\math.nim");
result = (NF)0;
nimln_(16, "C:\\CODE\\nim\\_myExamples_\\math.nim");
result = ((NF)(p1) / (NF)(p2));
popFrame();
return result;
}
static N_INLINE(void, initStackBottomWith)(void* locals) {
nimGC_setStackBottom(locals);
}
N_LIB_PRIVATE void PreMainInner(void) {
stdlib_ioInit000();
}
N_LIB_PRIVATE int cmdCount;
N_LIB_PRIVATE char** cmdLine;
N_LIB_PRIVATE char** gEnv;
N_LIB_PRIVATE void PreMain(void) {
void (*volatile inner)(void);
inner = PreMainInner;
systemDatInit000();
initStackBottomWith((void *)&inner);
systemInit000();
(*inner)();
}
N_LIB_PRIVATE N_CDECL(void, NimMainInner)(void) {
NimMainModule();
}
N_LIB_EXPORT N_CDECL(void, NimMain)(void) {
void (*volatile inner)(void);
PreMain();
inner = NimMainInner;
initStackBottomWith((void *)&inner);
(*inner)();
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason,
LPVOID lpvReserved) {
if(fwdreason == DLL_PROCESS_ATTACH) {
NimMain();
}
return 1;
}
N_LIB_PRIVATE N_NIMCALL(void, NimMainModule)(void) {
{
}
}
Alles anzeigen
Mit Return type int bekomme ich 1816193777 bzw. 0x6C40EAF1.
OK, dann gilt dasselbe, wie bei float: in AutoIt int64 definieren oder als Export in Nim int32 verwenden.
Wobei das eigentlich bei Int doch erst zum Tragen kommen dürfte, wenn der Bereich überschritten wird. Integerwerte die im Bereich von int32 liegen, sind doch identisch mit denen von int64. Bei mir hat das auch keinen Einfluß.
Interessant ist natürlich, wie man Funktionen für AutoIt verfügbar machen kann.
Dll-Erstellung ist recht simpel. Zu beachten ist:
• Nim erstellt standardmäßig 64Bit Dll (also in AutoIt: #AutoIt3Wrapper_UseX64=y erforderlich)
• Aufpassen bei den Datentypen. z.B. float in Nim ist float64 - muss in AutoIt dann mit double deklariert werden (oder in Nim als float32 - dann ist es in AutoIt float)
# Compile: nim c --app:lib math.nim
proc addiere(p1, p2: int): int {.stdcall,exportc,dynlib.} =
p1 + p2
proc subtrahiere(p1, p2: int): int {.stdcall,exportc,dynlib.} =
p1 - p2
proc multipliziere(p1, p2: int): int {.stdcall,exportc,dynlib.} =
p1 * p2
proc dividiere(p1, p2: float): float {.stdcall,exportc,dynlib.} =
p1 / p2
Alles anzeigen
#AutoIt3Wrapper_UseX64=y
Local $hDll = DllOpen("C:\CODE\nim\_myExamples_\math.dll")
Local $aRes, $a = 9, $b = 18
$aRes = DllCall($hDll, 'int', 'addiere', 'int', $a, 'int', $b)
If Not @error Then ConsoleWrite($aRes[0] & @CRLF)
$aRes = DllCall($hDll, 'int', 'subtrahiere', 'int', $a, 'int', $b)
If Not @error Then ConsoleWrite($aRes[0] & @CRLF)
$aRes = DllCall($hDll, 'int', 'multipliziere', 'int', $a, 'int', $b)
If Not @error Then ConsoleWrite($aRes[0] & @CRLF)
$aRes = DllCall($hDll, 'double', 'dividiere', 'double', $a, 'double', $b)
If Not @error Then ConsoleWrite($aRes[0] & @CRLF)
DllClose($hDll)
Alles anzeigen
"f:\Audio\MP3\Top\Top 40 Oldies Forever" "f:\Audio\MP3\Top\Top 40 Oldies Forever\"
Das ist ja auch nicht zulässig (auch in anderen Sprachen nicht). Mit dem Backslash am Ende maskierst du den Stringbegrenzer, sodass dieser statt Backslash am Ende des Parameters verbleibt.
Wieso? Was spricht denn dagegen? Jeder der lernt soll auch ausprobieren.
Und dennoch ist es doch seeeehr seltsam, dass Imagesearch als erste Funktion den Anfängern ins Auge springt.
Wieso landen Anfänger bei der Fülle der Funktionen gerade dort?! Das setzt gezielte Suche voraus und ebenso einen Plan, was man tun will.
Ich habe noch nie erlebt, dass man "zufällig" auf das Pixelgedöns stößt und sich sagt: Geil, hier kann ich was Lernen. ![]()
Wer Lernen möchte, arbeitet Tutorials durch, programmiert Bsp. aus der Hilfe nach und verändert sie etc.
Insofern ist ein Misstrauen in solchen Fällen angebracht und fast immer begründet.
Mal noch eine Frage zur Funktion selbst.
Ich hatte die Funktion "EnumDisplayMonitors" so verstanden, dass alle angeschlossenen physikalischen Monitore erkannt werden. Das stimmt aber nicht mit meinen Ergebnissen überein:
• Monitor 2 ist gespiegelt angeschlossen: wird nicht erkannt
• Monitor 2 ist erweitert angeschlossen: wird erkannt
Insofern denke ich, dass der DC wohl doch übergeben werden muss. Ich werde das nochmal testen.
EDIT: Ändert nichts, ob mit oder ohne DC. Es werden ausschliesslich erweiterte Monitore erkannt. Wenn das der Sinn der Funktion ist, ist es OK - hatte ich nur anders erwartet.
Ich hab's noch etwas abgeändert, so dass ich die benötigten Werte in einem Array habe
Array ist hier zu unflexibel (Datentypen kann man nicht mixen), wozu gibt es Objekte? ![]()
Und die steckst du dann in eine Sequenz von Objekten:
include winim/[inc\winuser]
type Monitor = object
hMon: HMONITOR
left, top, right, bottom, width, height: int
# globale Variable, um die Werte zu speichern
var sDispInfo = newSeq[Monitor]()
# Callback-Funktion (wird einmal pro angeschlossenen Monitor aufgerufen)
proc MyEnumProc(hMonitor: HMONITOR,hDC: HDC,rect: LPRECT,lparam: LPARAM): WINBOOL {.stdcall.} =
sDispInfo.add(
Monitor(hMon: hMonitor,
left: rect.left,
top: rect.top,
right: rect.right,
bottom: rect.bottom,
width: rect.right - rect.left,
height: rect.bottom - rect.top
))
return TRUE
EnumDisplayMonitors(0, nil, MyEnumProc, 0)
echo sDispInfo
Alles anzeigen
Wenn ich dein Bsp. in SciTE ohne zusätzliche Parameter starte, sollte die Ausgabe mit Index 1 enden, sie endet aber mit Index 4.
Dies liegt daran, weil (bei mir) am Ende von $CmdLineRaw noch 4 Leerzeichen stehen, bzw. 3, wenn zusätzliche Parameter übergeben werden. Ist das bei dir nicht so?
key: /ErrorStdOut
param: /ErrorStdOut
Hm, in dem Fall hat key doch gar keinen Parameter?!
Das sind alles Fälle, die bei der Verwendung niemals auftreten, sondern ausschließlich in der Testumgebung mit SciTE. Insofern kann es getrost ignoriert werden.
Zusätzliche Parameter: -file="test" --dir="m:\Temp" ; -file ist hier nicht korrekt, richtig wäre: --file
Der Parameter -file wird einfach unterschlagen... hier sollte das Script mit einer Fehlermeldung (Usage) abbrechen.
Ist so per Definition:
Wird ein längerer Name übergeben, wird dieser ignoriert
Fand ich sinnvoller, als das, was original in Nim damit passiert: "f", "i" und "l" würden dort als ShortNoVal erkannt werden, "e" wäre ShortVal mit val "test".
Aber die Überlegung ist natürlich nicht verkehrt, den User über einen fehlerhaft übergebenen Parameter zu informieren. Muss ich mir mal Gedanken machen.
Zusätzliche Parameter: -file="test" --d="m:\Temp" ; --d ist hier nicht korrekt, richtig wäre: -d
Hier sollte das Script auch mit einer Fehlermeldung abbrechen, da es sonst zu Konflikten mit -d kommen kann.
Auch hier ist das per Definition so, wobei ich hier das Verhalten aus Nim übernommen habe.
Der Name hat eine Länge von mindestens 1 Zeichen
Angeregt durch die Kommandozeilenbehandlung in Nim habe ich das dortige "parseopt" Modul als Vorlage für eine ähnliche Umsetzung in AutoIt genutzt.
Das ist nicht interessant für 2...3 Parameter in der Kommandozeile. Wer aber reine CUI-Anwendungen erstellt, wird u.U. eine Vielzahl möglicher Parameter bereitstellen wollen. Und da ist die in Nim genutzte Variante recht elegant und vielfältig. Für eine angenehmere Syntax habe ich Strukturen verwendet.
CMD-LINE REGELN
Grundlagen: • Parameter werden getrennt mit einem Leerzeichen. (key=val key2=val key3=val)
• Werte, die Leerzeichen enthalten, sind in Stringbegrenzer einzufassen.
(key="val with space")
• Zur Wertzuweisung können "=" oder ":" genutzt werden. (key=val | key:val)
• Nur für Zuweisungen mit ":" ist ein optionales Leerzeichen vor dem Wert möglich.
(key:val | key: val)
Typen: CMD_SHORTOPTION
• Beginnt mit "-" (-k=val)
• Der Name (key) hat eine Länge von nur 1 Zeichen!!
Wird ein längerer Name übergeben, wird dieser ignoriert, mit einer Ausnahme:
Sind im übergebenen Wert Leerzeichen enthalten, wird der Teil hinter dem ersten Leerzeichen erkannt als CMD_ARGUMENT,
gebildet aus den folgenden Nicht-Leerzeichen.
• [SONDERFALL] "-key"
Ein CMD_SHORTOPTION Parameter ohne Zuweisung darf länger sein als 1 Zeichen.
CMD_LONGOPTION
• Beginnt mit "--" (--key=val)
• Der Name hat eine Länge von mindestens 1 Zeichen.
• [SONDERFALL] "--" alleine
Die "--" Option wird üblicherweise verwendet, um alle folgenden Parameter als Argument zu markieren, unabhängig von
den dort verwendeten Zeichen. Es wird interpretiert als CMD_LONGOPTION und der Name ist ein Leerstring.
CMD_ARGUMENT
• Alle anderen Nicht-Leerzeichen (arg | /abc)
Optionen: Parameter können deklariert werden als "ParamNoVal". Das bedeutet, dass nur ein Key erwartet wird.
Da der Parameter keinen Wert hat, ist dieser immer ein Boolscher Parameter (übergeben oder nicht).
Wird trotzdem ein Wert zugewiesen, wird dieser ignoriert.
Um einen Parameter als "ParamNoVal" zu deklarieren, wird der Parser mit einer Liste dieser Parameter initialisiert.
Eine Liste für ShortNoVal und eine weitere für LongNoVal.
FUNCTIONEN:
_CmdLineParser_Init()
Initialisiert den Parser. Liest die Kommandozeile und erstellt eine eigene Struktur für jeden Parameter.
Gibt eine Struktur zurück mit:
.cmdline Die gesamte Parameterzeile
.cmdcount Die Anzahl der Parameter
_CmdLineParser_Next()
Gibt die nächste Parameter Struktur vom Stapel zurück.
Die Struktur der Parameter:
.index Der 0-basierte Index des Parameters
.pos Start Position des Parameters in der Parameterzeile
.kind $CMDLINEPARSE_ARGUMENT | $CMDLINEPARSE_LONGOPTION | $CMDLINEPARSE_SHORTOPTION
.param Der gesamte Parameter, z.B.: --file="my file path"
.key Der Key, z.B.: file
.val Der Wert, z.B.: "my file path"
_CmdLineParser_GetParamByKey()
Gibt die Parameter Struktur für einen übergebenen Key zurück.
_CmdLineParser_GetParamByIndex()
Gibt die Parameter Struktur für einen übergebenen Index zurück. (0-basiert)
Alles anzeigen
Für die Funktion _CmdLineParser_Next() habe ich einen Iterator implementiert, sodass bei jedem Aufruf der Folgeparameter ausgegeben wird. Nach Erreichen des letzten Parameters wird beim Folgeaufruf $CMDLINEPARSE_END zurückgegeben.
Ein Aufruf könnte z.B. so aussehen:
c:\Pfad\meine.exe -f -b -c:7 --file="path to file\file.ext" argX argY
Mi diesem Bsp. werden die gesamte Parameterzeile sowie die Informationen zu jedem Parameter ausgegeben:
#include "CmdLineParser.au3"
Local $Parser = _CmdLineParser_Init()
Local $sWrite = 'FullParameterLine: ' & $Parser.cmdline & @CRLF & @CRLF
Do
$Param = _CmdLineParser_Next()
If Not @error Then
$sWrite &= 'index: ' & $Param.index & @CRLF
$sWrite &= 'key: ' & $Param.key & @CRLF
$sWrite &= 'val: ' & $Param.val & @CRLF
$sWrite &= 'kind: ' & $Param.kind & @CRLF
$sWrite &= 'param: ' & $Param.param & @CRLF
$sWrite &= 'pos: ' & $Param.pos & @CRLF & @CRLF
EndIf
Until $Param = $CMDLINEPARSE_END
ConsoleWrite($sWrite & @CRLF)
Alles anzeigen
Übrigens, wenn ihr das aus SciTE heraus testet (mit Shift+F8 Parameter setzen) bekommt ihr 3 zusätzliche Parameter: /stdin /stdout "Programmaufrufzeile", die in kompilierten Skripten natürlich nicht enthalten sind.
;-- TIME_STAMP 2020-05-23 22:45:34 v 0.1
; ============================================================ ;
; ;
; Inspired by the Nim command line parser ;
; https://nim-lang.github.io/Nim/parseopt.html ;
; ;
; ============================================================ ;
#cs CMD-LINE RULES
Basics: • Parameters will delimited by one space. (key=val key2=val key3=val)
• Values with spaces inside has to be encapsulated with string delimiters.
(key="val with space")
• For the value assignment, you can use "=" or ":". (key=val | key:val)
• Only for assignment with ":" its allowed to have one optional space before the value.
(key:val | key: val)
Types: CMD_SHORTOPTION
• Starts with "-" (-k=val)
• Name (the key) has a length of only 1 character!!
If a longer key will used, it will ignored with one exception: If the passed value is a string with
space(s) inside, the part behind the 1st space will recognized as CMD_ARGUMENT, which are summarized
by the following contiguous non-space characters.
• [SPECIAL CASE] "-key"
A short option parameter without assignment can be longer than one character.
CMD_LONGOPTION
• Starts with "--" (--key=val)
• Identifier has a length of minimum 1 character.
• [SPECIAL CASE] "--" alone
The "--" option, commonly used to denote that every token that follows is an argument,
is interpreted as a long option, and its name is the empty string.
CMD_ARGUMENT
• All other non-space charcters (arg | /abc)
Options: Parameters can declared as "ParamNoVal". This means that only a key is expected.
Since this is a parameter with no value, it is always a Boolean parameter (passed or not).
If a value is passed anyway, it is ignored.
To declare the parameter as "ParamNoVal", you initialize the parser with a list of
this paramter(s). It's one list for ShortNoVal and another one for LongNoVal.
FUNCTIONS:
_CmdLineParser_Init()
Initializes the parser. Reads the command line and creates a separate structure for each parameter.
_CmdLineParser_Next()
Returnes the next parameter structure from stack.
_CmdLineParser_GetParamByKey()
Gets a parameter structure by its key
_CmdLineParser_GetParamByIndex()
Gets a parameter structure by its index (0-based)
#ce
; CmdLineKind:
Global Const $CMDLINEPARSE_END = 0xDEADBEEF
Global Const $CMDLINEPARSE_ARGUMENT = 1
Global Const $CMDLINEPARSE_LONGOPTION = 2
Global Const $CMDLINEPARSE_SHORTOPTION = 3
Global $_ga_tCmdLineParam[1]
Global $_gt_OptParser
; #FUNCTION# ====================================================================================================================
; Name ..........: _CmdLineParser_Init
; Description ...: Initializes the parser. Reads the command line and creates a separate structure for each parameter.
; ...............: Parameter structure:
; ...............: .index (0-based index of the parameter)
; ...............: .pos (start position of the parameter in the command line)
; ...............: .kind ($CMDLINEPARSE_ARGUMENT | $CMDLINEPARSE_LONGOPTION | $CMDLINEPARSE_SHORTOPTION)
; ...............: .param (the full param, i.e.: --file="my file path")
; ...............: .key (the key, i.e.: file)
; ...............: .val (the value, i.e.: "my file path")
; Parameters ....: $_sShortNoVal [optional] Comma separated list with short parameters, which have no values, i.e.: "f,c". (Default = "")
; ...............: $_sLongNoVal [optional] Comma separated list with long parameters, which have no values, i.e.: "corner,angle". (Default = "")
; Return values .: The Parser structure:
; ...............: .cmdline The full command line
; ...............: .cmdcount The count of parameters in command line
; Author ........: BugFix
; ===============================================================================================================================
Func _CmdLineParser_Init($_sShortNoVal="", $_sLongNoVal="")
Local $cmdLine = $CmdLineRaw
If $cmdLine = '' Then Return SetError(1,0,0)
Local $aTmp, $aCmds, $iCmdCount
Local $aListShortNoVal = ObjCreate("System.Collections.ArrayList")
Local $aListLongNoVal = ObjCreate("System.Collections.ArrayList")
If $_sShortNoVal <> '' Then
$aTmp = StringSplit($_sShortNoVal, ',')
For $i = 1 To $aTmp[0]
$aListShortNoVal.Add($aTmp[$i])
Next
EndIf
If $_sLongNoVal <> '' Then
$aTmp = StringSplit($_sLongNoVal, ',')
For $i = 1 To $aTmp[0]
$aListLongNoVal.Add($aTmp[$i])
Next
EndIf
$aCmds = __GetParamArray($cmdLine, $aListShortNoVal, $aListLongNoVal) ; [[sParam(--key=val), iKind, iPos, Key, Val]]
$iCmdCount = UBound($aCmds)
ReDim $_ga_tCmdLineParam[$iCmdCount]
; create parameter Dll-Struct Item
For $i = 0 To $iCmdCount -1 ; $Index, $_pos, $_kind, $_key, $_val, $_param
$_ga_tCmdLineParam[$i] = __tParam_Create($i, $aCmds[$i][2], $aCmds[$i][1], $aCmds[$i][3], $aCmds[$i][4], $aCmds[$i][0])
Next
; create parser Dll-Struct
$_gt_OptParser = __tOptParser_Create($cmdLine, $iCmdCount)
Return $_gt_OptParser
EndFunc ;==>_CmdLineParser_Init
; #FUNCTION# ====================================================================================================================
; Name ..........: _CmdLineParser_Next
; Description ...: Returnes the next parameter structure from stack.
; Return values .: Success Structure of next parameter
; ...............: Failure $CMDLINEPARSE_END set @error = 1, end of stack is reached.
; Author ........: BugFix
; Remarks .......: If $CMDLINEPARSE_END was returned, with next call of the function it starts again with the first parameter.
; ===============================================================================================================================
Func _CmdLineParser_Next()
Local Static $bIter = False
Local Static $Iter
If Not $bIter Then
$Iter = __IteratorNum(0, UBound($_ga_tCmdLineParam) -1, 1)
$bIter = True
EndIf
Local $iCurrentCmd = $Iter()
If $iCurrentCmd = Null Then
$bIter = False
Return SetError(1, 0, $CMDLINEPARSE_END)
Else
Return $_ga_tCmdLineParam[$iCurrentCmd]
EndIf
EndFunc ;==>_CmdLineParser_Next
; #FUNCTION# ====================================================================================================================
; Name ..........: _CmdLineParser_GetParamByKey
; Description ...: Gets a parameter structure by its key
; Parameters ....: $_sKey The searched key
; Return values .: Success The parameter structure
; ...............: Failure 0 set @error = 1, key not found
; Author ........: BugFix
; ===============================================================================================================================
Func _CmdLineParser_GetParamByKey($_sKey)
Local $t, $iCount = $_gt_OptParser.cmdcount
If $iCount > 0 Then
For $i = 1 To $iCount
$t = $_ga_tCmdLineParam[$i-1]
If $t.key = $_sKey Then Return $t
Next
EndIf
Return SetError(1,0,0)
EndFunc ;==>_CmdLineParser_FindOpt
; #FUNCTION# ====================================================================================================================
; Name ..........: _CmdLineParser_GetParamByIndex
; Description ...: Gets a parameter structure by its index (0-based)
; Parameters ....: $_iIndex The index of the searched parameter
; Return values .: Success The parameter structure
; ...............: Failure 0 set @error = 1, Index out of range
; Author ........: BugFix
; ===============================================================================================================================
Func _CmdLineParser_GetParamByIndex($_iIndex)
If $_iIndex < 0 Or $_iIndex > $_gt_OptParser.cmdcount -1 Then
Return SetError(1,0,0)
Else
Return $_ga_tCmdLineParam[$_iIndex]
EndIf
EndFunc ;==>_CmdLineParser_FindOpt
#Region - helper functions
; creates the parameter structure
Func __tParam_Create($_index, $_pos, $_kind, $_key, $_val, $_param)
Local $iLenK = StringLen($_key) +1, $iLenV = StringLen($_val) +1, $iLenP = StringLen($_param) +1
Local $tag = "struct;int pos;int index;int kind;wchar key[" & $iLenK & "];wchar val[" & $iLenV & "];wchar param[" & $iLenP & "];endstruct;"
Local $t = DllStructCreate($tag)
$t.index = $_index
$t.pos = $_pos
$t.kind = $_kind
If $_key <> '' Then $t.key = $_key
If $_val <> '' Then $t.val = $_val
$t.param = $_param
Return $t
EndFunc ;==>__tParam_Create
; creates the parser structure
Func __tOptParser_Create($_cmdLine, $_countParam)
Local $tag = "struct;" & _
"wchar cmdline[" & StringLen($CmdLineRaw) +1 & "];" & _
"int cmdcount;" & _
"endstruct;"
Local $t = DllStructCreate($tag)
$t.cmdline = $_cmdLine
$t.cmdcount = $_countParam
Return $t
EndFunc ;==>__tOptParser_Create
; selects all parameter from command line
Func __GetParamArray($_sLine, $_aListShortNoVal, $_aListLongNoVal)
Local $sPatternParam = _
'(?x)(?(DEFINE)' & _
' (?<String> "(?> [^"]+ | "" )*"' & _ ; double quoted string
' |' & _
" '(?> [^']+ | '' )*" & _ ; single quoted string"'
' )' & _
' (?<StartOrSpace> (?<= ^|\s)' & _ ; start of the line or after a space
' )' & _
' (?<SpaceOrEnd> (?= \s|$)' & _ ; before a space or line end
' )' & _
' (?<ArgumentsOnlyMarker> (?&StartOrSpace) -- (?= \s)' & _ ; all following params are read as argument
' )' & _
' (?<ShortParamNoVal> (?<!-) (?&StartOrSpace) - [^-\s:=]+ (?&SpaceOrEnd)' & _ ; short param w.o. value
' )' & _
' (?<LongParamNoVal> (?&StartOrSpace) -- [^-\s:=]+' & _ ; long param w.o. value
' )' & _
' (?<ShortParam> (?<!-) (?&StartOrSpace) - [^-\s:=] (: \s? | =) ((?&String) | \S+ )' & _ ; short param w. value
' )' & _
' (?<LongParam> (?&LongParamNoVal) (: \s? | =) ((?&String) | \S+ )' & _ ; long param w. value
' )' & _
' (?<Argument> (?&StartOrSpace) [^-]\S* (?&SpaceOrEnd)' & _ ; all other none-space
' )' & _
' (?<AllParam> (?&ShortParam) | (?&LongParam) | (?&ShortParamNoVal) |' & _
' (?&LongParamNoVal)(?&SpaceOrEnd) | (?&Argument) | (?&ArgumentsOnlyMarker)' & _
' )' & _
')' & _
'(?&AllParam)'
Local $aRes = StringRegExp($_sLine, $sPatternParam, 3)
Local $aParam[UBound($aRes)][5] ; [[sParam, iKind, iPos, Key, Val]]
Local $bArgsFollows = False, $aMatch
Local $iPosSum = 0
For $i = 0 To UBound($aParam) -1
$aParam[$i][0] = $aRes[$i]
$aParam[$i][1] = __GetParamType($aRes[$i])
$aParam[$i][2] = $iPosSum +1
$iPosSum += StringLen($aParam[$i][0]) +1 ; +1 following space
Switch $aParam[$i][1]
Case $CMDLINEPARSE_ARGUMENT
$aParam[$i][3] = $aParam[$i][0]
Case $CMDLINEPARSE_LONGOPTION
If $bArgsFollows Then
$aParam[$i][1] = $CMDLINEPARSE_ARGUMENT
$aParam[$i][3] = $aParam[$i][0]
Else
If $aParam[$i][0] = '--' Then
$bArgsFollows = True
Else
$aMatch = StringRegExp($aParam[$i][0], '--(\w+)(?:=|:\s?)?(.*)', 3)
If IsArray($aMatch) Then
$aParam[$i][3] = $aMatch[0]
If Not $_aListLongNoVal.Contains($aMatch[0]) Then $aParam[$i][4] = $aMatch[1]
EndIf
EndIf
EndIf
Case $CMDLINEPARSE_SHORTOPTION
If $bArgsFollows Then
$aParam[$i][1] = $CMDLINEPARSE_ARGUMENT
$aParam[$i][3] = $aParam[$i][0]
Else
$aMatch = StringRegExp($aParam[$i][0], '^-(?:(\w+$|\w)(?:=|:\s?)?(.*))', 3)
If IsArray($aMatch) Then
$aParam[$i][3] = $aMatch[0]
If Not $_aListShortNoVal.Contains($aMatch[0]) Then $aParam[$i][4] = $aMatch[1]
EndIf
EndIf
EndSwitch
;== DEBUG ==
;~ ConsoleWrite('> Param full: ' & $aParam[$i][0] & @TAB & ' Type(1-arg/2-long/3-short): ' & $aParam[$i][1] & @CRLF)
;~ ConsoleWrite('- Pos: ' & $aParam[$i][2] & @TAB & ' Key: ' & $aParam[$i][3] & @TAB & ' Val: ' & $aParam[$i][4] & @CRLF)
;== /DEBUG ==
Next
Return $aParam
EndFunc ;==>__GetParamArray
; detects the kind of a param, marked with "-", "--" or nothing
Func __GetParamType($_param, $_bLiteral=False)
Local $aConstLiteral[] = ['', 'CMD_ARGUMENT', 'CMD_LONGOPTION', 'CMD_SHORTOPTION']
If StringRegExp($_param, '^--') Then Return ($_bLiteral ? $aConstLiteral[$CMDLINEPARSE_LONGOPTION] : $CMDLINEPARSE_LONGOPTION)
If StringRegExp($_param, '^-') Then Return ($_bLiteral ? $aConstLiteral[$CMDLINEPARSE_SHORTOPTION] : $CMDLINEPARSE_SHORTOPTION)
Return ($_bLiteral ? $aConstLiteral[$CMDLINEPARSE_ARGUMENT] : $CMDLINEPARSE_ARGUMENT)
EndFunc ;==>__GetParamType
; returnes by each call the next number from a given range
Func __IteratorNum($_start=Null, $_end=Null, $_create=0)
Local Static $a[1], $index
If $_create Then
$index = -1
Local $n = 0
ReDim $a[$_end +1 -($_start)]
For $i = $_start To $_end
$a[$n] = $i
$n += 1
Next
Return __IteratorNum
Else
$index += 1
Return $index < UBound($a) ? $a[$index] : Null
EndIf
EndFunc ;==>__IteratorNum
#EndRegion
Alles anzeigen
Klasse! Mit dieser Vorlage habe ich die Daten auslesen können.
Freut mich, wenn ich helfen konnte.
Das coole daran, wenn man sich gemeinsam eine neue Sprache aneignet: Der Lerneffekt ist für beide da. ![]()
Ich hatte erst mal verschiedene Varianten ausprobiert um die Datei sauber einzubinden, bis ich endlich die optimale Syntax gefunden habe. - Wieder was gelernt. ![]()
Inzwischen habe ich mir angewöhnt mit "Suche in Dateien" (Startpunkt: .nimble) Begriffs- oder Funktionserklärungen zu suchen. Das ist recht effektiv.