Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Topics - Vortex

#1
Hello,

Here is a function to save a bitmap identified with a HBITMAP handle to a file. The main application loads a bitmap named Source.bmp and saves it as Destination.bmp

There are some GOTO statements in the function. I know that this is not encouraged in procedural programming but the code is not complicated and no spaghetti here.  Under the hood, the assembly code emitted by a compiler contains a lot of jmp instructions equivalent to GOTO.

FUNCTION SaveBmpFromHandle(hBitmap AS HBITMAP, FileName AS STRING) AS INTEGER

    LOCAL BmpSize AS INTEGER
    LOCAL pd AS PICTDESC
    LOCAL pPicture AS LPPICTURE
    LOCAL pStream  AS LPSTREAM
    LOCAL hGlobal AS HGLOBAL
    LOCAL hr AS HRESULT
    LOCAL pMem AS void PTR

    pd.cbSizeofstruct = SIZEOF(PICTDESC)
    pd.picType = PICTYPE_BITMAP
    pd.bmp.hbitmap = hBitmap
    pd.bmp.hpal = 0

    hr = OleCreatePictureIndirect(&pd, &IID_IPicture, FALSE, (void**)(&pPicture))
    IF  hr<>0 THEN EXIT FUNCTION

    CreateStreamOnHGlobal(0, TRUE, &pStream)
    IF  hr<>0 THEN GOTO relPict

    hr = pPicture->lpVtbl->SaveAsFile(pPicture, pStream, TRUE, &BmpSize);
    IF  hr<>0 THEN GOTO relStream

    hr = GetHGlobalFromStream(pStream, &hGlobal)
    IF hr<>0 THEN GOTO relStream

    pMem = GlobalLock(hGlobal)
    IF  pMem= 0 THEN GOTO relStream

    OPEN FileName FOR BINARY NEW OUTPUT AS hFile
    PUT$ hFile, pMem, BmpSize
    CLOSE hFile

relStream:

    hr = pStream->lpVtbl->Release(pStream)

relPict:

    hr = pPicture->lpVtbl->Release(pPicture)

    FUNCTION = BmpSize
END FUNCTION

' Main application

DIM AS HBITMAP hBmp
DIM AS INTEGER ImageSize

hBmp = BCX_LOADBMP("Source.bmp", 0)

ImageSize = SaveBmpFromHandle(hBmp, "Destination.bmp")

PRINT "Destination bitmap size = ", ImageSize
#2
With the API function OleLoadPicture, it's possible to get the handle of images stored in an executable :

GUI "Form1", PIXELS

MACRO IMAGE_SIZE = 10625

GLOBAL Form1 AS HWND
DIM hBitmap AS HBITMAP
EXTERN ImageLabel AS void PTR

SUB FORMLOAD()
    Form1 = BCX_FORM("LoadImageFromMem Demo", 0, 0, 380, 338)
    CENTER(Form1)
    SHOW(Form1)
END SUB

BEGIN EVENTS

    SELECT CASE CBMSG

    CASE WM_CREATE

        hBitmap = LoadImageFromMem(&ImageLabel, IMAGE_SIZE)
        EXIT FUNCTION

    CASE WM_PAINT

        DIM RAW ps AS PAINTSTRUCT
        DIM RAW hdc AS HDC
        DIM RAW hMemDC AS HDC
        DIM RAW bm AS BITMAP
        DIM RAW hOldBitmap AS HBITMAP

        hdc = BeginPaint(hWnd, &ps)
        hMemDC = CreateCompatibleDC(hdc)
        hOldBitmap = SelectObject(hMemDC, hBitmap)
        GetObject(hBitmap, SIZEOF(BITMAP), &bm)

        BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, _
        hMemDC, 0, 0, SRCCOPY)

        SelectObject(hMemDC, hOldBitmap)
        DeleteDC(hMemDC)
        EndPaint(hWnd, &ps)

        EXIT FUNCTION

    END SELECT

END EVENTS

FUNCTION LoadImageFromMem(ImageFile AS void PTR, ImageLen AS INTEGER) AS HBITMAP

    DIM AS LPSTREAM pStream
    DIM AS HGLOBAL  hGlobal
    DIM AS HRESULT hr
    DIM AS IPicture PTR IPict
    DIM AS HBITMAP hBmp
    DIM AS HBITMAP hOleBmp
    DIM AS LPVOID pMem

    hGlobal = GlobalAlloc(GMEM_MOVEABLE, ImageLen)

    pMem = GlobalLock(hGlobal)

    memcpy(pMem, ImageFile, ImageLen)

    CreateStreamOnHGlobal(pMem, TRUE, &pStream)

    OleLoadPicture(pStream, 0, FALSE, &IID_IPicture, (LPVOID PTR)&IPict)

    hr = IPict->lpVtbl->get_Handle(IPict, (OLE_HANDLE PTR)&hOleBmp)

    hBmp = CopyImage(hOleBmp, IMAGE_BITMAP, 0, 0, LR_COPYRETURNORG)

    hr = IPict->lpVtbl->Release(IPict)
    hr = pStream->lpVtbl->Release(pStream)

    FUNCTION = hBmp

END FUNCTION


The trick to embed the image is to convert it to a linkable MS COFF object module, ImgToMSCOFF.bat :

\PellesC\bin\poasm.exe /A%1 Image.asm

Examples, 32-bit and 64-bit MS COFF object files :

ImgToMSCOFF.bat IA32 Image.jpg

ImgToMSCOFF.bat AMD64 Image.jpg

While building the project with BED, you need to specift the name of the object file in the field Binary files to link :

Image.obj

Image.asm :

; @ModeBits : The current parser mode. An integer constant.
; Either 32 (for IA32) or 64 (for AMD64).

IF @ModeBits EQ 32

    .386
    .model flat,stdcall
    option casemap:none

ENDIF

PUBLIC ImageLabel

.data

ImageLabel:

INCBIN image.jpg


The powerful POASM statement INCBIN includes any binary file within the source assembly code.

You need to set the size of the image file here in the source code above :

MACRO IMAGE_SIZE = 10625
#3
The function OneChar's purpose is to return the Nth character of a string :

DIM AS STRING TempString
DIM AS STRING test
DIM AS INTEGER i

TempString[1] = 0

test = "This is a test"

FUNCTION OneChar (MyStr AS STRING, u AS INTEGER) AS STRING

    TempString[0] = MyStr[u]
    FUNCTION = TempString

END FUNCTION

FOR i = 0 TO LEN(test)-1
    PRINT OneChar(test, i)
NEXT i


BCX inserts the temporary string handler BCX_TempStr to the translated code. In my opinion, there is no need of this handler here :

char * OneChar (char *MyStr,int u)
{
  char   *BCX_RetStr= {0};
  TempString[0]=MyStr[u];
  BCX_RetStr = BCX_TempStr(strlen(TempString));
  strcpy(BCX_RetStr,TempString);
  return BCX_RetStr;
}

int main(int argc, char *argv[])
{
  TempString[1]=0;
  strcpy(test,"This is a test");
  for(i=0; i<=(int)strlen(test)-1; i++)
    {
      printf("%s\n",OneChar(test,i));
    }
  return EXIT_SUCCESS;   // End of main program
  }


My solution to eliminate BCX_TempStr is to replace the function returning type from STRING to char * :

FUNCTION OneChar (MyStr AS STRING, u AS INTEGER) AS char PTR

    TempString[0] = MyStr[u]
    FUNCTION = TempString

END FUNCTION

FOR i = 0 TO LEN(test)-1
    PRINT OneChar(test, i)
NEXT i


Translated code :

char* OneChar (char *MyStr,int u)
{
  TempString[0]=MyStr[u];
  return TempString;
}

int main(int argc, char *argv[])
{
  TempString[1]=0;
  strcpy(test,"This is a test");
  for(i=0; i<=(int)strlen(test)-1; i++)
    {
      printf("% .15G\n",(double)OneChar(test,i));
    }
  return EXIT_SUCCESS;   // End of main program
  }


The type casting here  printf("% .15G\n",(double)OneChar(test,i)); is not correct and the compiled code will not work. Replacing PRINT with inline printf :


FUNCTION OneChar (MyStr AS STRING, u AS INTEGER) AS char PTR

    TempString[0] = MyStr[u]
    FUNCTION = TempString

END FUNCTION

FOR i = 0 TO LEN(test)-1
    ! printf("%s\n", OneChar(test, i));
NEXT i


Is it possible to use here the original PRINT command to print the characters? What should be the correct type casting?
#4
Hi Kevin,

Studying the BCX_BITMAP sample in the BCX help file :

Bmp1 = BCX_BITMAP("", Form1, 115, 10, 10, 0, 0, 500)

The third parameter :

QuoteData type: INTEGER
CtlID The child-window identifier used to notify its parent about events.

In the example code, the CtlID is 115.  How did you retrieve this value?

The ExWinStyle parameter is 500, how did you get this value?

QuoteData type: INTEGER
ExWinStyle [OPTIONAL] The default window Extended Window Style for a BCX_BITMAP control is 0. For more information, visit the Microsoft Extended Window Styles webpage

I managed to build the example code with a bitmap file, here are my parameters :

Bmp1 = BCX_BITMAP("test.bmp", Form1, 0, 10, 10, 0, 0, 0)
#5
Hello,

An example to trim all spaces and tabs inside a string :

FUNCTION RemoveSpaces (MyStr AS STRING, buff AS STRING) AS INTEGER

    LOCAL buff2 AS LPBYTE
    LOCAL t as UCHAR

    SET lookupTbl [] AS UCHAR
        1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, _
        0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, _
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, _
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, _
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, _
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, _
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, _
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1

    END SET

    buff2 = (LPBYTE)buff
   
    DO
        t=*MyStr
        *buff = t
        buff = buff+lookupTbl[t]
        MyStr = MyStr+1
       
    LOOP WHILE t

    FUNCTION = (LPBYTE)buff-buff2-1
   
END FUNCTION


DIM l AS INTEGER
DIM b AS STRING

l = RemoveSpaces("   This   is a test. ", b)
PRINT "Trimmed string = ",b
PRINT "Length of the string = ",l
#6
Compiler Related Discussions / Portable Build Tools
March 02, 2024, 02:14:43 PM
PortableBuildTools :

QuotePortable, simple and fast installer for Visual Studio Build Tools.

Downloads standalone 64-bit MSVC compiler, linker & other tools, also headers/libraries from Windows SDK, into a portable folder, without installing Visual Studio. Has only bare minimum components - no UWP/Store/WindowsRT stuff, just files & tools for 64-bit native desktop app development.

https://github.com/Data-Oriented-House/PortableBuildTools

Modifying \BCX\bed\Bat\MSVC\Config32.bat to use BCX with Portable Build Tools, x86 host compiling 32-bit code :

SET MSVCFolder="R:\BuildTools-Hostx86-x86\VC\Tools\MSVC\14.39.33519\bin\Hostx86\x86"

CALL "R:\BuildTools-Hostx86-x86\devcmd.bat"

@SET BCX=D:\BCX


devcmd.bat created by Portable Build Tools :

@echo off

set WindowsSDKDir=R:\BuildTools-Hostx86-x86\Windows Kits\10
set WindowsSDKVersion=10.0.22621.0
set VCToolsInstallDir=R:\BuildTools-Hostx86-x86\VC\Tools\MSVC\14.39.33519\
set VSCMD_ARG_TGT_ARCH=x86

set MSVC_BIN=R:\BuildTools-Hostx86-x86\VC\Tools\MSVC\14.39.33519\bin\Hostx86\x86
set SDK_BIN=R:\BuildTools-Hostx86-x86\Windows Kits\10\bin\10.0.22621.0\x86;R:\BuildTools-Hostx86-x86\Windows Kits\10\bin\10.0.22621.0\x86\ucrt
set PATH=%MSVC_BIN%;%SDK_BIN%;%PATH%
set INCLUDE=R:\BuildTools-Hostx86-x86\VC\Tools\MSVC\14.39.33519\include;R:\BuildTools-Hostx86-x86\Windows Kits\10\Include\10.0.22621.0\ucrt;R:\BuildTools-Hostx86-x86\Windows Kits\10\Include\10.0.22621.0\shared;R:\BuildTools-Hostx86-x86\Windows Kits\10\Include\10.0.22621.0\um;R:\BuildTools-Hostx86-x86\Windows Kits\10\Include\10.0.22621.0\winrt;R:\BuildTools-Hostx86-x86\Windows Kits\10\Include\10.0.22621.0\cppwinrt
set LIB=R:\BuildTools-Hostx86-x86\VC\Tools\MSVC\14.39.33519\lib\x86;R:\BuildTools-Hostx86-x86\Windows Kits\10\Lib\10.0.22621.0\ucrt\x86;R:\BuildTools-Hostx86-x86\Windows Kits\10\Lib\10.0.22621.0\um\x86


A quick test, building the GetBmp SaveBmp Demo by Robert Wishlaw  :

BCX BASIC to C/C++ Translator (c) 1999-2024 by Kevin Diggins
Version 8.0.8 (12/27/2023) Compiled using MS Visual C++ for 64-bit Windows Systems
[Lines In: 113] [Lines Out: 742] [Statements: 79] [Time: 0.01 Sec's]
BCX translated [Savebmp.Bas] to [Savebmp.C] for a C Compiler
**************************************************************************
MSVC is compiling [ "D:\BCX\bed\SaveBmp" ] as a 32-bit GUI application.
SaveBmp.c
Linking "D:\BCX\bed\SaveBmp"
Microsoft (R) Incremental Linker Version 14.39.33519.0
Copyright (C) Microsoft Corporation.  All rights reserved.
**************************************************************************
MSVC built [ "D:\BCX\bed\SaveBmp".exe ]
**************************************************************************
Microsoft (R) C/C++ Optimizing Compiler Version 19.39.33519 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

#7
User Contributions / Dll to include file converter
October 31, 2023, 02:52:25 PM
Hello,

DllToDef is a console applicatiıon extracting all the exported functions from a Dll to a module definition file.

Usage example :

DllToDef.exe kernel32.dll

Output :

kernel32.dll exports 1393 functions.

The file kernel32.def is created.

$FILETEST OFF

DIM hMod AS HMODULE
DIM pNThdr AS PIMAGE_NT_HEADERS
DIM pDesc AS PIMAGE_EXPORT_DIRECTORY
DIM NumbOfNames AS DWORD
DIM i AS DWORD

DIM AddrOfNames AS DWORD PTR
DIM cmd$, ext$, deffile$

IF ARGC = 1 THEN

    PRINT "Usage : DllToDef.exe DllFile.dll"
    END

END IF

cmd$ = COMMAND$(1)
hMod = LOADLIBRARY(cmd$)

IF hMod =0 THEN

    PRINT "Error : " & cmd$ & " could not be loaded."
    END

END IF

ext$ = BCXSPLITPATH$(cmd$, FEXT)

pNThdr = (PIMAGE_NT_HEADERS)((LPBYTE)hMod + ((PIMAGE_DOS_HEADER)hMod)->e_lfanew)

pDesc = (PIMAGE_EXPORT_DIRECTORY)((LPBYTE)hMod + pNThdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress)

NumbOfNames = pDesc -> NumberOfNames;

IF NumbOfNames = 0 THEN
    PRINT cmd$ & " does not export any function(s)."
    FreeLibrary(hMod)
    END
END IF

deffile$ = BCXSPLITPATH$(cmd$, FNAME)+".def"

OPEN deffile$ FOR OUTPUT AS hFile

IF hFile==0 THEN

    PRINT "Cannot create the .def file."
    FreeLibrary(hMod)
    END

ENDIF

FPRINT hFile, "LIBRARY ", BCXSPLITPATH$(cmd$, FNAME)
FPRINT hFile, "EXPORTS"

AddrOfNames = (DWORD *)((LPBYTE)hMod + (size_t)pDesc -> AddressOfNames)

NumbOfNames = NumbOfNames-1

FOR i = 0 TO NumbOfNames

    FPRINT hFile, CHR$(34) , (char *)(*AddrOfNames+(LPBYTE)hMod) , CHR$(34)
    AddrOfNames = AddrOfNames+1

NEXT i

CLOSE hFile

PRINT cmd$ & " exports" & NumbOfNames+1 & " functions."

FreeLibrary(hMod)
#8
Hi Kevin,

The same hello world application built with Pelles C weights 36.5 Kb. With custom C run-time startup modules, the same application has a size of only 3 Kb. Of course, my tiny C run time library does not provide any memory initialization system. This method can be used with MS VC and MinGW. The UCRT approach is more professional as it provides all the functionality of the traditional C run-time library. The purpose of my example is to create very small applications like Masm\Poasm executables. The C run-time modules in the attachment can be converted to BCX.
#9
User Contributions / Inline 64-bit assembly
October 01, 2023, 04:32:08 AM
Hello,

Some C compilers are not allowing inline 64-bit assembly instructions. A workaround is to define an array of machine code and copying this to a memory portion allowed to execute code.

Code assembled with Poasm V12.00.1 :

.code

CallFunc64 PROC function:QWORD,ArgNumb:QWORD,ArrayOfParams:QWORD

NumbOfArgs TEXTEQU <QWORD PTR [rsp+16+136]>
func       TEXTEQU <QWORD PTR [rsp+8+136]>


    sub     rsp,8+16*8                 ; 8 bytes   = stack alignment
                                       ; 16 params = reserve shadow space
                                       ;             for maximum 16 params

;   Store the parameters rcx and rdx in the shadow space
;   as they will be overwritten later

    mov     QWORD PTR [rsp+8+136],rcx  ; store the function
    mov     QWORD PTR [rsp+16+136],rdx ; store the number of args

    mov     rax,r8

    mov     rcx,QWORD PTR [rax]
    add     rax,8

OneArg:

    cmp     rdx,1                      ; rdx -> ArgNumb
    je      finish
       
TwoArgs:

    mov     rdx,QWORD PTR [rax]
    add     rax,8

    cmp     NumbOfArgs,2
    je      finish

ThreeArgs:

    mov     r8,QWORD PTR [rax]
    add     rax,8

    cmp     NumbOfArgs,3
    je      finish

FourArgs:

    mov     r9,QWORD PTR [rax]
    add     rax,8

    cmp     NumbOfArgs,4
    je      finish

MoreArgs:

    mov     r10,32
    sub     NumbOfArgs,4
@@:
    mov     r11,QWORD PTR [rax]
    mov     QWORD PTR [rsp+r10],r11
    add     rax,8
    add     r10,8
    dec     NumbOfArgs
    jnz     @b

finish:

    call    func
   
    add     rsp,8+16*8

    ret

CallFunc64 ENDP

END


The code above accepts 3 parameters. The first is the function to be called from the inline code. The two others are the number of the arguments to be passed to the function and the address of the first element of the paramater array.

Extracting the flat binary code from the object module with the GNU binary tool objcopy. This can be obtained from a MSYS2 installation :

objcopy --dump-section .text=CallFunc64.bin CallFunc64.obj

Dumping the binary file CallFunc64.bin with MrBcx's Dump utility :

https://bcxbasiccoders.com/smf/index.php?topic=936.msg4740#msg4740

The BCX code :

' Compile only to 64-bit

FUNCTION WINMAIN()

    LOCAL hMod AS HMODULE
    LOCAL ArrayOfParams[6] AS LONGLONG
    LOCAL Asm64 AS void PTR

    ArrayOfParams[0] = (LONGLONG) NULL
    ArrayOfParams[1] = (LONGLONG) "This message box will be destroyed after 4 seconds."
    ArrayOfParams[2] = (LONGLONG) "Hello"
    ArrayOfParams[3] = (LONGLONG) MB_ICONWARNING
    ArrayOfParams[4] = (LONGLONG) LANG_NEUTRAL
    ArrayOfParams[5] = (LONGLONG) 4000 ' Timeout after 4 seconds

    hMod = LOADLIBRARY("user32.dll")

    SET InlineAsm [] AS UCHAR
        72, 129, 236, 136, 0, 0, 0, 72, 137, 140, 36, 144, 0, 0, 0, 72, 137, _
        148, 36, 152, 0, 0, 0, 76, 137, 192, 72, 139, 8, 72, 131, 192, 8, 72, _
        131, 250, 1, 116, 95, 72, 139, 16, 72, 131, 192, 8, 72, 131, 188, 36, _
        152, 0, 0, 0, 2, 116, 77, 76, 139, 0, 72, 131, 192, 8, 72, 131, 188, 36, _
        152, 0, 0, 0, 3, 116, 59, 76, 139, 8, 72, 131, 192, 8, 72, 131, 188, 36, _
        152, 0, 0, 0, 4, 116, 41, 73, 199, 194, 32, 0, 0, 0, 72, 131, 172, 36, _
        152, 0, 0, 0, 4, 76, 139, 24, 78, 137, 28, 20, 72, 131, 192, 8, 73, 131, _
        194, 8, 72, 255, 140, 36, 152, 0, 0, 0, 117, 231, 255, 148, 36, 144, 0, 0, _
        0, 72, 129, 196, 136, 0, 0, 0, 195
    END SET

    Asm64 = VirtualAlloc(NULL, SIZEOF(InlineAsm), MEM_COMMIT, PAGE_EXECUTE_READWRITE)
    memcpy(Asm64, InlineAsm, SIZEOF(InlineAsm))

    ((void (*)())Asm64)(GetProcAddress(hMod, "MessageBoxTimeoutA"), 6, ArrayOfParams)

    VirtualFree(Asm64, 0, MEM_RELEASE)
    FreeLibrary(hMod)

    FUNCTION = 0

END FUNCTION


This application displays a message box destroying itself after 4000 miliseconds. MessageBoxTimeout is an undocumented function :

https://www.codeproject.com/Articles/7914/MessageBoxTimeout-API

With thanks to MrBcx, here is a better method to implement DYNACALL :

https://bcxbasiccoders.com/smf/index.php?topic=952.0

Attached is the project containing the source files and the compiled executable.
#10
Bug Reports / long long type issue
September 30, 2023, 02:08:19 PM
Hello,

DIM x AS LONG LONG
PRINT SIZEOF(LONG LONG)


This code is not converted correctly to C.  The output is :

static long    x;  <----- It should be long long , not long
.
.
.
.
// *************************************************
//                  Main Program
// *************************************************

int main(int argc, char *argv[])
{
  printf("% d\n",(int)sizeof(longlong));    <----- no space character between the 1st long and the 2nd long
  return EXIT_SUCCESS;   // End of main program
  }


Tested with BCX Version 8.0.6 (09/29/2023)
#11
Bug Reports / Bc.exe crashing
September 26, 2023, 03:20:06 PM
Hello,

Bc.exe is crashing while trying to convert the code portion below :


DIM t AS LRESULT

$IFDEF _WIN64
DIM AS BYTE A[20] = { 72, 0x83,0xEC,8,0x4C,0x89,0xC8,0x48,0x8B,0x4C,0x24,48,0x48,0xD3,0xE8,0x48,0x83,0xC4,08,0xC3}
$ELSE
'    mov     eax,DWORD PTR [esp+12]
'    mov     ecx,DWORD PTR [esp+16]
'    shr     eax,cl
'    ret     16

DIM AS BYTE A[13] = { 139, 68, 36, 12, 139, 76, 36, 16, 211, 232, 194, 16, 0}
$ENDIF

t = CallWindowProcA((WNDPROC)(&A[0]), 0, 0, 4096, 4)

PRINT t

PRINT "The program is terminated."



I am working on a method to bypass the limitation of 64-bit inline assembly. Not an effective method but it seems to work at least for short 32-bit code.

Tested with Version 8.0.5 (09/06/2023)
#12
User Contributions / Window refresher
September 24, 2023, 05:26:25 AM
Hello,

Hee is a small application to refresh all the windows including the desktop.


GUI NOMAIN

MACRO VM_REFRESH = 28931

FUNCTION WINMAIN()

    EnumWindows(EnumWndProc, 0)

    FUNCTION = 0

END FUNCTION


FUNCTION EnumWndProc ( hWnd AS HWND, lParam AS LPARAM ) AS BOOL CALLBACK

    EnumChildWindows(hWnd, EnumChildWndProc, 0)

    FUNCTION = 1

END FUNCTION


FUNCTION EnumChildWndProc (hWnd AS HWND , lParam AS LPARAM ) AS BOOL CALLBACK

    LOCAL buffer AS STRING

    GetClassName(hWnd, buffer, 256)

    IF buffer = "SHELLDLL_DefView" THEN

        SendMessage(hWnd, WM_COMMAND, VM_REFRESH, 0)
        FUNCTION = 0

    END IF

    FUNCTION = 1

END FUNCTION
#13
Tips & Tricks / Simple binary file handling functions
September 22, 2023, 04:38:44 AM
ReadFileToMem function reading any binary file from disk.

' pFileName       : pointer fo file name
' pMem            : pointer to a pointer indicating the reserved memory to read the file
' pNumOfBytesRead : pointer to a variable that receives the number of the bytes read

FUNCTION ReadFileToMem(pFileName AS LPSTR, pMem AS LPBYTE PTR, pNumOfBytesRead AS INTEGER PTR) AS BOOL

    LOCAL hFile AS HANDLE
    LOCAL FileSize AS DWORD
    LOCAL hMem AS LPVOID
    LOCAL nBytesRead AS DWORD
    LOCAL t AS BOOL

    hFile = CreateFile(pFileName, GENERIC_READ, 0, 0, _
    OPEN_EXISTING, 0, 0)

    IF hFile = INVALID_HANDLE_VALUE THEN FUNCTION = 0

    FileSize = GetFileSize(hFile, 0)

    IF !(FileSize) THEN FUNCTION = 0

    hMem = VirtualAlloc(0, FileSize, MEM_COMMIT, PAGE_READWRITE)

    IF !(hMem) THEN FUNCTION = 0

    *pMem = hMem

    t = ReadFile(hFile, hMem, FileSize, &nBytesRead, 0)

    IF !(t) THEN
        VirtualFree(hMem, 0, MEM_RELEASE)
        FUNCTION = 0
    END IF

    *pNumOfBytesRead = nBytesRead
    CloseHandle(hFile)

    IF !(t) THEN FUNCTION = 0

    FUNCTION = 1

END FUNCTION


It's the user's responsibility to release the memory allocated by ReadFileToMem :

VirtualFree(pFile, 0, MEM_RELEASE)

WriteFileToDisc function writing data to disk :

' pFileName  : pointer fo file name
' pMemory    : pointer to the data to be written
' nSize      : number of bytes to be written

FUNCTION WriteFileToDisc(pFileName AS LPSTR, pMemory AS LPBYTE, nSize AS DWORD) AS BOOL

    LOCAL hFile AS HANDLE
    LOCAL pWritten AS DWORD
    LOCAL t AS BOOL

    hFile = CreateFile(pFileName, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0)

    IF hFile = INVALID_HANDLE_VALUE THEN FUNCTION = 0

    t = WriteFile(hFile, pMemory, nSize, &pWritten, 0)

    IF !(t) THEN FUNCTION = 0

    t = CloseHandle(hFile)

    IF !(t) THEN FUNCTION = 0

    FUNCTION = 1

END FUNCTION
#14
Hello,

The macro definition below works correctly :

MACRO EXITIF(Condition) = if (Condition) return 1;

DIM x AS INTEGER
DIM y AS INTEGER

x=9
y=0

EXITIF(y==0)

PRINT x,y


Macro translated correctly to the C  syntax :

//             User Defined Macros
// *************************************************

#define EXITIF(Condition) if(Condition)return 1;


If I replace the above EXITIF sample wit this one :

EXITIF(!(y))

the translation omits the negation symbol ! :

EXITIF((y));

Another test :  EXITIF(x!=y) is translated to EXITIF(x=y);

Is there a way to use the exclamation mark as a part of a macro parameter? Thanks.
#15
Bug Reports / Garbled output of BCX Mini Form Designer
September 18, 2023, 02:18:00 PM
Hello,

- Run BCX Mini Form Designer 1.03.10 ( Executable modification \ compilation date : 19 August 2023 )

- Without modifying the form. Click Save BAS file. The scale can be 1.0
- Save the source code.

Some garbled characters are appearing in the source code :

GUI "Form1",PIXELS



GLOBAL Form1 AS HWND


SUB FORMLOAD()
   Form1 = BCX_FORM("Form1", 0, 0, 409, 282)
   CENTER(Form1)
   SHOW(Form1)
END SUB

BEGIN EVENTS
SELECT CASE CBMSG
    CASE WM_CTLCOLORSTATIC, WM_CTLCOLOREDIT,WM_CTLCOLORLISTBOX
      SELECT CASE (HWND)CBLPARAM
h{h{     <---------------  This line
      END SELECT
   CASE WM_COMMAND
      SELECT CASE CBCTL
      END SELECT
END SELECT
END EVENTS
#16
Bug Reports / Underscore issue
September 13, 2023, 01:24:21 PM
BCX Version 8.0.5 does not handle correctly integer variables with a leading underscore :

DIM _a AS INTEGER

_a = 10

PRINT _a


BCX BASIC to C/C++ Translator (c) 1999-2023 by Kevin Diggins
Version 8.0.5 (09/06/2023) Compiled using MS Visual C++ for 32-bit Windows Systems
[Lines In: 5] [Lines Out: 249] [Statements: 4] [Time: 0.01 Sec's]
BCX translated [Sample.Bas] to [Sample.C] for a C Compiler
**************************************************************************
Pelles C is compiling [ "D:\BCX\bed\Sample".c ] as a 32-bit CONSOLE application.
D:\BCX\bed\Sample.c(249): error #2140: Type error in argument 1 to 'strcpy'; expected 'char * restrict' but found 'int'.
D:\BCX\bed\Sample.c(249): error #2140: Type error in argument 2 to 'strcpy'; expected 'const char * restrict' but found 'int'.
POLINK: fatal error: File not found: 'D:\BCX\bed\Sample.obj'.


Sample.c :

static int     _a;
.
.
.
int main(int argc, char *argv[])
{
  strcpy(_a,10);  <----- This line
  printf("% d\n",(int)_a);
  return EXIT_SUCCESS;   // End of main program
}
 

#17
Bug Reports / Issue with \PellesC\Include\ctype.h
August 29, 2023, 02:05:23 PM
Hello,

Trying to compile the example in the BCX documentation, I receive the following error message :

UNION MyUnion
   MyInt  AS DOUBLE
   MyChar AS UCHAR
END UNION

DIM Example AS MyUnion
DIM longvar AS LONG


Example.MyChar = 255

'longvar = (LONG) Example     ' This statement will -NOT- compile with any compiler.

longvar = CAST(LONG, Example) ' This statement WILL compile, thanks to the new CAST macro.

PRINT longvar                 ' Should PRINT 255

PAUSE


BCX BASIC to C/C++ Translator (c) 1999-2023 by Kevin Diggins
Version 8.0.3 (08/19/2023) Compiled using MS Visual C++ for 32-bit Windows Systems
[Lines In: 18] [Lines Out: 277] [Statements: 11] [Time: 0.01 Sec's]
BCX translated [File_2.Bas] to [File_2.C] for a C Compiler
**************************************************************************
Pelles C is compiling [ "D:\Tools2023\Coding\BCX\File_2".c ] as a 32-bit CONSOLE application.
E:\PellesC\Include\ctype.h(40): fatal error #1065: Failed converting input from 'U'.
POLINK: fatal error: File not found: 'D:\Tools2023\Coding\BCX\File_2.obj'.


Reading the C file :

int main(int argc, char *argv[])
{
 
  Example.MyChar=255;
  longvar= CAST (long,Example);
  printf("% G\n",(float)longvar);
  Pause();
  return EXIT_SUCCESS;   // End of main program
  }


Pelles C Version : 12.00.2
#18
Questions & Answers / Handling of DefWindowProc
August 24, 2023, 01:49:43 PM
Hello,

The standard callback function WndProc routes all the non-processed messages to DefWindowProc. In contrast, BCX is forwarding all the messages to DefWindowProc including the processes ones. What is the advantage of this preference calling DefWindowProc everytime?

QuoteCalls the default window procedure to provide default processing for any window messages that an application does not process.

https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-defwindowproca

A quick example. A standard WndProc function :

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(msg)
    {
        case WM_CLOSE:
            DestroyWindow(hwnd);
        break;
        case WM_DESTROY:
            PostQuitMessage(0);
        break;
        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

http://www.winprog.org/tutorial/simple_window.html

An example created with BCX Mini Form Designer :

GUI "Form1",PIXELS

CONST Btn1_Style    = WS_CHILD|WS_VISIBLE|WS_TABSTOP
CONST Btn1_ExtStyle = WS_EX_STATICEDGE
CONST Btn2_Style    = WS_CHILD|WS_VISIBLE|WS_TABSTOP
CONST Btn2_ExtStyle = WS_EX_STATICEDGE

ENUM
   ID_Btn1
   ID_Btn2
END ENUM

GLOBAL Form1 AS HWND
GLOBAL hBtn1 AS CONTROL
GLOBAL hBtn2 AS CONTROL


SUB FORMLOAD()
  DIM hFont AS HFONT
   Form1 = BCX_FORM("Form1", 0, 0, 391, 310)
   hBtn1 = BCX_BUTTON("Button1",Form1,ID_Btn1, 70, 32, 57, 41, Btn1_Style, Btn1_ExtStyle)
   hBtn2 = BCX_BUTTON("Button2",Form1,ID_Btn2, 71, 95, 57, 41, Btn2_Style, Btn2_ExtStyle)
   CENTER(Form1)
   SHOW(Form1)
END SUB

BEGIN EVENTS
SELECT CASE CBMSG
   CASE WM_COMMAND
      SELECT CASE CBCTL
          CASE ID_Btn1
            MSGBOX "You pressed the first button.", "MSGBOX Demo", MB_OK
          CASE ID_Btn2
            MSGBOX "You pressed the second button.", "MSGBOX Demo", MB_OK
      END SELECT
END SELECT
END EVENTS


Translated by BCX to :

LRESULT CALLBACK WndProc (HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam)
{
  if(Msg==WM_COMMAND ){
      if(LOWORD(wParam)==ID_Btn1 ){
          MessageBox (GetActiveWindow(),"You pressed the first button.","MSGBOX Demo",MB_OK );
          goto L1001;
        }
      if(LOWORD(wParam)==ID_Btn2 ){
          MessageBox (GetActiveWindow(),"You pressed the second button.","MSGBOX Demo",MB_OK );
        }
L1001:;
    }
  if(Msg==WM_DESTROY)
    {
       OleUninitialize();
       CoUninitialize();
       PostQuitMessage(0);
       return EXIT_SUCCESS;
    }
return DefWindowProc(hWnd,Msg,wParam,lParam); // endevents
}


In the classical C code example, all processed messages are terminated with return 0. BXC terminates the function with DefWindowProc regardless the processing status of any message.
#19
Hello,

Here is a quick sample to use external functions in the main BCX module :

Func.bas :

$NOMAIN

Function sum(a,b) As Integer Stdcall

Function = a + b

End Function


Test.bas :

' Declare Function sum(a,b) As Integer

! int __stdcall sum(int,int);

Print sum(40,10)


If I replace the inline C code with the Declare Function statements , BCX will translate it to :

C_IMPORT int  __stdcall sum(int,int);

equivalent to

__declspec(dllimport) __stdcall sum(int,int);

This is not suitable to call an external function as the name mangling will look different :

\PellesC\bin\podump.exe /SYMBOLS test.obj | findstr "sum"

0005 00000000 UNDEF  notype      external     | __imp__sum@8


which leads to :

POLINK: error: Unresolved external symbol '__imp__sum@8' - referenced from 'test.obj'.

The __imp__ prefix is required to create a Dll.

The inline C declaration is solving the problem :

\PellesC\bin\podump.exe /SYMBOLS test.obj | findstr "sum"

0005 00000000 UNDEF  notype      external     | _sum@8


Is possible to add a new keyword like Extrn to avoid the inline C code?

Extrn stdcall Function sum2(a,b) As Integer

Print sum(40,10)


or

Extrn cdecl Function sum2(a,b) As Integer

Since EXTERN is already a keywork ( storage class specifier ) , I am proposing an alternate symbol named Extrn
#20
Bug Reports / Global variable issue
February 27, 2022, 07:47:46 AM
Hello,

The code below is transled to C with BCX Version 7.8.1 :

GUI "Form1",PIXELS

CONST Btn1_Style    = WS_CHILD|WS_VISIBLE|WS_TABSTOP
CONST Btn1_ExtStyle = WS_EX_STATICEDGE

ENUM
   ID_Btn1
END ENUM

GLOBAL Form1 AS HWND
GLOBAL hBtn1 AS CONTROL
GLOBAL message AS STRING
message$="Hello world!"


SUB FORMLOAD()
    DIM hFont AS HFONT

   Form1 = BCX_FORM("Form1", 0, 0, 488, 378)
   hBtn1 = BCX_BUTTON("Button",Form1,ID_Btn1, 81, 51, 136, 81, Btn1_Style, Btn1_ExtStyle)
   CENTER(Form1)
   SHOW(Form1)
END SUB

BEGIN EVENTS

SELECT CASE CBMSG
   CASE WM_COMMAND
      SELECT CASE CBCTL
          CASE ID_Btn1
          $Asm
            push 0
            push OFFSET message
            push OFFSET message
            push 0
            call DWORD PTR [MessageBox]
          $Asm
      END SELECT
END SELECT
END EVENTS


PellesC V11 displays some error messages :

pocc -std:C11 -Tx86-coff -Ot -Ob1 -fp:precise -W1 -Gd -Ze -Zx gui.c

gui.c(537): warning #2099: Missing type specifier; assuming 'int'.
gui.c(537): error #2026: Expected an identifier.
gui.c(537): error #2001: Syntax error: expected ')' but found 'string constant'.

gui.c(537): error #2120: Redeclaration of 'strcpy', previously declared at E:\PellesC\Include\intrin.h(93); expected 'char * __cdecl function(char * restrict, const char * restrict)' but found 'int __cdecl function()'.


BCX initializes the string named message in the end of the C code :

  int WINAPI WinMain(HINSTANCE hInst,HINSTANCE hPrev,LPSTR CmdLine,int CmdShow) {
  MSG  Msg;
    strcpy(BCX_ClassName,"Form1");
    BCX_SetMetric("pixels");
    BCX_InitGUI();
    BCX_hInstance       =  hInst;
    BCX_WndClass.hIcon  =  LoadIcon(NULL,IDI_WINLOGO);
    BCX_RegWnd( BCX_ClassName, WndProc );
 
    // ******************************************
                    FormLoad();
    // ******************************************
   while(GetMessage(&Msg,NULL,0,0))
     {
      HWND hActiveWindow = GetActiveWindow();
      if(!IsWindow(hActiveWindow) || !IsDialogMessage(hActiveWindow,&Msg))
        {
          TranslateMessage(&Msg);
          DispatchMessage(&Msg);
        }
      }
   return Msg.wParam;
  }
 
  strcpy(message,"Hello world!");


Moving strcpy to the WinMain function solves the problem :

BCX_WndClass.hIcon  =  LoadIcon(NULL,IDI_WINLOGO);
    BCX_RegWnd( BCX_ClassName, WndProc );
    strcpy(message,"Hello world!");


Attached contains the .bas and .C files.