7

イベントに応答してウィンドウで GDI+ を使用して描画を行う必要があるグローバル フック DLL を作成しています。私の問題は、描画されているウィンドウが自分自身を再描画し続けるため、描画したものが必要になる前に消去されることです。必要な限り、ウィンドウが何も描画しないようにする方法はありますか?

私のフックは現在WH_CALLWNDPROCフックです。描画は、メッセージに応答して GDI+ を使用して行われますWM_SIZING。GDI +を使用してウィンドウDC(つまりGetWindowDC)に描画します。私が描いているものは正しく描かれていますが、ウィンドウのクライアント領域が再描画されるとほぼ瞬時に消去されます。私が描画しているウィンドウを作成したプログラムはメモ帳です。カーソルが点滅すると、描いたものが消えます。

ウィンドウのペイントを一時的に中断する方法を知っている人はいますか?

ありがとう!

4

4 に答える 4

5

ターゲット ウィンドウに重なるレイヤード ウィンドウにグラフィックを配置することをお勧めします。それが最もクリーンな方法のようです。結局のところ、あるレベルの概念では、これがウィンドウマネージャーの目的です:)

于 2010-11-23T21:59:50.320 に答える
4

私の意見では、テンフォーの答えが最善ですが、彼/彼女はをすべきかを伝えるだけでなく、それを行う方法説明しなければならなかったので、今はその方法を説明します:

注:私のソリューションの主なアイデアが必要な場合は、下の最後のステップ 9 にスキップできます (見つけるまで、下から上に検索を開始します)。

私の回答が大袈裟で、説明や詳細が多すぎて申し訳ありませんが、適切に読んでいただければ、理解して満足していただけることを約束します。

ステップ 1:新しいWNDCLASSEX構造体を作成し、キーワードcbSizeを使用してそのメンバーをこの構造体のサイズ (バイト単位) に設定し、メンバーを任意の値に設定します。また、hbrBackground メンバーを、いずれかの関数の戻り値に設定して、黒、白、または灰色のブラシを取得するか、または任意の色のブラシを取得する関数を設定します。すべての図面でまったく使用されていない色を選択することが非常に重要です!!! そして、あなたの喜びのためだけではありません!sizeoflpszClassName GetStockObject CreateSolidBrush

例えば:

WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.lpszClassName = "myNewClassName";
HBRUSH background_brush = GetStockObject(BLACK_BRUSH);
wcex.hbrBackground = background_brush;

手順 2:ウィンドウ プロシージャである新しい関数を定義し、その新しい関数ポインタを作成します。次に、のlpfnWndProcメンバーwcexをその関数ポインターに設定します。

手順 3:新しいウィンドウ プロシージャ関数の定義で、コードの最後の行でreturn 0;ある which を追加します。

その上で uMsg パラメータを切り替え、少なくともWM_PAINTケースを追加します。その場合、 の新しい変数を定義し、および関数PAINTSTRUCTを呼び出します。どちらの関数でも、 が変数の名前である場合、 のように変数を参照します。BeginPaintEndPaint&pspsPAINTSTRUCT

関数を呼び出した後BeginPaint、ターゲットウィンドウに必要なすべての描画関数を追加しますが、HDCすべての描画関数がターゲットウィンドウに属していないことを確認しますが、ウィンドウの最初のパラメーターでハンドルを持つウィンドウに属していることを確認してください手順、すなわちhWnd!!! または 関数を呼び出してGetDC 最初GetWindowDCにhWnd の hDC を取得します。

注:ターゲット ウィンドウの hDc を取得する必要はまったくありません。

ステートメントdefault: return DefWindowProc(hWnd, uMsg, wParam, lParam);のブロック内に次のコード行を追加することを忘れないでください。switch

ステップ 4:関数を使用してクラスを登録しRegisterClassExます。

例えば:RegisterClassEx(&wcex);

ステップ 5:アプリケーションで、関数を使用して新しいレイヤードウィンドウを作成しCreateWindowExます。この関数の最初のパラメーターを に設定して、作成する新しいウィンドウが階層化されていることを宣言することが非常に重要です。WS_EX_LAYERED

2番目"myNewClassName"のパラメーターを新しく登録されたクラスの名前設定します。NULLWS_POPUP | WS_VISIBLE

:は、境界線のないWS_POPUP新しいウィンドウを作成します。これが、メンバーを無視した理由であり、代わりに You can replace withに設定された3 番目のパラメーターも無視しました。結果は同じになりますが、クライアント領域全体に 1 ピクセルの厚さで薄く、アウトライン化された灰色の四角形も描画されます。とは対照的に何も描画せ、そのように境界線を削除するだけです。hIconwcexNULLWS_POPUPWS_POPUPWINDOWWS_POPUPWINDOWWS_POPUPWS_POPUPWINDOW

WS_VISIBLEウィンドウを表示するフラグも設定しました。このフラグを設定しない場合は、レイヤード ウィンドウを表示するために、 のShowWindow後に関数を呼び出す必要があります。CreateWindowEx

ステップ 6:SetLayeredWindowAttributesの後に関数を呼び出しますCreateWindowEx最初のパラメーターを、関数から返された新しく作成されたレイヤード ウィンドウのハンドルに設定しCreateWindowExます。

2 番目のパラメーターをウィンドウのクライアントの背景色に設定します。つまりbackground_brush、例の変数に格納されているソリッド ブラシの色に設定します。この変数は、タイプがWNDCLASSEX.

このパラメータのデータ タイプはCOLORREFであり、ではないことに注意HBRUSHしてください。つまり、ソリッド ブラシは受け入れず、単に色を受け入れます。

GetStockObject関数で選択したソリッド ブラシの色がわかっている場合は、そのCOLORREF構造を作成する方法がわかります。

代わりに関数を呼び出した場合は、関数を呼び出す前に、将来ソリッド ブラシの色と crKey を格納する構造体CreateSolidBrushの新しい変数を定義します。COLORREF後で、両方の関数で変数を使用できます:CreateSolidBrushSetLayeredWindowAttributes(2 番目のパラメーター、すなわちcrKey)。

上記の例に従って、wcex の hbrBackground メンバーのソリッド ブラシを格納するタイプの変数しかなく、パラメーターに適切な COLORREF がない場合:background_brushHBRUSHcrKey

HBRUSHタイプがCOLORREF構造化されるソリッド ブラシの色を取得するには、次のようにします。

GetObject 関数を使用して、ブラシの LOGBRUSH 情報を取得できます。その lbColor コンポーネントは、COLORREF でカラー値を提供します。GetRValue、GetGValue、および GetBValue 関数を使用して、個々のカラー コンポーネントを取得することもできます。

ソリッド ブラシとしてCOLORREF取得する方法を知らなければならなかった誰かへの Pravin の答え。HBRUSH

詳細については、次のリンクを参照してください。

http://forums.codeguru.com/showthread.php?423605-Color-of-HBRUSH

関数のパラメーターの適切なCOLORREF構造を取得する別の方法が必要な場合は、最初に新しく作成されたレイヤード ウィンドウの を取得した後で関数を試すことができます。または代わりに関数を呼び出します。最初のパラメーターを新しいレイヤード ウィンドウの dc に設定し、 2 番目のパラメーターをwcex または上記の例のメンバーに設定します。crKeySetLayeredWindowAttributesGetBkColorhDcSelectObjecthbrBackgroundbakcground_brush

次に、関数を呼び出して、関数のパラメーターの構造GetDCBrushColorを取得します。COLORREFcrKeySetLayeredWindowAttributes

3 番目のパラメーターを無視し( に設定)、4 番目のパラメーターにはフラグのみを設定しますbAlpha。crKey は hbrBackground の色です。階層化されたウィンドウのクライアント全体は描画されません。ただし、色が ではない他のピクセルは除きます。あとは、レイヤード ウィンドウをターゲット ウィンドウのクライアントにバインドするだけです。NULLLWA_COLORKEYcrKey

手順 7:または 関数を呼び出して、ターゲット ウィンドウのハンドルを取得しますFindWindow ( FindWindowExまだ取得していない場合)。

ステップ 8:メッセージ ループを記述します (または、以下のコードを選択してコピーし、自分のコードに貼り付けます)。

MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

ステップ 9 (最後):メッセージ ループ (while ループのブロック内) のどこ (TranslateMessage の前、DispatchMessage の後、またはそれらの間) に関係なく、レイヤード ウィンドウをターゲット ウィンドウのウィンドウに常にバインドする別のコードを記述します。クライアント:

まず、新しい POINT 構造体を作成し、その座標を (0; 0) に設定します (xおよびyメンバーを 0 に設定します)。

次に、関数を呼び出しClientToScreenます。最初のパラメーターをターゲット ウィンドウのハンドル (FindWindowまたはFindWindowEx関数から返される) に設定し、2番目のパラメーターで、前に作成した POINT 構造体を参照します。

関数の呼び出し後、ClientToScreenPOINT 構造体は、ターゲット ウィンドウの最も左端の座標を格納します (画面座標で、ピクセル単位で測定されます)。

ここで、ターゲット ウィンドウのクライアントのサイズを取得する必要があります。そのためには、次に構造体型の新しい変数を定義します。RECT

次に、関数を呼び出しGetClientRectます。最初のパラメーターをターゲット ウィンドウのハンドルに設定し、 2 番目RECTのパラメーターで型が構造体である変数を参照します。

GetClientRect関数の呼び出し後、RECT変数はターゲット ウィンドウのクライアントの幅と高さをピクセル単位で格納します。rightメンバーにはが格納され、メンバーbottomには高さが格納されます。型が構造体である変数のleftおよびtop メンバーを無視します。RECTこれらは使用も設定もされず、常にデフォルト値、つまりこの場合のみ 0 になります。

ターゲット ウィンドウのクライアントの位置 (場所とサイズ) を取得したら、次のように、または関数 を呼び出して、レイヤード ウィンドウをターゲット ウィンドウのクライアントにバインドできます。MoveWindow SetWindowPos

MoveWindow(hWnd, point.x, point.y, rect.right, rect.bottom, TRUE);
//If you want, you can set the last parameter to FALSE.
//OR
SetWindowPos(hWnd, hTargetWnd, point.x, point.y, rect.right, rect.bottom, NULL);
//If you want, you can specify some flags in the last parameter.
//Where hWnd is the handle of the layered window returned from CreateWindowEx function, and hTargetWnd is the handle of the target window returned from either FindWindow or FindWindowEx function.

この機能を使用することに決めたのにMoveWindow、ターゲット ウィンドウにペイントが表示されないという問題がある場合、それはレイヤード ウィンドウがターゲット ウィンドウのにあるためです。SetWindowPosこの問題を解決するには、代わりに関数の呼び出しを変更する必要があります。2 番目のパラメーターで、境界のあるレイヤード ウィンドウをターゲット ウィンドウのに配置するシステムを呼び出しました。それでも機能しない場合は、このパラメーターを変更して、またはフラグのいずれかに設定できます。 HWND_TOP HWND_TOPMOST

その後はうまくいくはずです。レイヤード ウィンドウを無効化、再描画、または更新して、ターゲット ウィンドウの描画を更新することを時々忘れないでください。または または関数を呼び出してそれを行うことができますInvalidateRect RedrawWindow UpdateWindow

例えば:

POINT point;
ClientToScreen(hTargetWnd, &point);

RECT rect;
GetClientRect(hTargetWnd, &rect);

MoveWindow(hWnd, point.x, point.y, rect.right, rect.bottom, TRUE);
//OR
SetWindowPos(hWnd, hTargetWnd, point.x, point.y, rect.right, rect.bottom, NULL);

InvalidateRect(hWnd, NULL, TRUE);
//If you want, you can set the third parameter to FALSE.

(上記のサンプル コードを選択してコピーし、自分のコードに貼り付けることもできます):

サンプル コードはメッセージ ループ内にある可能性があります。これは、階層化されたウィンドウがすべての瞬間にバインドされ、更新されることを意味します。メモ帳などのアプリケーションに属していない場合は、関数を呼び出す必要があります。この機能の詳細については、MSDN サイトおよび Web 上の他のサイトを参照してください。SetWindowsHookEx

メッセージループではなく、常に発生させたい場合は、他のスレッドの他のwhileループで実行できますが、そのための関数を作成して関数を呼び出す必要がありCreateThreadます。繰り返しになりますが、MSDN サイトや Web 上の他のサイトでその機能に関する情報を入手することもできます。

アプリケーションのプロセスが終了または終了したときにスレッドの while ループが終了していることを確認してください。

IsWindowまた、 while ループ条件でまたは関数を試してIsWindowVisible、レイヤード ウィンドウまたはターゲット ウィンドウが破棄または閉じられている (終了、最小化またはアイコン化されていない)ときに停止することを決定することもできます。

于 2014-08-24T20:36:41.300 に答える
2

ペイントを一時停止するように設定してから、通常の動作を復元するように設定して、 WM_SETREDRAWメッセージをウィンドウに送信してみてください。wParamFALSETRUE

ただし、これは非常に煩わしい解決策です。

于 2010-11-23T21:44:49.103 に答える
2

いいえ。

代わりに、 をフックしないのはなぜWM_PAINTですか。

于 2010-11-23T21:41:27.150 に答える