News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

MasmBasic

Started by jj2007, May 23, 2012, 10:16:07 PM

Previous topic - Next topic

jj2007

Update 8 January 2014:

- fixed a problem with MyNumericArray(?) and non-autoexpanding arrays (when used with ArrayRead, ? erroneously returned the initial count only)
- fixed a problem with Str$() and signed QWORDs
- introduced the option to leave a MovVal result on the FPU in ST(0):
  MovVal ST(0), "3.141592653589793238"

jj2007

Update 11 January fixed a minor problem with Trim$() and introduced voidTrue and voidFalse macros (see Assert and \Masm32\MasmBasic\MbGuide.rtf):

include \masm32\MasmBasic\MasmBasic.inc     ; download
  Init
  Dim My$()                                 ; create a string array
  mov ecx, Chr$("          Hello ")         ; you can't use eax or edx ...
  Let My$(0)=Trim$(ecx)                     ; ...  when assigning to an array,
  Let My$(1)=Str$(123)+Trim$(ecx)+Str$(456) ; but ecx is persistent
  Inkey "My$(0)=[", My$(0), "]", CrLf$, "My$(1)=[", My$(1), "]"
  Exit
end start

Output:
My$(0)=[Hello]
My$(1)=[123Hello456]

jj2007

It seems so :P

I've been fighting with pipes for a while, and google says I'm in good company - it's messy. After a lot of testing, it seems PeekNamedPipe is the best way to decide when to leave the read loop, so the new Launch$() uses this internally.

The sample below launches a console app that returns strings unsteadily, i.e. normally it takes around 50ms to see the next output, but occasionally it chokes for a whole second. This is a typical behaviour e.g. of archivers, and the coder must impose a timeout to decide if the delay is OK or if the child process hangs. In other words, the third arg of Launch$() is now the tolerated inactivity period:

include \masm32\MasmBasic\MasmBasic.inc        ; download
  Init

  PrintLine "## Launching SlowPrint.exe ##"        ; slowprint.exe must be in the same folder
  Let esi=Launch$("SlowPrint", SW_MINIMIZE, 1000)        ; max allowed inactive period of child process is 1000ms
  PrintLine Str$("\nFirst attempt, timeout=1000: exit code=%i\n", ExitCode($)), "Output:", Tb$, esi

  Let esi=Launch$("SlowPrint", SW_MINIMIZE, 1020)
  PrintLine Str$("\nSecond attempt, timeout=1020: exit code=%i\n", ExitCode($)), "Output:", Tb$, esi

  Let esi=Launch$("SlowPrint", SW_MINIMIZE, 1040)
  PrintLine Str$("\nThird attempt, timeout=1040: exit code=%i\n", ExitCode($)), "Output:", Tb$, esi, CrLf$

  Inkey "bye"
  Exit
end start

Output:
## Launching SlowPrint.exe ##

First attempt, timeout=1000: exit code=259
Output: La$?

Second attempt, timeout=1020: exit code=0
Output: sp15 sp14 sp13 sp12 sp11 sp10 sp9 sp8 sp7 sp6 sp5 sp4 sp3 sp2 sp1 sp0 2656 ms

Third attempt, timeout=1040: exit code=0
Output: sp15 sp14 sp13 sp12 sp11 sp10 sp9 sp8 sp7 sp6 sp5 sp4 sp3 sp2 sp1 sp0 2656 ms

jj2007

#153
MasmBasic update of 17 January:

The snippets file is now integrated into the package, see the "try 50+ snippets" link in the Hello World example.

Latest feature is a much easier handling of strings (e.g. paths) in structures:

include \masm32\MasmBasic\MasmBasic.inc        ; download

MyStruct STRUCT
  xText20$       db 20 dup(?)       ; 20 bytes of text
  xDword         dd ?               ; a dword, just for fun
  xText4$        db 4 dup(?)        ; more text
  xText1$        db 1 dup(?)        ; and one byte of text
MyStruct ENDS

.data?
myS        MyStruct <>
  Init
  ; assign strings with Let:
  Let myS.xText20$="This string can take a maximum of 20 chars"
  Let myS.xText4$="That one can take only 4 chars"
  Let myS.xText1$="1 char only..."
  ; print the strings:
  PrintLine "{", myS.xText20$, "}"
  PrintLine "{", myS.xText4$, "}"
  Print "{", myS.xText1$, "}"
  Exit
end start

Output:
{This string can take}
{That}
{1}


EDIT: Now it works also for structures defined with SetGlobals.

include \masm32\MasmBasic\MasmBasic.inc

PATHS STRUCT
path1        db MAX_PATH dup(?)
path2        db MAX_PATH dup(?)
PATHS ENDS

  SetGlobals MyPaths:PATHS             ; define a global structure

  Init
  SetGlobals                           ; let ebx point to the global memory
  mov ecx, CurDir$(0)                  ; get the current folder (0=no backslash)
  Let MyPaths.path1=ecx
  Let MyPaths.path2=MbExeFolder$       ; assign the folder from which the exe was started
  PrintLine "Path1=", MyPaths.path1
  Inkey "Path2=", MyPaths.path2
  Exit
end start

jj2007

#154
But MasmBasic will :biggrin:

include \masm32\MasmBasic\MasmBasic.inc  ; download
  Init
  Let esi="HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion"
  PrintLine "Checking ", esi
  GetRegKeyArray esi, Lev1$(), LastMod1()
  For_ ecx=0 To eax-1
  .if Age(LastMod1(ecx), d)<=31  ; changed in the last 31 days
      Print Str$("\nKey %i\t", ecx), fDate$(LastMod1(ecx)),\
       ", ", fTime$(LastMod1(ecx)), Tb$, "[", Lev1$(ecx), "]"
      GetRegKeyArray Cat$(esi+"\"+Lev1$(ecx)), Lev2$(), LastMod2()
      For_ ebx=0 To eax-1
        .if Age(LastMod2(ebx), h)<=24*3  ; changed in the last 3 days
            Print CrLf$, "- ", Tb$, fDate$(LastMod2(ebx)),\
            ", ", fTime$(LastMod2(ebx)), Tb$, Lev2$(ebx)
        .endif
      Next
    .endif
  Next
  Exit
end start


Sample output:

Checking HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion

Key 7   23.01.2014, 07:11:19    Explorer
Key 10  23.01.2014, 15:27:22    Group Policy
-       23.01.2014, 15:42:00    Regedit
-       23.01.2014, 11:35:39    SysTray
Key 11  23.01.2014, 07:11:30    HomeGroup
Key 13  08.01.2014, 07:11:42    Internet Settings
Key 18  23.01.2014, 14:08:57    Run
Key 19  23.01.2014, 07:11:20    RunOnce


Source & exe attached - the very latest version of MasmBasic is required.

EDIT: Typo Lev1$(ebx) corrected - it must be Lev1$(ecx), of course.

jj2007

Update 25 January: For ... Next loops will now not enter the first iteration if begin>end, in line with (most if not all) other Basic dialects. Example (from \Masm32\MasmBasic\Res\MbSnippets.asc):

include \masm32\MasmBasic\MasmBasic.inc        ; download
  Init                        ; ## nested For ... Next loops ##
  mov eax, 3
  For_ ebx=0 To eax-1        ; 0...2 (using eax is allowed here)
        ; first iteration is 1 to 0 and will be rejected (2*ebx=0, 2, 4, ...)
        For_ ecx=1 To ebx+ebx
                call MyTest
        Next
        Print
  Next
  Inkey "ok"
  Exit

MyTest proc
LOCAL ct
  For_ ct=0 To 9
        Print "*"
  Next  ct
  Print Str$("\tebx=%i, ", ebx), Str$("ecx=%i\n", ecx)
  ret
MyTest endp
end start

Output:

++++++++++      ebx=1, ecx=1
++++++++++      ebx=1, ecx=2

++++++++++      ebx=2, ecx=1
++++++++++      ebx=2, ecx=2
++++++++++      ebx=2, ecx=3
++++++++++      ebx=2, ecx=4

jj2007

31 January 2014 - improved Dll & Declare:

include \masm32\MasmBasic\MasmBasic.inc                ; include this library
.data
MyLongLong        LONGLONG 12345678901234567890
        Init                                       
; initialise the app

        Dll "shimgvw"                ; load the Windows Picture and Fax Viewer Library
        Declare void ImageView_Fullscreen, 4                ; ImageView_Fullscreen expects 4 dwords but returns nothing useful, therefore void
        ImageView_Fullscreen(0, 0, wCL$(1), SW_SHOW)        ; we need the wide version of the commandline arg
       
Err$(1)                                ; there is no retval, so we have to test for errors

        Dll "msvcr100"
        Declare void printf, C:?        ; don't return anything, C calling convention, vararg
        printf(cfm$("MyLongLong is %llX aka %llu\n"), MyLongLong, MyLongLong)        ; MyLL as hex and decimal figure
        Print "MyLongLong is ", Hex$(MyLongLong), Str$(" =  %u\n", MyLongLong)        ; standard MasmBasic syntax, for comparison

        Dll "NtDll"
        Declare RtlRandomEx, 1                ; one arg, _Inout_  PULONG Seed

        Dll "ntoskrnl.exe"
        Declare RtlRandomExNtos, 1 Alias "RtlRandomEx"        ; same but native API - will crash in user mode
        PrintLine "A random number: ", Hex$(RtlRandomEx(addr MyLongLong))

        Exit                        ; do a clean exit, inter alia FreeLibrary
end start

        - Dll performs LoadLibrary, Declare initialises the macro with GetProcAddress, Exit frees the libraries
        - use Declare void SomeFunction, ... if you don't need the return value
        - use Declare SomeFunction, C:3 for C calling convention with three args
        - use Declare SomeFunction, C:? for C calling convention with a variable number of arguments
        - the Alias keyword allows to declare a self-defined name; use in case of "already defined" errors
        - use Declare #123=SomeFunction, 2 to access a function by ordinal number; then mov ecx, SomeFunction(arg1, arg2)
        - if the function cannot be found, try the decorated name:
                Declare _SomeFunction@12, C:3        ; three args, C calling convention
                then use e.g. mov ecx, SomeFunction(arg1, arg2, arg3)
         - do not use Dll & Declare for the static libraries of the Masm32 package (user32, kernel32, ..., see Masm32rt.inc)
         - up to 9 libraries can be loaded

jj2007

Update 26 February (download):
- Declare accepts now entire structures:
      Dll Cat$(MbExeFolder$+"ZipDll\Zip32")
      Declare ZpArchive, 1      ; the arg is a ZCL structure with three DWORDs


- the second new feature is in response to questions how to protect one's code. It concerns the RichMasm editor, which has a new entry in the Edit & Format menu:

include \masm32\include\masm32rt.inc

.code   ; You can bet that Olly cannot recognise the code sequence which creates this string!
start:
   exit

end start


Now select the "You can bet..." part and click on Edit & Format, Obscure string.
Result:
include \masm32\include\masm32rt.inc

.code      ; You can bet that Olly cannot recognise the code sequence which creates this string!
start:
; ---- start ----
      call @F
@obs$      dd 0DB771968h,0DB567E06h,0B2240A75h,09257631Dh,0E6771078h,09216750Ah,0F1361D69h,0985E6A49h,0FD3D042Ch,0884C615Fh,0A8290530h,
0CB096058h,0BF29052Bh,0D6476244h,0B5221064h,0C14D7E0Ah,0A02E5E73h,0CC421153h,0B8237927h,098571C45h,0B8397D26h,0984C127Fh
@@:      pop edx
      mov ecx, sizeof @obs$/4
      .Repeat
            mov eax, [edx]
            xor eax, [edx-4]
            add edx, 4
            push eax
            dec ecx
      .Until Zero?

      PrintLine esp        ; that one is for MasmBasic, but...
      print esp            ; ... Masm32 print works fine, too

      add esp, sizeof @obs$
; ---- end ----
      exit

end start


I guess the code is self-explanatory for those who need this feature :P

hutch--

You could try a tool in the masm32 bin directory called "mangle.exe". Once the string is assembled into the binary its a genuine joy to track it down.  :P

jj2007

Quote from: hutch-- on February 26, 2014, 10:53:11 AM
You could try a tool in the masm32 bin directory called "mangle.exe". Once the string is assembled into the binary its a genuine joy to track it down.  :P
It's in \Masm32, not in \bin, and it works fine :t
My code is shorter, though, and confuses Olly a lot more (and no need to mfree).

Attached a test piece. The exe has two int 3s for use with Olly or any other debugger.
The source requires MasmBasic, of course.

jj2007

Update 1 March with two minor bugfixes:
- Launch could give a wrong ExitProcess return value
- buffers created with SetGlobals choked when invoked as e.g. MsgBox 0, addr buffer, ...

jj2007

#161
EDIT: Version SetupMasmBasic27March14.zip fixes a bug that prevented \Masm32\MasmBasic\Res\XlsViewer.asc to send text to Excel.

Update 27 March 2014:
- ArrayMerge does what the name says, it merges two string arrays:

include \masm32\MasmBasic\MasmBasic.inc      ; download
  Init                  ; ## merge two string arrays ##
  Dim Dest$()
  Dim Src$()
  xor ecx, ecx
  .Repeat
      Let Dest$(ecx)=Str$("Destination #%i", ecx)
      Let Src$(ecx)=Str$("Source #%i", ecx)
      inc ecx
  .Until ecx>=5
  ArrayMerge Dest$(), Src$()
  push Dest$(?)      ; put the new element counter on the stack
  xor ecx, ecx
  .Repeat
      Print Str$("\n#%i\t", ecx), Dest$(ecx)      ; see dest 0...4, then source 0...4
      inc ecx
  .Until ecx>=stack
  pop eax
  Exit
end start


Output:
#0      Destination #0
#1      Destination #1
#2      Destination #2
#3      Destination #3
#4      Destination #4
#5      Source #0
#6      Source #1
#7      Source #2
#8      Source #3
#9      Source #4


- Str$() can now handle leading spaces or zeros:

include \masm32\MasmBasic\MasmBasic.inc      ; download
  Init                  ; ## leading spaces for Str$() ##
  Print Str$("\nA number: %__i", 1)
  Print Str$("\nA number: %__i", 12)
  Print Str$("\nA number: %__i", 123)
  Print Str$("\nA number: %000i", 1)
  Print Str$("\nA number: %000i", 12)
  Print Str$("\nA number: %000i", 123)
  Exit
end start

Output:
A number:   1
A number:  12
A number: 123
A number: 0001
A number: 0012
A number: 0123

sinsi

Installing the latest version

sinsi

I see the menu problem is still there, in my case the text is yellow and unreadable.
Clicking one seems to stop it from working from then on too, unless you click another menu..

jj2007

The crash box is the equivalent to MS Word auto recovery. It happens when user exited an edit session in an, ehm, unusual way. Normally it shouldn't happen, RichMasm is pretty stable, but a forced reboot could cause that, for example. Or killing the editor with Task Manager. I usually click Yes.

Re menus, yes the yellow-brownish font was the compromise for Vista Aero, and I also don't like it. Do the menus work at least? And can you reproduce the hanging?