Capture by Window Hwnd
turgayozgur opened this issue · 2 comments
turgayozgur commented
Hi,
I have tried to capture the window by using its hwnd but still, I am getting the front(desktop) image. Is it possible to get a specific window's image by using DirectN?
class Program
{
static void Main()
{
// capture 10 shots and saves them as PNG
// this is a C# port of https://stackoverflow.com/a/30138664/403671
var hwnd = ..... // a MainWindowHandle of the sepecific process.
Direct3D9TakeScreenshots(hwnd, Constants.D3DADAPTER_DEFAULT, 10).ThrowOnError();
}
static HRESULT Direct3D9TakeScreenshots(IntPtr hwnd, uint adapter, int count)
{
using (var d3d = new ComObject<IDirect3D9>(Functions.Direct3DCreate9(Constants.D3D_SDK_VERSION)))
{
var mode = new _D3DDISPLAYMODE();
d3d.Object.GetAdapterDisplayMode(adapter, ref mode).ThrowOnError();
var parameters = new _D3DPRESENT_PARAMETERS_
{
Windowed = true,
BackBufferCount = 1,
BackBufferHeight = mode.Height,
BackBufferWidth = mode.Width,
SwapEffect = _D3DSWAPEFFECT.D3DSWAPEFFECT_DISCARD,
hDeviceWindow = hwnd, // !!!!! SET HWND !!!!!!
};
d3d.Object.CreateDevice(adapter, _D3DDEVTYPE.D3DDEVTYPE_HAL, hwnd /* !!!!! SET HWND !!!!!! */, Constants.D3DCREATE_SOFTWARE_VERTEXPROCESSING, ref parameters, out var dev).ThrowOnError();
using (var device = new ComObject<IDirect3DDevice9>(dev))
{
dev.CreateOffscreenPlainSurface(mode.Width, mode.Height, _D3DFORMAT.D3DFMT_A8R8G8B8, _D3DPOOL.D3DPOOL_SYSTEMMEM, out var surf, IntPtr.Zero).ThrowOnError();
using (var surface = new ComObject<IDirect3DSurface9>(surf))
{
var rc = new _D3DLOCKED_RECT();
surf.LockRect(ref rc, IntPtr.Zero, 0).ThrowOnError();
var pitch = rc.Pitch;
surf.UnlockRect();
var shots = new byte[count][];
for (var i = 0; i < count; i++)
{
shots[i] = new byte[pitch * mode.Height];
}
var sw = new Stopwatch();
sw.Start();
for (var i = 0; i < count; i++)
{
dev.GetFrontBufferData(0, surf).ThrowOnError();
surf.LockRect(ref rc, IntPtr.Zero, 0).ThrowOnError();
Marshal.Copy(rc.pBits, shots[i], 0, shots[i].Length);
surf.UnlockRect().ThrowOnError();
}
Console.WriteLine("Elapsed: " + sw.Elapsed);
for (var i = 0; i < count; i++)
{
SavePixelsToFile32bppPBGRA(mode.Width, mode.Height, (uint)pitch, shots[i], "cap" + i + ".png", WICConstants.GUID_ContainerFormatPng).ThrowOnError();
}
}
}
}
return 0;
}
static HRESULT SavePixelsToFile32bppPBGRA(uint width, uint height, uint stride, byte[] pixels, string filePath, Guid format)
{
if (filePath == null)
throw new ArgumentNullException(nameof(filePath));
using (var fac = new ComObject<IWICImagingFactory>((IWICImagingFactory)new WicImagingFactory()))
{
fac.Object.CreateStream(out var stream).ThrowOnError();
using (new ComObject<IWICStream>(stream))
{
const int GENERIC_WRITE = 0x40000000;
stream.InitializeFromFilename(filePath, GENERIC_WRITE).ThrowOnError();
fac.Object.CreateEncoder(format, IntPtr.Zero, out var encoder).ThrowOnError();
using (new ComObject<IWICBitmapEncoder>(encoder))
{
encoder.Initialize(stream, WICBitmapEncoderCacheOption.WICBitmapEncoderNoCache).ThrowOnError();
encoder.CreateNewFrame(out var frame, out var bag).ThrowOnError();
using (new ComObject<IWICBitmapFrameEncode>(frame))
{
frame.Initialize(null).ThrowOnError();
frame.SetSize(width, height).ThrowOnError();
frame.SetPixelFormat(ref format).ThrowOnError();
frame.WritePixels(height, stride, (int)(stride * height), pixels).ThrowOnError();
frame.Commit().ThrowOnError();
encoder.Commit().ThrowOnError();
}
}
}
}
return 0;
}
}
smourier commented
This question is not related to DirectN itself which is just a managed wrapper over native technologies.
One solution to capture specific windows with C# is to use WinRT's Windows.Graphics.Capture namespace: https://docs.microsoft.com/en-us/windows/uwp/audio-video-camera/screen-capture (note: contrary to what you might think, this works for regular Win32 desktop apps, not just UWP)
smourier commented
Closed due to lack of activity.