Hi,
I want to extract an array of arguments from the command line using CommandLineToArgvW().
I can do it in o2, but instead of dimming an array at pArrayOfArgumentPointer,
I got to loop and increment pArrayOfArgumentPointer by sizeof(sys) on each iteration.
Is there a more elegant way to do it like I did in PowerB below.
Thank you
LOCAL pArrayOfArgumentPointer AS WSTRINGZ POINTER
pArrayOfArgumentPointer = CommandLineToArgvW(BYVAL GetCommandLineW, ArgCount)
PRINT "pArrayOfArgumentPointer "; hex$(pArrayOfArgumentPointer)
PRINT "ArgumentCount "; ArgumentCount
DIM pArgArray(0 to ArgumentCount - 1) AS WSTRINGZ POINTER AT pArrayOfArgumentPointer
LOCAL index AS LONG
FOR index = 0 TO ArgumentCount - 1
PRINT FORMAT$(index) & "> [" + @pArgArray(index) + "]"
NEXT
Hi Pierre,
This is how I do it using sysutil.inc with an ANSI commandline.
It's not elegant on the inside, but it is flexible, and you can also read the arguments as a dynamic string array arga[..] if preferred. Quote stripping is included.
uses console
use sysutil
'from sysutil.inc
macro CreateArgv()
==================
sys argv[64]
int argc
string args = CommandLineArgs
'char args="one two three four" 'TEST DATA
string arga[64]
scope
indexbase 0
int i=1
do
arga[argc]=unquote getword args,i
if ascb=0 then exit do
argv[argc] = strptr arga[argc]
argc++
end do
end scope
end macro
CreateArgv()
function main cdecl (int argc,char*argv[]) as int
=================================================
printl "Arguments " argc
printl
printl "List:"
int i
for i=1 to argc
printl str(i) tab argv[i]
next
end function
'
main argc, byval @argv
wait
Are you working in Unicode?
This is another approach which is elegant on both the inside and the outside. It only requires a string (buffer) and an array of pointers.
'06/06/2023
'string mapping
'
uses console
'
procedure MapStrWord(sys ps,pm,int *ct)
=======================================
'WORD MAPPING FOR ANSI
'ps source string pointer
'pm map array of pointers
'ct word count
'
if ps=0 or pm=0
exit procedure
endif
'
byte *s = ps 'string bytes
sys *m = pm 'string pointers
int iw = 0 'in-word flag
int st = sizeof sys 'pointer stride
ct=0
'
subroutine LogPointer
if iw=0
ct++
m=@s
@m+=st 'PTR NEXT WORD
iw=1
endif
end subroutine
'
do
select s
case 0
exit do
case 1 to 32
s=0 'SET NULL TERMINATOR
iw=0 'READY FOR NEXT WORD
case 34
'SKIP QUOTE
gosub LogPointer
do
@s++
if s=0
@s--
exit do
endif
if s=34
exit do
endif
loop
case 33 to 255
gosub LogPointer
end select
@s++
loop
end procedure
'
'
'TEST
'====
string s=` one two three "four x" five `
int ee 'word count
sys m[0x400]
MapStrWord strptr(s), @m, ee
'
indexbase 1
print cr cr ee " words" cr cr
int i
for i=1 to ee
char *ch=m[i]
print i tab ch cr
next
print cr "ok" cr
wait
Hi,
I always learn reading how you manipulate o2 syntax.
I also found another way using CommandLineToArgvW().
It was hard for me to figure out the "pwzArgumentString =" line. Many try and non compilable result, at the end, it seem to work well. I can build my array from the for/next loop. As always, if you see a wrong, do tell.
Thank you very much Charles.
// KickExeArgument "D:\Dev\Oxygen\o2\oxygen.dll" "D:\Dev\Oxygen\o2\oxygen2.dll"
uses console
uses dialogs
! GetCommandLineW lib "kernel32.dll" alias "GetCommandLineW"() as DWORD
! CommandLineToArgvW lib "shell32.dll" alias "CommandLineToArgvW"(wzstring lpCmdLine, int pNumArgs) as DWORD
! GlobalFree lib "kernel32.dll" alias "GlobalFree"(byval hMem as DWORD) as DWORD
sys pwzArgumentString, long ArgumentCount, index
sys ArgPointerArray = CommandLineToArgvW(GetCommandLineW(), @ArgumentCount)
printl "Command line argument count: " ArgumentCount
for index = 0 TO ArgumentCount - 1
pwzArgumentString = *(ArgPointerArray + sizeof(sys) * index)
long CharCount = lStrLenW(pwzArgumentString)
string sAnsiArgumentString = nuls(CharCount)
WideCharToMultiByte(CP_ACP, null, pwzArgumentString, CharCount, strptr(sAnsiArgumentString), CharCount, null, null)
printl index ") [" sAnsiArgumentString "]"
next
GlobalFree(ArgPointerArray)
waitkey
Remembering that o2 is near to C, I took the example at CommandLineToArgvW (https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw) and did an adaptation that is quite satisfactory...
long ArgumentCount, index, sys szArglist
szArglist = CommandLineToArgvW(GetCommandLineW(), @ArgumentCount)
for index = 0 to ArgumentCount - 1
MessageBoxW(0, *(szArglist + index * sizeof sys), wstring("CommandLineToArgvW"), MB_OK OR MB_APPLMODAL OR MB_TOPMOST)
next
LocalFree(szArglist)
This alltogether looks very complicated, just for parsing some arguments?
Maybe we need additional, easy to use features in the String area?
Theo,
No worry, there is already a CommandLineArgs() function in sysutil.inc
Some people dislike pointers.
I love them. I also like to use Windows api directly, without wrapper.
If you want to know how to manage a pointer that point to an array
of wstring pointers then this code is for you to have fun.
If not, CommandLineArgs() is there.
On my side, as a newbie, the hard thing is to get familiar with all the possibilities of o2 syntax.
I've been reading your thoughts on managing pointers and directly using the Windows API, and I admire your enthusiasm and dedication in this complex area. It's quite refreshing to see your perspective on these topics.
In relation to your recent writing, I was wondering if you could share more about your experiences and learning process. Specifically, I am curious to know if you enjoy challenging yourself through these complex tasks.
Why we do not start working on an AI project then?
I look forward to hearing your thoughts. (Theo & ChatGPT) :-)
>share more about your experiences and learning process.
For me, at this stage, o2 is a jungle of includes with a strange and fascinating compiler.
The only way I know to learn is to jump in and try, try, and try again until success.
So, there is not much that I can share, the most part is already there in those post.
My hope is that Charles won't be bored by too many questions.
Quoteo2 is a jungle of includes with a strange and fascinating compiler
Hi Pierre
And that is the reason why i like to use o2
btw.
when you say CommandLineArgs()
do you maybe know how to send arguments from one program to another
as string ?
for example
App1 -> string -> App2
or is better to post new topic?
Here is some code that show two way, ShellExecute up to 2k command line argument and CreateProcess up to 32k command line. Remember, argument zero is always the exe filename...
Compile "GetCmdLine.exe" first then run "SendCmdLine.exe".
$filename "GetCmdLine.exe"
uses console
uses dialogs
! GetCommandLineW lib "kernel32.dll" alias "GetCommandLineW"() as DWORD
! CommandLineToArgvW lib "shell32.dll" alias "CommandLineToArgvW"(wzstring lpCmdLine, int pNumArgs) as DWORD
! LocalFree lib "kernel32.dll" alias "LocalFree"(byval hMem as DWORD) as DWORD
! SetConsoleScreenBufferSize lib "kernel32.dll" alias "SetConsoleScreenBufferSize"
(byval hConsoleOutput as sys, byval dwSize as COORD) as long
COORD Coordn : Coordn.x = 80 : Coordn.y = 700 'make console bigger to see complete string
SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), Coordn)
sys pwzArgumentString, long ArgumentCount, index
sys ArgPointerArray = CommandLineToArgvW(GetCommandLineW(), @ArgumentCount)
printl "Command line argument count: " ArgumentCount
for index = 0 TO ArgumentCount - 1
pwzArgumentString = *(ArgPointerArray + sizeof(sys) * index)
long CharCount = lStrLenW(pwzArgumentString)
string sAnsiArgumentString = nuls(CharCount)
WideCharToMultiByte(CP_ACP, null, pwzArgumentString, CharCount, strptr(sAnsiArgumentString), CharCount, null, null)
printl index ") [" sAnsiArgumentString "]"
next
printl "Command line argument lenght: " len(sAnsiArgumentString)
LocalFree(ArgPointerArray)
printl chr(13,10) "key..." : waitkey
uses console
uses dialogs
$filename "SendCmdLine.exe"
$targetFile "GetCmdLine.exe"
%HWND_DESKTOP 0
%STARTF_USESHOWWINDOW 1
%NORMAL_PRIORITY_CLASS 0x20
%INTERNET_MAX_PATH_LENGTH 2048
%INTERNET_MAX_SCHEME_LENGTH 32 'Longest protocol name length
%INTERNET_MAX_URL_LENGTH INTERNET_MAX_PATH_LENGTH + INTERNET_MAX_SCHEME_LENGTH + len("://") + 1 'NULL
%_32k 0x7FFF '32767
'ShellExecute 2k command line
printl "ShellExecute with " + str(INTERNET_MAX_URL_LENGTH) + " command line for [" + targetFile + "]"
string sParam = string(INTERNET_MAX_URL_LENGTH, "B") 'Create a 2k command line
ShellExecute(HWND_DESKTOP, "open", targetFile, strptr sParam, "", SW_SHOWNORMAL)
'CreateProcess 32k command line
printl "CreateProcess with 32k command line for [" targetFile "]"
zstring zExeAndCmdLine[_32k] = targetFile & " " & string(_32K - len(targetFile) - 2 , "A")
PROCESS_INFORMATION ProcessInf
STARTUPINFO StartupInf
StartupInf.cb = sizeof(StartupInfo)
StartupInf.dwFlags = STARTF_USESHOWWINDOW
StartupInf.wShowWindow = SW_SHOW
CreateProcess(NULL, @zExeAndCmdLine, NULL, NULL, TRUE,
NORMAL_PRIORITY_CLASS, NULL, NULL, @StartupInf, @ProcessInf)
CloseHandle(ProcessInf.hThread)
CloseHandle(ProcessInf.hProcess)
printl chr(13,10) "key..." : waitkey
Thanks Pierre ;)
It work...so SendCmdLine need CreateProcess()
i don't expected that ..but ok
In fact you may use ShellExecute(), ShellExecuteEx (), CreateProcess(), CreateProcessAsUser(), CreateProcessWithLogon().
Depending on what you do, if you don't really need CreateProcess(), yo can use ShellExecute(), it is easier.
PathGetArgs() will get the command line without parsing.
I did not check. Charles probably already have functions and macros for this...
ShellExecute(0, "open", "GetCmdLine.exe", "Hi, I am a command line without path!", "", SW_SHOWNORMAL)
! PathGetArgsA lib "shlwapi.dll" alias "PathGetArgsA"(zstring pszPath) as sys
zstring ptr ArgNoFilename = PathGetArgsA(GetCommandLineA())
printl "ArgNoFilename [" ArgNoFilename "]"
So why not use sort of "Parse$-Equivalent"?
It could possibly done in 2 Lines?
Because I show a way when you don't want parsing.
If you do, you use previous code. (-:~
Why i asking is just simple message as error message
from interpreter to code editor.
Like :
Error -Varible Not Forund [var] - line- 100
then if is want to parse this text that is on code editor side.
Quote from: Pierre Bellisle on June 09, 2023, 07:35:40 AMBecause I show a way when you don't want parsing.
If you do, you use previous code. (-:~
I never understand your coding. Its even worse then code from Jose :-).
Today I asked ChatGPT and i understand the code from ChatGPT.
FUNCTION CalculateInformationValue(BYVAL c AS DWORD) AS LONG
LOCAL R, G, B AS BYTE
LOCAL fR, fG, fB AS SINGLE
LOCAL gray AS SINGLE
; Extract R, G, B values from color
R = (c SHR 16) AND &HFF
G = (c SHR 8) AND &HFF
B = c AND &HFF
fR = R : fG = G : fB = B
ASM
; load constants into floating point registers
FLD DWORD PTR [fR]
FMUL DWORD PTR [0.2989]
FLD DWORD PTR [fG]
FMUL DWORD PTR [0.5870]
FADD ST(1), ST(0) ; Add
FLD DWORD PTR [fB]
FMUL DWORD PTR [0.1140]
FADD ST(1), ST(0) ; Add
; store result and empty FPU stack
FSTP DWORD PTR [gray]
END ASM
FUNCTION = gray
END FUNCTION
I was toying with the idea of combining parser functionality with pointer tables. The following code uses character offsets instead of a pointer, and takes the top byte to encode the character count of each word. The lower 3 bytes are used for the offset, allowing strings of up to 16Meg (4 bibles) to be referenced. By using word length it is no longer necessary to patch null terminators into the string. Furthermore self-terminating symbols like ( ) @ & can be properly parsed.
This is configured for ANSI strings:
'string mapping
'
'uses corewin
uses console
'
'USING LOWER 3 BYTES TO STORE WORD INDEX
'USING THE TOP BYTE TO STORE WORD LENGTH
'MUST STORE .DX FIRST
'THEN OVERLAY .LEN
'
type MapLen
===========
int idx
= 'UNION WITH
byte a,b,c,len
end type
'
procedure MapStrLenWord(sys ps,pm,int *ct)
==========================================
'WORD MAPPING FOR ANSI
'ps source string pointer
'pm map array of pointers
'ct word count
'
if ps=0 or pm=0
exit procedure
endif
'
byte *s = ps 'pointer to string bytes
MapLen *m = pm 'string indexes
int iw = 0 'in-word flag
int st = sizeof MapLen 'index stride
ct=0
'
subroutine LogIndex
if iw=0
ct++
m.idx=@s-ps
iw=1
endif
end subroutine
'
subroutine LogLen
if iw=1
m.len=@s-ps-m.idx
@m+=st 'PTR NEXT WORD
iw=0
endif
end subroutine
'
macro LogSymbol
if iw=0
gosub LogIndex
@s++
gosub loglen
continue do
endif
gosub LogLen
continue do
end macro
'
begin:
'
do
select s
case 0
gosub LogLen
exit do
case 1 to 32
gosub LogLen
iw=0 'READY FOR NEXT WORD
case 34
'SKIP QUOTE
gosub LogIndex
do
@s++
if s=0
@s--
exit do
endif
if s=34
exit do
endif
loop
case 35,46,95 ' #._
gosub logindex
case 33 to 47, ' !$%&'()*+,-/
58 to 64, ' :;<=>?@
91 to 96, ' [\]^`
123 to 126 '{|}~
LogSymbol()
case 48 to 255
gosub LogIndex
end select
@s++
loop
end procedure
'
'
'TEST
'====
string s=` one+7, two (three) "four x" five `
sys ps=strptr s
'sys ps=GetCommandLine
char*ch=ps
print ch cr
int ee 'word count
MapLen m[0x400]
MapStrLenWord ps, @m, ee
'
indexbase 1
print cr cr ee " words" cr cr
int i
for i=1 to ee
char *ch=m[i].idx and 0xffffff 'lower 3 bytes
int le=m[i].len
@ch+=ps 'add string pointer
print i tab left(ch,le) cr
next
print cr "ok" cr
wait
Hi Charles,
I tried your program and it gives me this error:
Cheers
It works for me. Have you got the complete source script? Are you using an old version of o2 that does not recognize 'procedure'.
are 121 lines of program and I use the version that I attach.
Cheers
Yes, you are still running 050. Replace it with 070 :)
Ok, he was going to get the dll of the P50 of another dir.
I had to delete it ;D
In 070, I fixed some deep-seated errors which exposed themselves in 64bit self-compiling, including a few of my OpenGl pieces.
Hi Charles,
I saw that in this program you use PROCEDURE and in this are inserted various SUBs and MACROs.
I wanted to ask you if PROCEDURE is like a module? And, the SUBs and MACROs contained in it can also be called from the outside in an autonomous way or not?
procedure is just a more formal name for sub, but I think it reads better on larger scripts.
But then you should provide an Editor with autocompletion, else its a lot to type.
I've never used autocompletion. As I see it, most code is derived from existing pieces of code, and the major task of programming is finding the right scripts, which is a higher level operation.