• ベストアンサー

Win32APIにて、簡易タイピングソフトを作成したのですが、うまく実行されません。

いつもお世話になっております。 小生、只今、WinXPSP3上でC言語とWin32APIを使用し、BCC5.5.1でコンパイルしながらWindowsプログラミングを勉強しています。 今回、質問させて頂きたいのは、「猫でもわかるWindowsプログラミング第2版」の第5章簡易タイピングソフトを作るの回で、掲載されてあったコードを自分なりに改造して、実行してみたのですが、ウィンドウが起動しても、真っ白の状態でタイピングの問題が出題されないようになってしまいました。 以下にコードを掲載致します。 ※文字数制限にひっかかったので、改造した部分だけを掲載させて頂きます。 #include <windows.h> #include <time.h> #pragma comment(lib, "winmm.lib") LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE, LPCSTR); BOOL InitInstance(HINSTANCE, int, LPCSTR); DWORD TypeStart(HWND, char*); //ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow, LPCSTR szClassName) { HWND hWnd; hWnd = CreateWindow( szClassName, "EasyTyping", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 200, 200, NULL, NULL, hInst, NULL); if(!hWnd){ return FALSE; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; } //ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { HDC hdc; PAINTSTRUCT ps; MMTIME mm; char szMondai[32] = {0}; char szInput[32] = {0}; char szCheck[32] = {0}; DWORD dwStart, dwEnd; BOOL bStart = FALSE, bSeikai = TRUE; switch(msg){ case WM_CREATE: srand((unsigned)time(NULL)); break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); TextOut(hdc, 0, 0, szMondai, (int)strlen(szMondai)); TextOut(hdc, 0, 40, szInput, (int)strlen(szInput)); if(bSeikai == TRUE){ SetTextColor(hdc, RGB(0, 0, 0)); } else{ SetTextColor(hdc, RGB(255, 0, 0)); } TextOut(hdc, 0, 80, szCheck, (int)strlen(szCheck)); EndPaint(hWnd, &ps); break; case WM_CHAR: if(wp == VK_SPACE && !bStart){ bStart = TRUE; dwStart = TypeStart(hWnd, szMondai); break; } if(bStart == FALSE){ return DefWindowProc(hWnd, msg, wp, lp); } if(wp == VK_ESCAPE){ strcpy(szMondai, ""); strcpy(szInput, ""); strcpy(szCheck, ""); InvalidateRect(hWnd, NULL, TRUE); bStart = FALSE; break; } wsprintf(szInput, "あなたの入力 = \"%c\"", (int)wp); if(szMondai[6] == szInput[14]){ bSeikai = TRUE; mm.wType = TIME_MS; timeGetSystemTime(&mm, sizeof(MMTIME)); dwEnd = mm.u.ms; wsprintf(szCheck, "反応時間[%dミリ秒]", dwEnd - dwStart); dwStart = TypeStart(hWnd, szMondai); } else{ bSeikai = FALSE; MessageBeep(MB_OK); strcpy(szCheck, "タイプミス!"); } InvalidateRect(hWnd, NULL, TRUE); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, msg, wp, lp); } return 0; } DWORD TypeStart(HWND hWnd, char *p_szMondai) { int n; MMTIME mm; n = rand() % 26; wsprintf(p_szMondai, "問題 = \"%c\"", 'a' + n); mm.wType = TIME_MS; timeGetSystemTime(&mm, sizeof(MMTIME)); InvalidateRect(hWnd, NULL, TRUE); return mm.u.ms; } #endif 以上です。 掲載されているコードは余りにもグローバル変数が多く、 自分自身余り、グローバル変数は好きではないので、関数内で収まるようにと改造したのですが。。。 お忙しい中、申し訳ございませんが、先輩方、アドバイス宜しくお願い致します。

質問者が選んだベストアンサー

  • ベストアンサー
  • Wr5
  • ベストアンサー率53% (2173/4061)
回答No.1

>char szMondai[32] = {0}; >char szInput[32] = {0}; により… >case WM_PAINT: >hdc = BeginPaint(hWnd, &ps); >TextOut(hdc, 0, 0, szMondai, (int)strlen(szMondai)); >TextOut(hdc, 0, 40, szInput, (int)strlen(szInput)); では常に空になっていますので、何も表示されないのは正常な動作です。 # 意図した動作とは違うでしょうけど、プログラムは「書かれたとおり」に動作しています。 元々、上記の2つはグローバル変数だったのではないですか? それぞれの変数(グローバル変数とローカル変数)の寿命について勉強されるとよいでしょう。 # あと、Windowsのメッセージ処理の単位。

HackHack
質問者

補足

Wr5さん、いつもご親切、ご丁寧に回答頂きありがとうございます。 ご指摘頂いた、内容にお答えさせて頂きます。 >それぞれの変数(グローバル変数とローカル変数)の寿命について勉強されるとよいでしょう。 変数のスコープについては事前にしらべ、グローバル変数は宣言したと同時に初期化されると分かりました。 (それを真似し、char szMondai[32] = {0};としています。) また、ローカル変数についても自分なりに熟知している所存です。 >元々、上記の2つはグローバル変数だったのではないですか? 仰る通り、グローバル変数でしたが、どうしても、グローバル変数を使うコード内容には自分自身思えないのです。 以下に書籍に掲載してあるコード(一部省略させて頂きます。)を記述させて頂きます。 #include <windows.h> #include <time.h> LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE, LPCSTR); BOOL InitInstance(HINSTANCE, int, LPCSTR); int TypeStart(HWND); char szMondai[32], szInput[32], szCheck[32]; DWORD dwStart, dwEnd; BOOL bStart = FALSE, bSeikai = TRUE; LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { HDC hdc; PAINTSTRUCT ps; MMTIME mm; switch(msg){ case WM_CREATE: srand((unsigned)time(NULL)); break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); TextOut(hdc, 0, 0, szMondai, (int)strlen(szMondai)); TextOut(hdc, 0, 40, szInput, (int)strlen(szInput)); if(bSeikai == TRUE){ SetTextColor(hdc, RGB(0, 0, 0)); } else{ SetTextColor(hdc, RGB(255, 0, 0)); } TextOut(hdc, 0, 80, szCheck, (int)strlen(szCheck)); EndPaint(hWnd, &ps); break; case WM_CHAR: if(wp == VK_SPACE && !bStart){ bStart = TRUE; TypeStart(hWnd); break; } if(bStart == FALSE){ return DefWindowProc(hWnd, msg, wp, lp); } if(wp == VK_ESCAPE){ strcpy(szMondai, ""); strcpy(szInput, ""); strcpy(szCheck, ""); InvalidateRect(hWnd, NULL, TRUE); bStart = FALSE; break; } wsprintf(szInput, "あなたの入力 = \"%c\"", (int)wp); if(szMondai[6] == szInput[14]){ bSeikai = TRUE; mm.wType = TIME_MS; timeGetSystemTime(&mm, sizeof(MMTIME)); dwEnd = mm.u.ms; wsprintf(szCheck, "反応時間[%dミリ秒]", dwEnd - dwStart); TypeStart(hWnd); } else{ bSeikai = FALSE; MessageBeep(MB_OK); strcpy(szCheck, "タイプミス!"); } InvalidateRect(hWnd, NULL, TRUE); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, msg, wp, lp); } return 0; } int TypeStart(HWND hWnd) { int n; MMTIME mm; n = rand() % 26; wsprintf(szMondai, "問題 = \"%c\"", 'a' + n); mm.wType = TIME_MS; timeGetSystemTime(&mm, sizeof(MMTIME)); dwStart = mm.u.ms; InvalidateRect(hWnd, NULL, TRUE); return 0; } 以上です。 グローバル変数を使わずに上記と同じ動きをするコードを作りたいのですが。。。 以上、ご確認と申し訳ございませんが、再度、アドバイス宜しくお願い致します。 乱文で申し訳ございません。

その他の回答 (1)

回答No.2

Wr5さんの回答の中でおそらく現在HackHackさんにとって最も重要なのは 「寿命」の二文字ではないかと推測します。 おそらくコードを使うと以下のように簡略化して説明できると思います。 現在 void f(){ int LifeSpanCheck=0; printf("%d",LifeSpanCheck); ++LifeSpanCheck; } といった関数を用意して int i=10; while (i--) f(); とでもして読んでみてください。 このとき、LifeSpanCheckはローカル変数なので「毎回関数が終了するときに必ず寿命が終わって破棄され」そして、関数が始まったら次の一文int LifeSpanCheck=0; で、毎回生成されそのとき0に初期化されます。 ゆえに、何回繰り返しても0しか出力されません。 ではグローバル変数ではどうでしょう? int LifeSpanCheck=0; void f(){ printf("%d",LifeSpanCheck); ++LifeSpanCheck; } この場合、LifeSpanCheckはプログラムが起動されたときに誕生し、プログラムが終了するまで生きているので、関数内でインクリメントするたびに値は増えていきます。 よって、0123456789と出力されます。 プロシージャも関数ですので全く同じ原理です。 char szMondai[32] = {0}; char szInput[32] = {0}; char szCheck[32] = {0}; DWORD dwStart, dwEnd; BOOL bStart = FALSE, bSeikai = TRUE; これらもまた、関数開始直後に生成され、毎回終了時に破棄されてしまいますので WM_CHARで呼ばれ、関数を抜け(またはInvalidateRectによる画面の無効化で別途呼び出されたとしても)再びプロシージャが呼ばれて今度WM_PAINT内に入った時、既にWM_CHAR内で処理した内容は「跡形もなくなってしまっている」のです。 関数内で宣言しながらグローバル変数と同じ寿命を持たせるには staticキーワードを使用します。 (関数内で宣言しているため関数外でその変数を使うことはできませんが、寿命はグローバル変数と同じになります。) void f(){ static int LifeSpanCheck=0; printf("%d",LifeSpanCheck); ++LifeSpanCheck; } これで、10回呼び出せば 0123456789と出力されるようになりました。 同じように static char szMondai[32] = {0}; static char szInput[32] = {0}; static char szCheck[32] = {0}; static DWORD dwStart, dwEnd; static BOOL bStart = FALSE, bSeikai = TRUE; で、関数を抜けても死なない変数が作れます。 それ以外の部分に別の問題が潜んでいる可能性もありますが とりあえず、それを試してみてください。

HackHack
質問者

お礼

ご回答頂き誠にありがとうございます。 提示されたソースコードを記述させて頂き、コンパイル、実行してみたところ、仰った通りの動作を確認させて頂きました。 その動作と自分のWndProc関数の動作を何回も見比べて、やっと、何故、コードが動作しないのかがわかりました!! LongSecretさん、Wr5さん。 本当にお忙しい中、アドバイス頂き誠にありがとうございました。 心より感謝申し上げます。

関連するQ&A