Pointer to an array of wzString pointers

Started by Pierre Bellisle, June 06, 2023, 06:07:18 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Pierre Bellisle

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
  •  

Charles Pegge

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?

Charles Pegge

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

Pierre Bellisle

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
  •  

Pierre Bellisle

#4
Remembering that o2 is near to C, I took the example at 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)
  •  

Theo Gottwald

This alltogether looks very complicated, just for parsing some arguments?
Maybe we need additional, easy to use features in the String area?

Pierre Bellisle

#6
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.
  •  

Theo Gottwald

 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) :-)

Pierre Bellisle

>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.
  •  

Zlatko Vid

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?
  •  

Pierre Bellisle

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
  •  

Zlatko Vid

Thanks Pierre  ;)

It work...so SendCmdLine need CreateProcess()
i don't expected that ..but ok
  •  

Pierre Bellisle

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 "]"

  •  

Theo Gottwald

So why not use sort of "Parse$-Equivalent"?
It could possibly done in 2 Lines?

Pierre Bellisle

Because I show a way when you don't want parsing.  
If you do, you use previous code. (-:~
  •