ページ 11

右クリックで何度もウィンドウを生成したい。

Posted: 2012年4月16日(月) 08:52
by s707
開発環境はvc++2010 express,DXライブラリです。

右クリックを押してウィンドウを生成し
ウィンドウを閉じた後
もう一度生成することが出来ません。

TrackPopup関数を使っても処理が止まらないように
マルチスレッドを使っています。

ご助力の程、よろしくお願いします。

コード:


//main.cpp
#include <windows.h>
#include <process.h>
#include <WindowsX.h>
#include "DxLib.h"
#include "dayData.h"
#include "draw_clock.h"
#include "Setup.h"
#include "resource.h"
#include <windowsx.h>

#define APP_NAME TEXT("clock")
#define chara_width 64
#define WM_POSTENDTHREAD (WM_USER)// スレッド終了を伝えるメッセージ

void jihou(struct dayData* day);
void plan(struct dayData* day);
void Time(struct dayData* day);
void date(struct dayData* day);

LRESULT CALLBACK WindowProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK WindowProc2 (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
unsigned __stdcall mythread(void *lpx);

static HANDLE hThread;
static HWND hDlg[3];
static int chara_id;
HINSTANCE hInst,hInst2;
POINT pt;
struct dayData day[471];

unsigned __stdcall mythread(void *lpx){
	WNDCLASSEX wc;
	HWND hWnd;
	MSG msg;

	// ウィンドウクラスの情報を設定
	wc.cbSize = sizeof(wc);               // 構造体サイズ
	wc.style = CS_HREDRAW | CS_VREDRAW;   // スタイル
	wc.lpfnWndProc = WindowProc2;             // ウィンドウプロシージャ
	wc.cbClsExtra = 0;                    // 拡張情報1
	wc.cbWndExtra = 0;                    // 拡張情報2
	wc.hInstance = hInst;                 // インスタンスハンドル
	wc.hIcon = (HICON)LoadImage(          // アイコン
		NULL, MAKEINTRESOURCE(IDI_APPLICATION), IMAGE_ICON,
		0, 0, LR_DEFAULTSIZE | LR_SHARED
	);
	wc.hIconSm = wc.hIcon;                // 子アイコン
	wc.hCursor = (HCURSOR)LoadImage(      // マウスカーソル
		NULL, MAKEINTRESOURCE(IDC_ARROW), IMAGE_CURSOR,
		0, 0, LR_DEFAULTSIZE | LR_SHARED
	);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); // ウィンドウ背景
	wc.lpszMenuName = NULL;                     // メニュー名
	wc.lpszClassName = _T("Default Class Name");// ウィンドウクラス名
	
	// ウィンドウクラスを登録する
	if( RegisterClassEx( &wc ) == 0 ){ return 1; }

	// ウィンドウを作成する
	hWnd = CreateWindow(
		wc.lpszClassName,      // ウィンドウクラス名
		_T("Sample Program"),  // タイトルバーに表示する文字列
		WS_OVERLAPPEDWINDOW,   // ウィンドウの種類
		CW_USEDEFAULT,         // ウィンドウを表示する位置(X座標)
		CW_USEDEFAULT,         // ウィンドウを表示する位置(Y座標)
		CW_USEDEFAULT,         // ウィンドウの幅
		CW_USEDEFAULT,         // ウィンドウの高さ
		NULL,                  // 親ウィンドウのウィンドウハンドル
		NULL,                  // メニューハンドル
		hInst2,                 // インスタンスハンドル
		NULL                   // その他の作成データ
	);
	if( hWnd == NULL ){ return 1; }

	// ウィンドウを表示する
	ShowWindow( hWnd, SW_SHOW );
	UpdateWindow( hWnd );

	while( 1 )
	{
		BOOL ret = GetMessage( &msg, NULL, 0, 0 );  // メッセージを取得する
		if( ret == 0 || ret == -1 )
		{
			// アプリケーションを終了させるメッセージが来ていたら、
			// あるいは GetMessage() が失敗したら( -1 が返されたら )、ループを抜ける
			break;
		}
		else
		{
			// メッセージを処理する
			TranslateMessage( &msg );
			DispatchMessage( &msg );
		}
	}
	
	return 0;
}

LRESULT CALLBACK WindowProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	static int top,left;

	switch(uMsg) {
	case WM_NCHITTEST:
		return HTCAPTION;
	case WM_LBUTTONDOWN:
		{
			// マウス状態管理用変数
			int nClickNow = 0 , nClickPrev = 0;

			// マウス状態の更新
			nClickNow = (GetMouseInput() & MOUSE_INPUT_LEFT);

			if( nClickNow != nClickPrev){
				int MouseX = 0,MouseY = 0;
				GetMousePoint( &MouseX , &MouseY ) ;// マウスの位置を取得

				if(MouseX > chara_x && MouseX < chara_x + chara_width && MouseY > chara_y && MouseY < chara_y + chara_width ){
					date(day);//日付を再生
					Time(day);//時刻を再生
					plan(day);
				}
			}
			SendMessage(GetMainWindowHandle(), WM_NCLBUTTONDOWN, HTCAPTION, 0);//キャラクター画像の座標以外の時

			// 今回のクリック状態を保持する
			nClickPrev = nClickNow;
			break;
		}
	case WM_COMMAND:
		switch(LOWORD(wParam)){
		case IDM_OPTION:
			if(!hDlg[0])hDlg[0] = CreateDialog(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG), GetMainWindowHandle(), MyDlgProc);
			break;
		case IDM_EXIT:			
			Setup_save(day);//設定を書き込む
			SendMessage(hWnd, WM_CLOSE, 0, 0);
			break;
		case IDM_VERSION:
			if(!hDlg[1])hDlg[1] = CreateDialog(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG2), GetMainWindowHandle(), MyDlgProc2);
			break;
		}
		break;
	case WM_RBUTTONDOWN:
		if (hThread == NULL) {
			hThread = (HANDLE)_beginthreadex(NULL, 0, mythread, NULL, 0, NULL);
		}
		break;
	case WM_POSTENDTHREAD:
		if (WaitForSingleObject(hThread, 0) == WAIT_TIMEOUT) {
			// まだスレッドが終了してなかったらメッセージを投げ直す
			// ※デッドロック回避のため
			PostMessage(hWnd, WM_POSTENDTHREAD, wParam, lParam);
		}
		else {
			// スレッドの終了を検知できたら戻り値を取得してハンドルをクローズ
			DWORD ExitCode;
			ExitCode = -1;
			GetExitCodeThread(hThread, &ExitCode);
			CloseHandle(hThread);
			hThread = NULL;
		}
		break;
	case WM_CLOSE:
		DestroyWindow(hWnd);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	}
	return 0;
}


LRESULT CALLBACK WindowProc2 (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	switch(uMsg) {
	case WM_RBUTTONDOWN:
		HMENU hMenu,hSubMenu;
		pt.x = LOWORD(lParam);
		pt.y = HIWORD(lParam);

		hMenu = LoadMenu(hInst,"MYMENU");
		hSubMenu = GetSubMenu(hMenu ,0);
		ClientToScreen(GetMainWindowHandle() , &pt);
		TrackPopupMenu(hSubMenu,TPM_LEFTALIGN,pt.x,pt.y,0,GetMainWindowHandle(),NULL);
		DestroyMenu(hMenu);
		break;
	case WM_CLOSE:
		PostMessage(GetMainWindowHandle(), WM_POSTENDTHREAD, 0, 0);// スレッド終了を伝えるメッセージを親ウィンドウに投げる
		DestroyWindow(hWnd);
		break;
	default:
		return ( DefWindowProc(hWnd , uMsg , wParam , lParam) );
	}

	return 0;
}


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

	// 画面モードの変更
	SetGraphMode( 1680 , 1050 , 32 ) ;//パソコンの解像度に合わせる

	SetMainWindowText("clock");
	ChangeWindowMode( TRUE );

	SetUseBackBufferTransColorFlag( TRUE );// ウインドウの透過色モードON
	SetWindowStyleMode(2);
	SetAlwaysRunFlag( TRUE );

	SetHookWinProc( WindowProc );
	if ( DxLib_Init( ) == -1 ) return -1;

	SetDrawScreen(DX_SCREEN_BACK);

	Load_Graph();
	plan(day);//予定を音声再生する	

	while ( ProcessMessage( ) == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 ) {
		ClearDrawScreen( );

		draw_face(day);
		draw_clock(day);

		jihou(day);//全ての描画関数が終わってから呼ぶ

		ScreenFlip( );
	}
	DxLib_End();
	return 0;
}


Re: 右クリックで何度もウィンドウを生成したい。

Posted: 2012年4月16日(月) 16:15
by ISLe
mythread関数のメッセージループを永久に抜けないので、hThreadがNULLに戻らず、結果としてウィンドウを再び開くことができません。

GetMessageが0以外(-1を除く)を返す条件を確認してウィンドウアプリケーションとして適切な処理を行なってください。

あとmythread関数でウィンドウクラスの登録やウィンドウの作成に失敗したときも後処理が走らないので同様の不具合が出ます。

Win32 APIを使ったウィンドウアプリケーションの基本的なプログラミングについてきちんと勉強したほうが良いと思います。
少なくとも参考にするコードは意味を理解して使うようにしましょう。

Re: 右クリックで何度もウィンドウを生成したい。

Posted: 2012年4月19日(木) 04:14
by s707
ISLe様、ご返信ありがとうございます。
コードを見直して次に進めました。

http://dixq.net/forum/viewtopic.php?f=3&t=4881
現在はこちらを参考に
TrackPopupMenu関数を別スレッドから
呼び出そうとしている所です。

Re: 右クリックで何度もウィンドウを生成したい。

Posted: 2012年4月24日(火) 17:36
by s707
ISLe様、解決しました。
ありがとうございます。