マウスの周辺を拡大して表示するソフトウェアをつくっています。
開発環境はWindows XP, VC++6.0, SDKです。
マウスの周辺を拡大表示することと、それを追跡することはできたのですが、
タイマをつかっているせいか、画面がチカチカしてしまいます。
また、プログラムを実行した時点で一番上にある情報しか得られません。
そのウィンドウを閉じたり、そのウィンドウの上に新しいウィンドウを開いたとしても
取得できる情報はかわりません。
情報が更新されないという問題が発生しています。
どのようにすればよいでしょうか?
以下に私のつくったプログラムをのせておきます。
どなたかおわかりになる方がいらっしゃいましたら、お教えください。
よろしくお願いいたします。
---以下、ソースです---
WINAPI WndProc(
HWND hwnd,
UINT message,
UINT wParam,
LONG lParam)
{
static short cxClient, cyClient;
BITMAP bm;
HDC hdc;
PAINTSTRUCT ps;
static POINT pt;
switch (message) {
case WM_SIZE:
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
return 0;
case WM_CREATE:
SetTimer(hwnd, 1, 100, NULL);
return 0;
case WM_TIMER:
GetCursorPos(&pt);
ReleaseDC(hwnd, hdc);
case WM_PAINT:
InvalidateRect(hwnd, NULL, TRUE);
hdc = BeginPaint(hwnd, &ps);
if (hBitmap != NULL) {
SelectObject(hdcMem, hBitmap);
GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&bm);
if (draw_mode == IDM_BITBLT) {
StretchBlt(hdc, 0, 0, cxClient, cyClient,
hdcMem, pt.x - 8, pt.y - 6, pt.x + 8,
pt.y + 6, SRCCOPY);
} else {
SetStretchBltMode(hdc, COLORONCOLOR);
StretchBlt(hdc, 0, 0, cxClient, cyClient,
hdcMem, 0, 0, bm.bmWidth,
bm.bmHeight, SRCCOPY);
}
}
EndPaint(hwnd, &ps);
return 0;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDM_CAPTURE:
ShowWindow(hwnd, SW_HIDE);
Sleep(2000);
CaptureScreen();
ShowWindow(hwnd, SW_SHOW);
break;
case IDM_BITBLT:
case IDM_STRETCH:
draw_mode = LOWORD(wParam);
break;
case IDM_EXIT:
DestroyWindow(hwnd);
break;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
とりあえず気づいたところ。
case WM_TIMER: に break; がない。
もし意図しているとしたら、WM_PAINT でしか使ってはいけない
BeginPaint を WM_TIMER でも使っていることになる。
case WM_TIMER: のところにある ReleaseDC(hwnd, hdc);
の hdc は不定値。
そもそも ReleaseDC は必要ない。(対応する GetDC が見当たらない。)
case WM_PAINT: に InvalidateRect があるのはおかしい。
hdcMem, hBitmap が何なのか不明。
> case WM_TIMER: に break; がない。
> もし意図しているとしたら、WM_PAINT でしか使ってはいけない
> BeginPaint を WM_TIMER でも使っていることになる。
> case WM_TIMER: のところにある ReleaseDC(hwnd, hdc);
> の hdc は不定値。
> そもそも ReleaseDC は必要ない。(対応する GetDC が見当たらない。)
breakを入れると、更新されなくなってしまい、マウスを動かしてもその周辺の情報を取
得し続けることができなくなってしまいました。
ReleaseDCは削除しても、変わらず動きました。。
実は、現在警告が3つ出ます。(実行はできるのですが。)
ReleaseDCを削除すると、警告が一つ減りました。
> case WM_PAINT: に InvalidateRect があるのはおかしい。
> hdcMem, hBitmap が何なのか不明。
申し忘れてしまい、御迷惑おかけしてしまいました。
すべてを私がつくったわけではなく、一部を以下のサイトから引用させていただいており
ます。case WM_PAINTは引用させていただいた箇所です。
私の勉強不足ではありますが、ここの内容は理解しきれておりません。
http://www.microsoft.com/japan/developer/winds/Sdk/Samples/SDKSample04.htm
なお、InvalidateRect を削除してしまってもマウスの動きに合わせて画面情報を更新し
続けることはできなくなってしまいます。
> http://www.microsoft.com/japan/developer/winds/Sdk/Samples/SDKSample04.htm
酷いソースですね。
>hdcMem = CreateCompatibleDC(hdc);
>hBitmap = CreateCompatibleBitmap(hdc, screen.cx, screen.cy);
リソースを解放してないし。
WM_PAINT に InvalidateRect があるのはどうなんですかね?
BeginPaint の前にあるから意味があるのでしょうか?
普通は、InvalidateRectで更新リージョンを追加した結果としてWM_PAINTが送られると
思うのですが。
とりあえず、
case WM_TIMER:
InvalidateRect(hwnd, NULL, FALSE); // 更新リージョンを追加して
UpdateWindow(hwnd); // 再描画を指示
return 0;
case WM_PAINT:
CaptureScreen(); // 最新の画像を取得
hdc = BeginPaint(hwnd, &ps);
(省略)
EndPaint(hwnd, &ps);
return 0;
のようにしてみてください。
あと、前述のように CaptureScreen() 内に問題があるので直す必要があります。
え・・・ひどいソースだったのですか・・・。
まず、isshiさんのおっしゃるとおりに修正してみました。
すると、実行しても何も起こりませんでした。
そこで、私が重要だと思って入れた
GetCursorPos を追加したところ、以前までは「実行」メニューの「全画面のコピー」と
いうものを選択してから動き出していたのですが、このプログラムを実行した段階ですで
に動き出していました。
また、30秒くらいすると、エラー音のようなものが連続して鳴り始めて止まらなくなって
しまいました。
CaptureScreen の内容もどのように変更すればよいのかわかりません。
もう少し勉強が必要ですね。
こんな感じでどうでしょうか?
Tさんの期待する動作かどうか分かりませんが。
WinMain は省略。
(WindowsXP home SP1, VC++7.1 で動作確認)
// test.c
#define ID_TIMER 1
#define ELAPSE 100
#define MAG 4 // 拡大倍率
int draw_mode = IDM_BITBLT;
void OnPaint(HWND hwnd)
{
HDC hdcsrc, hdcdst;
PAINTSTRUCT ps;
SIZE sizesrc;
RECT dstrect;
hdcdst = BeginPaint(hwnd, &ps);
hdcsrc = GetDC(NULL);
sizesrc.cx = GetDeviceCaps(hdcsrc, HORZRES);
sizesrc.cy = GetDeviceCaps(hdcsrc, VERTRES);
GetClientRect(hwnd, &dstrect);
if (draw_mode == IDM_BITBLT){
POINT pt;
GetCursorPos(&pt);
StretchBlt(
hdcdst, 0, 0, dstrect.right, dstrect.bottom,
hdcsrc, pt.x - 8, pt.y - 6, dstrect.right / MAG, dstrect.bottom / MAG,
SRCCOPY);
}
else {
SetStretchBltMode(hdcdst, COLORONCOLOR);
StretchBlt(
hdcdst, 0, 0, dstrect.right, dstrect.bottom,
hdcsrc, 0, 0, sizesrc.cx, sizesrc.cy,
SRCCOPY);
}
ReleaseDC(NULL, hdcsrc);
EndPaint(hwnd, &ps);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM
lParam)
{
switch (message) {
case WM_CREATE:
SetTimer(hwnd, ID_TIMER, ELAPSE, NULL);
break;
case WM_TIMER:
if (wParam == ID_TIMER){
KillTimer(hwnd, ID_TIMER);
InvalidateRect(hwnd, NULL, FALSE);
UpdateWindow(hwnd);
SetTimer(hwnd, ID_TIMER, ELAPSE, NULL);
}
break;
case WM_PAINT:
OnPaint(hwnd);
break;
case WM_COMMAND:
switch (LOWORD(wParam)){
case IDM_BITBLT:
case IDM_STRETCH:
draw_mode = LOWORD(wParam);
break;
case IDM_EXIT:
DestroyWindow(hwnd);
break;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
isshiさんのおっしゃるとおりに書き換えてみました。
なんとかエラーも警告も0にできたのですが、
実行するときにAccessViolationというエラーがでてしまい、
デバッガが起動します。
そして、解決方法がわからず、実行はできませんでした・・・。
私のソースをそのままコピーしましたか?
ステップ実行するなりして、エラー個所を特定してください。
(Windows2000 SP4, VC++6.0 SP6 でも問題ありませんでした。)
isshiさんのソースをそのまま追記するのではないんですよね?
もとのプログラムの一部を削除しないとだめだと思って、削除した部分がよくなかったの
でしょうか・・・。
ステップ実行しても、常に同じエラーが出てしまいます。
isshiさん、どうもありがとうございます。
再度、いじってみたらできました。
プログラムを実行した時点ですでに画面拡大をはじめているので、こちらのほうがよいと
思います。
isshiさんの教えていただいたソースはまだちょっと理解できていないので、
じっくり読んで理解したいと思います。
どうもありがとうございました。