The purpose of this article is to explain a method of enumeration in DirectX 7. If you are developing a Direct Draw application, the Driver and Display Mode enumeration areas are relevant. If you are developing a Direct 3D application, all areas are relevant.
A Driver can be thought of as a video card. Enumerating the Drivers is equivalent to determining what video cards are installed. In a system with one video card, enumerating the drivers will reveal only one option, usually named "Primary Display Driver". If you have more than one video card, n + 1 drivers will be enumerated, where n is the number of video cards installed(If you have 2 video cards, 3 drivers will be enumerated). The reason n + 1 Drivers are enumerated is because the Primary Display Driver is always enumerated first. This is the Driver that your start menu will be located on. The Primary Display Driver will then be enumerated a second time, but with the manufacturer's identification made available. Each of the drivers enumerated will be identified by a descriptive name, as well as a GUID(Global Unique Identifier). The GUID is the value you later use when creating the DirectDraw object.
A Display Mode is simply a combination of width, height, depth and other more advanced information. When you enumerate the Display Modes, you will be given a list of Resolutions that you can save for later use.
A Device is a way for Direct 3D to determine how to render primitives. On a Driver that supports Hardware 3D acceleration, you will usually enumerate a minimum of 2 Devices. The first is the Software RGB Rasterizer which DirectX describes as RGB Emulation. The second Device is the Direct3D HAL(Hardware Abstraction Layer), or simply, the 3D accelerated card installed. Each Device also has a GUID that can be saved and later used when Creating a Device.
Each type of enumeration is accomplished by calling an Enumeration function(such as DirectDrawEnumerateEx) and passing the pointer of a callback function. The callback function is called by the Enumeration function once for every object enumerated. For example:
In this example, calling DirectDrawEnumerateEx would invoke DirectX to call EnumDisplayDrivers once for each Driver enumerated.
// Callback Declaration
HRESULT CALLBACK EnumDisplayDrivers( GUID FAR* pGuid,
HMONITOR hMonitor );
// Enumeration Function
DirectDrawEnumerateEx(( LPDDENUMCALLBACKEX )EnumDisplayDrivers,
For this article, I have decided to create some structures to hold the various enumeration information and linked the structures together in a singly linked list. The DisplayDriver structures is as follows:
This structure can be used to save the Driver's GUID and Description. DisplayDriverList is declared as a global variable to access the List for Display Drivers. Since Display Modes and Devices are dependent on the Driver, their Lists are included inside the Driver structure.
D3Device and DisplayMode are defined in a similar way:
The call to enumerate Drivers is DirectDrawEnumerateEx(). The parameters passed are: the pointer to the callback function, the pointer to any variable you wish to pass, and lastly, the Flags. For Example:
EnumDisplayDrivers will be called once for each Driver found. The code for EnumDisplayDrivers follows
// Driver Enumeration Function
DirectDrawEnumerateEx(( LPDDENUMCALLBACKEX )EnumDisplayDrivers, NULL,
In EnumDisplayDrivers, a new DisplayDriver variable is allocated. This structure is filled with the relevant information that was passed to this function. I chose to save the GUID and the Description. The name of a Driver is currently not very descriptive and in my opinion, not nearly as useful as the Description. The D3DDevice and DisplayMode lists are initialized to NULL along with the Next value. Next, the Driver is added to the end of the global DisplayDriverList.
HRESULT CALLBACK EnumDisplayDrivers( GUID FAR* pGuid, LPSTR DriverDescription,
LPSTR DriverName, LPVOID Context,
HMONITOR HMonitor )
DisplayDriver* NewDD = new DisplayDriver;
if( NewDD )
ZeroMemory( NewDD, sizeof( DisplayDriver ));
if( pGuid )
CopyMemory( &( NewDD->Guid ), pGuid, sizeof( GUID ));
NewDD->Description = new char[strlen( DriverDescription ) + 1];
if( NewDD->Description )
strcpy( NewDD->Description, DriverDescription );
NewDD->DisplayModeList = NULL;
NewDD->D3DDeviceList = NULL;
NewDD->Next = NULL;
if( !DisplayDriverList )
DisplayDriverList = NewDD;
for( DisplayDriver* TheDD = DisplayDriverList; TheDD->Next; TheDD = TheDD->Next )
TheDD->Next = NewDD;
To be able to enumerate the Display Modes, a valid DirectDraw object must already be created with DirectDrawCreateEx(). As you should know, the DirectDrawCreateEx() function takes the GUID of a Display Driver as it's first argument. If this GUID value is NULL, the primary Display Driver is assumed to be the Driver being created. Since the Display Mode and Devices are dependent on the Display Driver created, this is done for each Display Driver enumerated.
The call to enumerate Display Modes is EnumDisplayModes(). The parameters passed are: the flags, a DDSURFACEDESC2 value to check against, the pointer to any variable you wish to pass, and lastly, the pointer to the callback function For Example:
for( DisplayDriver* TheDD = DisplayDriverList; TheDD; TheDD = TheDD->Next )
DirectDrawCreateEx( &( TheDD->Guid ), ( void** ) &pDirectDraw, IID_IDirectDraw7, NULL );
pDirectDraw->SetCooperativeLevel( hWnd, DDSCL_NORMAL );
// Enumerate Display Modes and D3D Devices
// Release DirectDraw and Direct3D objects
EnumDisplayModes will be called once for each Display Mode found. Notice that I passed the address of the current DisplayDriver's DisplayModeList variable. The code for EnumDisplayModes follows
// Display Mode Enumeration Function
pDirectDraw->EnumDisplayModes( 0, NULL, &( TheDD->DisplayModeList ),
( LPDDENUMMODESCALLBACK2 )EnumDisplayModes );
In EnumDisplayModes, a variable is declared to refer to the DisplayModeList value passed. A new DisplayMode variable is then allocated. This structure is filled with the relevant information that was passed to this function. This information includes the Width, Height and Depth of the Mode Enumerated. Next, the Mode is added to the end of the DisplayModeList passed.
HRESULT CALLBACK EnumDisplayModes( LPDDSURFACEDESC2 pDDSD, LPVOID Context )
DisplayMode** DMList = ( DisplayMode** )Context;
DisplayMode* NewDM = new DisplayMode;
if( NewDM )
NewDM->Width = pDDSD->dwWidth;
NewDM->Height = pDDSD->dwHeight;
NewDM->Depth = pDDSD->ddpfPixelFormat.dwRGBBitCount;
NewDM->Next = NULL;
if( !( *DMList ))
*DMList = NewDM;
for( DisplayMode* TheDM = *DMList; TheDM->Next; TheDM = TheDM->Next )
TheDM->Next = NewDM;
To be able to enumerate the Devices, a valid Direct3D object must be available. This is achieved by using QueryInterface on the DirectDraw object as shown below:
The call to enumerate Direct3D Devices is EnumDevices(). The parameters passed are: the pointer to any variable you wish to pass, and the pointer to the callback function For Example:
// QueryInterface for the Direct3D Object
pDirectDraw->QueryInterface( IID_IDirect3D7, ( void** ) &pDirect3D );
EnumDevices will be called once for each Device found. Notice that I passed the address of the current DisplayDriver's D3DDeviceList variable. The code for EnumDevices follows
pDirect3D->EnumDevices(( LPD3DENUMDEVICESCALLBACK7 )EnumDevices,
&( TheDD->D3DDeviceList ));
In EnumDevices, a variable is declared to refer to the D3DDeviceList value passed. A new D3DDevice variable is then allocated. This structure is filled with the relevant information that was passed to this function. As opposed to the Display Driver information, the Name is more useful for our purposes. The GUID is saved, as well as a BOOL flag to indicate whether the Device is Hardware or Software. Next, the Device is added to the end of the D3DDeviceList passed.
HRESULT CALLBACK EnumDevices( LPSTR DeviceDescription, LPSTR DeviceName,
LPD3DDEVICEDESC7 pD3DDD, LPVOID Context )
D3DDevice** DevList = ( D3DDevice** )Context;
D3DDevice* NewDev = new D3DDevice;
if( NewDev )
CopyMemory( &( NewDev->Guid ), &( pD3DDD->deviceGUID ), sizeof( GUID ));
NewDev->Name = new char[strlen( DeviceName ) + 1];
if( NewDev->Name )
strcpy( NewDev->Name, DeviceName );
NewDev->IsHW = pD3DDD->dwDevCaps & D3DDEVCAPS_HWRASTERIZATION;
NewDev->Next = NULL;
if( !( *DevList ))
*DevList = NewDev;
for( D3DDevice* TheDev = *DevList; TheDev->Next; TheDev = TheDev->Next )
TheDev->Next = NewDev;
As mentioned above, during enumeration of the various Display Modes and Devices, the DirectDraw and Direct3D objects should be released at the end of the loop.
Releasing all the allocated memory is always a good idea, and can be achieved as follows:
if( DisplayDriverList )
DisplayDriver* TheDD, * NextDD;
for( TheDD = DisplayDriverList; TheDD; TheDD = NextDD )
NextDD = TheDD->Next;
if( TheDD->Description )
if( TheDD->DisplayModeList )
DisplayMode* TheDM, * NextDM;
for( TheDM = TheDD->DisplayModeList; TheDM; TheDM = NextDM )
NextDM = TheDM->Next;
if( TheDD->D3DDeviceList )
D3DDevice* TheDev, * NextDev;
for( TheDev = TheDD->D3DDeviceList; TheDev; TheDev = NextDev )
NextDev = TheDev->Next;
if( TheDev->Name )
DisplayDriverList = NULL;
As you can see, there is more work involved than what I have outlined here. You need a way to either automatically select the enumerated values, or a way to allow your users to select them. Most of the information contained in this article is also in the DirectX 7 Docs. If you need more information, I suggest looking there.
For a full, uninterrupted listing of the code contained in this article, see the attached file.
- [email="firstname.lastname@example.org"]Nathan Davies[/email]
Worlds of Wonder Games
Sept 28, 1999