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
I TRYED TO RUN YUOR GAME BUT IT DOESNT WORKS!!!!!!1Or, if you're lucky, the email read:
IT SAYS UNABEL TO LOCATE COMPONANT
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.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.
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.
LoadLibrary()
on each DLL listed. If it loads, use GetProcAddress()
to find all
functions called by the executable.
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 DirectXinstead, 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.
dx8DynamicStartup()
.
dx8DynamicShutdown()
.
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.
dx8DynamicIsAvailable()
.
This returns a BOOL
which is TRUE
if and only if DirectX 8 is available.
Direct3DCreate8()
DirectSoundCreate8()
DirectSoundEnumerateA()
DirectInput8Create()
DirectSoundCaptureCreate8()
, I have good
news and bad news. The good news: it's a one-step process. The bad news:
it means writing a big hairy macro. I'm not going to go into this in depth,
but basically, you'll add a new PROCESS_ENTRYPOINT()
line to
the DX8DYNAMIC_MODULES_AND_ENTRYPOINTS
list. The four arguments to
that macro, in order, are: the name of the original function, the name of
the HMODULE
variable where the function is found (for the DirectSound
functions, that'd be hModuleDsound
), the return type of the
function, and the arguments to the function in parentheses.
d3d8.lib
,
dinput8.lib
, and dsound.lib
in your project. Since
you're not using the main entry points of the first three, MSVC won't add those
DLLs to the Import Address Table, so it won't hurt anything. Also, they still
contain things that you'll need, like the GUIDs for the DirectInput devices.
d3dx8.lib
, you ask? Well, the DirectX guys are a little
weird. There's a d3dx8d.dll
for debug builds... but for
release builds, d3dx8.lib
is a static library, so it
gets linked directly into your program. Since there's no DLL dependency, there's
nothing for dx8dynamic to manage here.
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.
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.
sprintf()
,
and I needed that so that I could print floating-point numbers in a familiar format.
In a similar vein, dx8Diagnostics only depends on functionality in
Windows that has been there since Windows 95 / NT 4.0. Anything that it calls
that might not be installed is handled gracefully. In other words, it should
run, and produce useful output, on a vanilla Windows 95 machine.
ASSERT_SUCCESS()
, ASSERT_RETURN()
,
and RETURN()
macros. They're like a simple form of exception handling,
written in straight C. (Okay, I admit it, I'm probably still more comfortable in
C than C++.)
dx8DiagnosticsPrinter
, instantiate it, and
pass it in to dx8Diagnostics::append()
.
GetModuleHandle(NULL)
when it needs an instance handle for DirectInput8Create()
, which theoretically
is the wrong thing to do... it should really take a module handle as a parameter somewhere,
and pass it in there. But I don't expect this to make any difference for any real users.
/* ** 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.
dx8DiagnosticsPrinter
classes? Maybe?
larry