Home PageDownloadBeginner's ZoneAmateur's ZoneExpert's ZoneTips PageProgramming LinksE-Mail Me

Expert Zone
Using Windows 95 Functions from GFA Basic for Windows 3.0

One of the very best things about Windows is the way it uses DLL's for all it's functions. This means you can directly access a lot of the commands from Windows 95 and all of the commands from Windows 3.1 in GFA Basic almost as easily as you would from a Windows 95 programming language. To get lists of these functions I recommend you download the Microsoft Win32 SDK from the Microsoft Software Developer's Network. You can access those on-line if you prefer at msdn.microsoft.com. All the examples are in C, but they are quite easy to convert to GFA.

Once you have a list of the commands you can just declare them using the DLL and DECL functions and then it's just like calling any other API command. Here's an example, the LoadImage() function can be used to load bitmaps. When calling a function in this way remember that GFA is a 16-bit language and therefore C int variables are words.

DLL #1,"USER"
  DECL WORD LoadImage(W,L,W,W,W,W)
ENDDLL
OPENW #1
file$="Bitmap Name (Short Filenames)"+CHR$(0)
bmp&=^LoadImage(0,V:file$,0,0,0,128 | 16)
'128=Loadrealsize  16=Loadfromfile
REPEAT
  PEEKEVENT
  PUT 0,0,bmp&
UNTIL MOUSEK
FREEBMP bmp&
FREEDLL 1
CLOSEW #1

Yes I know it's just like LOADBMP but it's an example of how easy it is.

Here's a much more useful example of using Windows 95 functions, using FindFirstFile and FindNextFile to get long filenames:

'************************* Declare the functions ***********************
DLL #1,"kernel"
  DECL LONG FindFirstFile(L,L)
  DECL BOOL FindNextFile(L,L)
  DECL BOOL FindClose(L)
ENDDLL

'**** Create the structure that FindFirstFile and FindNextFile fill ****
TYPE FIND_DATA:
- LONG Attribute
- CHAR*40 Dummy$
- CHAR*260 FileName$
- CHAR*14 DOSFileName$
ENDTYPE
FIND_DATA:FIND_DATA.

'******************** Set up the 'Constants' used *********************
FILE_ATTRIBUTE_ARCHIVE%=$00000020
FILE_ATTRIBUTE_DIRECTORY%=$00000010
FILE_ATTRIBUTE_HIDDEN%=$00000002
FILE_ATTRIBUTE_NORMAL%=$00000080
FILE_ATTRIBUTE_READONLY%=$00000001
FILE_ATTRIBUTE_SYSTEM%=$00000004
FILE_ATTRIBUTE_TEMPORARY%=$00000100
FILE_ATTRIBUTE_ATOMIC_WRITE%=$00000200
FILE_ATTRIBUTE_XACTION_WRITE%=$00000400

OPENW #1
han%=^FindFirstFile("C:\WINDOWS\*.*",V:FIND_DATA.)
' Find all Files/Folders in the Windows Directory
REPEAT
  f$=ZTRIM$(FIND_DATA.FileName$)  ' Remove the spaces and the null termination
  IF f$<>"." AND f$<>".."         ' Who want's to see these? :-)
    PRINT f$;
    IF AND(FIND_DATA.Attribute,FILE_ATTRIBUTE_DIRECTORY%)
      ' If the current file is a directory
      PRINT "  <DIR>";
    ENDIF
    PRINT
  ENDIF
UNTIL ^FindNextFile(han%,V:FIND_DATA.)=0  'Returns 0 when there are no more files
~^FindClose(han%)   'Free up the memory (very important)
REPEAT
  SLEEP
UNTIL MENU(1)=4 'Wait until you close the window
CLOSEW #1
FREEDLL 1

Now this all seems very easy, but if you have tried the above code on Windows NT it wouldn't have worked. This is because a 16-bit application cannot call a 32-bit DLL like this. The reason this works on '95 is that it doesn't use 32-bit DLLs at all, it uses updated 16-bit DLL's so when you use DLL #1,"kernel", that isn't Kernel32.DLL but an updated version of Windows 3.1's kernel (KRNL386.EXE). Windows NT however doesn't allow updated DLLs, 16-bit programs run in the same 16-bit enviroment that they would have on 3.1, except for a few functions. Fortunately these few functions, also availiable under 95, allow you to call a 32-bit DLL from a 16-bit program. This is called generic thunking. Here is an example of generic thunking at work:

DLL #1,"kernel"
  ' -----------------------------------------------------------------------
  ' Declare WOW (Windows on Win32) functions from the 16-bit kernel
  ' -----------------------------------------------------------------------
  DECL DWORD LoadLibraryEx32W(L,L,L)
  DECL BOOL FreeLibrary32W(L)
  DECL DWORD GetProcAddress32W(L,L)
  DECL DWORD GetVDMPointer32W(L,W)
ENDDLL
call32%=GetProcAddress(GetModuleHandle("kernel"),"CallProc32W")
user32%=^LoadLibraryEx32W("user32.dll",0,0)

wall$="C:\WINDOWS\FOREST.BMP"+CHR$(0)
SystemParInfoAdr%=^GetProcAddress32W(user32%, "SystemParametersInfoA")
~P:(call32%)(L:20, L:0, L:V:wall$, L:1,L:SystemParInfoAdr%, L:%0010, L:4)

~^FreeLibrary32W(user32%)
FREEDLL 1

Now this is quite complicated but I'll have a go explaining it. In the new version of the 16-bit kernel there are 5 functions that allow you to use 32-bit DLLs. These are LoadLibraryEx32W, FreeLibrary32W, GetProcAddress32W, GetVDMPointer32W and CallProc32W. They can be declared using the normal method except CallProc32W which can have different numbers of parameters depending on what function you are calling, and GFA doesn't allow that.

The first thing the program does declares the first 4 functions and then uses GetProcAddress to get the address of the function CallProc32W. Then the program uses LoadLibrary32W to load in the 32-bit DLL User32.DLL. Now the interesting bit. In the same way as we got the address of CallProc32W, except using the 32-bit compatible functions, we now get the address of the 32-bit function SystemParametersInfoA from User32.DLL.

Next we use the very powerful GFA command P: to call CallProc32W (located at the address call32%) with the parameters for SystemParametersInfo, followed by CallProc32W's own parameters. The SystemParametersInfo parameters don't really matter but the first tells it to change the wallpaper of the desktop, the second is meaningless, the third is the address of the string containing the name of the file to use as the background image, and the last tells it to put the changes in the registry to make them permanent. Now come the parameters for CallProc32W, the first is the address of the function to call, the second is a bit mask for which of the parameters of the function you are calling are addresses (see below for the reason for this), and the last parameter is how many parameters there are for the function.
NOTE: Whenever you call CallProc32W you have to prefix all the parameters with L:, making sure they are passed as LONGs. This is the same even if the documentation says that it takes a BYTE or a WORD.

So that's it, the last 2 lines just clean up, just like mother always told you to.

Well it's not quite it. What does GetVDMPointer32W and the bit mask in the call to CallProc32W do? The reason for these being here are quite simple, addresses are different in 32-bit programs. GetVDMPointer converts a 16:16 address (16-bit) to a 0:32 address (32-bit). The bit mask just tells CallProc32W which parameters it should convert for you. If you are calling a 32-bit function that is passed a TYPE with addresses in it simply convert them using newadr%=^GetVDMPointer32W(oldadr%,0). An example of this can be downloaded here.

Also remember when calling functions in this way that a C int variable is a long (32-bits).


Windows 95 Look Tips

It is very likely that your programs will be run using Windows 95 or NT, so it is nice to have them look like 95/NT programs. Unfortunately this can be difficult using GFA.

If you don't want a window to have a sizeable border (for most programs sizeable borders are a pain) you cannot make it look like a Windows 95 window (with the raised edge). To get that 95 look you have to use dialog boxes only. Fortunately you can use dialog boxes just like windows:

DIALOG #1,10,10,200,200,"Win95 Test",DS_MODALFRAME | WS_CAPTION |
    WS_SYSMENU,-10,"MS Sans Serif" 'ALL ON SAME LINE!
ENDDIALOG
han&=GetSystemMenu(DLG(1),0)
~DeleteMenu(han&,SC_SIZE,MF_BYCOMMAND)
~DeleteMenu(han&,SC_MAXIMIZE,MF_BYCOMMAND)
' Fixes a problem with the system menu that allows the box to be sized
SHOWDIALOG #1
dc&=GetDC(DLG(1))            'You can either get the DC here or GetDC(DLG(1)) every time you
SETDC dc&                    'need to draw, use API commands such as Poly() and Rectangle()
DEFFILL 0                    'to draw and when finished drawing ReleaseDC(DLG(1),dc&)
GRAPHMODE R2_COPYPEN,OPAQUE  'The latter it seen as the better programming technique
'REST OF PROGRAM HERE

The only difference between that and a normal Win 95 window is the lack of an icon on the title bar and task bar, but it's a small price to pay. Don't forget to use dialog specific functions if there are any for what you're are trying to do. Fortunately most window functions such as SHOWW work fine with SHOWW #DLG(1), so you shouldn't have too much trouble.

To use Windows 95 controls (with the nice indented borders) you can't use GFA's control creation functions. To create a '95 control do this:

param%=WS_VISIBLE | WS_CHILDWINDOW | WS_TABSTOP | CBS_DROPDOWNLIST
~CreateWindowEx(512,"COMBOBOX","",param%,x,y,width,height,DLG(1),100,_INSTANCE,0)

Is identical to:

COMBOBOX 100,x,y,width,height,WS_VISIBLE | WS_TABSTOP | CBS_DROPDOWNLIST

Except for the border.

You also have to send them a WM_SETFONT message to use the Win 95 font (they default to the 3.x bold monstrosity). The default Win 95 font is "MS Sans Serif",HEIGHT -10,WEIGHT FW_NORMAL, or for a trick, if you have set the font for the dialog in the DIALOG statement you can do this:

param%=WS_VISIBLE | WS_CHILDWINDOW | WS_TABSTOP
DIALOG #1,10,10,200,200,"Edit and Button",WS_CAPTION | WS_SYSMENU,-10,"MS Sans Serif"
  BUTTON "Hello",200,45,60,60,25,BS_DEFPUSHBUTTON
  ~CreateWindowEx(512,"EDIT","",param%,10,100,100,20,DLG(1),100,_INSTANCE,0)
ENDDIALOG
~SendMessage(DLG(1,100),WM_SETFONT,SendMessage(DLG(1,200),WM_GETFONT,0,0),0)

Now you don't have to worry about creating and destroying the font.


If you think I can help you with your GFA Basic Windows problems don't hesitate to e-mail me. I'll try my best to answer any questions. Also if you have got any tips for GFA Basic Windows, useful procedures, handy listings or recommendations of good web sites, news groups or anything else about GFA Basic send them too.

Home PageDownloadBeginner's ZoneAmateur's ZoneExpert's ZoneTips PageProgramming LinksE-Mail Me