5

Because processing was slow by D3DPOOL_SCRATCH, I wrote the desktop capture program to reference for the report on the Internet. However, a result is a pitch-black picture. Is this the result of a console program or is there any other cause?

#include <stdio.h>
#include <Windows.h>
#include <d3d9.h>

void main()
{
    CoInitialize(NULL);

    LPDIRECT3D9 d3d9;
    LPDIRECT3DDEVICE9 d3ddev;
    d3d9 = Direct3DCreate9(D3D_SDK_VERSION);

    int ww = GetSystemMetrics(SM_CXSCREEN);
    int wh = GetSystemMetrics(SM_CYSCREEN);

    HWND hwnd = GetDesktopWindow();
    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory(&d3dpp, sizeof(d3dpp));
    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
    d3dpp.BackBufferCount = 1;
    d3dpp.BackBufferWidth = ww;
    d3dpp.BackBufferHeight = wh;
    d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
    d3dpp.MultiSampleQuality = 0;
    d3dpp.EnableAutoDepthStencil = TRUE;
    d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
    d3dpp.hDeviceWindow = hwnd;
    d3dpp.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
    d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
    d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;

    d3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &d3ddev);

    IDirect3DSurface9* render;
    IDirect3DSurface9* dest;
    d3ddev->CreateOffscreenPlainSurface(ww, wh, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &dest, NULL);
    d3ddev->GetRenderTarget(0, &render);
    d3ddev->GetRenderTargetData(render, dest);

    D3DLOCKED_RECT bits;
    dest->LockRect(&bits, NULL, D3DLOCK_READONLY);

    // If a capture is successful, colors other than black(0x00000000) should enter. 
    for(int i = 0; i < 100; i++){
        printf("%02X ", *((BYTE*)bits.pBits + i));
    }

    dest->UnlockRect();

    render->Release();
    dest->Release();
    d3ddev->Release();
    d3d9->Release();

    CoUninitialize();
}
Tank2005
  • 899
  • 1
  • 14
  • 32
  • I tried running your code. I got the following errors: 1> Source.obj : error LNK2019: unresolved external symbol _Direct3DCreate9@4 referenced in function _main 1>MSVCRTD.lib(crtexew.obj) : error LNK2019: unresolved external symbol _WinMain@16 referenced in function ___tmainCRTStartup I do not understand what they mean. I am new to DirectX and am trying to build a screen capture program. Can you help me out with where I am going wrong or what direction must I go in? – newbie2015 Jul 02 '15 at 06:57

2 Answers2

5

This is nothing related to the application type, if you want to get the data of desktop image, you should use the following function

GetFrontBufferData

So instead of calling

d3ddev->GetRenderTarget(0, &render);
d3ddev->GetRenderTargetData(render, dest);

You should call

d3ddev->GetFrontBufferData(0, dest);
zdd
  • 8,258
  • 8
  • 46
  • 75
  • But, the question title said desktop capture, I think you should change the title to match your request and turn to DirectShow for video capture. – zdd Oct 13 '12 at 00:48
  • Is the screen shot of the desktop obtainable by DirectShow? – Tank2005 Oct 13 '12 at 03:03
  • If you capture a live video played with Overlay Surface, you will got nothing, since Overlay surface can not be captured by common technologies. you need some hook, that's also what I am looking for. – zdd Oct 13 '12 at 04:34
  • Please don't delete your comments, you delete your's and leave others related to yours there, it will make whoever read this topic confused. – zdd Oct 14 '12 at 07:04
4

If you are running on a version of Windows before Windows 8, the GetFrontBufferData API works fine, as given by another answer. However, starting with Windows 8, there is a new desktop duplication API that can be used to capture the desktop in video memory, including mouse cursor changes and which parts of the screen actually changed or moved. This is far more performant than the D3D9 approach and is really well-suited to doing things like encoding the desktop to a video stream, since you never have to pull the texture out of GPU memory. The new API is available by enumerating DXGI outputs and calling DuplicateOutput on the screen you want to capture. Then you can enter a loop that waits for the screen to update and acquires each frame in turn.

If you want to encode the frames to a video, I'd recommend taking a look at Media Foundation rather than DirectShow, since Media Foundation has a lot better performance and can use D3D11 rather than D3D9 to do the encoding. It's also a bit easier to use, depending on your scenario. Take a look at Media Foundation's Sink Writer for the simplest method of encoding the video frames.

For full disclosure, I work on the team that owns the desktop duplication API at Microsoft, and I've personally written apps that capture the desktop (and video, games, etc.) to a video file at 60fps using this technique, as well as a lot of other scenarios.

zhuman - MSFT
  • 1,028
  • 12
  • 16