tenfour 的答案在我看来是最好的,但他/她也必须解释如何去做,而不仅仅是告诉做什么,所以现在我将解释如何:
注意:如果您想要我的解决方案中的主要思想,那么您可以跳到下面的最后一步 9(从下到上开始搜索,直到找到它)。
对不起,如果我的回答夸大了,解释和详细太多了,但我保证如果你能正确阅读,你会理解并感到满意。
第 1 步:创建新WNDCLASSEX
结构,然后cbSize
使用关键字将其成员设置为此结构的大小(以字节为单位),sizeof
并将lpszClassName
成员设置为您想要的任何内容。还将 hbrBackground 成员设置为任 GetStockObject
一函数的返回值,以获得黑色、白色或灰色画笔,或函数以获取您想要的任何颜色的画笔。选择所有图纸根本不使用的颜色非常重要!!!而不仅仅是为了您的快乐! CreateSolidBrush
例如:
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.lpszClassName = "myNewClassName";
HBRUSH background_brush = GetStockObject(BLACK_BRUSH);
wcex.hbrBackground = background_brush;
第二步:定义一个新函数,即窗口过程,然后为其创建新的函数指针。然后将lpfnWndProc
成员设置wcex
为该函数指针。
第三步:在新的窗口过程函数定义中,添加return 0;
最后一行代码。
在它上面切换 uMsg 参数,并至少添加WM_PAINT
大小写。在这种情况下,定义 的新变量PAINTSTRUCT
,并调用BeginPaint
和EndPaint
函数。在这两个函数中,您都引用变量&ps
,例如 ifps
是PAINTSTRUCT
变量的名称。
调用函数后BeginPaint
,在目标窗口上添加你想要的所有绘图函数,但要确保HDC
所有绘图函数不属于目标窗口,而是属于你在窗口的第一个参数中有句柄的窗口程序,即hWnd
!!!首先调用其中一个 GetDC
或 GetWindowDC
函数来检索 hWnd 的 hDC 。
注意:您根本不需要检索目标窗口的 hDc!
不要忘记在语句default: return DefWindowProc(hWnd, uMsg, wParam, lParam);
块内添加以下代码行。switch
第四步:RegisterClassEx
使用函数注册类。
例如:RegisterClassEx(&wcex);
第 5 步:在您的应用程序中,使用该功能创建新的分层窗口。将此函数的第一个参数设置为声明您创建的新窗口是分层CreateWindowEx
的,这一点非常重要。WS_EX_LAYERED
将第二个参数设置为新注册类的名称,例如"myNewClassName"
,忽略第三个参数(设置为NULL
),并在第四个参数中设置以下标志:WS_POPUP | WS_VISIBLE
注意:WS_POPUP
将创建没有边框的新窗口。这就是我忽略的hIcon
成员wcex
以及设置为
You can replace with的第三个参数的原因。结果将是相同的,但还会绘制轮廓灰色矩形,整个客户区只有 1 个像素的厚度。不绘制任何与 形成对比的东西,只是像它一样删除边框。NULL
WS_POPUP
WS_POPUPWINDOW
WS_POPUPWINDOW
WS_POPUP
WS_POPUPWINDOW
我还设置了WS_VISIBLE
标志以显示窗口。如果你不设置这个标志,那么你必须在ShowWindow
之后调用函数CreateWindowEx
,以显示分层窗口。
第6步:调用SetLayeredWindowAttributes
函数后CreateWindowEx
。将第一个CreateWindowEx
参数设置为从函数返回的新创建的分层窗口的句柄。
将第二个参数设置为窗口客户端的背景颜色,即存储在background_brush
示例变量中的实心画笔的颜色,用于设置类型为 的 wcex 的 hbrBackground 成员WNDCLASSEX
。
注意这个参数的数据类型是COLORREF
,而不是HBRUSH
,即它不接受实心画笔,而只是一种颜色!
如果您知道在GetStockObject
函数中选择的实心画笔的颜色,那么您就知道如何创建它的COLORREF
结构。
如果您CreateSolidBrush
改为调用函数,则在调用它之前,定义新的COLORREF
结构变量,该变量将存储实体画笔的颜色和将来的 crKey。稍后您可以在两个函数中使用该变量:CreateSolidBrush
和SetLayeredWindowAttributes
(在第二个参数中,即crKey
)。
如果您按照上面的示例进行操作,并且您只有background_brush
类型为HBRUSH
存储 wcex 的 hbrBackground 成员的实体画笔的变量,并且您没有任何适当的 COLORREFcrKey
参数,那么:
为了获得类型为结构的实心画笔的颜色,HBRUSH
然后COLORREF
:
可以使用 GetObject 函数获取画笔的 LOGBRUSH 信息,其中的 lbColor 分量会给出 COLORREF 中的颜色值。您也可以使用 GetRValue、GetGValue 和 GetBValue 函数来获取各个颜色分量。
普拉文对必须知道如何获得坚实刷子COLORREF
的人的回答。HBRUSH
以下是更多信息的链接:
http://forums.codeguru.com/showthread.php?423605-Color-of-HBRUSH
如果您想要另一种方法来检索函数参数的正确COLORREF
结构,那么您可以先检索新创建的分层窗口的
,然后尝试该函数。或改为调用函数。将第一个参数设置为新分层窗口的 dc,并将第二个参数设置为 wcex 或上述示例的成员。crKey
SetLayeredWindowAttributes
GetBkColor
hDc
SelectObject
hbrBackground
bakcground_brush
然后只需调用GetDCBrushColor
函数即可获取函数参数的COLORREF
结构。crKey
SetLayeredWindowAttributes
忽略第三个参数(设置bAlpha
为NULL
),在第四个参数中只设置LWA_COLORKEY
标志。crKey 是 hbrBackground 的颜色,分层窗口的整个客户端不会被绘制,除了它们颜色不是的其他像素crKey
。现在您所要做的就是将分层窗口绑定到目标窗口的客户端。
第 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
orFindWindowEx
函数返回),并在第二个参数中引用您之前创建的 POINT 结构。
调用ClientToScreen
函数后,您的 POINT 结构存储目标窗口最左上边缘的坐标(以屏幕坐标为单位,以像素为单位测量)。
现在您必须检索目标窗口客户端的大小。为此,接下来定义RECT
类型为结构的新变量。
然后调用GetClientRect
函数。将第一个参数设置为目标窗口的句柄,并在第二个参数中引用类型为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
函数的调用来解决此问题。在第二个参数中,我调用了我想将有界分层窗口放置在目标窗口上方的系统。如果它仍然不起作用,您可以更改此参数并将其设置为要么 HWND_TOP
或 HWND_TOPMOST
标志。
我确信在那之后它应该可以正常工作。有时不要忘记使分层窗口无效或重绘或更新分层窗口以更新目标窗口上的图形。您可以调用or or函数来执行此操作。 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.
(您可以选择并复制上面的示例代码并将其粘贴到您的代码中):
示例代码可以在消息循环中,表示分层窗口每时每刻都会被绑定和更新,但如果你不想每时每刻,而是每次你做某事,或者每次发生某事时,与目标窗口,不属于您的应用程序,例如记事本,那么您必须调用该SetWindowsHookEx
函数。您可以在 MSDN 站点和 Web 上的其他站点上获得有关该功能的更多信息。
如果您希望每时每刻都发生这种情况,但不是在消息循环中,那么您可以在其他线程的其他 while 循环中执行此操作,但您将为它创建一个函数并调用CreateThread
函数。同样,您还可以在 MSDN 站点和 Web 上的其他站点上获取有关该功能的信息
确保当您的应用程序进程退出或终止时,线程的 while 循环结束。
您还可以在 while 循环条件中尝试其中一个IsWindow
或IsWindowVisible
函数,以确定在分层窗口或/和目标窗口被销毁或关闭(已完成,不会被最小化或图标)时停止。