Repsol

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
ハンドルされていない例外&アクセス違反

Repsol

#1

投稿記事 by ハンドルされていない例外&アクセス違反 » 14年前

お世話になります。

DirectX9で、DirectInputを使用しようとしたところ、
件名のようなエラーが生じます。

スプライト(char.png)をキーボード操作によって移動しようとするプログラムを組んでいるのですが、
Win32メッセージ以外で組むのは初めてです。

何分、DirectInputも、まだ少しかじった程度の未熟者です。
また勘違い等している可能性が高いのですが、
よろしければ、ご教授願いたいです。
(基本的なところで間違っていると思います。四苦八苦状態です…)

コード:

#define STRICT // 型チェックを厳密に行う
#define WIN32_LEAN_AND_MEAN // ヘッダーから余り使われない関数を省く

#include <stdio.h>
#include <windows.h>
#include <d3d9.h>
#include <d3dx9.h> 
#include <dinput.h>
#pragma warning( disable : 4996 ) // disable deprecated warning 
#include <strsafe.h>
#pragma warning( default : 4996 )

//-----------------------------------------------------------------------------
// Global variables
//-----------------------------------------------------------------------------
LPDIRECT3D9             g_pD3D       = NULL; // Used to create the D3DDevice
LPDIRECT3DDEVICE9       g_pd3dDevice = NULL; // Our rendering device
LPD3DXSPRITE            g_pSprite      = NULL; // スプライト変数宣言
LPDIRECT3DTEXTURE9      g_pTexture     = NULL; // テクスチャクラス変数宣言
LPDIRECTINPUT8          g_pDinput      = NULL; // DirectInputオブジェクトのポインタ
LPDIRECTINPUTDEVICE8    g_lpDIDevice    = NULL; // DirectInputデバイスオブジェクトへのポインタ

D3DXVECTOR3             vec3Position;

char                    ImgFile[]      ="char.png";
float                   fPosition_X = 0, fPosition_Y = 0;

//-----------------------------------------------------------------------------
// 初期化関数
//-----------------------------------------------------------------------------
HRESULT InitDinput(HWND);
VOID Move();

//-----------------------------------------------------------------------------
//HRESULT InitDinput(HWND hWnd)
//ダイレクトインプットの初期化関数
//-----------------------------------------------------------------------------
HRESULT InitDinput(HWND hWnd)
{
	HRESULT hr;

	// 「DirectInput」オブジェクトの作成
	 if( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), 
		 DIRECTINPUT_VERSION,IID_IDirectInput8, (VOID**)&g_pDinput, NULL ) ) )
	 {
		 return hr;
	 }
    // 「DirectInputデバイス」オブジェクトの作成
    if( FAILED( hr = g_pDinput->CreateDevice( GUID_SysKeyboard,
		&g_lpDIDevice, NULL ) ) )
	{
		return hr;
	} 
	// デバイスをキーボードに設定
    if( FAILED( hr = g_lpDIDevice->SetDataFormat( &c_dfDIKeyboard ) ) )
	{
		return hr;
	}
	// 協調レベルの設定
    if( FAILED(hr= g_lpDIDevice->SetCooperativeLevel( 
		hWnd,DISCL_NONEXCLUSIVE | DISCL_BACKGROUND )) )
	{
		return hr;
	}
	
	// 入力制御開始
	g_lpDIDevice->Acquire();

	return S_OK;
}

//-----------------------------------------------------------------------------
// Name: InitD3D()
// Desc: Initializes Direct3D
//-----------------------------------------------------------------------------
HRESULT InitD3D( HWND hWnd )
{
    if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
        return E_FAIL;

    D3DPRESENT_PARAMETERS d3dpp; 
    ZeroMemory( &d3dpp, sizeof(d3dpp) );
    d3dpp.Windowed = TRUE; // true でウィンドウモード
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;

    if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
                                      D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                      &d3dpp, &g_pd3dDevice ) ) )
    {
        return E_FAIL;
    }
	D3DXCreateSprite( g_pd3dDevice, &g_pSprite ); // スプライトの作成
	D3DXCreateTextureFromFileEx( g_pd3dDevice, ImgFile, 0, 0, 0, 0, D3DFMT_A1R5G5B5,
		D3DPOOL_MANAGED, D3DX_FILTER_LINEAR, D3DX_FILTER_LINEAR,
		D3DCOLOR_ARGB(255,255,255,255), // 透過色の設定
		NULL, NULL, &g_pTexture); // テクスチャの読み込み

    return S_OK;
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
VOID Move()
{
		char key[256];
		HRESULT hr;

		hr = g_lpDIDevice->GetDeviceState( 256, key);
		if FAILED(hr)
		{
			return;
		}

			if(key[DIK_LEFT] & 0x80)
			{
				fPosition_X-=4;
			}
			if(key[DIK_RIGHT] & 0x80)
			{
				fPosition_X+=4;
			}
			if(key[DIK_UP] & 0x80)
			{
				fPosition_Y-=4;
			}
			if(key[DIK_DOWN] & 0x80)
			{
				fPosition_Y+=4;
			}
}

//-----------------------------------------------------------------------------
// Name: Cleanup()
// Desc: Releases all previously initialized objects
//-----------------------------------------------------------------------------
VOID Cleanup()
{
	g_lpDIDevice->Unacquire();

    if( g_pd3dDevice != NULL) 
        g_pd3dDevice->Release();

    if( g_pD3D != NULL)
        g_pD3D->Release();

	if( g_pSprite != NULL)
		g_pSprite->Release();

	if( g_pTexture != NULL)
		g_pTexture->Release();

	if( g_pDinput != NULL)
		g_pDinput->Release();
	
	if( g_lpDIDevice != NULL)
		g_lpDIDevice->Release();

}

//-----------------------------------------------------------------------------
// Name: Render()
// Desc: Draws the scene
//-----------------------------------------------------------------------------
VOID Render()
{
    if( NULL == g_pd3dDevice )
        return;

    // Clear the backbuffer to a blue color
    g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );
    
    // Begin the scene
    if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
    {
        // Rendering of scene objects can happen here
		D3DXVECTOR3 vec3Position(fPosition_X,fPosition_Y,0);

		// 引数に「D3DXSPRITE_ALPHABLEND」を設定し透過処理
		g_pSprite->Begin(D3DXSPRITE_ALPHABLEND); // スプライト描画開始

		// スプライト描画関数
		g_pSprite->Draw(g_pTexture, NULL, NULL, NULL, 0xFFFFFFFF);
		g_pSprite->End(); // スプライト描画終了

        // End the scene
        g_pd3dDevice->EndScene();
    }
// Present the backbuffer contents to the display
    g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
}

//-----------------------------------------------------------------------------
// Name: MsgProc()
// Desc: The window's message handler
//-----------------------------------------------------------------------------
LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg )
    {
        case WM_DESTROY:
            Cleanup();
            PostQuitMessage( 0 );
            return 0;

        case WM_PAINT:
            Render();
			Move();
            ValidateRect( hWnd, NULL );
            return 0;
    }

    return DefWindowProc( hWnd, msg, wParam, lParam );
}

//-----------------------------------------------------------------------------
// Name: WinMain()
// Desc: The application's entry point
//-----------------------------------------------------------------------------
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
{
    // Register the window class
    WNDCLASS wc = { sizeof(WNDCLASS), CS_CLASSDC, MsgProc, 0L, 0L, 
                      GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
                      "D3D Tutorial", NULL };
    RegisterClass( &wc );

    // Create the application's window
    HWND hWnd = CreateWindow( "D3D Tutorial", "D3D Tutorial 01: CreateDevice", 
                              WS_OVERLAPPEDWINDOW, 100, 100, 640, 480,
                              NULL, NULL, wc.hInstance, NULL );

	
    // Initialize Direct3D
    if( SUCCEEDED( InitD3D( hWnd ) ) )
    { 
        // Show the window
        ShowWindow( hWnd, SW_SHOWDEFAULT );
        UpdateWindow( hWnd );

        // Enter the message loop
        MSG msg; 
        while( GetMessage( &msg, NULL, 0, 0 ) )
        {
            TranslateMessage( &msg );
            DispatchMessage( &msg );
        }
    }
	// ダイレクトインプットの初期化関数を呼ぶ
	if(FAILED(InitDinput(hWnd)))
	{
		return 0;
	}

    UnregisterClass( "D3D Tutorial", wc.hInstance );
    return 0;
}
エラーが出る箇所は決まって、Move()関数内になります。
また、投稿の際に、「投稿禁止単語」がソース内に見つかったので、
その部分は削除させて頂きました。
宜しくお願い致します。

Repsol

ハンドルされていない例外&アクセス違反

#2

投稿記事 by Repsol » 14年前

申し訳ありません……。
件名とユーザー名を逆にして投稿してしまったようです。

しひ

Re: Repsol

#3

投稿記事 by しひ » 14年前

気になるところを挙げると、
・DirectInputを初期化する前にMove()関数が呼ばれている。
・WNDCLASSの初期化が違う。
・fPosition_X-=4ではなく fPosition_X-=4.0fと書くべき。

Repsol

ハンドルされていない例外&アクセス違反

#4

投稿記事 by Repsol » 14年前

しひさん、御返事ありがとうございます。

・WNDCLASSの初期化が違う。
すみません。
これは以降の単語が投稿禁止用語に設定されていましたので、削除させて頂きました。
正しくは「EX」が付きます。

・fPosition_X-=4ではなく fPosition_X-=4.0fと書くべき。
ありがとうございます。
直しました。

・DirectInputを初期化する前にMove()関数が呼ばれている。
一番解らないのがこれなんです。
下記のようにコードを書き換えましたら、アクセス違反の表示は消えました。

コード:

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
{

//-------------
//ウィンドウの初期化処理がここに記述されています。
//禁止単語が含まれているので省きます。
//-------------

    // Initialize Direct3D
    if( SUCCEEDED( InitD3D( hWnd ) ) )
    { 
        // Show the window
        ShowWindow( hWnd, SW_SHOWDEFAULT );
        UpdateWindow( hWnd );

        // Enter the message loop
        MSG msg; 
        while( GetMessage( &msg, NULL, 0, 0 ) )
        {
            TranslateMessage( &msg );
            DispatchMessage( &msg );
        }
	}
	// ダイレクトインプットの初期化関数を呼ぶ
	if(FAILED(InitDinput(hWnd)))
	{
		return 0;
	}
		Move();

    UnregisterClass( "D3D Tutorial", wc.hInstance );
    return 0;
}
ただ、自分でも文法がいまいち解らないせいか、
Move()関数を何処に置いたらいいのか、判然としません。
今の時点では、まだキャラクターは動きません。

しひ

Re: Repsol

#5

投稿記事 by しひ » 14年前

なるほど、確かに禁止単語だ。一体何が引っ掛かっているかと思いきや盲点でした。

Move()関数はひとまず前の場所で良いです。
ただ、メッセージループより前にDirectInputの初期化をしておかないと
MsgProc()関数内でMove()関数が呼ばれたときにアクセス違反になってしまいます。
「ウィンドウの初期化処理がここに記述されています。」付近でDirectInputの初期化をしてねってことです。
暇なプログラマ さんが書きました:fPosition_X と fPosition_Y がキーを押されたときに移動していることが分かりますが。
それ以外で、fPosition_X とfPosition_Yが使用されていません。

キャラクターが画像表示部分に追加すればよいと思います。(どこにあるか分からなかった)
Render()関数内で使われているようですね。

Repsol

ハンドルされていない例外&アクセス違反

#6

投稿記事 by Repsol » 14年前

御返事ありがとうございます。
しひ さんが書きました:Move()関数はひとまず前の場所で良いです。
ただ、メッセージループより前にDirectInputの初期化をしておかないと
MsgProc()関数内でMove()関数が呼ばれたときにアクセス違反になってしまいます。
「ウィンドウの初期化処理がここに記述されています。」付近でDirectInputの初期化をしてねってことです。
成る程。ありがとうございます。
DirectInputの初期化をメッセージループの前に置くのですね。
暇なプログラマ さんが書きました:fPosition_X と fPosition_Y がキーを押されたときに移動していることが分かりますが。
それ以外で、fPosition_X とfPosition_Yが使用されていません。
ありがとうございます。
下記のように修正しました。

コード:

VOID Render()
{
    if( NULL == g_pd3dDevice )
        return;

    // Clear the backbuffer to a blue color
    g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );
    
    // Begin the scene
    if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
    {
		D3DXVECTOR3 vec3Position(fPosition_X,fPosition_Y,0);

		g_pSprite->Begin(D3DXSPRITE_ALPHABLEND); 
		g_pSprite->Draw(g_pTexture, NULL, NULL, &vec3Position, 0xFFFFFFFF);
		g_pSprite->End(); 

        // End the scene
        g_pd3dDevice->EndScene();
    }
// Present the backbuffer contents to the display
    g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
}
ただ、まだこれでもキャラクターが動きません。
「&vec3Position」の値を変更すると、キャラクタの座標は変わるので、これは間違ってはいないと思うのですが、
キーボード入力による操作(キャラクターの移動)を受け付けないのです。

Move()関数は、ひしさんの仰るとおり、以前の位置から変えておりません。

やはりMove()関数内の記述に誤りがあるのでしょうか?

コード:

VOID Move()
{
		
		HRESULT hr;

		BYTE diks[256];

		hr = g_lpDIDevice->GetDeviceState( sizeof(diks), &diks);
		if FAILED(hr)
		{
			return;
		}

			if(diks[DIK_LEFT] & 0x80)
			{
				fPosition_X-=4.0f;
			}
			if(diks[DIK_RIGHT] & 0x80)
			{
				fPosition_X+=4.0f;
			}
			if(diks[DIK_UP] & 0x80)
			{
				fPosition_Y-=4.0f;
			}
			if(diks[DIK_DOWN] & 0x80)
			{
				fPosition_Y+=4.0f;
			}
}
何度も申し訳ありません。

しひ

Re: Repsol

#7

投稿記事 by しひ » 14年前

はい、動かないと思います。
余計かもしれませんが、一応ウィンドウについてざっくり説明します。
詳しくは猫でも分かる等を読んで下さい。

ウィンドウは常にメッセージを発生するので、そのメッセージに応じた処理をさせる必要があります。
その処理を振り分けているのがMsgProc()関数のところです。
WM_DESTROYというメッセージが来たらcase WM_DESTROY:以下を、
WM_PAINTというメッセージが来たらcase WM_PAINT:以下を処理させます。

WM_PAINTが発生するのはウィンドウを描き直す必要があるときです。
具体的には、ウィンドウの大きさを変更したとき、被っていた他のウィンドウが無くなったときなど。
現在、case WM_PAINT:以下にMove()関数やRender()関数が書かれています。
このままだと、ウィンドウの大きさを変更したり何だりしたときにしか、入力の処理や描画の処理をしてくれません。
アクションゲームなどの常に入力と描画をするものにとって、それでは困ります。

それでどうするかというと、メッセージ云々に係わらず常に入力の監視や描画を行うようにします。
while文内に入力や描画の処理を書き、ウィンドウを消したりしない限りループさせ続けるのが定石です。

コード:

// メッセージの処理
LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
	switch( msg )
	{
		case WM_DESTROY: // ウィンドウを破棄するとき
			PostQuitMessage( 0 );
		return 0;
	}

	return DefWindowProc( hWnd, msg, wParam, lParam );
}

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
{
	/* 初期化をする */
 
	while( 1 ) // 無限ループ
	{
		// 入力や描画など
		Move();
		Render();
		Sleep( 0 );
	
		// ここでメッセージを受け取る
		MSG msg;
		while ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
		{
			TranslateMessage( &msg );
			DispatchMessage( &msg );
		}
		
		if( msg.message == WM_QUIT ) break; // ウィンドウを終了するときに抜ける
	}

	/* 後処理をする */

	return 0;
}
ゲームプログラミングの館では、メッセージ云々をDxLibにやらせていたと思いますが
大体こんな構造になっているはず……。

Repsol

Re:

#8

投稿記事 by Repsol » 14年前

しひさん、ありがとうございます。
ご呈示されたコードで、無事に動きました。

やはり文法的な事柄を理解していないと見るべきなのでしょうか。
しひさんの文章を読み、メッセージ処理の概念が徐々に掴めてきました。
仰るとおり、書籍やサイトを読みながら、勉強しようと思います。
しひ さんが書きました:メッセージ云々に係わらず常に入力の監視や描画を行うようにします。
while文内に入力や描画の処理を書き、ウィンドウを消したりしない限りループさせ続けるのが定石です。
基本的な流れとしては、初期化した後に、メッセージに関わらない箇所にMove()関数等を書いていくと認識しました。
その際、ゲーム用の無限ループを記述するのも忘れていました。
申し訳ない限りです。

本当にありがとうございます。
解決チェックを押させて頂きますね。

閉鎖

“C言語何でも質問掲示板” へ戻る