Interactive PowerBasic Forum

IT-Consultant: Charles Pegge => OxygenBasic Examples => Topic started by: Pierre Bellisle on June 06, 2023, 06:07:18 AM

Title: Pointer to an array of wzString pointers
Post by: Pierre Bellisle on June 06, 2023, 06:07:18 AM
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
Title: Re: Pointer to an array of wzString pointers
Post by: Charles Pegge on June 06, 2023, 10:08:36 AM
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?
Title: Re: Pointer to an array of wzString pointers
Post by: Charles Pegge on June 07, 2023, 12:28:10 AM
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
Title: Re: Pointer to an array of wzString pointers
Post by: Pierre Bellisle on June 07, 2023, 03:11:26 AM
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
Title: Re: Pointer to an array of wzString pointers
Post by: Pierre Bellisle on June 07, 2023, 06:22:20 AM
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)
Title: Re: Pointer to an array of wzString pointers
Post by: Theo Gottwald on June 07, 2023, 08:38:31 AM
This alltogether looks very complicated, just for parsing some arguments?
Maybe we need additional, easy to use features in the String area?
Title: Re: Pointer to an array of wzString pointers
Post by: Pierre Bellisle on June 07, 2023, 09:39:08 AM
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.
Title: Re: Pointer to an array of wzString pointers
Post by: Theo Gottwald on June 07, 2023, 09:48:15 AM
 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) :-)
Title: Re: Pointer to an array of wzString pointers
Post by: Pierre Bellisle on June 07, 2023, 10:02:57 AM
>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.
Title: Re: Pointer to an array of wzString pointers
Post by: Zlatko Vid on June 07, 2023, 02:37:59 PM
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?
Title: Re: Pointer to an array of wzString pointers
Post by: Pierre Bellisle on June 08, 2023, 08:10:09 AM
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
Title: Re: Pointer to an array of wzString pointers
Post by: Zlatko Vid on June 08, 2023, 03:44:55 PM
Thanks Pierre  ;)

It work...so SendCmdLine need CreateProcess()
i don't expected that ..but ok
Title: Re: Pointer to an array of wzString pointers
Post by: Pierre Bellisle on June 08, 2023, 11:46:21 PM
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 "]"

Title: Re: Pointer to an array of wzString pointers
Post by: Theo Gottwald on June 09, 2023, 07:25:57 AM
So why not use sort of "Parse$-Equivalent"?
It could possibly done in 2 Lines?
Title: Re: Pointer to an array of wzString pointers
Post by: Pierre Bellisle on June 09, 2023, 07:35:40 AM
Because I show a way when you don't want parsing.  
If you do, you use previous code. (-:~
Title: Re: Pointer to an array of wzString pointers
Post by: Zlatko Vid on June 09, 2023, 10:02:07 AM
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.
Title: Re: Pointer to an array of wzString pointers
Post by: Theo Gottwald on June 09, 2023, 11:39:08 AM
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
Title: Re: Pointer to an array of wzString pointers
Post by: Charles Pegge on June 09, 2023, 12:06:54 PM
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
Title: Re: Pointer to an array of wzString pointers
Post by: Nicola on July 03, 2023, 04:21:41 PM
Hi Charles,
I tried your program and it gives me this error:

Cheers
Title: Re: Pointer to an array of wzString pointers
Post by: Charles Pegge on July 03, 2023, 05:18:29 PM
It works for me. Have you got the complete source script?  Are you using an old version of o2 that does not recognize 'procedure'.
Title: Re: Pointer to an array of wzString pointers
Post by: Nicola on July 03, 2023, 05:24:05 PM
are 121 lines of program and I use the version that I attach.
Cheers
Title: Re: Pointer to an array of wzString pointers
Post by: Charles Pegge on July 03, 2023, 05:35:52 PM
Yes, you are still running 050. Replace it with 070 :)
Title: Re: Pointer to an array of wzString pointers
Post by: Nicola on July 03, 2023, 06:17:53 PM
Ok, he was going to get the dll of the P50 of another dir.
I had to delete it   ;D
Title: Re: Pointer to an array of wzString pointers
Post by: Charles Pegge on July 03, 2023, 06:26:53 PM
In 070, I fixed some deep-seated errors which exposed themselves in 64bit self-compiling, including a few of my OpenGl pieces.
Title: Re: Pointer to an array of wzString pointers
Post by: Nicola on July 03, 2023, 09:51:18 PM

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?
Title: Re: Pointer to an array of wzString pointers
Post by: Charles Pegge on July 04, 2023, 04:56:40 AM
procedure is just a more formal name for sub, but I think it reads better on larger scripts.
Title: Re: Pointer to an array of wzString pointers
Post by: Theo Gottwald on July 04, 2023, 09:34:36 AM
But then you should provide an Editor with autocompletion, else its a lot to type.
Title: Re: Pointer to an array of wzString pointers
Post by: Charles Pegge on July 05, 2023, 05:32:15 AM
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.