See the question and my original answer on StackOverflow

What you must do first when you program DirectX (D3D, DXGI, Direct2D, etc.) is enable the DirectX debug layer. Once that is done, when running your code in debug mode, you will see this in debug output:

D3D11 ERROR: ID3D11DeviceContext::CopyResource: Cannot invoke CopyResource when the Formats of each Resource are not the same or at least castable to each other, unless one format is compressed (DXGI_FORMAT_R9G9B9E5_SHAREDEXP, or DXGI_FORMAT_BC[1,2,3,4,5,6,7]_* ) and the source format is similar to the dest according to: BC[1|4] ~= R16G16B16A16|R32G32, BC[2|3|5|6|7] ~= R32G32B32A32, R9G9B9E5_SHAREDEXP ~= R32. [ RESOURCE_MANIPULATION ERROR #284: COPYRESOURCE_INVALIDSOURCE]

This is precious information. You should never copy a texture (desktopTexture) to another (stagingTexture) without being certain they have the same formats, and in fact they don't.

So, change this:

var textureDesc = new Texture2DDesc
{
    ...
    Format = Format.FormatR8G8B8A8Unorm,
    ...
};

into this:

var textureDesc = new Texture2DDesc
{
    ...
    Format = Format.FormatB8G8R8A8Unorm,
    ...
};

Now the error is gone but ... you still have all zeroes in your resource.

The reason is DXGI Output Duplication is not a screenshot API, it's an API that acquires (aka copies) a new desktop frame when one is available, so it's really something that is meaningful during "some elapsed amount of time". For example, when nothing moves on the screen, no frame is acquired (and you'll get a timeout, which is expected).

So, in your code the easiest way to see it working is either to run the acquiring code in a loop, or just add a timer before the AcquireNextFrame call.

Thread.Sleep(200); // wait a bit here
hr = duplicatedOutput->AcquireNextFrame(1000, &frameInfo, &desktopResource);

PS: the code is missing Release calls (desktopResource, desktopTexture, etc.)