[ Home | Chapter 1 | Chapter 2 | Chapter 3 | Chapter 4 | Chapter 5 ]

David Joffe's Guide to Programming Games with DirectX

Chapter 3: A simple DirectDraw sample

3.1 Setting up DirectX under Visual C/C++

I most likely won't be doing DirectX development under Watcom or Borland C/C++ or Delphi or VisualBASIC etc; so if you want such info included, please send as much info as you can on generally getting DirectX programs to compile in these environments + troubleshooting etc.

Firstly, the directories must be set up so that Visual C/C++ can find the DirectX include files and libraries.

Access the Tools/Options/Directories tabbed dialog.

Select "library directories" from the drop-down list, and add the directory of the DX SDK libraries, e.g. "d:\dxsdk\sdk\lib"

Select "include directories" from the drop-down list, and add the directory of the DX SDK header files, e.g. "d:\dxsdk\sdk\inc".

If you are going to be using some of the DX utility headers used in the samples, then also add the samples\misc directory, e.g. "d:\dxsdk\sdk\samples\misc".

Note: In Visual C/C++ 4.2, the header file directory for the DirectX SDK must be located *before* the default VC++ development include directories, so that the compiler searches the DXSDK directories first. As far as I can tell, the reason for this is that with VC++ 4.2 some of the DirectX was included, but those header files seem to be out of date or something because they don't work.

To use DirectDraw you must tell it which library file the DirectDraw routines are in. Ditto for Direct3D. Go to Build/Settings/Link and in the Object/Library modules box add ddraw.lib to use DirectDraw, and d3drm.lib if you want to use Direct3D Retained mode. Seperate these with spaces.

3.2 The DirectDraw sample

Firstly, heres a screenshot of the small simple sample application we're putting together here.

[Screenshot 1]

The general routine for starting a DirectDraw application is the following:

  1. Set up our global variables
  2. Initialize a DirectDraw object
  3. Set the "cooperative level" and display modes as necessary (explained later)
  4. Create front and back flipping surfaces, and a clipper if necessary
  5. Attach the clipper if you're in windowed mode
  6. Perform flipping. If in full-screen mode, just flip. If in windowed mode, you need to blit from the back surface to the primary surface each frame.

3.3 Setting up global variables: Globals.h and Globals.cpp

We are going to need a number of global variables for our DirectDraw application. Firstly, we need a variable for accessing the DirectDraw object. Also, we'll need a boolean variable to track whether or not we are in full-screen mode, called bFullScreen. Then we need two DirectDrawSurface variables for the primary surface and the back surface which form our double-buffered surface-flipping structure. For windowed mode, we'll need a clipper object to attach to the primary surface to prevent DirectDraw from drawing outside of the window's edges. If using a 256 color mode, we'll probably want a structure to store the palette we are going to use. Then, we need an HWND object to let the clipper know which window's boundaries it should clip to. Finally, we'll need a boolean variable to start and stop animation of the DirectDraw application, once it is initialized. Create a global variable for purposes of accessing the DirectDraw object, and a boolean variable to track whether or not we are in full-screen mode.

These global variables, along with the global functions, I place in a seperate file called Globals.cpp which I add to my project in Visual C/C++. They could all have been members of a class, but I have chosen to make them global for easier access across all modules, to conceptually seperate the DX stuff from the rest of the program code, and to make it simpler to use these functions in non-MFC applications.

LPDIRECTDRAW pDD;                // DirectDraw object
LPDIRECTDRAWSURFACE pDDSPrimary; // DirectDraw primary surface
LPDIRECTDRAWSURFACE pDDSBack;    // DirectDraw back surface
LPDIRECTDRAWPALETTE pDDPal;      // Palette (coming sometime soonish)
LPDIRECTDRAWCLIPPER pClipper;    // Clipper for windowed mode
HWND ddWnd;                      // Handle of window
BOOL bFullScreen;                // are we in fullscreen mode?
BOOL bAnimating;                 // are we animating?

I also make a function to initialize all of these variables:

void InitDirectXGlobals()
{
    pDD = NULL;
    pDDSPrimary = NULL;
    pDDSBack = NULL;
    pDDPal = NULL;
    pClipper = NULL;
    bFullScreen = FALSE;
    bAnimating = FALSE;
}

Here is the general layout of my Globals.h and Globals.cpp files:

Globals.h

#ifndef _GLOBALS_H_
#define _GLOBALS_H_

include <d3drm.h>

extern LPDIRECTDRAW pDD;
...

extern void InitDirectXGlobals();
...

#endif

Globals.cpp

#include "stdafx.h"

#include <ddraw.h>
#include <d3drm.h>

#include "Globals.h"

LPDIRECTDRAW pDD;
...

void InitDirectXGlobals()
{
	pDD = NULL;
...
}

3.4 Initializing the DirectDraw system

Now we need a routine to initialize the directdraw system. The DirectDraw system must be initialized every time you switch between full-screen and windowed mode (I think :) - gimme a bit of time). The DirectDrawCreate function call is used to create a DirectDraw object.

BOOL InitDDraw()
{
    HRESULT hr;

    // Create the DirectDraw object
    // The first NULL means use the active display driver
    // The last parameter must be NULL
    hr = DirectDrawCreate(NULL, &pDD, NULL);
	if (FAILED(hr)) {
        TRACE("Unable to create DDraw object\n");
        return FALSE;
    }

    // The DirectDraw object initialized successfully
    return TRUE;
}

3.5 Setting the screen mode

The function SetCooperativeLevel is used to tell the system whether or not we want to use full-screen mode or windowed. In full-screen mode, we have to get exclusive access to the DirectDraw device, and then set the display mode. For windowed mode, we set the cooperative level to normal.

BOOL SetMode() {
    HRESULT hr;
    if (bFullScreen) {
        // Set the "cooperative level" so we can use full-screen mode
        hr = pDD->SetCooperativeLevel(AfxGetMainWnd()->GetSafeHwnd(),
            DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_NOWINDOWCHANGES);
        if (FAILED(hr)) {
            pDD->Release();
            return FALSE;
        }
        // Set 640x480x256 full-screen mode
        hr = pDD->SetDisplayMode(640, 480, 8);
        if (FAILED(hr)) {
            TRACE("Error setting display mode: %d\n", int(LOWORD(hr)));
            pDD->Release();
            return FALSE;
        }
    } else {
        // Set DDSCL_NORMAL to use windowed mode
        hr = pDD->SetCooperativeLevel(AfxGetMainWnd()->GetSafeHwnd(),
            DDSCL_NORMAL);
    }

    // Success
    return TRUE;
}

3.6 Creating surfaces

OK ... now that we've got that bit of initialization out of the way, we need to create a flipping structure. No, I'm not cursing the structure .. "flipping" as in screen page-flipping :).

Anyway, we need to create one main surface that everyone will see, and a "back" surface. All drawing is done to the back surface. When we are finished drawing we need to make what we've drawn visible. In full-screen mode, we just need to call a routine called Flip, which will turn the current back surface into the primary surface and vice versa. In windowed mode, we don't actually flip the surfaces - we copy the contents of the back buffer onto the primary buffer, which is what's inside the window. In other words, we "blit" the back surface onto the primary surface.

Anyway, here is the bit of code to create the surfaces. Right now the code is ignoring full-screen mode and only catering for windowed mode, but that'll change. Also, if there are errors in this code, consider them "exercises" ... :). Naah, just kidding. If there are errors, mail me.

UINT CreatePrimarySurface()
{
    DDSURFACEDESC ddsd; // A structure to describe the surface we want
    HRESULT hr;         // Holds return values for function calls

    // Screw the full-screen mode (for now)

    // This clears all fields of the DDSURFACEDESC structure to 0
    memset(&ddsd, 0, sizeof(ddsd));
    // The first parameter of the structure must contain the
    // size of the structure.
    ddsd.dwSize = sizeof(ddsd);

    // The dwFlags paramater tell DirectDraw which DDSURFACEDESC
    // fields will contain valid values
    ddsd.dwFlags = DDSD_CAPS;
    ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;

    // Create the primary surface
    hr = pDD->CreateSurface(&ddsd, &pDDSPrimary, NULL);
    if (FAILED(hr))
    {
        TRACE("Error: pDD->CreateSurface (primary)\n");
        TraceErrorDD(hr);
        pDD->Release();
        pDD = NULL;
        return 1;
    }

    // Now to create the back buffer
    ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS;
    // Make our off-screen surface 320x240
    ddsd.dwWidth = 320;
    ddsd.dwHeight = 240;

    // Ignore this: I'm trying stuff still
    /*memset(&ddsd.ddpfPixelFormat, 0, sizeof(ddsd.ddpfPixelFormat));
	ddsd.ddpfPixelFormat.dwSize = sizeof(ddsd.ddpfPixelFormat);
	ddsd.ddpfPixelFormat.dwFlags = DDPF_PALETTEINDEXED8;
//	ddsd.ddpfPixelFormat.dwRGBBitCount = 8;*/

    // Create an offscreen surface
    ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;

    hr = pDD->CreateSurface(&ddsd, &pDDSBack, NULL);
    if (FAILED(hr)) {
        TRACE("Error: CreateSurface (back)\n");
        TraceErrorDD(hr);
        return 2;
    }

    // success
    return 0;
}

3.7 Creating the Clipper

Now that we've created the surfaces, we need to create a clipper (if we're running in windowed mode), and attach the clipper to the primary surface. This prevents DirectDraw from drawing outside the windows client area.

UINT CreateClipper()
{
    HRESULT hr;

    // Create the clipper using the DirectDraw object
    hr = pDD->CreateClipper(0, &pClipper, NULL);
    if (FAILED(hr)) {
        TRACE("Error: CreateClipper\n");
        TraceErrorDD(hr);
        return 1;
    }

    // Assign your window's HWND to the clipper
    ddWnd = AfxGetMainWnd()->GetSafeHwnd();
    hr = pClipper->SetHWnd(0, ddWnd);
    if (FAILED(hr)) {
        TRACE("Error: SetHWnd\n");
        TraceErrorDD(hr);
        return 2;
    }

    // Attach the clipper to the primary surface
    hr = pDDSPrimary->SetClipper(pClipper);
    if (FAILED(hr)) {
        TRACE("Error: SetClipper\n");
        TraceErrorDD(hr);
        return 3;
    }
    // success
    return 0;
}

3.8 Putting it all together

Now that we have all these initialization routines, we need to actually call them, so the question is, where to call them? In an MFC application, a logical place to do this is in the application's InitInstance routine.

BOOL CDirectDrawApp::InitInstance()
{
#ifdef _AFXDLL
    Enable3dControls();
#else
    Enable3dControlsStatic();
#endif

    LoadStdProfileSettings(0);

    CSingleDocTemplate* pDocTemplate;
    pDocTemplate = new CSingleDocTemplate(
        IDR_MAINFRAME,
        RUNTIME_CLASS(CDirectDrawDoc),
        RUNTIME_CLASS(CMainFrame),
        RUNTIME_CLASS(CDirectDrawView));
    AddDocTemplate(pDocTemplate);

    CCommandLineInfo cmdInfo;
    ParseCommandLine(cmdInfo);

    if (!ProcessShellCommand(cmdInfo))
        return FALSE;

    InitDirectXGlobals();
    InitDDraw();
    SetMode();
// These will be coming soonish
//    TRACE("Calling LoadJascPalette\n");
//    LoadJascPalette("inspect.pal", 10, 240);
    TRACE("Calling CreatePrimarySurface\n");
    CreatePrimarySurface();
    TRACE("Calling CreateClipper\n");
    CreateClipper();
// Also coming soonish
//    TRACE("Calling AttachPalette\n");
//    AttachPalette(pDDPal);

    // Start the animation
    bAnimating = TRUE;

    return TRUE;
}

3.9 Restoring lost surfaces

As if all this initialization wasn't enough, we also have to make sure our DirectDraw surfaces are not getting "lost". The memory associated with DirectDraw surfaces can be released under certain circumstances, because it has to share resources with the Windows GDI. So each time we render, we first have to check if our surfaces have been lost and Restore them if they have. This is accomplished with the IsLost function.

// Checks if the memory associated with surfaces
// is lost and restores if necessary.
// Not sure about fullscreen/windowed;
// I'll get back to you people on this :)
BOOL CheckSurfaces()
{
    // Check the primary surface
    if (pDDSPrimary) {
        if (pDDSPrimary->IsLost() == DDERR_SURFACELOST) {
            pDDSPrimary->Restore();
            return FALSE;
        }
    }
    return TRUE;
}

3.10 The rendering loop

Now that we've got most of the general initialization out of the way, we need to set up a rendering loop. This is basically the main loop of the game, the so-called HeartBeat function. So we're going to call it just that.

The HeartBeat function gets called during your applications idle-time processing; so we need to override the application's OnIdle function. Use ClassWizard or the toolbar wizard thingy to create a handler for idle-time processing for your main application class. Here I am assuming you know a bit about MFC. Sorry about that; that'll probably change though with time as my knowledge improves and as I have more time to work on this.

Note that your applications OnIdle handler gets called even when your program does not have the current focus, and we'd rather not have your application do anything unless it has the current focus. Trust me on this one :). This is what we use bAnimating for; we set it to FALSE when we receive the WM_ACTIVATE(APP?) message with a parameter of FALSE. We set this variable to TRUE just after we've set up DirectDraw and the surfaces etc., and we are ready to begin.

BOOL CDirectDrawApp::OnIdle(LONG lCount) 
{
    CWinApp::OnIdle(lCount); // Call the previous default handler

    if (bAnimating) {
        // Our game's heartbeat function
        HeartBeat();
        // prevent's too much flicker. We'll need a smarter system
        // soon, this one is temporary
        Sleep(50);
    }
    // request more idle-time, so that we can render the next loop!
    return TRUE;
}

3.11 The HeartBeat function

Now let's look at the heartbeat function. At the moment mine just draws a silly block in the top left of the window that changes color each frame.

BOOL CDirectDrawApp::HeartBeat()
{
    // Variables for the blocks color, in RGB format
    static r = 0;
    static g = 100;
    static b = 150;
    r++;
    g += 3;
    b += 2;
    if (r > 255) r = 0;
    if (g > 255) g = 0;
    if (b > 255) b = 0;

    // The destination rectangle on my 320x240 off-screen surface
    CRect rc(10,30,100,70);

    // We draw the rectangle using a blit.
    // This structure describes how to do the blit.
    DDBLTFX fx;
    // Zero out all fields of the blit effects structure
    memset(&fx, 0, sizeof(fx));
    // The dwSize field must contain the size of the structure
    fx.dwSize = sizeof(DDBLTFX);

    // Set the color we want to draw the rectangle
    fx.dwFillColor = RGB(r,g,b);

    // Blit. Note that we blit to the back buffer
    HRESULT hr;
    hr = pDDSBack->Blt(&rc, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT,
        &fx);
    if (FAILED(hr)) {
        TRACE("Error blitting: ");
        TraceErrorDD(hr);
        return FALSE;
    }

    // Call our routine for flipping the surfaces
    FlipSurfaces();

    // No major errors
    return TRUE;
}
Note that Blt parameter DDBLT_WAIT. If a surface is busy then DirectDraw will return an error, without performing the blit. Passing the DDBLT_WAIT option will instruct DirectDraw to wait until the surface becomes available and then perform the blit.

3.12 Flipping surfaces

Now let's look at the function that performs the surface flipping. This time I *do* take full-screen mode into account ... I'm inconsistent, eh? :-).

UINT FlipSurfaces()
{
    HRESULT hr;
    // source and destination rectangles
    RECT rcSrc;
    RECT rcDest;
    POINT p;

    // Make sure no surfaces have been lost
    CheckSurfaces();

    // if we're windowed do the blit, else just Flip
    if (!bFullScreen)
    {
        // find out where on the primary surface our window lives
        p.x = 0; p.y = 0;
        ::ClientToScreen(ddWnd, &p);
        ::GetClientRect(ddWnd, &rcDest);
        OffsetRect(&rcDest, p.x, p.y);
        SetRect(&rcSrc, 0, 0, 320, 240);
        hr = pDDSPrimary->Blt(&rcDest, pDDSBack, &rcSrc, DDBLT_WAIT,
            NULL);
    } else {
        hr = pDDSPrimary->Flip(NULL, DDFLIP_WAIT);
    }

    // success!
    return 0;
}

3.13 Tracing DirectDraw errors

Here is a pretty useful debugging function for DirectX apps, called TraceErrorDD:

void TraceErrorDD(HRESULT hErr)
{       
    switch (hErr)
    {
	case DDERR_ALREADYINITIALIZED:
		TRACE("DDERR_ALREADYINITIALIZED"); break;
	case DDERR_CANNOTATTACHSURFACE:
		TRACE("DDERR_CANNOTATTACHSURFACE"); break;
	case DDERR_CANNOTDETACHSURFACE:
		TRACE("DDERR_CANNOTDETACHSURFACE"); break;
	case DDERR_CURRENTLYNOTAVAIL:
		TRACE("DDERR_CURRENTLYNOTAVAIL"); break;
	case DDERR_EXCEPTION:
		TRACE("DDERR_EXCEPTION"); break;
	case DDERR_GENERIC:
		TRACE("DDERR_GENERIC"); break;
	case DDERR_HEIGHTALIGN:
		TRACE("DDERR_HEIGHTALIGN"); break;
	case DDERR_INCOMPATIBLEPRIMARY:
		TRACE("DDERR_INCOMPATIBLEPRIMARY"); break;
	case DDERR_INVALIDCAPS:
		TRACE("DDERR_INVALIDCAPS"); break;
	case DDERR_INVALIDCLIPLIST:
		TRACE("DDERR_INVALIDCLIPLIST"); break;
	case DDERR_INVALIDMODE:
		TRACE("DDERR_INVALIDMODE"); break;
	case DDERR_INVALIDOBJECT:
		TRACE("DDERR_INVALIDOBJECT"); break;
	case DDERR_INVALIDPARAMS:
		TRACE("DDERR_INVALIDPARAMS"); break;
	case DDERR_INVALIDPIXELFORMAT:
		TRACE("DDERR_INVALIDPIXELFORMAT"); break;
	case DDERR_INVALIDRECT:
		TRACE("DDERR_INVALIDRECT"); break;
	case DDERR_LOCKEDSURFACES:
		TRACE("DDERR_LOCKEDSURFACES"); break;
	case DDERR_NO3D:
		TRACE("DDERR_NO3D"); break;
	case DDERR_NOALPHAHW:
		TRACE("DDERR_NOALPHAHW"); break;
	case DDERR_NOCLIPLIST:
		TRACE("DDERR_NOCLIPLIST"); break;
	case DDERR_NOCOLORCONVHW:
		TRACE("DDERR_NOCOLORCONVHW"); break;
	case DDERR_NOCOOPERATIVELEVELSET:
		TRACE("DDERR_NOCOOPERATIVELEVELSET"); break;
	case DDERR_NOCOLORKEY:
		TRACE("DDERR_NOCOLORKEY"); break;
	case DDERR_NOCOLORKEYHW:
		TRACE("DDERR_NOCOLORKEYHW"); break;
	case DDERR_NODIRECTDRAWSUPPORT:
		TRACE("DDERR_NODIRECTDRAWSUPPORT"); break;
	case DDERR_NOEXCLUSIVEMODE:
		TRACE("DDERR_NOEXCLUSIVEMODE"); break;
	case DDERR_NOFLIPHW:
		TRACE("DDERR_NOFLIPHW"); break;
	case DDERR_NOGDI:
		TRACE("DDERR_NOGDI"); break;
	case DDERR_NOMIRRORHW:
		TRACE("DDERR_NOMIRRORHW"); break;
	case DDERR_NOTFOUND:
		TRACE("DDERR_NOTFOUND"); break;
	case DDERR_NOOVERLAYHW:
		TRACE("DDERR_NOOVERLAYHW"); break;
	case DDERR_NORASTEROPHW:
		TRACE("DDERR_NORASTEROPHW"); break;
	case DDERR_NOROTATIONHW:
		TRACE("DDERR_NOROTATIONHW"); break;
	case DDERR_NOSTRETCHHW:
		TRACE("DDERR_NOSTRETCHHW"); break;
	case DDERR_NOT4BITCOLOR:
		TRACE("DDERR_NOT4BITCOLOR"); break;
	case DDERR_NOT4BITCOLORINDEX:
		TRACE("DDERR_NOT4BITCOLORINDEX"); break;
	case DDERR_NOT8BITCOLOR:
		TRACE("DDERR_NOT8BITCOLOR"); break;
	case DDERR_NOTEXTUREHW:
		TRACE("DDERR_NOTEXTUREHW"); break;
	case DDERR_NOVSYNCHW:
		TRACE("DDERR_NOVSYNCHW"); break;
	case DDERR_NOZBUFFERHW:
		TRACE("DDERR_NOZBUFFERHW"); break;
	case DDERR_NOZOVERLAYHW:
		TRACE("DDERR_NOZOVERLAYHW"); break;
	case DDERR_OUTOFCAPS:
		TRACE("DDERR_OUTOFCAPS"); break;
	case DDERR_OUTOFMEMORY:
		TRACE("DDERR_OUTOFMEMORY"); break;
	case DDERR_OUTOFVIDEOMEMORY:
		TRACE("DDERR_OUTOFVIDEOMEMORY"); break;
	case DDERR_OVERLAYCANTCLIP:
		TRACE("DDERR_OVERLAYCANTCLIP"); break;
	case DDERR_OVERLAYCOLORKEYONLYONEACTIVE:
		TRACE("DDERR_OVERLAYCOLORKEYONLYONEACTIVE"); break;
	case DDERR_PALETTEBUSY:
		TRACE("DDERR_PALETTEBUSY"); break;
	case DDERR_COLORKEYNOTSET:
		TRACE("DDERR_COLORKEYNOTSET"); break;
	case DDERR_SURFACEALREADYATTACHED:
		TRACE("DDERR_SURFACEALREADYATTACHED"); break;
	case DDERR_SURFACEALREADYDEPENDENT:
		TRACE("DDERR_SURFACEALREADYDEPENDENT"); break;
	case DDERR_SURFACEBUSY:
		TRACE("DDERR_SURFACEBUSY"); break;
	case DDERR_CANTLOCKSURFACE:
		TRACE("DDERR_CANTLOCKSURFACE"); break;
	case DDERR_SURFACEISOBSCURED:
		TRACE("DDERR_SURFACEISOBSCURED"); break;
	case DDERR_SURFACELOST:
		TRACE("DDERR_SURFACELOST"); break;
	case DDERR_SURFACENOTATTACHED:
		TRACE("DDERR_SURFACENOTATTACHED"); break;
	case DDERR_TOOBIGHEIGHT:
		TRACE("DDERR_TOOBIGHEIGHT"); break;
	case DDERR_TOOBIGSIZE:
		TRACE("DDERR_TOOBIGSIZE"); break;
	case DDERR_TOOBIGWIDTH:
		TRACE("DDERR_TOOBIGWIDTH"); break;
	case DDERR_UNSUPPORTED:
		TRACE("DDERR_UNSUPPORTED"); break;
	case DDERR_UNSUPPORTEDFORMAT:
		TRACE("DDERR_UNSUPPORTEDFORMAT"); break;
	case DDERR_UNSUPPORTEDMASK:
		TRACE("DDERR_UNSUPPORTEDMASK"); break;
	case DDERR_VERTICALBLANKINPROGRESS:
		TRACE("DDERR_VERTICALBLANKINPROGRESS"); break;
	case DDERR_WASSTILLDRAWING:
		TRACE("DDERR_WASSTILLDRAWING"); break;
	case DDERR_XALIGN:
		TRACE("DDERR_XALIGN"); break;
	case DDERR_INVALIDDIRECTDRAWGUID:
		TRACE("DDERR_INVALIDDIRECTDRAWGUID"); break;
	case DDERR_DIRECTDRAWALREADYCREATED:
		TRACE("DDERR_DIRECTDRAWALREADYCREATED"); break;
	case DDERR_NODIRECTDRAWHW:
		TRACE("DDERR_NODIRECTDRAWHW"); break;
	case DDERR_PRIMARYSURFACEALREADYEXISTS:
		TRACE("DDERR_PRIMARYSURFACEALREADYEXISTS"); break;
	case DDERR_NOEMULATION:
		TRACE("DDERR_NOEMULATION"); break;
	case DDERR_REGIONTOOSMALL:
		TRACE("DDERR_REGIONTOOSMALL"); break;
	case DDERR_CLIPPERISUSINGHWND:
		TRACE("DDERR_CLIPPERISUSINGHWND"); break;
	case DDERR_NOCLIPPERATTACHED:
		TRACE("DDERR_NOCLIPPERATTACHED"); break;
	case DDERR_NOHWND:
		TRACE("DDERR_NOHWND"); break;
	case DDERR_HWNDSUBCLASSED:
		TRACE("DDERR_HWNDSUBCLASSED"); break;
	case DDERR_HWNDALREADYSET:
		TRACE("DDERR_HWNDALREADYSET"); break;
	case DDERR_NOPALETTEATTACHED:
		TRACE("DDERR_NOPALETTEATTACHED"); break;
	case DDERR_NOPALETTEHW:
		TRACE("DDERR_NOPALETTEHW"); break;
	case DDERR_BLTFASTCANTCLIP:
		TRACE("DDERR_BLTFASTCANTCLIP"); break;
	case DDERR_NOBLTHW:
		TRACE("DDERR_NOBLTHW"); break;
	case DDERR_NODDROPSHW:
		TRACE("DDERR_NODDROPSHW"); break;
	case DDERR_OVERLAYNOTVISIBLE:
		TRACE("DDERR_OVERLAYNOTVISIBLE"); break;
	case DDERR_NOOVERLAYDEST:
		TRACE("DDERR_NOOVERLAYDEST"); break;
	case DDERR_INVALIDPOSITION:
		TRACE("DDERR_INVALIDPOSITION"); break;
	case DDERR_NOTAOVERLAYSURFACE:
		TRACE("DDERR_NOTAOVERLAYSURFACE"); break;
	case DDERR_EXCLUSIVEMODEALREADYSET:
		TRACE("DDERR_EXCLUSIVEMODEALREADYSET"); break;
	case DDERR_NOTFLIPPABLE:
		TRACE("DDERR_NOTFLIPPABLE"); break;
	case DDERR_CANTDUPLICATE:
		TRACE("DDERR_CANTDUPLICATE"); break;
	case DDERR_NOTLOCKED:
		TRACE("DDERR_NOTLOCKED"); break;
	case DDERR_CANTCREATEDC:
		TRACE("DDERR_CANTCREATEDC"); break;
	case DDERR_NODC:
		TRACE("DDERR_NODC"); break;
	case DDERR_WRONGMODE:
		TRACE("DDERR_WRONGMODE"); break;
	case DDERR_IMPLICITLYCREATED:
		TRACE("DDERR_IMPLICITLYCREATED"); break;
	case DDERR_NOTPALETTIZED:
		TRACE("DDERR_NOTPALETTIZED"); break;
	case DDERR_UNSUPPORTEDMODE:
		TRACE("DDERR_UNSUPPORTEDMODE"); break;
	case DDERR_NOMIPMAPHW:
		TRACE("DDERR_NOMIPMAPHW"); break;
	case DDERR_INVALIDSURFACETYPE:
		TRACE("DDERR_INVALIDSURFACETYPE"); break;
	case DDERR_DCALREADYCREATED:
		TRACE("DDERR_DCALREADYCREATED"); break;
	case DDERR_CANTPAGELOCK:
		TRACE("DDERR_CANTPAGELOCK"); break;
	case DDERR_CANTPAGEUNLOCK:
		TRACE("DDERR_CANTPAGEUNLOCK"); break;
	case DDERR_NOTPAGELOCKED:
		TRACE("DDERR_NOTPAGELOCKED"); break;
	case DDERR_NOTINITIALIZED:
		TRACE("DDERR_NOTINITIALIZED"); break;
	default:
		TRACE("Unknown Error"); break;
	}
	TRACE("\n");
}

3.14 Cleaning up

Coming soon.

Article updated: 2 September 1997


Article by David Joffe
http://www.geocities.com/SoHo/Lofts/2018/