• Welcome to Jose's Read Only Forum 2023.
 

FreeBasic vs. PowerBasic vs. Whatever

Started by Donald Darden, June 20, 2007, 08:04:20 AM

Previous topic - Next topic

0 Members and 2 Guests are viewing this topic.

Donald Darden

FreeBasic is a relatively new development, but draws principally on the old QBASIC and QuickBasic syntax for its commands and structures.  Many PowerBasic users will likely want to play with it, either to compare it to PowerBasic in terms of features, or compare the resulting code for size and speed.  Since FreeBasic is actually free, and can be easily found and downloaded from the Internet, this is a viable option.

We are going to be discussing some of the similarities and differences between FreeBasic and PowerBasic here. 

For instance, like C and C++, PowerBasic has adopted the requirement that the main body of code in an executable program must be initiated in a function.  In C and C++ this function is named Main().  With PowerBasic compilers PB/CC and PB/Win, you have three choices for what the main function can be called.  It can be named MAIN(), PBMAIN(), or WINMAIN().

In FreeBasic, it is understood that any body of code that lies outside other subs or functions makes up the main program.  This is how it was with QBASIC and QuickBasic.  I've worked with programs where you could follow code that made up the main body for awhile, then see intersperced DATA statements, and here and there a SUB or FUNCTION, then you would pick of the trail of the main body of code again.  It worked, but it was a nightmare to try and unravel.

Well, can you create a function called MAIN(), PBMAIN(), or WINMAIN() in FreeBasic?  Sure, why not.  PowerBasic treats these as keywords, but FreeBasic has no restrictions on their use.  So you can write FUNCTION MAIN, put your main body of code in there, then use an END FUNCTION to end it.  And it compiles just fine.  But when you try to run your program, nothing really happens.  What gives?

PowerBasic is designed to look for the main function, and program execution begins by calling it.  FreeBasic does not; to it, FUNCTION MAIN() is just another function.  It still looks for the first executable statement that does not appear in a Function or Sub.  In order for your Main function to run in FreeBasic, you would have to call it.  You can simply put Main down on a line by itself, and it all works.  Here is a simple example to illustrate this step:

FUNCTION MAIN
  COLOR 15,1
  CLS
  PRINT "Hello, World!"
  DO
  LOOP
END FUNCTION

Main     'comment this out, and nothing happens when you compile and run


Another difference in FreeBasic involves the PRINT statement.  In the other BASICs mentioned, the use of semicolons between different items to be printed can be left out or replaced by a space or tab.  For instance, a statement like PRINT "a = "; a can be written PRINT "a = "a or as PRINT "a = "       a, and it works fine.  But FreeBasic requires the semicolon to be in place, or you will get an "End of Statement Expected" error.

Donald Darden

How do you measure the strength of a particular compiler or language?  Here are some of the factors I consider:

(1)  Ability to to conceptualize a problem within the scope of the language
(2)  Robustness and flexibility of the language, which makes it adaptive
(3)  Consistency of expression and syntax, so that rules are easily mastered
(4)  Extensibility, enabling the ability to build and use modules or add new functions and features.
(5)  Range of data types and structures, as well as operators for those types.

The first point is a little hard to explain, but if I can't get a "feel" for the way a language works, then trying to figure out how to use it would be difficult.  Some languages just seem better for certain types of problems than others.  But if a language is adaptive or extensible, then it can be reshaped to fit new needs.

For purposes of this discussion, Adaptive would be the ability to build new procedures, that is, Macros, subs, and functions, into the language which act to give it new power,  Extensibility is really the same thing, but here we are focusing on the ability to intermix with other languages through static and shared libraries, and even to build those libraries yourself.

PowerBasic owners know that both the PB/CC and the PB/Win compilers support
linking to Dynamic Link Libraries (DLLs), also called "shared" llibraries, but only
PB/Win is able to create a DLL.  So a PB/CC owner might find it important that
FreeBasic is able to create DLLs, which might eliminate the need to buy PB/Win as well.  At the same time, the newer PowerBasic compilers do not support the older method of linking to static libraries, but that capability is retained in FreeBasic.  So FreeBasic offers an interesting possibility of bridging between a number of technologies that have evolved over time.

PowerBasic offers the strongest set of data types and operators, and right now
it sounds like FreeBasic still needs some work with making its dynamic variable length strings work as well as they should.  In general, the problem seems to be with strings that contain bytes of zero value, or what are called null bytes.

When C was developed, the designers decided that using a null byte was a good way to mark the end of a string.  But in Basic, dynamic strings accept null bytes, because there is a separate integer that marks the number of characters that are currently assigned to the string.  The good news is that any language that supports ASCIIZ, or what are often called null-terminated strings, are all going to work the same and be shareable.  With a suitable library invoked or with further development, FreeBasic should be able to  interface with dynamic variable length strings as well.

The other good news is that FreeBasic looks on track to become an exceptionally well suited replacement to the old QBASIC and QuickBASIC languages, and may actually become the preferred language of choice for anyone that comes from that background.  It's adherance to the original syntax  is quite strong, but it is doubtful that original code for QBASIC or QuickBASIC would actually compile without some modifications.  This is because FreeBasic's interpretation of the original syntax is a bit more rigid than required with the original compilers.   

Charles Pegge

FreeBasic definitely has a better syntax than PowerBasic. It is minor things that make a big difference like  being able to dimension multiple variables and initialise them in one expression. Also assignment operators: += -= make many expressions cleaner.

Scope blocks help to modularise code which uses the same variable names, avoiding name conflicts.

On the subject of dynamic strings, I translated my script engine into Freebasic. Although it is not a large program (2200 lines), it uses dynamic strings intensively, both text and binary and I have not encountered any corruption problems so far. But I tend to use fairly small strings most of the time.

A number of useful functions to be found in PB are missing in FB. I miss USING$ in particular. SORT was useful too. Other functions like REPLACE$ are easier to write.

Donald Darden

Q:  What does PowerBasic do with the following statement?

DIM aa, bb, cc, dd, ee, ff, gg, hh AS STRING

A:  It allows for variables aa, bb, cc, dd, ee, ff, gg, and hh to be used as
strings.  The variables are considered to be local unless they are defined as
GLOBAL or STATIC somewhere.  They are dynamic, variable length strings

Q:  How would FreeBasic interpret this same statement (assuming it can)?

A:  FreeBasic does recognize a single DM statement with multiple variables that are separated by commas.  However, in this case it sees variables aa through gg as being dimensioned as the default type, and only hh is explicitly dimensioned as a string.  Strings would also be dynamic, variable length, but the default scope of the variable would depend on where defined.  If outside any function or sub, the scope would be global.  However, the the DIM statement were modified this way for FreeBasic:

DIM aa AS STRING, bb, cc, dd, ee, ff, gg, hh

then all the variables would be recognized as strings.

Q:  What relationship exists between the variables nn and nn$ in each language?

A:  In earlier BASICs and FreeBasic, nn can be defined as either a numeric or
string variable, and nn$ is always a string variable.  If nn is defined as a string,
then nn and nn$ mean the same thing.  However, if nn is defined as a different
type, then nn and nn$ refer to different variables.  In fact, without the included
type symbol, it can be difficult to determine which variable is being referred to
both by the compiler and on reading and debugging the source code yourself.

Accordingly, PowerBasic compilers now treat nn, both with and without a type symbol, as being the same variable, and once defined or used as the default type, any effort to use it as a different type will cause a compile time error.
Thus each variable name is made more unique in PowerBasic.

Q:  What is the relatioship between VARPTR(nn$), STRPTR(nn$), LEN(nn$), and SIZEOF(nn$)?

A:  VARPTR(nn$) is a pointer that points to what is called a descriptor of nn$.
The variable in this case is a dybamic string of variiable length, and has both location and length associated with it.  VARPTR(nn$) is a 32-bit value that actually points directly at STRPTR(nn$), which is another 32-bit value that points to the first byte position assigned to nn$.  Thus STRPTR(nn$) actually
shows us were the string contents is in memory.  However, with dynamically
allocated strings, this position may change, in which case STRPTR(nn$) allows us to find it again.  LEN(nn$) is how many characters are currently assigned to nn$,
and is a 32-bit value store either after the location pointed to by VARPTR(nn$)
(VARPTR(nn$)+4) in the case of FreeBasic, or before the STRPTR(nn$) location
(VARPTR(nn$)-4) in the case of PowerBasic.  Using LEN(nn$), you do not really care where the length is stored in memory, but when resorting to inline assembly code, this can be very helpful information, because VARPTR() equates to BYREF
parameter passing for strings.  SIZEOF(nn$) always returns 4 in PowerBasic, as their are four bytes to the PowerBasic string handle.  According to the FreeBasic Help file, it should instead return the number of bytes in the string, the same as LEN() does, but actually returns 12.  One might conclude that there are then 12 bytes to the FreeBasic handle, but this is uncertain at this time.   SIZEOF() is
generally most useful to finding the length of fixed-length strings, and for checking the resulting size of UDTs (User Defined Types).

Donald Darden

Okay, you recognize you maybe don't know everything about BASIC, or about the way different versions are implemented, and you are dutifully reading everything you can find, and looking on line, and here you are.  Now what can I tell you at this point that you maybe do not know or understand?

Let me guess:  How about source, include, headerm resource files, and projects?

A source file you probably already know.  It usually has an extention of .BAS, and it is a text file that you write or modify that gets compiled into a program, right?  Yep, that's it.  That's so fundamental, that you probably got it almost immediately when you got introduced to programming.

And you might then say, with some comfidence, that an Include file is what you
use an #INCLUDE or $INCLUDE statement to have included into your program, right?  Well, that's pretty close.  But .INC files, which are also text files, are designed to be inserted into your source file at the exact point where the #INCLUDE or $INCLUDE statement appears.  You don't see them there, but the statement that brings them in is actually like a place marker, telling the compiler that this is the point where the named file's contents need to be inserted.  The only stipulation with #INCLUDE or $INCLUDE is that it be a text file - the extention used is not important.  So you could include a .BAS, ,INC, .TXT. or
any other text-based file.  The real thing is that the contents of that file have to be recognized as valid BASIC statements, or you will not be able to use it in this manner.

You really don't hear about header files with PowerBasic, but there is frequent mention of them with C/C++ and FreeBasic.  A Header file is again a text file,
but one that is presumed to be stripped of all executable code, and all that
remains is DECLARE statements, TYPE and UNION constructions, and any constant assignments.  These are also envoked with the INCLUDE method.

In a way then, naming a file to have a .BAS extention might mean that it is a source file for a complete program, and naming another file with a .INC could mean that it is to be used by one or more .BAS files to insert shared or common code, and then header files could be included near the top of the .BAS file to make known to the compiler any declared subs or functions, even those that are in DLLs, and any types or unions that might be used in dimensioning statements,
and any constants that might be referred to in the program.

That's pretty much the idea, but there is nothing explicit to tell you what should
or should not be included in each file type, and these then just become general
guidelines.  Many developers never write files with the extention .INC until they
are convinced that they want to use the same exact code over and over in different programs.  With PowerBasic, the Win32API files are actually header files, but have the .INC extensions, which is perfectly legal.  If you use multiple languages, with slightly different syntax, you may find that they all have .BAS
extentions, but a certain .BAS file may only compile with FreeBasic, and another
file may only compile with PowerBasic, and so on.  IF they all have .BAS extensions, how can you tell them apart?

How about if you named .BAS files for FreeBasic as .FBB, and .BAS files for PowerBasic as PBB?  Well, if that works for you, fine.  But most IDE Editors only
show source file extensions that they expect, although this might be configurable.  And you still have a problem with PowerBasic, because they have two different compilers, and different versions that are not 100 percent cross-compatable.  The most common method for keeping them separate is in the folder structure you set up on your PC.  Your source files for FreeBasic might sit in a subfolder called BAS under your FreeBasic folder, and your include files would sit in their own subfolder called INC, either under the BAS subfolder or under the FreeBasic folder,

Now what are resource files?  Well, resource files give you a way to identify
some of the things that personalize your session when you run a program.
Resource files can be used to identify icon files, sound files, video clips, the
size and position of the main window, what toolbars are displayed, and so on.
I don't use resource files myself, but they are not uncommon.

And that brings us to projects.  A project is a way of identifying exactly what files are intended to be used together to create a finished product.  They identify the main program file, all dependent source files, where to find them,
and even allow you to organize your project by first identifying which files and what versions of those files are involved.  You can group everything at once,
such as when modifying and recompiling a program, then archiving it for later reference.  Though you can work without creating a project, once you get into programming in depth, and using a lot of existing code, or structuring your efforts along several development lines, the ability to define and use a project can be very helpful.

Donald Darden

#5
FreeBasic provides a simple DLL build and call under the folder .\examples\DLL.  The  mydll.bas program is compiled first, to make myddl.dll.  Then you can compile test.bas, which creates Test.exe, which actually calls the function in mydll.dll.  The testload.bas program can be compiled next, and it also calls the function in mydll.dll, but in this case, it only needs to be able to find mydll.dll when the program is actually run, not at the time when the program is built.  This approach is useful when not all client machines will have a copy of the necessary DLL files, or certain freatures can be triggered only if a given DLL file with a given procedure embedded exists.

To ensure everybody is on the same page here, I'm including the source code for the three files from FreeBasic:
mydll.bas:

''
'' mydll -- simple dll test
''
'' compile as: fbc -dll mydll.bas (will create mydll.dll and libmydll.dll.a under Win32,
''                                 or libmydll.so under Linux)
''
'' note: libmydll.dll.a is an import library, it's only needed when creating
''       an executable that calls any of mydll's functions, only distribute
''       the DLL files with your apps, do not include the import libraries,
''       they are useless to end-users
''

#include once "mydll.bi"
  ''
  '' note: do not add any executable code to the main module (ie: outside
  '' any function), because that code will never be executed as only DllMain
  '' is invoked by Windows at the initialization
  ''


''::::::
''
'' simple exported function, the full prototype is at mydll.bi (the EXPORT clause must be used here)
''
FUNCTION AddNumbers ( BYVAL operand1 AS INTEGER, BYVAL operand2 AS INTEGER ) AS INTEGER EXPORT

  FUNCTION = operand1 + operand2

END FUNCTION

test.bas:

''
'' test -- calls a mydll's function and prints the result
''
'' compile as: fbc test.bas (couldn't be simplier, eh?)
''

#include "mydll.bi"

randomize Timer
Color 15,1
cls

dim as integer x = rnd * 10
dim as integer y = rnd * 10

print x; " +"; y; " ="; addnumbers( x, y )

Do While InKey$=""

Loop

testload.bas:

''
'' testload -- loads mydll at runtime, calls a mydll's function and prints the result
''
'' compile as: fbc testload.bas
''
'' note: requires the compiled mydll dynamic library to be available in current
''       directory; see mydll.bas for info on how to create this.
''

dim library as any ptr
dim addnumbers as function( byval operand1 as integer, byval operand2 as integer ) as integer

'' Note we specify just "mydll" as library file name; this is to ensure
'' compatibility between Windows and Linux, where a dynamic library
'' has different file name and extension.
''
library = dylibload( "mydll" )
if( library = 0 ) then
print "Cannot load the mydll dynamic library, aborting program..."
end 1
end if

addnumbers = dylibsymbol( library, "AddNumbers" )
if( addnumbers = 0 ) then
print "Cannot get AddNumbers function address from mydll library, aborting program..."
end 1
end if

randomize Timer
Color 15,1
cls

dim as integer x = rnd * 10
dim as integer y = rnd * 10

print x; " +"; y; " ="; addnumbers( x, y )

'' Done with the library; the OS will automatically unload libraries loaded by a process
'' when it terminates, but we can also force unloading during our program execution to
'' save resources; this is what the next line does. Remember that once you unload a
'' previously loaded library, all the symbols you got from it via dylibsymbol will become
'' invalid, and accessing them will cause the application to crash.
''
dylibfree library
Do While InKey$=""

Loop

mydll.bi:



''
'' all FB functions are by default STDCALL on Windows and also PUBLIC,
'' so nothing else has to be added (note that FBC will not include "mydll"
'' to linker's list when creating the DLL, only when using it on an .exe)
''

declare function AddNumbers lib "mydll" alias "AddNumbers" ( byval operand1 as integer, byval operand2 as integer ) as integer

That's four files, not three, right?  Actually, the mydll.bi file might be called a header file in some cases.  The .BI extension likely stands for BASIC INCLUDE file, and often might be named .INC instead.  It provides useful information to the compiler at compile time, but often may not contain any executable code itself.  The equivalent in C or C++ would be files that end with .h for an extension.

Alright, so we have four files rather than three, and if you stick them in the same folder, then compile them with FreeBasic as indicated in the comments in each, You should be able to compile and run the resulting programs test.exe and testload.exe.

Now here are the same four files, but modified to compile and run with PB/CC instead.  If you have access to PB/Win rather than PB/CC, you can comment out the COLOR and CLS statements, and replace the PRINT statement with a MSGBOX statement.  Since MSGBOX is not able to print numeric values directly, you would also have to wrap the iResult or returned value from AddNumbers in a STR$() and append these to other string elements.

Now let's look at the same files, but modified for PowerBasic:
mydll.bas:

#COMPILE DLL
''
'' mydll -- simple dll test
''
'' compile as: fbc -dll mydll.bas (will create mydll.dll and libmydll.dll.a under Win32,
''                                 or libmydll.so under Linux)
''
'' note: libmydll.dll.a is an import library, it's only needed when creating
''       an executable that calls any of mydll's functions, only distribute
''       the DLL files with your apps, do not include the import libraries,
''       they are useless to end-users
''


'#INCLUDE once "mydll.bi"      'supported in FreeBasic
#INCLUDE "mydll.bi"            'best equivalent in PowerBasic
  ''
  '' note: do not add any executable code to the main module (ie: outside
  '' any function), because that code will never be executed as only DllMain
  '' is invoked by Windows at the initialization
  ''


''::::::
''
'' simple exported function, the full prototype is at mydll.bi (the EXPORT clause must be used here)
''
'FUNCTION AddNumbers ( BYVAL operand1 AS INTEGER, BYVAL operand2 AS INTEGER ) AS INTEGER EXPORT 'FreeBasic
FUNCTION AddNumbers ( BYVAL operand1 AS INTEGER, BYVAL operand2 AS INTEGER ) EXPORT AS INTEGER  'PowerBasic

  FUNCTION = operand1 + operand2

END FUNCTION

test.bas:

''
'' test -- calls a mydll's function and prints the result
''
'' compile as: fbc test.bas (couldn't be simplier, eh?)
''

#INCLUDE "mydll.bi"
#IF %DEF(%PB_CC32) OR %DEF(%PB_WIN32)
  FUNCTION PBMAIN
#ENDIF

  RANDOMIZE TIMER
  COLOR 15,1
  CLS

  DIM x AS INTEGER: x = RND * 10
  DIM y AS INTEGER: y = RND * 10

  PRINT x; " +"; y; " ="; addnumbers( x, y )

  DO WHILE INKEY$=""

  LOOP
#IF %DEF(%PB_CC32) OR %DEF(%PB_WIN32)
  END FUNCTION
#ENDIF

testload.bas:

''
'' testload -- loads mydll at runtime, calls a mydll's function and prints the result
''
'' compile as: fbc testload.bas
''
'' note: requires the compiled mydll dynamic library to be available in current
''       directory; see mydll.bas for info on how to create this.

''
DECLARE FUNCTION addnumbers(BYVAL operand1 AS INTEGER, _  'this is PowerBasic syntax
     BYVAL operand2 AS INTEGER) AS INTEGER

#IF %DEF(%PB_CC32) OR %DEF(%PB_WIN32)
  $INCLUDE "c:\win32api\win32api.inc"
  FUNCTION PBMAIN
    DIM fAddr AS DWORD, iResult AS INTEGER 'a couple of items for PowerBasic

#ENDIF

    'DIM library AS ANT PTR                 'not allowed as PowerBasic type
    DIM library AS DWORD PTR                'what powerbasic will accept instead
    'DIM addnumbers AS FUNCTION( BYVAL operand1 AS INTEGER, _ 'not allowed in PowerBasic syntax
    '    BYVAL operand2 AS INTEGER ) AS INTEGER
    'PowerBasic does not permit DECLARE statements inside procedures, so the
    'equivalent DECLARE for addnumbers was moved up above.

    '' Note we specify just "mydll" as library file name; this is to ensure
    '' compatibility between Windows and Linux, where a dynamic library
    '' has different file name and extension.

    'library = dylibload( "mydll" )    'not according to BowerBasic syntax
    library = loadlibrary("mydll")     'PowerBasic using loadlibrary function
    IF (library = 0) THEN
        PRINT "Cannot load the mydll dynamic library, aborting program..."
        END 1
    END IF

    'addnumbers = dylibsymbol( library, "AddNumbers" )   'not supported in PowerBasic syntax
    fAddr = GetProcAddress(library, "AddNumbers")  'PowerBasic's use of GetProcAddress API

    'IF (AddNumbers = 0) THEN     'FreeBasic allows function name to be overloaded
    IF (fAddr = 0) THEN           'PowerBasic wants a separate variable for entry address
        PRINT "Cannot get AddNumbers function address from mydll library, aborting program..."
        END 1
    END IF

    RANDOMIZE TIMER
    COLOR 15,1
    CLS

    'DIM AS INTEGER x = RND * 10       'FreeBasic supports dim and assign in 1 statement
    'DIM AS INTEGER y = RND * 10       'FreeBasic supports dim and assign in 1 statement
    DIM x AS INTEGER: x = RND * 10     'PowerBasic requires two statements for same thing
    DIM y AS INTEGER: y = RND * 10     'PowerBasic requires two statements for same thing

    'PRINT x; " +"; y; " ="; addnumbers( x, y )   'with inbedded dynlibrary support, easier
    CALL DWORD fAddr USING AddNumbers( x, y ) TO iResult  'you call the function using declare & parms
    PRINT x; " +"; y; " ="; iResult    'PowerBasic is just a bit more awkward

    '' Done with the library; the OS will automatically unload libraries loaded by a process
    '' when it terminates, but we can also force unloading during our program execution to
    '' save resources; this is what the next line does. Remember that once you unload a
    '' previously loaded library, all the symbols you got from it via dylibsymbol will become
    '' invalid, and accessing them will cause the application to crash.
    ''
    'dylibfree library                 'syntax not supported by PowerBasic
    FreeLibrary library                'PowerBasic does it with FreeLibrary API

    DO WHILE INKEY$=""
    LOOP

#IF %DEF(%PB_CC32) OR %DEF(%PB_WIN32)
  END FUNCTION
#ENDIF

mydll.bi:



''
'' all FB functions are by default STDCALL on Windows and also PUBLIC,
'' so nothing else has to be added (note that FBC will not include "mydll"
'' to linker's list when creating the DLL, only when using it on an .exe)
''

DECLARE FUNCTION AddNumbers LIB "i:\pbwin90\samples\FreeBasic\mydll.dll" ALIAS "AddNumbers" ( BYVAL operand1 AS INTEGER, BYVAL operand2 AS INTEGER ) AS INTEGER

Since most of the changes had to be made in testload.bas, it is the only source file that is heavily commented.  The same reasons given there will apply to the other source files.

If you have PB/CC instead of PB/Win, you will probably immediately groan as you note the #COMPILE DLL metastatement in mydll.bas.  PowerBasic's Console Compiler does not produce DLLs, right?  So you feel that leaves you out.  Well, here is an eyeopener for you.  First, change that #COMPILE DLL to #COMPILE EXE if you want, or just remove it altogether.  Then drop to the bottom of the source code and add two lines of text:

FUNCTION MAIN
END FUNCTION

Now you can compile it.  Yes, it creates an exe file, fut the use of EXPORT in your defined FUNCTIONs and SUBs means that the system can link to them externally.  You can even put code in FUNCTION MAIN and use it as a regular EXE file, or not , as it suits you.  You will have to give the EXE extention in the LIB clause when trying to DECLARE any functions or subs for use in other programs.

Make sure you either change the #INCLUDE and library load statements to provide the path to the folder where you place these files. or use the PowerBasic's IDE Options button under Windows on the toolbar, select the Compiler Tab, then modify the PB Include path so that the folder will be searched (note the 3 dot (...) button on the right side for making change here).

Once you have played with this for awhile, you will undoubtedly feel that the
FreeBasic syntax and simpler form puts it somewhat ahead of PowerBasic.  I
won't argue with that finding, but don't rush to condemn PowerBasic.  It has
many strong features going for it, including extended precision floating point,
currency types, along with the types associated with FreeBasic.  However, the dynamic, variable length strings are organized in memory in a different way.  Jose posted this information earlier for FreeBasic:

128 typedef struct _FBSTRING {
  129     char           *data;    /**< pointer to the real string data */
  130     int             len;     /**< String length. */
  131     int             size;    /**< Size of allocated memory block. */
  132 } FBSTRING;

That structure is what is reported on by SIZEOF(), which is 12 for FreeBasic.
Each field is 32-bits (4 bytes) long, and SIZEOF() is giving you the size of the
structure as 12 bytes.  PowerBasic, on the other hand, has this internal structure for the dynamic, variable length strings:

    length    AS DWORD       'length of string (number of bytes)
    location  AS DWORD      'corresponds to STRPTR() for string

But in PowerBasic, SIZEOF() returns 4, whereas this structure has 8 bytes.  The PowerBasic Help file explains this by stating that SIZEOF() returns the length of the handle for the string, which is an internal representation used by PowerBasic.

The question is, are the DLLs created by FreeBasic and PowerBasic capable of being used with the other language?  Now starting out, I depended on several sites for downloading and installing FreeBasic, and the same for FBEdit, and I set up my own directory treee with FBEdit under FreeBasic, then found that the FBEdit bundle I later downloaded had FreeBasic installed under it.  The mishmash that resulted was proving problematic, and I finally deleted the whole directory tree, then installed the FBEdit bundle in its place.  Everything seems to be working better as a result.  Now before that time, I had already tried to use DLLs produced in the two languages with the other, and it wasn't working.  So using the code above, I am in the process of trying again, now that everything seems to be working better.

My time trial on PE Explorer ran out today, but rather than pay $129 to get a license, I decided to try DumpPE, which came up on a thread I was reading.  I searched the Internet for it, then found out it had been bundled with MASM32,
something I had acquired earlier.  So in searching my own PC, I found DumpPE located at \masm32\bin\.  typing DumpPE /?, I got some basic info on the program, but it turns out you just need to use a filename to have it perform the dump to a console screen.  Only the screen closes immediately.  So here is what I did.  I wrote a BAT file and put it on my desktop, and this is what it has in it:

i:\masm32\bin\dumppe %1 > dumpdll.txt
type dumpdll.txt
pause

Now I just grab the DLL file in question, and drag and drop it onto the BAT file icon, then click on "Open With" when the dialog box comes up.  I can then walk the resulting file with the mouse.

Like the PE Explorer, DumpPE shows that in the FreeBasic version, then function name AddNumbers in the DLL shows up as AddNumbers@8.  Using WinDiff, another available download that compares the difference between two files, I tried to see how many differences there were in the dumps from the two DLLs, one created by FreeBasic and the other by PowerBasic.  There were a great many differences, so that did not prove fruitful.  What I am going to do now is see if the Test.bas program in one language will work with the DLL made from the other.  I found a post on PowerBasic about DEF files for Microsoft languages that may have a bearing here, and will research that further if there proves to be a problem still.

I'm no expert in this stuff, so expect errors as I go along, and I will continue to make corrections as I learn of those errors.

José Roca

 
Quote
32-bit dword for length vs. just 16-bit word for length in FreeBasic.

Wrong. The FB runtime has been written in C, and in C an int is a LONG, not an INTEGER.

Eros Olmi

#7
In C, int can be 16 or 32 bit depending on compiler and system.
short int is 16 bit
long int is 32 bit

http://msdn2.microsoft.com/en-us/library/ms881376.aspx
thinBasic Script Interpreter - www.thinbasic.com | www.thinbasic.com/community
Win7Pro 64bit - 8GB Ram - Intel i7 M620 2.67GHz - NVIDIA Quadro FX1800M 1GB

José Roca

 
Correct. But as Donald is using a version for 32 bits, it is a long. If he was using a version for, e.g. DOS, then it would be 16 bits.

Donald Darden

#9
Thanks, José.  I forgot that Int can mean either in C and C++, and took it the wrong way.  I modified and changed the post above, and really appreciate the feedback.  It clarifies my understanding and helps keep me from putting out bad info.

I'm posting some questions to the FreeBasic.net forums, with some answers coming back.  When I used PE Explorer and DumpPE, they both showed a difference in the exported function name, as the FB version had an included @8 at the end of the name.  Jose pointed to an article about decorative names, and how _STDCALL conventions may include an @ followed by the total bytes in the arguments list passed to the function, so that AddNumbers@8 signals that the two parameters, integer1 and integer2, had a combined link of 8 bytes.  But it also pointed out that this is not consistent behavour on the part of linkers.  When I tried WinDiff to see other differences between the dumps of the two DLL files made by DumpPE,  The differences were significant, in large part because the linkers used in creating the DLL files were scoped differently.  But here is the explanation I got in response:
Quote
FB follows the GCC (GNU Compiler Collection) standards when mangling symbols, and because both statically and dynamically libraries must be supported (what neither PB or VB allows), every standard-call name is mangled with the @n suffix.

MS' choice was to mangle only the import library (the *.dll.a files in [fb-dir]/lib/win32) but leaving the DLL symbols unmangled, but LD (the GNU linker) doesn't follow that.

So, in short, if a DLL has no import library to be used by a C compiler, you have create one using pexports + dlltool. Now, if it follows the MS standard, then you will have to edit the .def file and add the mangling by hand like "foo = foo@12\nbar = bar@4", not a fun task..

In 0.17 you can create DLL's that are compatible with the MS mangling to be used in VB or PB, by passing a command to LD through FB when building the dll: "fbc mydll.bas -dll -Wl --kill-at".

Eros Olmi

If I'm not wrong, if you add ALIAS clause plus function name in the exported function, they will be exported with your alias name without @ followed by the number of total bytes expected into the stack for parameters.

Eros
thinBasic Script Interpreter - www.thinbasic.com | www.thinbasic.com/community
Win7Pro 64bit - 8GB Ram - Intel i7 M620 2.67GHz - NVIDIA Quadro FX1800M 1GB

Eros Olmi

I've checked in one of my DLL and to avoid @x in the name of exported functions/subs, add a Cdecl clause.
Example:
FUNCTION MyFunction Cdecl ALIAS "MyFunction " (BYVAL Whatever AS LONG) AS Long EXPORT
Will produce MyFunction in DLL while
FUNCTION MyFunction ALIAS "MyFunction " (BYVAL Whatever AS LONG) AS Long EXPORT
Will produce MyFunction@4 in DLL.

thinBasic Script Interpreter - www.thinbasic.com | www.thinbasic.com/community
Win7Pro 64bit - 8GB Ram - Intel i7 M620 2.67GHz - NVIDIA Quadro FX1800M 1GB

Donald Darden

And if I remember correctly, CDECL changes the order of the passed parameters as
they appear on the stack, which you can addomodate in the calling statement, but needs to be taken into account.  I'm sure someone will correct me if I'm wrong.

If you fail to identify a function by name, then I guess the next step would be to range through the possible ordinal values for similar names, ones that are not case sensitive and exclude the possible "@" symbol and what follows.  If you write code like this, you reduce errors caused by case mismatches as well.

Charles Pegge


This is a good article on calling conventions.

http://www.codeproject.com/cpp/calling_conventions_demystified.asp


cdecl and stdcall both pass parameters onto the stack from right to left.
The difference is that with cdecl, the caller cleans the parameters off the stack afterwards, whereas in stdcall the callee function does it. This is slightly faster on the x86, but disallows passing a variable number of parameters.

CDECL function names are decorated with an underscore prefix. STDCALL functions are decorate with an underscore prefix plus an @ suffix followed by the number of bytes passed.

Donald Darden

The article is helpful, and explains a lot.  I quote this from its conclusion:
Quote
To cut a long story short, we'll outline the main differences between the calling conventions:

    * __cdecl is the default calling convention for C and C++ programs. The advantage of this calling convetion is that it allows functions with a variable number of arguments to be used. The disadvantage is that it creates larger executables.
    * __stdcall is used to call Win32 API functions. It does not allow functions to have a variable number of arguments.
    * __fastcall attempts to put arguments in registers, rather than on the stack, thus making function calls faster.
    * Thiscall calling convention is the default calling convention used by C++ member functions that do not use variable arguments.

In most cases, this is all you'll ever need to know about the calling conventions.
The only problem is, I don't quite get the ThisCall convention used by C++.  When the article says This is passed in the ECX register, exactly what is "this"?  It doesn't say, and I could not determine it from looking briefly at the Assembly code.

It's unfortunate that multiple calling conventions exist to complicate our lives.  You would think that there would be some hard and fast rules that could automate the methods needed for making the calls, rather than forcing us to delve into the matter ourselves.  There should be no problem as long as you write DLLs and use DLLs in the same language, but venture into the world of trying to join the results of different languages together, and it tends to get complicated.