Win32APIにてユニークポインタを用いた際にWM_PAINT内部でプログラムがが止まる

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
しろうと

Win32APIにてユニークポインタを用いた際にWM_PAINT内部でプログラムがが止まる

#1

投稿記事 by しろうと » 4年前

現在Windows10にてVisual Studio 2017を用いてwin32apiのプログラミングを行っているものです。

ユニークポインタを用いたWM_PAINT内部のメソッドを動作させようとすると止まってしまうという問題が生じたため、相談いたします。
問題というのは、WM_PAINT内部でユニークポインタによるメソッドを使用すると挙動が止まるというものです。
なお、今回作成したプログラムではほかにもWM_KEYDOWN、デッドタイムなどにも同じようにメソッドを用いているのですが、そちらは問題なく挙動しています。
そのため、メソッド内部の動作がおかしいのではないかと思い、メソッド内部の実行処理をなくしてみたのですが、それでも止まる状態にあります。
問題の洗い出しのためにデバッグを行った際には『例外がスローされました:読み取りアクセス違反。
this->process_current_level._Mypair._Myval2 が nullptr でした。』というエラーが帰ってきました。
しかし、nullptrであればほかの同クラスのメソッドでも止まるはずです。
いろいろ試してはみたのですが、自分ではもうわかりません。

なぜ、WM_PAINT内で呼び出したメソッドのみが止まるのでしょうか。
回答をよろしくお願いします。


下にコードを貼ります。
後、WM_PAINT内部に普通に実行内容を書いた場合には普通に動きます。

application_handler.h

コード:

#pragma once
#include "include.h"
#include "process.h"

class Application_Handler {
public:
	Application_Handler(HWND hWnd) {
		hwnd = hWnd;
	}
	~Application_Handler(void) {

	}

	void init(Process*);
	void input(WPARAM wp);
	void update();
	void render();

private:
	HWND hwnd;
	int main_phase;
	int chapter_phase;
	unique_ptr<Process> process_current_level;
};
application_handler.cpp

コード:

#include "application_handler.h"

void Application_Handler::init(Process* lev) {
	process_current_level.reset(lev);
}

void Application_Handler::input(WPARAM wp) {
	process_current_level->input(wp);
}

void Application_Handler::update() {
	process_current_level->update();
}

void Application_Handler::render() {
	process_current_level->render();
}

process.h

コード:

#pragma once
#include "include.h"

class Process {
public:
	Process() {
	}

	virtual void input(WPARAM) = 0;
	virtual void update() = 0;
	virtual void render() = 0;
};

class Chapter1 : public Process {
public:
	Chapter1(HWND hWnd) : Process() {
		hwnd = hWnd;
		x = 0;
		flag = 1;
		hBitmap = (HBITMAP)LoadImage(NULL, "aaa.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION);
		GetObject(hBitmap, sizeof(BITMAP), &bmp);
	}

	void input(WPARAM) override;
	void update() override;
	void render() override;
private:
	PAINTSTRUCT ps;
	HWND hwnd;
	int x;
	bool flag;
	HBITMAP hBitmap;
	BITMAP bmp;
	HDC main_hdc;
	HDC map;
};
process.cpp

コード:

#include "process.h"

void Chapter1::input(WPARAM wp) {
	switch (wp) {
	case VK_A:
		MessageBox(0,0,0,0);
		break;
	}
}

void Chapter1::update() {
	if (flag == 1) {
		x++;
		if (x > 100) {
			flag = !flag;
		}
	}
	else {
		x--;
		if (x < -100) {
			flag = !flag;
		}
	}
}

void Chapter1::render() {
}
system.cpp

コード:

#include "system.h"
Application_Handler* System::application_handler;

LRESULT _stdcall System::proc(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param) {
	HINSTANCE hinst = (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE);//GetModuleHandle(NULL);

	switch (msg) {
	case WM_CREATE:

		break;
	case WM_DESTROY:

		PostQuitMessage(0);
		break;
	case WM_KEYDOWN:
		application_handler->input(w_param);
		break;
	case WM_PAINT:
		application_handler->render();
		break;
	}

	return DefWindowProc(hwnd, msg, w_param, l_param);
};

bool System::init_window() {
	WNDCLASSEX Main_Win_Class = {
		sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW ,proc,NULL,NULL,main_hinst,LoadIcon(NULL, IDI_APPLICATION),LoadCursor(NULL,IDC_ARROW),(HBRUSH)GetStockObject(BLACK_BRUSH),"TITLEMENU","MAIN"
	};
	if (!RegisterClassEx(&Main_Win_Class))
		return false;
	main_hwnd = CreateWindowEx(
		WS_EX_WINDOWEDGE | WS_EX_OVERLAPPEDWINDOW | WS_EX_COMPOSITED, "MAIN", "fast break", WS_CAPTION | WS_VISIBLE | WS_OVERLAPPEDWINDOW ^ WS_SIZEBOX ^ WS_MAXIMIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, (rect.right - rect.left), (rect.bottom - rect.top), NULL, LoadMenu(main_hinst, "MAIN_MENU"), main_hinst, NULL
	);
	SetMenu(main_hwnd, NULL);
	return true;
}

void System::run() {
	application_handler->init(new Chapter1(main_hwnd));

	while (main_msg.message != WM_QUIT) {
		if (PeekMessage(&main_msg, NULL, NULL, NULL, PM_REMOVE)) {
			DispatchMessage(&main_msg);
			application_handler->update();
		}
		else {
			fps_handler->Fps_Handle_Control(main_hwnd);
		}
	}

	delete application_handler;
}

shio
記事: 8
登録日時: 4年前

Re: Win32APIにてユニークポインタを用いた際にWM_PAINT内部でプログラムがが止まる

#2

投稿記事 by shio » 4年前

LoadImageの第一引数をmain_hinstやGetModuleHandle(0)にしてみたらどうですか。

ISLe
記事: 2650
登録日時: 13年前
連絡を取る:

Re: Win32APIにてユニークポインタを用いた際にWM_PAINT内部でプログラムがが止まる

#3

投稿記事 by ISLe » 4年前

System::init_windowやSystem::runを呼び出す箇所のソースがないので想像になりますが…

CreateWindowExの引数からWS_VISIBLEを取り除くと良いかもしれません。

メッセージループの直前でポインタをセットしてますけど、WS_VISIBLEがあると速攻で描画されます。
それがWM_PAINT内部でnullptrの例外が発生する原因だと思います。

あるいは、
if (process_current_level) process_current_level->render();
というふうにして、ポインタを所有しているときだけ呼び出すのが良いでしょう。

実行中に、ポインタの中身を切り替えて使う想定に見えるので、後者をお勧めします。

しろうと

Re: Win32APIにてユニークポインタを用いた際にWM_PAINT内部でプログラムがが止まる

#4

投稿記事 by しろうと » 4年前

有難うございます。
if (process_current_level) process_current_level->render();
とすることで動くようになりました。
おっしゃられていたようにポインタの生成前に描画するように呼ばれていたためと考えられます。
助けていただきありがとうございました。

また、ここからは追加となるのですが、メソッドで行った描画とWM_PAINT内に直接かいた描画では
メソッドで行った描画の方がはるかに遅いです。
この原因として何か考えられるものはあるでしょうか。

shio
記事: 8
登録日時: 4年前

Re: Win32APIにてユニークポインタを用いた際にWM_PAINT内部でプログラムがが止まる

#5

投稿記事 by shio » 4年前

LoadImageはファイルから読むときはインスタンスがNULLでもよかったんですね。
適当なこと言ってすみませんでした。
WM_PAINT内に直接かく内容とメソッド内の処理は全く同じなんですか?

ISLe
記事: 2650
登録日時: 13年前
連絡を取る:

Re: Win32APIにてユニークポインタを用いた際にWM_PAINT内部でプログラムがが止まる

#6

投稿記事 by ISLe » 4年前

WM_PAINTに直接書くかどうかだけで動作速度が大きく変わることはないはずです。

メソッドで描画を行うようにした際、WM_PAINT内に直接かいたのと比べて欠落したり変化している処理はありませんか?

例えば、Chapter1クラスに、PAINTSTRUCT型のpsメンバがありますが、使われている様子がありません。
BeginPaintとEndPaintを適切に呼ばないと、無効領域がクリアされません。
無効領域がクリアされるまで、WM_PAINTはひっきりなしに繰り返し呼び出されます。

おそらく、system.cppの
fps_handler->Fps_Handle_Control(main_hwnd);
で、フレームレートに沿ってInvalidateRect関数(あるいはUpdateWindow?)を呼び出しているものと思います。
そのようにして、1フレームに1回の描画を期待しても、無効領域をクリアしない限りそうなりません。
オフトピック
余談ですが、特にフレームレートを意識したプログラムなら、ウィンドウの背景ブラシをNULLにして、背景の塗り潰し処理が行われないようにし、WM_PAINT内でクライアント領域全体を描画するようにするのをお勧めします。
他のアプリケーションのウィンドウからフォーカスを切り替えたときなどに、一瞬背景色で塗り潰されて見えるチラつきを防ぐことができます。

フレームレートを前提とした処理を一時停止して、GDIを前面にした処理を行いたい場合があったなら、WM_ERASEBKGNDメッセージをハンドリングして、背景の塗り潰しをするかどうか切り替えも可能です。

しろうと

Re: Win32APIにてユニークポインタを用いた際にWM_PAINT内部でプログラムがが止まる

#7

投稿記事 by しろうと » 4年前

返信ありがとうございます。
原因がわかりましたので後学の方のためにも記しておこうと思います。

速度が低下した原因ですが、デッドタイムをとらえ間違えていたのが原因だったようです。

当初、私は下記のように制御していました。

コード:

void System::run() {
	while (main_msg.message != WM_QUIT) {
		if (PeekMessage(&main_msg, NULL, NULL, NULL, PM_REMOVE)) {
			DispatchMessage(&main_msg);
			application_handler->update();
		}
		else {
			fps_handler->Fps_Handle_Control(main_hwnd);
		}
	}

	delete application_handler;
}
デッドタイム中にゲーム処理をさせようとしていたわけです。
私はデッドタイムを名の通り無駄な時間のことだと思っていたのですが、デッドタイムとはメッセージを処理していない状態を指すそうです。
つまり、私は何か入力をし続けないとゲーム処理が更新されないプログラムを作っていたことになります。
(入力時にはメッセージが生成されるため)

ここが大きな間違いでした。
正しくは

コード:

void System::run() {
	while (main_msg.message != WM_QUIT) {
		if (PeekMessage(&main_msg, NULL, NULL, NULL, PM_REMOVE)) {
			DispatchMessage(&main_msg);
		}
		else {
			application_handler->update();
			fps_handler->Fps_Handle_Control(main_hwnd);
		}
	}

	delete application_handler;
}
としなければいけません。

最後に、熱心に付き合っていただいたお二人に感謝申し上げます。
ありがとうございました。

返信

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