dx8Diagnostics

Overview

I started out writing a "quick" tool to dump out Direct3D capabilities, for remote debugging purposes. Two days and an endless number of revisions later, I have a library with two subsystems:
dx8Dynamic
Dynamically links to Direct3D 8, DirectSound 8, and DirectInput 8 at runtime instead of at loadtime, allowing you to tell the user they don't have DirectX 8 (instead of the Windows loader refusing to run you at all), and
dx8Diagnostics
Dumps all kinds of configuration information about the user's computer to an HTML file.
dx8Dynamic can be used standalone. However, dx8Diagnostics depends on dx8Dynamic, so it can act intelligently when run on a computer without DirectX 8.

The dx8Diagnostics web page is here:

http://www.midwinter.com/~lch/programming/dx8diagnostics/
And you can download a fresh copy of the source code here:
http://www.midwinter.com/~lch/programming/dx8diagnostics/dx8diagnostics.zip

dx8Dynamic

What It's For

Have you ever gotten one of those support emails that said:
I TRYED TO RUN YUOR GAME BUT IT DOESNT WORKS!!!!!!1
IT SAYS UNABEL TO LOCATE COMPONANT
Or, if you're lucky, the email read:
I attempted to run your game, but it doesn't work on my machine!
It displays the following error message:
	SpaceGoblin.EXE - Unable To Locate Component
	The application has failed to start because D3D8.DLL was not
	found.  Re-installing the application may fix this problem.
and only gives me an "OK" button.
Often, the user gets this message because their computer doesn't have the version of DirectX that your game requires. This is especially true of DirectX 8, as they renamed all the DLLs.

The real problem here is the incomprehensible error message that Windows gives when this happens. If the dialog box instead said something nice, like this:

You don't have DirectX 8 installed! SpaceGoblin requires DirectX 8 or above, and can't run until you install it. Would you like to go to the Microsoft DirectX download site right now?
with lovely Yes and No buttons below, you'd be far less likely to get that annoying support call. But you don't have an opportunity to display a nice dialog box like that, because Windows isn't even running your program. (The error message the user receives is evidence of that.)

Well, now you can display that dialog box, or do anything else you like. dx8Dynamic lets your game start even on machines that don't have DirectX 8 installed. You can detect whether DirectX 8 started up correctly, and if it didn't you can display a nice message and exit cleanly.

How It Works

Whenever Windows runs a program, it goes through the following astonishingly oversimplified process:
  1. Load the .EXE file into memory.
  2. Go down the executable's Import Address Table, and call LoadLibrary() on each DLL listed. If it loads, use GetProcAddress() to find all functions called by the executable.
  3. Jump to the executable's entry point.
(For more on this process, read Matt Pietrek's excellent article An In-Depth Look into the Win32 Portable Executable File Format parts 1 and 2, published in MSDN, February and March 2002.)

The problem occurs in the middle step there, in the statement "if it loads". If Windows fails to load even one of the DLLs listed, the program cannot run, and it displays that tiresome error message. If you use Direct3D 8 normally, your Import Address Table will list d3d8.dll; if your user doesn't have DirectX 8 or above installed, they won't have d3d8.dll, and your game won't run.

dx8Dynamic lets you sidestep the whole problem, by not using Windows to load DirectX—instead, it is loaded manually once your program is already up and running. It loads the DLLs for you, then loads their entry points into global function pointers. The result isn't any slower than using DirectX normally, and it gives you the opportunity to display your own error message.

How To Use It

To use dx8Dynamic:
  1. Include the dx8dynamic.cpp in your project, and include dx8dynamic.h in any files where you use the big DirectX entry points.
  2. When your program starts up, call dx8DynamicStartup().
  3. When your program shuts down, call dx8DynamicShutdown().
  4. Every place you make a call to a global function in DirectX, add the word dx8Dynamic to the front. For instance, instead of calling Direct3DCreate8(), you would call dx8DynamicDirect3DCreate8(). Note that you don't need to touch method calls on COM objects, like the Direct3D object itself.
  5. If you want to check whether DirectX 8 is available, call dx8DynamicIsAvailable(). This returns a BOOL which is TRUE if and only if DirectX 8 is available.

Remarks

dx8Diagnostics

What's It For

For many game developers, the first line of defense when a user is having problems with their game is to say "Could you run DxDiag and send me the results?" And indeed, DxDiag is in many ways a godsend. But there were three things about using it that I didn't like:
  1. It dumps high-level information about the graphics and sound cards, but tells you nothing about their capabilities. I don't know about you, but I don't remember how many texture stages a GeForce 2 MX has right off the top of my head.
  2. It forces your user to run a program, then go find a file, then send it to you. If your user is a computer novice, walking them through this process can be quite a trial.
  3. Its output is a text file, and its only formatting is in assuming you're reading it using a monospaced font. Oh, how eighties.
But wait! What's that up in the sky? Is it a bird? Is it a plane? No, it's dx8Diagnostics to the rescue! dx8Diagnostics has none of those shortcomings:
  1. It dumps all the DirectX 8 DSCAPS, DIDEVICEINSTANCE, and D3DCAPS fields, as well as all supported D3DDISPLAYMODE modes and all compressed DirectX texture formats (DXT[1-5]) that can be used natively by the card.
  2. It's a library that you can call from your program. You can write the output to a file or to a memory buffer; after that, you can do what you like with it.
  3. Its output is reasonable-looking HTML. (Me, I think it's right purty, but your tastes may vary.)
If you'd like to see what its output looks like, you can see it here.

How It Works

There's no magic in this sort of programming. It just takes a lot of reading the Windows and DirectX API documentation, and a willingness to churn through all the different data you could possibly dump out.

How To Use It

To use dx8Diagnostics, add the following files to your project: Also, add the following libraries: Now you're ready to call it. Here's what a minimal program using dx8Diagnostics looks like:
	if (dx8DiagnosticsStartup() == S_OK)
		{
		dx8Diagnostics *diagnostics;
		dx8DiagnosticsCreate(&diagnostics, "spacegoblin");
		dx8DiagnosticsWrite(diagnostics);
		dx8DiagnosticsDestroy(&diagnostics);

		dx8DiagnosticsShutdown();
		}
This would create a diagnostics object with the default diagnostics printers (all of 'em, but without making the slow WHQL date/time stamp calls), and write the results to a file called dx8diagnostics.html in the current directory.

To write to a different filename, you'd call dx8diagnosticsWriteToFile(diagnostics, filename) before calling dx8diagnosticsWrite(diagnostics).

To write to a buffer in memory, you'd call dx8DiagnosticsWriteToMemory(diagnostics, buffer, bufferSize) before calling dx8DiagnosticsWrite(diagnostics). When dx8Diagnostics is done, the buffer will contain a zero-terminated string that is equivalent to what would have been written to a file. There's currently no mechanism for expanding the buffer; if dx8Diagnostics runs out of space in the buffer, it simply stops writing.

Remarks

Licensing

Here's the license:
/*
** Copyright (C) 2003 Larry Hastings
**
** This software is provided 'as-is', without any express or implied warranty.  In no event will the authors be held liable for any damages arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
** The dx8Diagnostics / dx8Dynamic homepage is here:
**		http://www.midwinter.com/~lch/programming/dx8diagnostics/
*/
In non-legalese, my goal was to allow you to do anything you like with the software, except claim that you wrote the original version. If my license prevents you from doing something you'd like to do, contact me (my email address is in the source) and we can discuss it.

The Future

Obviously, it'd be nice if dx8Diagnostics printed out more stuff... processor speed would be nice. But I think I've spent enough time on this thing... maybe some kind programmer somewhere will send me some spiffy dx8DiagnosticsPrinter classes? Maybe?

Version History

1.0
Monday, January 20th, 2003
Initial public release.

Happy runtime-linking and diagnosing!


larry