Enumerating Resources Included in
Visual DataFlex 8.2 and higher Applications

A Data Access Worldwide White Paper
by Vincent Oorsprong

July 2002
Last Edited: June 24, 2003

Overview
The Visual DataFlex 8 and higher product generates .EXE programs. Starting with version 8.2 it is possible to include image files (bitmaps and icons) and version resources in the compiled program. Having images linked into the compiled program has the following advantages:

There are times when you need to find out if an image was included in the program as a resource. When using an image, the Visual DataFlex virtual machine will automatically load the included image resource if available. In case it is not included, the DataFlex search path will be used to provide the image (only if it is found, of course). When it switches to the external source and the search for an image file fails, the image will - in most cases - only not appear and cause no errors. However, if you don't know the name of the image file, you cannot try to load it to resolve the problem. This means that you cannot make a list of all included image files. This paper will show you how one can find out from within the program if the image file was included or not.

Methods to find out if the image was included

  1. Try to load the image
  2. Use enumeration functions

Try to load the image
This method is used by, for example, the cImageList class. If you want to load an image with AddImage or AddTransparentImage, the system will first try to load the image from the executable program file and, if it can't load it, it will try the standard Visual DataFlex search path.

Let us look at some lines of code from cImageList.pkg:

   ...
   Move (LoadImage(GetModuleHandle(0), sImage, IMAGE_BITMAP, 0, 0, 0)) To hBitmap
   If (hBitmap =0) Begin // the bitmap was not in the EXE resource
      Get_File_Path sImage To sImage // find path in DFPATH, if appropriate
      If (sImage <>"") Begin // The image was found!
         Move (LoadImage(0, sImage, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE))To hBitmap
      End
   End
   ...


This code is part of the function AddTransparentImage and first tries to load the image from the current compiled program file. This is done by passing the module handle of the current module (read program) to the LoadImage Windows API function. If the load was successful, the handle of the image (=hBitmap) will be not equal to zero and the AddTransparentImage function uses this image handle to work with.

You can use this same method to test if a certain (known by name) bitmap was included or not. Such a function could be:

Function IsBitmapIncluded String sBitmapName Returns Boolean
   Boolean bIncluded
   Handle hBitmap
   Integer iVoid

   Move (LoadImage (GetModuleHandle (0), sBitmapName, IMAGE_BITMAP, 0, 0, 0)) To hBitmap
   If (hBitmap <> 0) Begin
      Move True To bIncluded
      Move (DeleteObject (hBitmap)) To iVoid
   End

   Function_Return bIncluded
End_Function // IsBitmapIncluded


Figure 1: IsBitmapIncluded function used in a view

If you want to test the above function and compile the same view as shown in Figure 1, download the attached source of the view, register it as an external component in the IDE in a workspace of your choice, and click the Test Compile/Run button. The source code was created in Visual DataFlex 8.2 and can be used from that version and up.

^top

Use enumeration functions
The Windows API has some functions which we can use to enumerate the resources included in a module. The functions are:

  1. EnumResourceTypes
  2. EnumResourceNames
Note: If above (detailed) links do not work, please find the topics yourselves at msdn.microsoft.com

To understand what the above functions might return, you need to know that each resource is classified as a resource type. The following resource types are defined and might be included:


  • RT_CURSOR
  • RT_BITMAP (*)
  • RT_ICON (*)
  • RT_MENU
  • RT_DIALOG
  • RT_STRING
  • RT_FONTDIR
  • RT_FONT
  • RT_ACCELERATOR
  • RT_RCDATA
  • RT_MESSAGETABLE
  • RT_GROUP_CURSOR
  • RT_GROUP_ICON (*)
  • RT_VERSION (*)
  • RT_DLGINCLUDE
  • RT_PLUGPLAY
  • RT_VXD
  • RT_ANICURSOR
  • RT_ANIICON
  • RT_HTML
(*): Note that Visual DataFlex currently only support RT_ICON, RT_GROUP_ICON, RT_BITMAP and RT_VERSION.

With the function EnumResourceTypes, you can find out which of the above resource types are included in your compiled program file. With the function EnumResourceNames, you can find out what names are stored for a given resource type. We can use this function to build a list of included images.

Both WinAPI functions use a callback function, which is called for each resource type or resource name. It is not possible to use a Visual DataFlex function as callback function. So, to implement the enumeration, we need to build a special Dynamic Link Library (DLL) file. You can download the attached compiled DLL and/or download the Visual "C" source code of the DLL and build it yourself. In the DLL, both WinAPI functions are implemented and the result of the enumeration is sent to a DataFlex object via the callback function. The DLL exports the following three functions:
  1. RegisterVDFEnumResourcesWindow
  2. EnumResTypeProc
  3. EnumResNameProc

The callback functions in our own DLL communicate with your Visual DataFlex program by sending newly defined Windows messages to a particular window handle.

To be able to let this all work, a new class, cEnumRscInfo, has been created. When an object of this class is activated (paged), the window handle assigned by the virtual machine is passed to the DLL using the RegisterVDFEnumResourcesWindow function. Only one object of this class is supported per program, but we never need more than one to enumerate our resources anyway.

The class has the following methods defined:

Method nameTypePublicDescription
Construct_ObjectPNoRegisters the Windows messages WM_ENUMRESOURCENAMES and WM_ENUMRESOURCETYPES. These are supposed to be new messages and the equal to the names registered by the new DLL. These messages will be used to communicate. The message identifiers are connected to the methods OnResourceTypesCallBack and OnResourceNamesCallBack.
The class creates two child objects of the Array class to store the enumerated types and names. Their object IDs are stored in the properties phoResourceTypes and phoResourceNames.
OnResourceTypesCallBackPNoWill be called each time a resource type is found by the enumeration functions of the DLL and passed to the Visual DataFlex object of this class. Methods of this type (external_functions) are defined with two parameters, but only the first one is used in this case. The first parameter contains a pointer to a structure with information about the enumerated type. From this structure, only a pointer to the enumerated type will be used. While the other structure members could be used to do some checking, they are not used by the OnResourceTypesCallBack method.
OnResourceNamesCallBackPNoWill be called each time a resource name of a given type was found by the enumeration functions in the DLL and passed to the Visual DataFlex object of this class. Methods of this type (external_functions) are defined with two parameters but only the first one is used in this case. The first parameter contains a pointer to a structure with information about the enumerated name. From this structure only a pointer to the enumerated name will be used. While the other structure members could be used to do some checking, they are not used by the OnResourceNamesCallBack method.
DoAddResourceTypePNoThis method will be used by the OnResourceTypesCallBack method and stores the found type in the array child object that was created to store the resource types. The items of the object whose ID is stored in phoResourceTypes should be used to find out if the desired resource type was included or not.
DoAddResourceNamePNoThis method will be used by the OnResourceNamesCallBack method and stores the found name in the array child object that was created to store the resource names. The items of the object whose ID is stored in phoResourceNames should be used to find out if the desired resource name was included or not.
PagePNoPasses the window handle to the DLL. If the object is never PAGEd, the DLL does not have a window handle to communicate with and the request to enumerate the resource types and names will not work.
DoEnumResourceTypesPYesThis method will load the EnumResourceInformation.dll to get a handle to DLL, which it needs for futher instructions. If the handle returned is zero, the enumeration request will not work. Inside the DLL the method with the name EnumResTypeProc is searched and this should be found, else the enumeration will not work. The next step is to delete all previous enumerated resource types from the internal array object. Finally, Windows will be instructed by sending the method EnumResourceTypes to start the enumeration. When the enumeration is ready, the method stops and other DataFlex functions/operations can take place.
DoEnumResourceNamesPYesThis method will load the EnumResourceInformation.dll to get a handle to DLL, which it needs for futher instructions. If the handle returned is zero, the enumeration request will not work. Inside the DLL the method with the name EnumResNameProc is searched and this should be found, else the enumeration will not work. The next step is to delete all previous enumerated resource names from the internal array object. Finally, Windows will be instructed by sending the method EnumResourceNames to start the enumeration. When the enumeration is ready, the method stops and other DataFlex functions/operations can take place.
The method takes one required parameter which should contain the type (one of the predefined RT_xxx ones) to be enumerated. Use only resource types that are present.
CopyPointerToDfStringFNoCopies pointer to a DataFlex string variable.

After implementation of an object of this class the result may look like:


Figure 2: Results of RT_BITMAP resource name enumeration in a view

The above view (source code) works as follows:

  1. The OnClick event of the Enumerate Resource Types button sends the message DoEnumResourceTypes of an object of the cEnumRscInfo class.
  2. This method fills the array object, whose ID is stored in the property phoResourceTypes, with enumerated information. The contents of this array will be listed in the left list object.
  3. When the user clicks on one of the enumerated types, the message DoEnumResourceNames is sent of the earlier used object of the cEnumRscInfo class.
  4. This method fills the array object whose ID is stored in the property phoResourceNames with enumerated information. The contents of this array will be listed in the list object on the right side of the view.

The enumerated names are the same names as we specified in the .CFG file of the program, which is used by the Visual DataFlex linker. This may or may not be the same name as the original file on disk. In fact, Figure 2 shows one name with the .BMP extension and one without. The name without was assigned a different name via the .CFG file than its original disk variant. The original name has been removed.

When looking at icon resources, we see that icons are listed as RT_ICON and also as RT_GROUP_ICON.


Figure 3: Results of RT_ICON resource name enumeration in a view

The enumerated names of RT_ICON are the internal ID's used by the runtime and assigned by the compiler,


Figure 4: Results of RT_GROUP_ICON resource name enumeration in a view

The enumerated names of RT_GROUP_ICON are the names used in the .CFG file and, like bitmaps, the original name is gone. The icon with the number "0" is special, because it is your program icon. If you look in the .CFG file, you should see that the application icon cannot be given a name. The compiler stores it in your executable program as an icon with the name "0" and the virtual machine looks for this specific name.

Note: As you can see, figures 2, 3 and 4 show all four resource types that are currently supported in our Visual DataFlex programs. Retrieving information about the version resource works differently and should be retrieved via the cVersionInfo class supplied with Visual DataFlex 8.2.


^top

Data Access Worldwide
14000 SW 119 Ave
Miami, FL 33186
305-238-0012
Domestic Sales: 800-451-3539
Fax: 305-238-0017
email: sales@dataaccess.com
Newsgroup Server: news.dataaccess.com
Internet: http://www.dataaccess.com

Data Access Worldwide - Asia Pacific
Suite 5, 333 Wantirna Road, Wantirna VIC 3152 Australia
Phone: +61 3 9800 4233 f: +61 3 9800 4255
Sales: asiapacific@DataAccess.com
Support: support.asiapacific@DataAccess.com
Internet: http://www.DataAccess.com/AsiaPacific

Data Access Worldwide - Brasil
Av.Paulista, 1776 - 21st.Floor
São Paulo -SP - Brazil
CEP 01310-921
Phone: 5511-3262-2000
Fax 5511-3284-1579
Sales: info@dataaccess.com.br
Support: suporte@dataaccess.com.br
Internet: http://www.dataaccess.com.br

Data Access Worldwide - Europe
Lansinkesweg 4
7553 AE Hengelo
The Netherlands
Telephone: +31 (0)74 - 255 56 09
Fax: +31 (0)74 - 250 34 66
Sales: info@dataaccess.nl
Support: support@dataaccess.nl
Internet: http://www.dataaccess.nl

Data Access Technical Support
800-451-3539 / 305-232-3142
email: support@dataaccess.com
Visit our Support Home page to see all of our Support options: http://www.dataaccess.com/support

Copyright Notice
This document is property of Data Access Corporation. With credit to Data Access Corporation for its authorship, you are encouraged to reproduce this information in any format either on paper or electronically, in whole or in part. You may publish this paper as a stand alone document within your own publications conditional on the maintenance of the intent, context, and integrity of the material as it is presented here.

DataFlex is a registered trademark of Data Access Corporation.
Windows
is a registered trademark of Microsoft Corporation.

NO LIABILITY FOR CONSEQUENTIAL DAMAGES
To the maximum extent permitted by applicable law, in no event shall Data Access Corporation be liable for any special, incidental, indirect, or consequential damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or any other pecuniary loss) arising out of the use of or inability to use any information provided in this document, even if Data Access Corporation has been advised of the possibility of such damages. Because some states and jurisdictions do not allow the exclusion or limitation of liability for consequential or incidental damages, the above limitation may not apply to you.

^ top