0

I'm writing a small c++ app that uses Desktop Duplication API to get the display output. I've never done c programming before, and I got to where I am by staring at the win32 API documentation. https://learn.microsoft.com/en-us/windows/win32/api/dxgi1_2/


#include <iostream>
#pragma comment(lib, "windowsapp")
#include <roapi.h>
//#pragma comment(lib, "dxgi")
#include <dxgi1_2.h>

using namespace std;

int main()
{   

    cout << RoInitialize(RO_INIT_SINGLETHREADED);

    // intermediate variables for casting
    IDXGIOutput* pDisplay_old;

    IDXGIFactory1* pFactory;
    IDXGIAdapter1* pGPU;
    IDXGIOutput1* pDisplay;

    IDXGIOutputDuplication* pCapture;
    DXGI_OUTDUPL_DESC captureDesc;

    // create factory
    if (CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&pFactory) != S_OK) return 1;

    // get GPU
    if (pFactory -> EnumAdapters1(0, &pGPU) != S_OK) return 1;

    // get display
    if (pGPU -> EnumOutputs(0, &pDisplay_old) != S_OK) return 1;
    pDisplay = (IDXGIOutput1*)pDisplay_old;


    DXGI_OUTDUPL_FRAME_INFO frameInfo;
    IDXGIResource* pFrame;

    HRESULT captureResult;
    do
    {
        // create capture

       // cout << pDisplay -> DuplicateOutput(pGPU, &pCapture);
        //return 0;
        if (pDisplay -> DuplicateOutput(pGPU, &pCapture) != S_OK) return 1;
        pCapture -> GetDesc(&captureDesc);

        cout << captureDesc.ModeDesc.Width << ' ' << captureDesc.ModeDesc.Height;

        do
        {
            captureResult = pCapture -> AcquireNextFrame(2000, &frameInfo, &pFrame);
            if (captureResult == S_OK)
            {

                cout << "HI";

                captureResult = pCapture -> ReleaseFrame();
            }
            else if (captureResult == DXGI_ERROR_ACCESS_LOST) break;
            else return 1;
        }
        while (true);
    }
    while (true);

}

I'm using visual studio 2022 witn only "desktop development with c++" enabled, on windows 11 insider build: 22623.1037 ni_release on a regular home PC with display, beyboard, mouse, etc

The code worked fine until DuplicateOutput(), when it complained E_NOINTERFACE. I'm certain there is an interface since index 0 for EnumAdapters1 and EnumOutputs are where the desktop is displayed, and I obviously have a display attached with the desktop. According to this guy https://devblogs.microsoft.com/oldnewthing/20041213-00/?p=37043, I need marshalling and apartments or something, so after more research, I tried RoInitialize() with both RO_INIT_SINGLETHREADED and RO_INIT_MULTITHREADED. Now, DuplicateOutput throws this exception

enter image description here

It seems to happen within the library itself, which makes me think that it's either not my fault or I really messed something up, probably the latter.

I'm really confused now, and would like some assistance, thanks!

EDIT: I replaced "pDisplay = (IDXGIOutput1*)pDisplay_old;" with "pDisplay_old -> QueryInterface(&pDisplay);", and I'm back to E_NOINTERFACE, but I think I'm on the right track, how do I fix this error?

EDIT2: I looked at a related question AcquireNextFrame not working (Desktop Duplication API & D3D11), and followed the answer to add D3D11CreateDevice to my code:



#include <iostream>
#pragma comment(lib, "dxgi")
#pragma comment(lib, "d3d11")
#include <d3d11.h>
#include <dxgi1_2.h>

using namespace std;

int main()
{   

    // intermediate variables for casting
    IDXGIOutput* pDisplay_old;

    IDXGIFactory1* pFactory;
    IDXGIAdapter* pGPU;
    ID3D11Device* pD3DDevice;
    IDXGIDevice* pDevice;
    IDXGIOutput1* pDisplay;

    IDXGIOutputDuplication* pCapture;
    DXGI_OUTDUPL_DESC captureDesc;

    // create DXGI factory
    if (CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&pFactory) != S_OK) return 1;

    // get GPU adapter
    if (pFactory -> EnumAdapters(0, &pGPU) != S_OK) return 2;

    // create D3D11 device
    D3D_FEATURE_LEVEL D3DFeatures [6]
    {
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0,
        D3D_FEATURE_LEVEL_9_3,
        D3D_FEATURE_LEVEL_9_2,
        D3D_FEATURE_LEVEL_9_1
    };
    cout << D3D11CreateDevice(pGPU, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, D3DFeatures, sizeof(D3DFeatures), D3D11_SDK_VERSION, &pD3DDevice, NULL, NULL); //!= S_OK) return 3;
    return 0;

    // get DXGI device from that
    pD3DDevice -> QueryInterface(&pDevice);

    // get display
    if (pGPU -> EnumOutputs(0, &pDisplay_old) != S_OK) return 4;
    pDisplay_old -> QueryInterface(&pDisplay);


    DXGI_OUTDUPL_FRAME_INFO frameInfo;
    IDXGIResource* pFrame;

    HRESULT captureResult;
    do
    {
        // create capture

        cout << pDisplay -> DuplicateOutput(pD3DDevice, &pCapture);
        return 0;
        if (pDisplay -> DuplicateOutput(pGPU, &pCapture) != S_OK) return 5;
        pCapture -> GetDesc(&captureDesc);

        cout << captureDesc.ModeDesc.Width << ' ' << captureDesc.ModeDesc.Height;

        do
        {
            captureResult = pCapture -> AcquireNextFrame(2000, &frameInfo, &pFrame);
            if (captureResult == S_OK)
            {

                cout << "HI";

                captureResult = pCapture -> ReleaseFrame();
            }
            else if (captureResult == DXGI_ERROR_ACCESS_LOST) break;
            else return 6;
        }
        while (true);
    }
    while (true);

D3D11CreateDevice seems like a complex function and for me it keeps complaining invalid_arg. I'm not sure how to fix that

Tiger Yang
  • 61
  • 4
  • 2
    `pDisplay = (IDXGIOutput1*)pDisplay_old;` is wrong, you must always use `QueryInterface` to get an interface from another. And you don't need RoInitialize. – Simon Mourier Jan 01 '23 at 09:11
  • I replaced it with "pDisplay_old -> QueryInterface(&pDisplay);", and I'm back to E_NOINTERFACE, but I think I'm on the right track, how do I fix this error? – Tiger Yang Jan 04 '23 at 16:54
  • I don't get E_NOINTERFACE (you shouldn't) on this QueryInterface call. What is wrong then is DuplicateOutput expects a Direct3D device, not an adapter interface reference. – Simon Mourier Jan 04 '23 at 17:18
  • I've worked on it and updated the post above – Tiger Yang Jan 07 '23 at 01:51
  • Your code is wrong again, use `D3D_DRIVER_TYPE_UNKNOWN` if you pass an adapter as 1st arg (or ask for hardware and pass nullptr as 1st arg) and use `ARRAYSIZE(D3DFeatures)`, not `sizeof(D3DFeatures)` as 6th arg. Use DirectX Debug Layer https://learn.microsoft.com/en-us/windows/win32/direct3d11/using-the-debug-layer-to-test-apps https://walbourn.github.io/direct3d-sdk-debug-layer-tricks/ to ease debugging – Simon Mourier Jan 07 '23 at 07:36

1 Answers1

0

The solution was provided in the comments:

pDisplay = (IDXGIOutput1*)pDisplay_old; is wrong, you must always use QueryInterface to get an interface from another. And you don't need RoInitialize. – Simon Mourier Jan 1 at 9:11

I replaced it with "pDisplay_old -> QueryInterface(&pDisplay);", and I'm back to E_NOINTERFACE, but I think I'm on the right track, how do I fix this error? – Tiger Yang Jan 4 at 16:54

I don't get E_NOINTERFACE (you shouldn't) on this QueryInterface call. What is wrong then is DuplicateOutput expects a Direct3D device, not an adapter interface reference. – Simon Mourier Jan 4 at 17:18

I've worked on it and updated the post above – Tiger Yang Jan 7 at 1:51

Your code is wrong again, use D3D_DRIVER_TYPE_UNKNOWN if you pass an adapter as 1st arg (or ask for hardware and pass nullptr as 1st arg) and use ARRAYSIZE(D3DFeatures), not sizeof(D3DFeatures) as 6th arg. Use DirectX Debug Layer learn.microsoft.com/en-us/windows/win32/direct3d11/… walbourn.github.io/direct3d-sdk-debug-layer-tricks to ease debugging – Simon Mourier Jan 7 at 7:36

Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
Tiger Yang
  • 61
  • 4