17

这让我感到困惑。DirectX 绕过一切并直接与设备驱动程序对话,因此 GDI 和其他常用方法将不起作用 - 除非 Aero 被禁用(或不可用),否则出现的只是屏幕左上角的黑色矩形。我已经尝试过其他几个论坛上的建议,使用 DirectX 获取后台缓冲区并保存它,但我得到了相同的结果:

设备->GetFrontBufferData(0, 表面); D3DXSaveSurfaceToFile("文件名", D3DXIFF_BMP, 表面, NULL, NULL);

启用 Aero 后,有没有办法获取另一个全屏 DirectX 应用程序的屏幕截图?

4

7 回答 7

23

看看弯路

使用 Detours,您可以检测调用,如Direct3DCreate9,IDirect3D9::CreateDeviceIDirect3D9::Present在其中执行设置所需的操作,然后进行帧捕获。

于 2009-12-26T00:03:55.330 回答
9

这是一个通过 DLL 注入和使用 EasyHook 的函数挂钩(如 Microsoft Detours)挂钩 IDirect3DDevice9 对象的 C# 示例。这类似于 FRAPS 的工作方式。

这使您可以在窗口/全屏模式下捕获屏幕并使用后缓冲区,这比尝试从前缓冲区检索数据要快得多。

一个小的 C++ 帮助程序 DLL 用于确定 IDirect3DDevice9 对象的方法以在运行时挂钩。

更新:对于 DirectX 10/11,请参阅D3D 9、10 和 11 的屏幕截图和覆盖

于 2010-05-15T02:02:18.420 回答
3

这是我刚才用作测试的代码片段,它似乎可以工作。

宽度和高度是窗口模式下屏幕的大小,而不是窗口。所以对我来说,它们设置为 1280 x 1024,而不是我渲染到的窗口大小。

您还需要将 mEngine->getDevice() 替换为某种获取 IDirect3DDevice9 的方法。我只是将此代码插入到一个随机的 d3d 应用程序中,我必须使其更易于测试。但我可以确认它捕获了该应用程序的输出和同时运行的另一个 d3d 应用程序。

哦,我假设这是 D3D9,因为您没有说,我不确定 d3d10 或 11

IDirect3DSurface9* surface;
mEngine->getDevice()->CreateOffscreenPlainSurface(width, height, D3DFMT_A8R8G8B8,     D3DPOOL_SCRATCH, &surface, NULL);
mEngine->getDevice()->GetFrontBufferData(0, surface);
D3DXSaveSurfaceToFile("c:\\tmp\\output.jpg", D3DXIFF_JPG, surface, NULL, NULL);
surface->Release();
于 2009-12-26T14:54:18.563 回答
2

有一个类似 fraps 的开源程序:taksi但看起来已经过时了

于 2009-12-26T13:23:18.277 回答
2

按照 J99 的回答,我使代码适用于窗口模式和全屏模式。它也在 D3D9 中完成。

  IDirect3DSurface9* surface;
  D3DDISPLAYMODE mode;
  pDev->GetDisplayMode(0, &mode); // pDev is my *IDirect3DDevice
  // we can capture only the entire screen,
  // so width and height must match current display mode
  pDev->CreateOffscreenPlainSurface(mode.Width, mode.Height, D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &surface, NULL);
  if(pDev->GetFrontBufferData(0, surface)==D3D_OK)
  {
    if(bWindowed) // a global config variable
    {
      // get client area in desktop coordinates
      // this might need to be changed to support multiple screens
      RECT r;
      GetClientRect(hWnd, &r); // hWnd is our window handle
      POINT p = {0, 0};
      ClientToScreen(hWnd, &p);
      SetRect(&r, p.x, p.y, p.x+r.right, p.y+r.bottom);
      D3DXSaveSurfaceToFile(szFilename, D3DXIFF_JPG, surface, NULL, &r);
    }
    else
      D3DXSaveSurfaceToFile(szFilename, D3DXIFF_JPG, surface, NULL, NULL);
  }
  surface->Release();

看起来 CreateOffscreenPlainSurface 的格式和池参数必须完全相同。

于 2012-10-02T11:33:01.107 回答
2

这里有一些关于 Fraps 工作原理的讨论。这并不简单。

http://www.woodmann.com/forum/archive/index.php/t-11023.html

任何试图从不同的 DirectX 设备读取前端缓冲区的技巧,我怀疑可能只是偶尔会因为未初始化内存的运气而起作用。

于 2009-12-29T07:05:23.103 回答
1

您可能想看看我的 Investigo 项目。

它使用 DirectX 代理 DLL 来拦截 DirectX API 函数。

那里已经有代码可以在调用 Present 期间截取屏幕截图。虽然它还不能从 UI 访问。不过,您应该能够轻松启用代码。

http://www.codeproject.com/Articles/448756/Introducing-Investigo-Using-a-Proxy-DLL-and-embedd

于 2012-11-10T08:04:11.627 回答