Home
Using Resources in an XBasic Executable

A 'resource' is a block of binary data added to an executable module. Resources may be icons, cursors, bitmap images, compressed images (JPEG, etc), strings, data arrays, or anything else the programmer wants to include in an EXE or DLL. The primary motivation for putting resources into an executable module is that all required data are in one file with the executable code, and not scattered in numerous separate files.

This document describes a strategy for using resources in executable modules built from XBasic source code. The information pertains only to Windows programs, not Linux. This strategy has the following components:

- modifications to xapp.xxx and xdll.xxx which to allow resources to be added easily to an EXE or DLL, simply by including an RC file in the same folder as the source (.x) file.

- code that can be added to any program, or to a separate DLL, to allow the resources to be accessed in the standalone program.

- a special resource.mak file, which will create an RSM file (resource module, actually a DLL that contains only resources, no functions). This file can be used during program development in the PDE, so that resources can be accessed even though the standalone EXE has not yet been created.

Programming examples, and other useful files, can be downloaded here.



Contents:

A. Adding resources to an XBasic executable file.
B. Using the resources in an EXE.
      ICON
      CURSOR
      BITMAP
      User-defined
C. Using the resources in a DLL.
D. Using resources when running in the PDE.



A. Adding resources to an XBasic executable (EXE or DLL) module.

This discussion assumes you know how to create an EXE or DLL from an XBasic program. Also, the method relies on revised versions of xapp.xxx and xdll.xxx, which are in the XBasic\templates folder. Make backup copies of the original versions of these files, then copy the revised versions into the templates folder. If your autoexec.bat does not set the XBDIR environment variable, you will need to edit xapp.xxx and xdll.xxx, replacing the string "$(XBDIR)" with the path to your XBasic folder.

1. Create a resource script (.rc) file, which is an ordinary text file that lists the resources to be included in the executable. The default .rc file (xb.rc) includes the following resources:

    window  ICON    window.ico
    n       CURSOR  n.cur
    s       CURSOR  s.cur
    e       CURSOR  e.cur
    w       CURSOR  w.cur

Each line gives the name of the resource, the type of resource, and the name of the file containing the data for the resource (these files are in the XBasic\images folder).

The .rc file for your program should list these default resources, plus any additional resources you want to include in the executable. Having said this, it appears that the cursor files listed in xb.rc are leftover from previous XBasic versions, and are never likely to be used in their present form. It seems to be possible to leave them out. Also, although there must be an icon named "window", any .ico file can be used to provide the data for this resource. Thus if you want to use "MyIcon.ico" as the icon for your program, substitute the line

    window  ICON    MyIcon.ico

in your .rc file.

The following example .rc file changes the standard window icon, and includes a BMP-format image, and a user-defined SLONG data array as additional resources:

    window  ICON     MyIcon.ico
    myimage BITMAP   \MyImages\MyImage.bmp
    data    SLARRAY  arraydata.dat

Give the .rc file the same name as your XBasic source (eg., use myprogram.rc as the name of the .rc file for myprogram.x), and save the .rc file in the same folder. The files containing the resource data (MyIcon.ico, etc.) can be in the XBasic\images folder, the XBasic\include folder, or in the same folder as your program. If a file is any other folder, the .rc file should give the complete path.

3. Create a .mak file for your program, by loading the program into the XBasic PDE, and selecting 'assembly' from the 'run' menu. Then run nmake on the .mak file.

4. The executable file thus created should contain the desired resources, and should require only the runtime version of xb.dll (ie, the renamed xbrun.dll) to run properly.


B. Using the resources in an EXE.

The procedure necessary to use the resources included in an EXE depends on the type of resource. Resources other than icons and cursors require direct function calls to the Windows API, which means that the program must IMPORT "kernel32" in the PROLOG. Also, it is necessary edit kernel32.dec (in the XBasic\include folder) to add the following EXTERNAL FUNCTIONs:

    EXTERNAL FUNCTION  FindResourceA (hModule, lpName, lpType)
    EXTERNAL FUNCTION  SizeofResource (hModule, hResInfo)
    EXTERNAL FUNCTION  LockResource (hResData)

1. ICON

As noted above, the icon that Windows uses for a program can be changed simply by changing the .ico file that is associated with the name "window". An alternative is to include a second .ico resource (in addition to window.ico), and give it a name that alphabetically precedes the name "window". Eg., given the .rc file

    window  ICON    window.ico
    myicon  ICON    MyIcon.ico

Windows Explorer will use the icon in MyIcon.ico as the program's icon. With this method, however, the title bar and task bar icons will still be the standard XBasic window.ico, unless the program calls XgrSetWindowIcon() or sends the #SetWindowIcon message (see below). Note that if you want to include an icon resource, but don't want Explorer to use it as the program's icon, you must give it a name that comes alphabetically after "window".

XBasic provides limited support for manipulating icons. The central function, XgrRegisterIcon(), loads an icon resource and assigns a number to it. This number can be used to draw the icon within the window (XgrDrawIcon() and related functions), or to change the icon displayed in the title bar and taskbar (XgrSetWindowIcon()).

    XgrRegisterIcon (iconName$, @icon)  'iconName$ is the name given in the .rc file
    XgrDrawIcon (grid, icon, x, y)      'draws the icon in grid at coordinates (x, y)
    XgrSetWindowIcon (window, icon)     'changes icon used in the title bar and taskbar

(Note that despite the window argument, XgrSetWindowIcon() changes the icon for ALL XBasic windows associated with the program.)

Other icon functions:

    XgrIconNameToNumber (iconName$, @icon)
    XgrIconNumberToName (icon, @iconName$)
    XgrDrawIconGrid     (grid, icon, xGrid, yGrid)
    XgrDrawIconScaled   (grid, icon, x#, y#)
    XgrGetWindowIcon    (window, @icon)

GUIdesigner programs can use the messages #GetWindowIcon and #SetWindowIcon:

    XuiSendMessage (grid, #GetWindowIcon, @iconNumber, 0, 0, 0, 0, @iconName$)

#SetWindowIcon can be used in two ways - using the icon number, or the icon name. In the second case, the icon number is returned as an output:

  XuiSendMessage (grid, #SetWindowIcon, iconNumber, 0, 0, 0, 0, 0)
  XuiSendMessage (grid, #SetWindowIcon, @iconNumber, 0, 0, 0, 0, @iconName$)

2. CURSOR

  The handling of cursors in XBasic is similar to the handling of icons.  XgrRegisterCursor() is used to load a cursor resource, and XgrSetCursor() is used to select a registered cursor.  XBasic keeps track of two user cursors, the 'normal' cursor and the override cursor.  If the override cursor is set (using XgrSetCursorOverride()), it is used instead of the normal cursor; the normal cursor is used when the override cursor is not set.

  Even when an application has set the cursor, it will usually be changed as soon as the mouse is moved out of the application's window, and is not automatically restored when the mouse moves back into the window.  It is up to the application to detect that the mouse has moved into a window (by monitoring the #MouseEnter message), and to set the cursor accordingly.  In GUIDesigner programs, this is done automatically once a #SetCursor message has been sent to a grid or kid.

  The following Graphics Designer functions can be used with cursors:

    XgrRegisterCursor     (cursorName$, @cursor)
    XgrCursorNameToNumber (cursorName$, @cursor)
    XgrCursorNumberToName (cursor, @cursorName$)
    XgrGetCursor          (@cursor)
    XgrSetCursor          (cursor, @oldCursor)
    XgrGetCursorOverride  (overrideCursor)
    XgrSetCursorOverride  (overrideCursor, @oldOverrideCursor)

GUIDesigner programs can use the following messages:

    #GetCursor
    #SetCursor

Mouse messages that return the current position of the mouse cursor:

  #MouseDown, #MouseDrag, #MouseEnter, #MouseExit, #MouseMove, #MouseUp, #MouseWheel

Messages that deal with the text cursor, and are not relevant to the mouse cursor:

  #CursorH, #CursorV, #GetTextCursor, #HideTextCursor, #SetTextCursor, #ShowTextCursor, #GetCursorXY, #SetCursorXY

3. BITMAP

XBasic provides no built-in support for loading bitmap resources, so direct Windows API calls are required. Assuming the bitmap is included in the RC file with the line

    image BITMAP image.bmp

the code below illustrates how this bitmap can be loaded and converted to a form directly usable by XBasic image-processing functions. As mentioned above, a few changes must first be made to kernel32.dec, and kernel32 must be IMPORTed.

    UBYTE image[]

    $RT_BITMAP = 2 'Windows constant signifying a BITMAP resource type

  'get the handle of the running EXE
    hInstance = GetModuleHandleA (0)

  'find the bitmap within the EXE module
    hResInfo = FindResourceA (hInstance, &"image", $RT_BITMAP)

  'load the resource
    hResData = LoadResource (hInstance, hResInfo)

  'get a pointer to the resource data
    lpResData = LockResource (hResData)

  'allocate an array of sufficient size for the image. The BITMAP
  'resource does not include the 14-byte BITMAPFILEHEADER that is
  'present at the beginning of every BMP-format file, so space
  'for this header must be included.
    resSize = SizeofResource (hInstance, hResInfo)
    DIM image[resSize + 14 - 1]

  'fill the BITMAPFILEHEADER in the first 14 bytes of image[].
  'The following is valid only for a 24-bits-per-pixel image.
    image[0] = 'B': image[1] = 'M' 'bytes identify a BMP-format image
    XLONGAT(&image[2]) = UBOUND(image[]) + 1 'file size
    USHORTAT(&image[10]) = 54 'offset to beginning of bitmap data

  'copy BITMAPINFOHEADER and bitmap data from the resource into image[]
    XstCopyMemory (lpResData, &image[14], resSize)


4. User-defined resources.

User-defined resources are stored as raw bytes copied directly from the data file into the EXE or DLL. Any data whatsoever can be included as a resource, so there can be no general method for recovering and using the resource. Commonly, however, what needs to be done is that memory needs to be allocated, the resource needs to be loaded into the allocated memory, and then further processing may be necessary to prepare the data for use.

As an example, consider an array of SLONG data stored as a user-defined resource defined in the RC file with the line

    data SLARRAY arraydata.dat

Any name can be given to the resource type (what I have called SLARRAY here), as long as it is not the name of a standard resource type. This array resource could be loaded as follows:

    SLONG array[]

  'get the handle of the running EXE
    hInstance = GetModuleHandleA (0)

  'find the resource within the module
    hResInfo = FindResourceA (hInstance, &"data", &"SLARRAY")

  'load the resource
    hResData = LoadResource (hInstance, hResInfo)

  'get a pointer to the resource data
    lpResData = LockResource (hResData)

  'copy resource data into the array[]
    resSize = SizeofResource (hInstance, hResInfo) 'number of bytes
    XstGetTypedArray ($$SLONG, resSize, @array[]) 'dimension the SLONG array
    XstCopyMemory (lpResData, &array[], resSize) 'copy data from resource to array

In this example, no further processing of the loaded data is necessary. As a second example, consider a file containing a collection of strings written using XstSaveStringArray(), which saves strings to a file using "\n" characters to mark the end of each string. This could be included as a resource using a line like

    strdata STRARRAY stringfile.dat

in the RC file. The following code could be used to load and use the strings:

  'get the handle of the running EXE
    hInstance = GetModuleHandleA (0)

  'find the resource within the module
    hResInfo = FindResourceA (hInstance, &"data", &"SLARRAY")

  'load the resource
    hResData = LoadResource (hInstance, hResInfo)

  'get a pointer to the resource data
    lpResData = LockResource (hResData)

  'copy resource data into a string
    resSize = SizeofResource (hInstance, hResInfo) 'number of bytes
    strdata$ = NULL$(resSize) 'allocate a string of sufficient size
    XstCopyMemory (lpResData, &strdata$, resSize) 'copy data from resource to string

  'parse out the individual strings into an array
    XstStringToStringArray (strdata$, @strarray$[])

Finally, consider a JPEG image stored as a resource.

    jpgimage JPEG image.jpg

"JPEG" is the user-defined resource type. This image can be loaded as a UBYTE array, which can then be converted from JPEG format to BMP format for use by an XBasic program. Since XBasic has no built-in JPEG conversion functions, a third-party DLL will be necessary for this to work.

    UBYTE jpeg[], image[]

  'get the handle of the running EXE
    hInstance = GetModuleHandleA (0)

  'find the resource within the module
    hResInfo = FindResourceA (hInstance, &"image", &"JPEG")

  'load the resource
    hResData = LoadResource (hInstance, hResInfo)

  'get a pointer to the resource data
    lpResData = LockResource (hResData)

  'copy resource data into the jpeg[] array
    resSize = SizeofResource (hInstance, hResInfo) 'number of bytes
    DIM jpeg[resSize-1] 'dimension the jpeg array
    XstCopyMemory (lpResData, &jpeg[], resSize) 'copy data from resource

  'further processing is necessary to convert to jpeg[] data to a BMP format image[]

C. Using the resources in a DLL.

Resources can be placed in a DLL and accessed almost as easily as in the EXE, with the exception of ICON and CURSOR resources. These are best placed in the EXE, because the built-in XBasic functions for dealing with them will not work with icons and cursors in a DLL. It is possible to use Windows API functions to retrieve and manipulate such resources in a DLL, but it is probably not worth the trouble.

All other resources can be accessed from a DLL by a simple modification of the procedures outlined for EXEs. All that is necessary is to load the DLL, and free it when done.

As an example, consider the array of SLONG data discussed in the previous section.

    data SLARRAY arraydata.dat

This array resource could be loaded from a DLL as follows:

    SLONG array[]

  'load the DLL and get a handle. If the library has already been
  'loaded, this just returns a handle for it.
    hInstance = LoadLibraryA (&dllname$)

  'the following steps are identical to the EXE example:

  'find the resource within the module
    hResInfo = FindResourceA (hInstance, &"data", &"SLARRAY")

  'load the resource
    hResData = LoadResource (hInstance, hResInfo)

  'get a pointer to the resource data
    lpResData = LockResource (hResData)

  'copy resource data into the array[]
    resSize = SizeofResource (hInstance, hResInfo) 'number of bytes
    XstGetTypedArray ($$SLONG, resSize, @array[]) 'dimension the SLONG array
    XstCopyMemory (lpResData, &array[], resSize) 'copy data from resource to array

  'free the library handle. If this is the only open handle, then
  'the library will be unloaded.
    FreeLibrary (hInstance)

D. Using resources when running in the PDE.

A drawback to the use of resources, as opposed to separate data files, is that during program development in the XBasic PDE, the EXE to which the resources will be added does not yet exist. It is possible to add the resources to xb.exe, either by recompiling the XBasic source code or by using a program such as Resource Hacker, but this makes sense only for resources that will be useful in a wide variety of programs.

A better approach is to put the required resources into a DLL, which the program in the PDE can load in order to access them. This DLL, which contains only resources and no executable functions, can be created by a MAK file like the following:

  RCNAME = progname
  RC = $(RCNAME).rc
  RES = $(RCNAME).res
  RBJ = $(RCNAME).rbj
  RSM = $(RCNAME).rsm

  $(RSM): $(RBJ)
    link -dll -out:$(RSM) $(RBJ) msvcrt.lib

  $(RBJ): $(RC)
    rc -i $(XBDIR)\images -r $(RC)
    cvtres -i386 $(RES) -o $(RBJ)

progname is the name of the program's source code, without the .x extension. Given an RC file named progname.rc, this MAK file will create a 'resource module' (RSM file, actually a DLL with no functions) that contains the required resources. In the program's source code, a few lines will determine whether to load resources from this RSM (if the program is running in the PDE), or from the standalone EXE:

    module$ = "progname.rsm"
    XstGetApplicationEnvironment (@standalone, 0)
    IF standalone THEN
        hInstance = GetModuleHandleA (0)
      ELSE
        hInstance = LoadLibraryA(&module$)
    END IF

  'load resource as described in previous examples
    ...
    ...
  'free the instance handle.
    IFZ standalone THEN FreeLibrary (hInstance)

With the RSM and code like this, the program will run almost identically whether in the PDE or as an EXE, except that the EXE will not require the RSM to run. The only differences are with icon and cursor resources, which as noted previously cannot be accessed by the built-in XBasic functions from a source outside the EXE.


Home