在非透明父窗口(win32)上创建一个透明的子窗口

4
我有一个程序不是由我编写的。我没有它的源代码,而该程序的开发人员在独立开发。他给了我该程序的HWNDHINSTANCE句柄。
我使用win32 api在他的窗口上创建了一个子窗口。
我首先需要让这个子窗口在某些区域具有透明性,在其他区域具有不透明性(像游戏中的HUD),这样用户可以在两个窗口中看到东西。
第二件事是将所有输入指向父窗口。我的子窗口不需要输入。
我知道WS_EX_TRANSPARENT只会让子窗口在最后绘制,就像画家算法一样。
我不能使用WS_EX_LAYERED,因为它是一个子窗口。
附言:
我已经四处寻找解决方案,但并没有找到任何解决方法,尽管互联网上有类似的问题。
实际上,这是一个HUD样的东西,针对那个游戏。我不能直接在父窗口上绘制,因为多线程复杂度很高以及其他原因。
-- 编辑 ---------------------------
我仍在努力。我正在尝试不同的方法,包括你们建议的。是否有一种方法可以结合directXSetWindowRgn()函数或directxBitBlt()函数?我认为这样做就可以解决问题了。目前,我将所有这些东西都作为子窗口和层窗口进行测试。

虽然不太美观,但你可以在窗口上创建一个顶层框架并跟踪下面窗口的移动。然后设置透明区域: http://www.codeguru.com/cpp/w-d/dislog/non-rectangulardialogs/article.php/c5037/Creating-Shaped-Windows-Using-Regions-with-Win32.htm - Pete
如果您将窗口设置为透明,那么只能看到父窗口。您是想在父窗口上打一个洞吗? - Ben
你至少需要WS_EX_TRANSPARENT。你将要面对的一个问题是,作者将在WM_ERASEBKGND消息上擦除他的窗口,这将清除你从样式标志中获取的背景像素。因此,你需要子类化他的窗口过程并抑制或替换该消息。关于使用WM_PAINT绘制的内容是否仍然有效,这是一个需要自己找出答案的开放性问题。如果它包含任何具有反锯齿边缘的文本或图形,则成功的可能性很低。考虑到成功的可能性不高,建议以评论的形式发布。 - Hans Passant
@HansPassant,WS_EX_TRANSPARENT 是最令人困惑的窗口样式之一。它并不会使窗口透明,而只是改变了渲染顺序。 - Mark Ransom
是的,它还使Hit Test透明..不是可见性..嘿嘿 - Deamonpog
3个回答

4

您可以在 Windows 8 及更高版本中使用 WS_EX_LAYERED 来创建 子窗口

为了支持早期版本的 Windows,只需创建一个无边框的级别分层窗口作为弹出窗口,并确保它位于游戏窗口的适当位置上方。大多数用户不会一直移动他们正在使用的窗口,因此,虽然您需要监视父窗口的移动并重新定位 HUD,但这不应该成为问题。

不占用焦点(如果是子窗口)或激活(如果是弹出窗口)更有趣,但仍然很容易实现:操作系统实际上不会自动将单击的窗口分配给焦点或激活 - Windows WindowProc 通过调用 SetFocus、某种变体的 SetActiveWindowSetForegroundWindow 来获取焦点或激活。在这里要注意的重要事情是,如果您消耗了所有鼠标和非客户区鼠标消息而没有将它们传递给 DefWindowProc,则由于单击而导致 HUD 从游戏窗口窃取激活或键盘焦点的情况将永远不会发生。

作为弹出窗口或另一个线程上的窗口,您可能需要手动处理窗口过程收到的任何鼠标消息,并将它们发布到游戏窗口。否则,响应 WM_NCHITTEST HTTRANSPARENT (类似于 WS_EX_TRANSPARENT 实现的效果)可以使系统继续将鼠标消息向下传递直到找到目标。

4

朋友们,最终我做了一些疯狂的事情才实现了它。但效率不是很高,比如直接使用DirectX进行绘图。

我做了以下几件事:

  • CreateWindowEx中使用了(WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_TOOLWINDOW)()
  • 创建窗口后,从窗口样式中删除了(WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE),并从扩展窗口样式中删除了(WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE | WS_EX_APPWINDOW)
  • 这样就得到了一个没有边框的窗口,并且也不显示在任务栏上。同时,hittest会被传递到我的窗口后面的任何东西上。
  • 子类化了另一个窗口的窗口过程,并获取了以下信息:
    • WM_CLOSEWM_DESTROY,发送WM_CLOSEWM_DESTROY消息到我的窗口
    • WM_SIZEWM_MOVE,根据其他窗口调整我的窗口大小和位置
    • WM_LBUTTONUPWM_RBUTTONUPWM_MBUTTONUP,使我的窗口置于顶部,并仍然保持对其他窗口的焦点,以使我的窗口不会被隐藏在其他窗口后面
  • 让DirectX设备有两个通道:
    • 在第一个通道中,它将所有元素绘制为黑色,放在白色背景上,并将后台缓冲区数据复制到另一个表面(因此它给出了黑白二进制图像)。
    • 在第二个通道中,它正常地绘制物体。
  • 创建另一个线程,通过读取那个黑白表面来保持窗口透明度,使用SetWindowRgn()函数。

这完美地运行着,唯一的问题是它不太擅长使事物透明。

另一个问题是给绘制的对象添加alpha混合。

但是您可以使用SetLayeredWindowAttributes()函数轻松设置总alpha(透明度)。

感谢你们给我的所有帮助,你们告诉我的所有事情都被使用并指导了我,正如你们所看到的。 :)

可悲的是,我们决定不使用这种方法,因为效率问题 :(

但我学到了很多东西,这是一次很棒的经历。这才是最重要的 :)

谢谢你们 :)


1

您可以使用SetWindowRgn在父窗口中创建一个洞。

此外,仅因为它不是您的窗口,并不意味着您不能将其变成分层窗口。

http://msdn.microsoft.com/en-us/library/ms997507.aspx

最后,您可以通过子类化来控制另一个窗口 - 实质上,您将自己的Wndproc替换为他们的Wndproc,以处理您希望处理的消息,然后将其余部分传递给他们原始的Wndproc。

SetWindowRgn()只是让屏幕在特定的部分绘制,对吧?我尝试了一下,它确实有帮助。谢谢 :) - Deamonpog
我之前看过那个链接并尝试了一下,但我担心它会以某种其他方式影响那个程序(游戏)。因此,我正在尝试不更改那个窗口,只获取它的句柄和其他信息,并操纵我的窗口。也谢谢你提供的这个想法 :) - Deamonpog

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接