46

我想在第 3 方全屏 Windows 应用程序之上显示一些自定义图形。

你玩过 Steam 游戏吗?它有一个可执行文件 GameOverlayUI.exe,可让您从游戏中访问 Steam 窗口。(GUI 元素看起来是自定义绘制的,我不知道这是否有必要;但它们在游戏中看起来与在桌面上的完全一样。)它甚至可以“暗淡”游戏图形的背景。

我想知道如何编写这样的应用程序。

我也想知道解决方案的范围有多广。你能对所有 OpenGL 应用程序使用一种方法吗?对于所有 Direct3D 减去 DirectDraw 应用程序?对于所有全屏 Windows 应用程序?

我正在寻找更“现代”和通用的解决方案——覆盖命令提示符或 320x240 索引颜色应用程序不是问题。

编辑:一些澄清:全屏应用程序不是我的。它可以是任何东西。最有可能的是,这将是一个 3D 游戏。我想在游戏图形的顶部显示屏幕右下角的数字时钟。如果可能的话,我想避免诸如注入代码或调试进程之类的技巧。

4

4 回答 4

27

好吧,这是另一个例子。Xfire 是一个聊天程序,它将其界面叠加到游戏中,因此您可以在玩游戏时接收消息。他们这样做的方法是在进程内存级别编辑 d3d/opengl DLL,例如注入程序集跳转。我知道这一点是因为他们的方法导致我的 mod 崩溃,而且我实际上看到了他们注入 d3d9.dll 的奇怪汇编代码。

下面是 Xfire 是如何做到这一点的:

  1. 瞄准游戏进程
  2. 在其进程内存中搜索 d3d.dll/opengl.dll
  3. 向进程中注入一个包含自定义接口逻辑的 DLL
  4. 通过在进程内存中找到的 d3d/opengl 函数中手动编写程序集跳转将接口的函数连接到程序流

由于您需要劫持属于该游戏的图形设备,因此没有其他可以想象的方法。我敢打赌,Steam 的做法与 Xfire 相同。所以,是的,除非有人创建了一个有用的库来做你需要在低级别做的所有事情,否则没有简单的方法来做到这一点。

您还询问了有关在叠加层下调暗游戏图形的问题。这只是一个DrawPrimitive在屏幕上绘制填充透明矩形的简单调用。

于 2009-11-05T20:54:30.603 回答
25

在这里创建了一个包含完整代码的 Gist

这是一门非常深奥的艺术,有几种方法可以做到这一点。我更喜欢所谓的Direct3D Wrapper。自从我这样做以来已经有好几年了,但我曾经用一个游戏做到过。我可能遗漏了一些细节,但我只是希望说明这个想法。

所有 Direct3D9 游戏都需要在屏幕渲染并准备好绘制后调用IDirect3DDevice9::EndScene 。所以理论上,我们可以将自定义代码放入EndScene其中,在游戏中绘制我们想要的东西。我们通过为游戏创建一个看起来像 IDirect3DDevice9 的类来做到这一点,但它实际上只是一个将所有函数调用转发到真实类的包装器。所以现在,在我们的自定义类中,我们的包装函数EndScene如下所示:

HRESULT IDirect3DDevice9::EndScene()
{
   // draw overlays here

   realDevice.EndScene();
}

然后我们将其编译成一个名为 d3d9.dll 的 DLL,并将其放到游戏可执行文件的目录中。真正的 d3d9.dll 应该在 /system32 文件夹中,但游戏首先在当前目录中查找,然后加载我们的。游戏加载后,它使用我们的 DLL 创建包装类的实例。现在每次EndScene调用时,我们的代码都会执行以在屏幕上绘制我们想要的内容。

我认为您可以尝试使用 OpenGL 的相同原理。但是为了防止篡改,我认为一些现代游戏试图阻止这种情况。

于 2009-11-02T17:51:34.433 回答
0

我一直在思考这个问题。对于 OpenGL,我会将 WGL 的 SwapBuffers 与 Detours 挂钩,在游戏结束后绘制我的东西,然后自己交换缓冲区。

于 2009-11-16T18:11:33.810 回答
-1

我已经使用 DirectX 和 WPF 视口完成了叠加(实际上是一样的),使用叠加在 3D 可视化之上绘制了一些不错的 2D GDI+ 内容。

我通过使用“实际”可视化顶部的面板进行管理,将颜色设置为透明,除了我想要覆盖的位。工作得很好,但是将单击、拖动和其他事件通过“绘图”层传递到 3D 控件有点痛苦。

于 2009-05-29T13:39:45.407 回答