Using DirectX with C++Builder
This article is meant to help Borland users get started with the DirectX SDK. It covers everything from installing the DirectX SDK to using DirectDraw and Direct3D in VCL applications, compiling the SDK samples, using the Console Wizard to create straight WinAPI applications, and other issues. Introduction
In the DirectX and C++Builder Graphics newsgroups , two questions that come up often are "Can I use DirectX with Borland compilers?" and "Do I have to buy Visual C++ in order to write DX apps?" On Friday, August 11, a Microsoft employee posted the following reply: "Of course you are on the safe side when you use VC++. I would recommend it to you. After all its the compiler all the stuff is being build with at MS!" (I'm not sure whether that's a real incentive... ;-) However, it is certainly possible to build DX apps with C++Builder (the current Borland C++ environment), and you should let noone tell you anything else! To be fair, that Microsoft employee did not conceal it, and Microsoft did make sure that Borland users can use the DirectX SDK. After all, C++Builder is a fully ANSI-compliant Win32 development environment (more than VC++!). In fact, there are just a few minor issues to take care of when using the DX SDK and BCB together. This article will help you turn your BCB IDE into a "DXDE"... Downloading and installing the DirectX SDK
The good news: C++Builder ships with all the header files and libraries required for DX development.
The other news: New DirectX versions are likely to be released more often than you buy new versions of C++Builder. In order to always have the latest SDK (Software Development Kit; contains help files, headers, libraries, samples, tools, etc.), your first stop will be at the Microsoft DirectX Developer Center . After new versions of the SDK are released (along with new versions of the end-user runtime), it can be downloaded (usually for a few weeks) or ordered on CD-ROM. As the SDK becomes larger and larger with every new version (the last one was 128 MB!), it is probably cheapest and easiest to order it. However, if you do not need the samples and the utilities (the latter are not frequently updated for new SDKs), you can also download the help files (.chm), the headers (.h), and the libraries (.lib) separately . The next step will be setting up your IDE to use the new files. It is not required and not a good idea to overwrite the old headers and libraries in the CBuilder "include" and "lib" sub-folders. Instead, change the global project options. Close all currently opened projects in C++Builder and go to "Project | Options". On the "Folders and Definitions" tab, add the DX SDK paths: Add "\include" to the include path and "\lib\Borland" to the libraries path. Note: The DX SDK paths need to appear prior to the other paths. For example, if you had "$(BCB)\include" as your include path, make it "c:\dxsdk\include;$(BCB)\include", not the other way round. Change "c:\dxsdk" to whatever path you installed the SDK to. We'll get back to the project options soon. But first... Building the SDK samples
The SDK contains the sample applications in both pre-compiled and source code form. Usually, you will want to experiment with the code and modify it. For that, you will have to create a C Builder project. Following are step-by-step instructions that will help you do this: Copy the entire folder containing the sample into your own folder (e.g. "C:\My Documents"). That way, you will be able to restore the original code if you break it. In C Builder, click the "New" button. Choose the "Console Wizard" and create a new "Window (GUI)" application without the VCL (which is not needed here but can be used). Note: In the latest versions of C Builder, you can also specify the "project source". Select the file that contains the WinMain function (usually "winmain.cpp") for this. Specify "C " as the source code type. If you did not specify the project source in the Console Wizard, remove the body from the WinMain function that the IDE created and declare it as "extern", so that it looks like this: extern WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow); Then, "link to" winmain.cpp by clicking the "Add to Project..." button ("Project | Add to Project...", Shift F11). Note: Alternatively, you can also copy and paste the code from winmain.cpp (or whichever file declares WinMain) into the project source ("Unit1.cpp" for apps created by the console wizard). Add all the other .cpp (or .c) files from the sample folder to the project. Add .res and .rc files as well. Note: Instead of adding everything, you can also take a look into the sample "makefile" and see which files it uses. This might also bring up files from different folders, commonly \samples\multimedia\d3dim\src\d3dframe. Link to these files as well. DirectX apps use functions from the DirectX system DLLs. These functions are imported through .lib files. For DirectDraw, for example, you will have to link to ddraw.lib, for Direct3D to ddraw.lib and d3dim.lib. All DirectX apps will require you to link to dxguid.lib. (See the DX SDK help files on GUIDs, dxguid.lib, and INITGUID.) Note: Use the .lib files from the \lib\Borland folder. The others will generate an error about an "invalid OMF record". See the Borland site for more information about the "COFF vs. OMF" issue . You can attempt a build. If you get "unresolved external" errors, you did not link to all the required files, either .lib or .cpp. Check the "makefile" whether the sample uses files from different folders that you forgot. See also "The Direct3D Framework" below. Note: The only samples that you cannot build are the "Direct3DX Utility Library" samples. You cannot use D3DX with C Builder at all. (You can use Direct3D, however.) "d3dx.lib" is a static library (as compared to an import library), which means that there is no DLL that it exports from. Someone would have to re-compile Microsoft's D3DX source code with a Borland compiler in order to create an OMF library (or to wrap D3DX up in a VC -created DLL). Microsoft says Borland is responsible. Borland says Microsoft is responsible... Using DirectDraw and Direct3D on VCL Forms
The Visual Component Library is one of the most outstanding features of C Builder. It would be a shame if DirectX apps could not take advantage of it! In fact, it is easy to integrate DirectDraw (and Direct3D) functionality into VCL projects. The following step-by-step instructions will show you how to do this. (You can download the project as a ZIP file from here.) Create a new application. Double-click the main form to create a handler for its OnCreate event. Add the following code: PostMessage(Handle, WM_INITDIRECT3D, 0, 0); Note: WM_INITDIRECT3D is not a pre-defined Windows message. It is a user-defined message that we'll handle in a user-defined function. This is to ensure that all Form creation messages have been processed and the Form has been displayed before we attempt to initialize Direct3D. Add "#define WM_INITDIRECT3D (WM_USER 101)" to Unit1.h and register a custom message handler for the message by adding the following code to the declaration of class TForm1: void __fastcall InitDirect3D(TMessage &Msg);
VCL_MESSAGE_HANDLER(WM_INITDIRECT3D, TMessage, InitDirect3D)
END_MESSAGE_MAP(TForm) Add the regular Direct3D initialization code to the function body of InitDirect3D. (It is not the purpose of this tutorial to introduce you to Direct3D. See the References section for links to DirectDraw and Direct3D tutorials .) At the end of InitDirect3D, add the following call: PostMessage(Handle, WM_ENTERGAMELOOP, 0, 0); Register "GameLoop" as the message handler for WM_ENTERGAMELOOP (which you can #define as WM_USER 102). Implement GameLoop as follows: while (!UserWantsToQuit)
} Note: This game loop will ensure that the framerate is as high as possible. For games, TTimers would not be good enough. Calling ProcessMessages lets the app still react to messages. The regular events are fired. Implement RenderScene. Add "bool UserWantsToQuit" to class TForm1. Set it to false in the constructor and to true in the (float)cos;sinf=(float)sin;acosf=(float)acos;asinf=(float)asin;
tanf=(float)tan;atanf=(float)atan;sqrtf=(float)sqrt" Alternatively, you can add #defines to the headers of the affected units. Note: The cast to float does not lose any precision when compared to Visual C apps. They would have used floats in the first place... The second difference (by which only d3dtextr.cpp is affected, as far as I know) concerns variable scopes. In C Builder, the following construct is valid: for (int i=0; i < 1000; i )
for (int i=0; i < 2000; i )
DoSomething2(i); When compiled with Visual C , this gives an error: "Redefinition of 'int i'". Visual C treats the code as follows: int i;
for (i=0; i < 1000; i )
int i; // of course, this is a redefinition!
for (i=0; i < 2000; i )
... According to the ANSI C standard, C Builder is right and Visual C is wrong. However, the programmers of d3dtextr (and probably other files from the DX SDK) relied on the loop counter to be valid outside of the loop, as is the case with Visual C : for (int i=0; i < 1000; i )
for (i=0; i < 2000; i ) /* 'i' is undefined.
It only exists in the first loop. */
return i; /* 'i' is undefined.
It only exists in the first loop. */ C Builder will not compile this. The solution is to place "int i;" near the top of the function and to reference it in the loops: int i;
for (i=0... Note: You can also emulate Visual C behavior by enabling "MFC compatibility" on the "Advanced compiler options" tab in "Project | Options". What else?
I hope this article could answer all your questions about using DirectX with C Builder. If you have any questions, suggestions, or comments, feel free to e-mail mailto:firstname.lastname@example.org?subject=CBuilder DirectX Feedback me! mailto:email@example.com?subject=CBuilder DirectX Feedback