Sign in to follow this  
goochie

Puzzling Access Violation

Recommended Posts

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[i] = tempMaterials[i].MatD3D;
		material[i].Ambient = material[i].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[i]);
		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

Share this post


Link to post
Share on other sites
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!



Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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]

    Share this post


    Link to post
    Share on other sites
    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.

    Share this post


    Link to post
    Share on other sites
    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)

    Share this post


    Link to post
    Share on other sites
    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.

    Share this post


    Link to post
    Share on other sites
    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).

    Share this post


    Link to post
    Share on other sites
    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.

    Share this post


    Link to post
    Share on other sites
    Debugging isn't working. Visual Studio 2005 has problems when running the program in debug mode. Even if I set a breakpoint well before the point where it tries to load the mesh, my mouse just turns to the spinning circle (Vista user) and I can't click on anything in VS. The only thing I can do is quit out of VS. I'll see if the problem could be because the program is trying to launch fullscreen.

    Edit: Even setting the program up to run in a small window results in my monitor flashing black, then the program window blinks on the screen and disappears, then Visual Studio locks up and I have to quit. Frustrating...

    Edit x2: Shift + F5 stops the program without closing VS (guess i should get to know the keystrokes, huh). Got DirectX's debugging output working, and a few lines before I get the access violation, I find:
    Quote:

    Direct3D9: (INFO) :======================= Hal SWVP device selected

    Direct3D9: (INFO) :HalDevice Driver style b

    Direct3D9: :BackBufferCount not specified, considered default 1
    Direct3D9: :Subclassing window 000b0e32
    Direct3D9: :StartExclusiveMode
    Direct3D9: (ERROR) :Display mode is unsupported
    Direct3D9: (ERROR) :Display mode is unsupported
    Direct3D9: (ERROR) :Display mode is unsupported
    Direct3D9: (ERROR) :Display mode is unsupported
    Direct3D9: (ERROR) :Unable to set the new mode. CreateDevice/Reset Fails
    Direct3D9: (ERROR) :Failed to initialize primary swapchain
    Direct3D9: (ERROR) :Failed to initialize Framework Device. CreateDeviceEx Failed.


    I also found that commenting out the testTank.init() line still results in an access violation.
    Quote:

    First-chance exception at 0x0041f870 in TankTest1.exe: 0xC0000005: Access violation reading location 0x00000000.
    Unhandled exception at 0x0041f870 in TankTest1.exe: 0xC0000005: Access violation reading location 0x00000000.

    Now it seems this error is coming from the initD3D() function itself, and not so much because of the mesh loader. Is it possible that something in my DirectX SDK could've gotten hosed?

    [Edited by - goochie on August 14, 2008 5:41:29 PM]

    Share this post


    Link to post
    Share on other sites
    Quote:
    Original post by goochie
    Debugging isn't working. Visual Studio 2005 has problems when running the program in debug mode. Even if I set a breakpoint well before the point where it tries to load the mesh, my mouse just turns to the spinning circle (Vista user) and I can't click on anything in VS. The only thing I can do is quit out of VS. I'll see if the problem could be because the program is trying to launch fullscreen.

    Edit: Even setting the program up to run in a small window results in my monitor flashing black, then the program window blinks on the screen and disappears, then Visual Studio locks up and I have to quit. Frustrating...

    Edit x2: Shift + F5 stops the program without closing VS (guess i should get to know the keystrokes, huh). Got DirectX's debugging output working, and a few lines before I get the access violation, I find:
    Quote:

    Direct3D9: (INFO) :======================= Hal SWVP device selected

    Direct3D9: (INFO) :HalDevice Driver style b

    Direct3D9: :BackBufferCount not specified, considered default 1
    Direct3D9: :Subclassing window 000b0e32
    Direct3D9: :StartExclusiveMode
    Direct3D9: (ERROR) :Display mode is unsupported
    Direct3D9: (ERROR) :Display mode is unsupported
    Direct3D9: (ERROR) :Display mode is unsupported
    Direct3D9: (ERROR) :Display mode is unsupported
    Direct3D9: (ERROR) :Unable to set the new mode. CreateDevice/Reset Fails
    Direct3D9: (ERROR) :Failed to initialize primary swapchain
    Direct3D9: (ERROR) :Failed to initialize Framework Device. CreateDeviceEx Failed.
    I haven't done any debugging on Vista, but fullscreen debugging of D3D applications is always a pain - You can't really do it unless you have a multiple monitor setup.
    The error message there tells me that you're trying to set a display mode that isn't supported by your graphics code, which is 0x0 pixels. In fullscreen mode you have to fill in more members of the D3DPRESENT_PARAMETERS structure, including (but not limited to) the backbuffer width and height, otherwise D3D doesn't know what display mode you want it to use.

    Quote:
    Original post by goochie
    I also found that commenting out the testTank.init() line still results in an access violation.
    Quote:

    First-chance exception at 0x0041f870 in TankTest1.exe: 0xC0000005: Access violation reading location 0x00000000.
    Unhandled exception at 0x0041f870 in TankTest1.exe: 0xC0000005: Access violation reading location 0x00000000.

    Now it seems this error is coming from the initD3D() function itself, and not so much because of the mesh loader. Is it possible that something in my DirectX SDK could've gotten hosed?
    Well, yes. If you don't initialise your tank class, it'll have null pointers in it. That access violation is the direct result of trying to use a null pointer. The debugger will break on the line that causes the access violation, then you can inspect the pointers and see which one is null. It's possible your SDK is hosed, but very unlikely. If it was hosed, you probably wouldn't even be able to compile.


    Overall, it looks like you're not doing any error checking. You MUST check the return value for every D3D function that returns a pointer, so you can handle D3D failing. The primary cause here seems to be that CreateDevice() is failing and you're not noticing. That means your D3D device pointer is null, which you later go on to dereference.
    The FAILED() macro should always be used to check if a function failed, for example:

    // create a device class using this information and the info from the d3dpp stuct
    HRESULT hResult = d3d->CreateDevice(D3DADAPTER_DEFAULT,
    D3DDEVTYPE_HAL,
    hWnd,
    D3DCREATE_SOFTWARE_VERTEXPROCESSING,
    &d3dpp,
    &d3ddev);
    if(FAILED(hResult))
    {
    // An error occurred. hResult is the error code which you can log, or pass to
    // DXGetErrorString().
    // Can't continue, so need to bail out. You'll probably want to throw an
    // exception or return false.
    return;
    }


    As an aside, you can never make any assumptions about the hardware, you really need to check that the graphics card supports the backbuffer size and format you request via the various D3D functions.

    Share this post


    Link to post
    Share on other sites

    Create an account or sign in to comment

    You need to be a member in order to leave a comment

    Create an account

    Sign up for a new account in our community. It's easy!

    Register a new account

    Sign in

    Already have an account? Sign in here.

    Sign In Now

    Sign in to follow this