Puzzling Access Violation

Started by
10 comments, last by Evil Steve 15 years, 8 months ago
Continuing with my tank-game project, I managed to write my own Tank class allowing all rendering functions for an individual tank in the game to be handled by the class. Once I got the code to compile, I ran it and I was rewarded with a black screen and the tank model popping up in the middle--exactly what I wanted. However, when I added in one line to continuously increment the tank's itsAngle variable (a float value) to make it spin, I started getting this error:
Quote:Unhandled exception at 0x00411841 in TankTest1.exe: 0xC0000005: Unhandled exception at 0x00411841 in TankTest1.exe: 0xC0000005: Access violation reading location 0xcccccccc.
The last time I got an error like this, it was because my code couldn't find the mesh file. However, even when I commented out the single line that I added, I still get the error, and the program hasn't run successfully since. The debugger isn't much help, because after I get this error, Visual Studio just locks up and I have to hit Alt F4 to close it, or else my entire windows session locks and I have to log out. I'm just not sure what is causing this, since all I did was add one line to increment a float variable that the method should have access to. The main program code in all its glory:

#include "Tank.h"

//define Tank constructors
Tank::Tank()
{
	itsX = 0;
	itsY = 0;
	itsZ = 0;
	itsAngle = 0;
	itsHealth = 100;
	itsTarget = NULL;
}

Tank::Tank(float x, float y, float z, float angle)
{
	itsX = x;
	itsY = y;
	itsZ = z;
	itsAngle = angle;
	itsHealth = 100;
	itsTarget = NULL;
}

//define Tank destructor
Tank::~Tank()
{
	mesh->Release();
	delete material;
}

//define Tank functions
void Tank::Fire()
{
	if (itsTarget != NULL)
	{
		//if tank has a target, deal damage
		char damage = 20;
		itsTarget->SetHealth(itsTarget->GetHealth() - damage);
	}
	else
	{/**or else do nothing**/}

}

void Tank::Init(LPDIRECT3DDEVICE9 &d3d_dev)
{
	LPD3DXBUFFER bufTankMaterial;

	D3DXLoadMeshFromX(L"M1A1.x",
						D3DXMESH_SYSTEMMEM,
						d3d_dev,
						NULL,
						&bufTankMaterial,
						NULL,
						&numMaterials,
						&mesh);

	D3DXMATERIAL* tempMaterials = (D3DXMATERIAL*)bufTankMaterial->GetBufferPointer();

	material = new D3DMATERIAL9[numMaterials];

	for (DWORD i = 0; i < numMaterials; i++)
	{
		material = tempMaterials.MatD3D;
		material.Ambient = material.Diffuse;
	}
}

void Tank::Render(LPDIRECT3DDEVICE9 &d3d_dev)
{
	D3DXMATRIX matRotateY;
	D3DXMATRIX matTranslate;
        //itsAngle += 0.3f; <--the line I added that broke it
	D3DXMatrixRotationY(&matRotateY,itsAngle);
	D3DXMatrixTranslation(&matTranslate,itsX,itsY,itsZ);
	d3d_dev->SetTransform(D3DTS_WORLD, &(matRotateY*matTranslate));

	for (DWORD i = 0; i < numMaterials; i++)
	{
		d3d_dev->SetMaterial(&material);
		mesh->DrawSubset(i);
	}
}

/**MAIN PROGRAM CODE STARTS HERE**/

// global variables
int const SCREEN_WIDTH = GetSystemMetrics(SM_CXSCREEN)/2;
int const SCREEN_HEIGHT = GetSystemMetrics(SM_CYSCREEN)/2;
Tank * testTank = new Tank();
#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEY_UP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)

// global DirectX objects
LPDIRECT3D9 d3d;    // the pointer to our Direct3D interface
LPDIRECT3DDEVICE9 d3ddev;    // the pointer to the device class
LPDIRECT3DVERTEXBUFFER9 t_buffer = NULL;    // the pointer to the vertex buffer
LPDIRECT3DSURFACE9 z_buffer = NULL;    // the pointer to the z-buffer

// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd,
                         UINT message,
                         WPARAM wParam,                         LPARAM lParam);

// function prototypes
void initD3D(HWND hWnd);    // sets up and initializes Direct3D
void render_frame(void);    // renders a single frame
void cleanD3D(void);    // closes Direct3D and releases memory
void init_graphics(void);    // 3D declarations
void init_light(void);    // sets up the light and the material

// the entry point for any Windows program
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    HWND hWnd;
    WNDCLASSEX wc;

    ZeroMemory(&wc, sizeof(WNDCLASSEX));

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = (WNDPROC)WindowProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.lpszClassName = L"WindowClass1";

    RegisterClassEx(&wc);

    hWnd = CreateWindowEx(NULL,
                          L"WindowClass1",
                          L"Tank Battle!",
                          WS_EX_TOPMOST | WS_POPUP,
                          0, 0,
                          SCREEN_WIDTH, SCREEN_HEIGHT,
                          NULL,
                          NULL,
                          hInstance,
                          NULL);

	ShowWindow(hWnd, nCmdShow);

    // set up and initialize Direct3D
    initD3D(hWnd);

    // enter the main loop:

    MSG msg;

    while(TRUE)
    {
        DWORD starting_point = GetTickCount();

        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if (msg.message == WM_QUIT)
                break;

            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        render_frame();

        // check the 'escape' key
        if(KEY_DOWN(VK_ESCAPE))
            PostMessage(hWnd, WM_DESTROY, 0, 0);

        while ((GetTickCount() - starting_point) < 25);
    }

    // clean up DirectX and COM
    cleanD3D();

    return msg.wParam;
}

// this is the main message handler for the program
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    // sort through and find what code to run for the message given
    switch(message)
    {
        // this message is read when the window is closed
        case WM_DESTROY:
            {
                // close the application entirely
                PostQuitMessage(0);
                return 0;
            } break;
    }

    // Handle any messages the switch statement didn't
    return DefWindowProc (hWnd, message, wParam, lParam);
}

void initD3D(HWND hWnd)
{
    d3d = Direct3DCreate9(D3D_SDK_VERSION);

    D3DPRESENT_PARAMETERS d3dpp;

    ZeroMemory(&d3dpp, sizeof(d3dpp));
    d3dpp.Windowed = FALSE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.hDeviceWindow = hWnd;
    d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
    d3dpp.BackBufferWidth = SCREEN_WIDTH;
    d3dpp.BackBufferHeight = SCREEN_HEIGHT;
    d3dpp.EnableAutoDepthStencil = TRUE;    // automatically run the z-buffer for us
    d3dpp.AutoDepthStencilFormat = D3DFMT_D16;    // 16-bit pixel format for the z-buffer

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

    init_graphics();    // call the function to initialize the triangle
    init_light();    // call the function to initialize the light and material

    d3ddev->SetRenderState(D3DRS_LIGHTING, TRUE);    // turn on the 3D lighting
    d3ddev->SetRenderState(D3DRS_ZENABLE, TRUE);    // turn on the z-buffer
    d3ddev->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(50, 50, 50));    // ambient light

    return;
}

void render_frame(void)
{
    d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
    d3ddev->Clear(0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

    d3ddev->BeginScene();

    // SET UP THE TRANSFORMS

    D3DXMATRIX matView;    // the view transform matrix
    D3DXMatrixLookAtLH(&matView,
    &D3DXVECTOR3 (0.0f, 4.0f, 8.0f),    // the camera position
    &D3DXVECTOR3 (0.0f, 0.0f, 0.0f),    // the look-at position
    &D3DXVECTOR3 (0.0f, 1.0f, 0.0f));    // the up direction
    d3ddev->SetTransform(D3DTS_VIEW, &matView);    // set the view transform to matView

    D3DXMATRIX matProjection;    // the projection transform matrix
    D3DXMatrixPerspectiveFovLH(&matProjection,
                               D3DXToRadian(45),    // the horizontal field of view
                               SCREEN_WIDTH / SCREEN_HEIGHT,    // the aspect ratio
                               1.0f,    // the near view-plane
                               100.0f);    // the far view-plane
    d3ddev->SetTransform(D3DTS_PROJECTION, &matProjection);    // set the projection

    // draw the tank
	testTank->Render(d3ddev);

    d3ddev->EndScene(); 

    d3ddev->Present(NULL, NULL, NULL, NULL);

    return;
}

void cleanD3D(void)
{
	delete testTank;
    d3ddev->Release();    // close and release the 3D device
    d3d->Release();    // close and release Direct3D

    return;
}

void init_graphics(void)
{
	testTank->Init(d3ddev);

    return;
}

void init_light(void)
{
    D3DLIGHT9 light;    // create the light struct

    ZeroMemory(&light, sizeof(light));    // clear out the struct for use
    light.Type = D3DLIGHT_DIRECTIONAL;    // make the light type 'directional light'
    light.Diffuse.r = 0.5f;    // .5 red
    light.Diffuse.g = 0.5f;    // .5 green
    light.Diffuse.b = 0.5f;    // .5 blue
    light.Diffuse.a = 1.0f;    // full alpha (we'll get to that soon)

    D3DVECTOR vecDirection = {-1.0f, -0.3f, -1.0f};    // the direction of the light
    light.Direction = vecDirection;    // set the direction

    d3ddev->SetLight(0, &light);    // send the light struct properties to light #0
    d3ddev->LightEnable(0, TRUE);    // turn on light #0

    return;
}
I can post the header if people would like, but I'm fairly sure the problem is in this .cpp
Advertisement
Quote:Access violation reading location 0xcccccccc.


Im guessing somewhere in your code your trying to dereference a pointer which is pointing to garbage, and not NULL, so any null checks you have wont work.

Step through your code and figure out which line is causing this, then you can re run your app and look at the state of the offending pointer before reaching the crash and see what is going on, you could even use a data breakpoint to see what is setting the value of the pointer.

Hope that helps!



Quote:Original post by goochie
Access violation reading location 0xcccccccc.


Quote:Original post by Wikipedia
0xCCCCCCCC : Used by Microsoft's C++ debugging runtime library to mark uninitialised stack memory
Some observations:
  • Your Tank class constructor doesn't set the mesh pointer to null, so you can't ever check if it's a valid pointer. Likewise with the material pointer.
  • Your Tank class destructor calls mesh->Release(), but never checks for null pointers.
  • Several functions take a reference to your D3D device pointer pointer. That implies that the pointer is going to be changed, but you never change it in the function, which is bad form.
  • You never check the return value from D3DXLoadMeshFromX, so if it fails, you'll get undefined behaviour (Likely a crash). You really need to check the return value with the FAILED() macro (See any of the SDK samples for usage).
  • This is A Bad Thing, since you're taking the address of a temporary (I think this is a Visual Studio only extension): d3d_dev->SetTransform(D3DTS_WORLD, &(matRotateY*matTranslate));
  • This casts a function pointer, which you never want to do unless you're absolutely sure what you're doing. If the code doesn't compile without the cast, it's likely to corrupt the stack and/or crash at runtime: wc.lpfnWndProc = (WNDPROC)WindowProc;
  • You don't use AdjustWindowRectEx() to make sure you're creating the correct window size. This isn't a problem for fullscreen mode, but will cause you problems in windowed mode.
  • You only process one message, then render a frame. If rendering a frame causes another window message to be produced, you'll never clear your window message queue, meaning your app will get sluggish, and probably crash.
  • This is a busy wait, and will eat up 100% of the CPU. You should add a Sleep(0); call to the while loop: while ((GetTickCount() - starting_point) < 25);
  • You never check if the backbuffer format is compatible with the depth buffer format, meaning CreateDevice() has a reasonable chance of failure.
  • You never check if the backbuffer format is usable, meaning CreateDevice() has a reasonable chance of failure.
  • You never check the return value of CreateDevice(), meaning any failure will go unnoticed and probably crash your app. Again, you want to use the FAILED() macro here.
  • You're clearing the depth-buffer and backbuffer seperately. You should clear them both at once by OR-ing the flags together like so: d3ddev->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
  • cleanD3D assumes all pointers are valid and doesn't check for null pointers. It should also set the pointers to null after releasing them (So it can check for null pointers if called again).

    If you don't mind me asking, where did you get the code from initially? A lot of that code looks familiar (Particularly the bust wait loop), and if it's from an online tutorial it may be worth me E-mailing the author and asking him to please fix his code, since it's horrific for a tutorial / example code.

    Hope some of this helps [smile]
  • Google says it's from several code snippits at http://www.directxtutorial.com/, along with 209 other search results that copied the busy loop.
    Quote:Original post by frob
    Google says it's from several code snippits at http://www.directxtutorial.com/, along with 209 other search results that copied the busy loop.


    Yup, that's what I've been following. It really isn't the best coding, even I've found that. It's a start, at least, and my plan was to go back and rewrite the things that could've been done better as I learned more.

    Thanks for the to-do list, Evil Steve. I'll get to work implementing your suggestions and will see if that fixes things.

    Just one thing I'm wondering, could it be possible that all these things were always wrong with the program, and that it was just sheer luck that it compiled and ran without problems the first time I built it? I'm still just puzzled at why it suddenly stopped working correctly.
    Quote:Original post by goochie
    Just one thing I'm wondering, could it be possible that all these things were always wrong with the program, and that it was just sheer luck that it compiled and ran without problems the first time I built it? I'm still just puzzled at why it suddenly stopped working correctly.
    If the original worked and that doesn't, then it's definitely something that you've added (My guess is the mesh stuff). As others said, somewhere you're using a pointer that's not been set up correctly - the debugger will be able to tell you exactly what pointer, and where the problem is happening.

    If you're not familiar with the debugger, I can highly recommend this article (Assuming some flavour of Visual Studio)
    That's the thing though, the original working version had everything other than that commented-out line in the Tank::Render() function.

    And just wondering, if it's bad form to take a pointer to the d3d device without changing it, how else can I make sure the tank's render() and init() functions know where to find the D3D device? Passing them a pointer so they can use the device seemed like the only way I could accomplish that.
    Okay, I added in the FAILED() check for the mesh loading function. It's giving a "File Not Found" error. The last time I encountered this, I fixed it by putting the model in a path where no folders had spaces in their names and giving the function the entire path.

    When given the exact path this time, the error changes to "Invalid Call."

    The .x file I'm looking for is in the build folder with the compiled .exe, and the names do match (this program HAS worked before and loaded the model successfully when given just the filename).
    Quote:Original post by goochie
    And just wondering, if it's bad form to take a pointer to the d3d device without changing it, how else can I make sure the tank's render() and init() functions know where to find the D3D device? Passing them a pointer so they can use the device seemed like the only way I could accomplish that.
    You're pass a reference to a pointer to a D3D device, you only want to be passing a pointer to a D3D device. For example, this line:
    void Tank::Init(LPDIRECT3DDEVICE9 &d3d_dev)
    Should be:
    void Tank::Init(LPDIRECT3DDEVICE9 d3d_dev)
    And similarly for all silar lines. A LPDIRECT3DDEVICE9 is typedef'd as a pointer to a device class already.

    Quote:Original post by goochie
    Okay, I added in the FAILED() check for the mesh loading function. It's giving a "File Not Found" error. The last time I encountered this, I fixed it by putting the model in a path where no folders had spaces in their names and giving the function the entire path.

    When given the exact path this time, the error changes to "Invalid Call."

    The .x file I'm looking for is in the build folder with the compiled .exe, and the names do match (this program HAS worked before and loaded the model successfully when given just the filename).
    Are you using forward slashes, or escaping your backslashes? Remember that backslashes are escape codes, so you'll need to use a double backslash instead of one. I.e. if your mesh is in C:\foo.x, you'll need to use the path "C:/foo.x" or "C:\\foo.x".

    The Debug Runtimes will also be able to give you more information about why the function is failing.

    This topic is closed to new replies.

    Advertisement