Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

DirectX 9 SDK with .NET Forms (C++)

0.00/5 (No votes)
6 Sep 2009 2  
Shows you how to implement a DirectX Render Window into a .NET Form.

Introduction

When searching the internet, it becomes painfully clear that finding a decent tutorial to implement DirectX 9.0c into .NET Forms is nearly impossible. If you do manage to find one, chances are it is meant for C#.

If you are wondering why a programmer would want to do this, here is my personal response:

Compared to the Win32 API, .NET Forms are far more simple to include in a project and far easier to manage. The only setback is that .NET Forms were designed for C# and not C++. However, with a few simple lines of code, you can change your program so that it will take advantage of C# libraries (.NET Forms in particular). This is ideal if you are trying to construct your own level editor without all the mess generated by Win32.

If you are reading this tutorial, then I expect you know how to do the following:

  • Have an intermediate knowledge of C++.
  • Know how to use DirectX9 SDK, if not: www.directxtutorial.com.
  • I also assume you have your own copy of Visual Studio. (Note: I use the Visual C++ Express Edition from Microsoft; if you are using Visual Studio 2005, there might be minor variations.)
  • Furthermore, I assume you have the DirectX SDK installed in your computer.

Writing the code

First, you need to configure the C++ compiler so it is able to use libraries from C#. To do this, go to the menu bar, select 'Project', and go to 'Properties'. A window will open up, go to 'Configuration Properties' and select 'General'. Now, go to the box labeled 'Common Language Runtime Support' and change it to /clr. Now we can use libraries and namespaces from C# in our code.

Create a new C++ file for the entry point of your program (I will refer to this as main.cpp). Then, create a new Windows Form as a header (under Visual C++ -> UI) (I will refer to this as DX9Forms.h and DX9Forms.cpp). Adding a Windows Form to your headers should have created both a header file (.h) and a code file (.cpp). If not, add a code file and remember to include your form. Now, start with the main entry point of this program, I will use WinMain().

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
                   LPSTR lpCmdLine, int nCmdShow)

Now, include all of your headers:

#include "iostream"
#include "windows.h"
#include "DX9Form.h"
#include "d3d9.h"
using namespace Forms_DX9;

Finally, you only need one line of code to run the form:

Application::Run(gcnew DX9Form());

We will change the constructor of the class DX9Form later; for now, leave it as it is.

main.cpp will not change much throughout the course of this tutorial.

//--------------------------------
//File: main.cpp
//--------------------------------

#include "iostream"
#include "windows.h"
#include "DX9Form.h"
#include "d3d9.h"

//Use the Forms_DX9 namespace containing the DX9Form class

using namespace Forms_DX9;

//--------------------------------
// Main Program
//--------------------------------

//Entry Point
int WINAPI WinMain(HINSTANCE hInstance,
        HINSTANCE hPrevInstance,
        LPSTR lpCmdLine,
        int nCmdShow)
{
    //Run the Application
    Application::Run(gcnew DX9Form());
}

Go the file 'DX9Form.h', you should be in Designer view. Create a panel in your form (this is not needed for the program, but I find it easier to use the coordinates of the panel, then to input them manually).

Now, view the code of the 'DX9Form.h'. The majority of the code has already been generated by Visual Studio. (If you have never used forms in C++ before, this might be a little overwhelming; I suggest you experiment a little with it to get comfortable using it.)

Enter this under #pragma once:

#using "System.dll"
#include <d3d9.h>
#include <windows.h>
#pragma comment (lib, "d3d9.lib")

As you can probably see, we are using System.dll, a C# library.

Now we need to define two globals, one of LPDIRECT3D9 and the other of LPDIRECT3DDEVICE9.

Yet, if you simply use them as traditional globals, the program won't compile, so put them both in to a namespace named 'globals' and define them as static members.

namespace globals
{
    static LPDIRECT3DDEVICE9 d3ddev;
    static LPDIRECT3D9 d3d;
}

using namespace globals;

We now need to modify the constructor of our DX9Forms class which was generated by Visual Studio.

protected:

    bool mReleased;
    HWND hDX9;

public:

DX9Form(HINSTANCE hInstance)
{

    mReleased = false;
    mInit = false;

    InitializeComponent();

    WNDCLASSEX wc;
    ZeroMemory(&wc, sizeof(WNDCLASSEX));

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.hbrBackground = NULL;
    wc.lpszClassName = "WindowClass";

    RegisterClassEx(&wc);

    hDX9 = CreateWindowEx(NULL, "WindowClass", NULL, WS_CHILD,
            this->panel1->ClientRectangle.X, this->panel1->ClientRectangle.Y,
            this->panel1->Width, this->panel1->Height,
            (HWND)(void*)this->panel1->Handle.ToPointer(),
            NULL,
            hInstance,
            NULL);

    ShowWindow(hDX9, SW_SHOW); 
}

In this segment of code, we modify the constructor as well as add a boolean value to the DX9Form class and a Window Handle which points to our render window (hDX9). We use panel1 to specify where we want the window to be created and what size it should be; if you have done Win32 API programming, this should look familiar to you.

Now we need to implement two functions, initD3D() and render(). initD3D() creates a device for DirectX. render() clears the window and renders a frame.

Here are the definitions:

protected: void initD3D(HWND hHandle);

public : static void render();

Write these functions in DX9Forms.cpp:

//File: DX9Form.cpp
#include "DX9Form.h"

using namespace Forms_DX9;
using namespace globals;

void DX9Form::initD3D(HWND hHandle)
{

    globals::d3d = Direct3DCreate9(D3D_SDK_VERSION);
    // create the Direct3D interface

    D3DPRESENT_PARAMETERS d3dpp;
    // create a struct to hold various device information

    ZeroMemory(&d3dpp, sizeof(d3dpp)); // clear out the struct for use

    d3dpp.Windowed = true; // program windowed, not fullscreen

    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; // discard old frames

    d3dpp.hDeviceWindow = hHandle;
    // set the window to be used by Direct3D

    // create a device class using this information
    // and information from the d3dpp stuct
    globals::d3d->CreateDevice(D3DADAPTER_DEFAULT,
            D3DDEVTYPE_HAL,
            hHandle,
            D3DCREATE_SOFTWARE_VERTEXPROCESSING,
            &d3dpp,
            &globals::d3ddev);
}

void DX9Form::render(void)
{

    // clear the window to a deep blue
    globals::d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, 
                              D3DCOLOR_XRGB(0, 40, 100), 1.0f, 0);
    globals::d3ddev->BeginScene(); // begins the 3D scene

    // do 3D rendering on the back buffer here
    globals::d3ddev->EndScene(); // ends the 3D scene
    globals::d3ddev->Present(NULL, NULL, NULL, NULL);
    // displays the created frame
}

Now we need to write the window protocol; the DirectX device will render when the WM_PAINT message is called.

//File: DX9Form.cpp

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{

    switch (message)
    {
        case WM_DESTROY:
        {
            PostQuitMessage(0);
            return 0;
        }
        break;
        case WM_PAINT:
        {
            DX9Form::render();
            return 0;
        }
        break;
    }
    return DefWindowProc (hWnd, message, wParam, lParam);
}

Events

We are almost done. Now we just need to define two events; one will be triggered when the form is first shown and will call initD3D(), and the other will be triggered when the form is closing and will release d3ddev and d3d.

To create an event, select your form in the form designer and then go to the Properties window, and in the Properties window, go to Events (represented by a lightning bolt). To create a new event, just double click in a box next to what you what the event by triggered by (in this case, Shown). After the event is created, you should be redirected to its corresponding code.

Now, treat the event as if it were a function and write your code between the braces. You need to call initD3D() and render the first frame.

private: System::Void DX9Form_Shown(System::Object^ sender, System::EventArgs^ e) 
{ 
    initD3D(hDX9);
    render();
}

Now create another event for FormClosing; we need to release d3ddev and d3d.

private: System::Void DX9Form_FormClosing(System::Object^ sender, 
                 System::Windows::Forms::FormClosingEventArgs^ e) 
{
    if (!mReleased)
    {
        globals::d3ddev->Release();
        globals::d3d->Release();
        mReleased = true;
    }
}

Finally, we need to adjust our code in main.cpp as we have changed the constructor of DX9Form.

//new line: Application::Run(gcnew DX9Form(hInstance));
//--------------------------------
//File: main.cpp
//--------------------------------

#include "iostream"
#include "windows.h"
#include "DX9Form.h"
#include "d3d9.h"

//Use the Forms_DX9 namespace containing the DX9Form class
using namespace Forms_DX9;

//--------------------------------
// Main Program
//--------------------------------

//Entry Point
int WINAPI WinMain(HINSTANCE hInstance,
            HINSTANCE hPrevInstance,
            LPSTR lpCmdLine,
            int nCmdShow)
{
    //Run the Application
    Application::Run(gcnew DX9Form(hInstance));
}

Compile and run this; it should work; if it doesn't, I have included source code that you can download.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here