public override int Capture(FrameInfo frame) { var res = Result.Ok; try { //Try to get the duplicated output frame within given time. res = DuplicatedOutput.TryAcquireNextFrame(0, out var info, out var resource); //Somehow, it was not possible to retrieve the resource or any frame. if (res.Failure || resource == null || info.AccumulatedFrames == 0) { resource?.Dispose(); return(FrameCount); } //Copy resource into memory that can be accessed by the CPU. using (var screenTexture = resource.QueryInterface <Texture2D>()) { //Copies from the screen texture only the area which the user wants to capture. Device.ImmediateContext.CopySubresourceRegion(screenTexture, 0, new ResourceRegion(TrueLeft, TrueTop, 0, TrueRight, TrueBottom, 1), StagingTexture, 0); } //Get the desktop capture texture. var data = Device.ImmediateContext.MapSubresource(StagingTexture, 0, MapMode.Read, MapFlags.None, out var stream); if (data.IsEmpty) { Device.ImmediateContext.UnmapSubresource(StagingTexture, 0); stream?.Dispose(); resource.Dispose(); return(FrameCount); } #region Get image data //Set frame details. FrameCount++; frame.Path = $"{Project.FullPath}{FrameCount}.png"; frame.Delay = FrameRate.GetMilliseconds(SnapDelay); frame.DataLength = stream.Length; frame.Data = new byte[stream.Length]; //BGRA32 is 4 bytes. for (var height = 0; height < Height; height++) { stream.Position = height * data.RowPitch; Marshal.Copy(new IntPtr(stream.DataPointer.ToInt64() + height * data.RowPitch), frame.Data, height * Width * 4, Width * 4); } BlockingCollection.Add(frame); #endregion Device.ImmediateContext.UnmapSubresource(StagingTexture, 0); resource.Dispose(); return(FrameCount); } catch (SharpDXException se) when(se.ResultCode.Code == SharpDX.DXGI.ResultCode.WaitTimeout.Result.Code) { return(FrameCount); } catch (SharpDXException se) when(se.ResultCode.Code == SharpDX.DXGI.ResultCode.DeviceRemoved.Result.Code || se.ResultCode.Code == SharpDX.DXGI.ResultCode.DeviceReset.Result.Code) { //When the device gets lost or reset, the resources should be instantiated again. DisposeInternal(); Initialize(); return(FrameCount); } catch (Exception ex) { LogWriter.Log(ex, "It was not possible to finish capturing the frame with DirectX."); OnError.Invoke(ex); return(FrameCount); } finally { try { //Only release the frame if there was a sucess in capturing it. if (res.Success) { DuplicatedOutput.ReleaseFrame(); } } catch (Exception e) { LogWriter.Log(e, "It was not possible to release the frame."); } } }
public override int CaptureWithCursor(FrameInfo frame) { var res = Result.Ok; try { //Try to get the duplicated output frame within given time. res = DuplicatedOutput.TryAcquireNextFrame(0, out var info, out var resource); //Checks how to proceed with the capture. It could have failed, or the screen, cursor or both could have been captured. if (res.Failure || resource == null || (info.AccumulatedFrames == 0 && info.LastMouseUpdateTime <= LastProcessTime)) { //Somehow, it was not possible to retrieve the resource, frame or metadata. //frame.WasDropped = true; //BlockingCollection.Add(frame); resource?.Dispose(); return(FrameCount); } else if (info.AccumulatedFrames == 0 && info.LastMouseUpdateTime > LastProcessTime) { //Gets the cursor shape if the screen hasn't changed in between, so the cursor will be available for the next frame. GetCursor(null, info, frame); return(FrameCount); } //Saves the most recent capture time. LastProcessTime = Math.Max(info.LastPresentTime, info.LastMouseUpdateTime); //Copy resource into memory that can be accessed by the CPU. using (var screenTexture = resource.QueryInterface <Texture2D>()) { //Copies from the screen texture only the area which the user wants to capture. Device.ImmediateContext.CopySubresourceRegion(screenTexture, 0, new ResourceRegion(TrueLeft, TrueTop, 0, TrueRight, TrueBottom, 1), BackingTexture, 0); //Copy the captured desktop texture into a staging texture, in order to show the mouse cursor and not make the captured texture dirty with it. Device.ImmediateContext.CopyResource(BackingTexture, StagingTexture); //Gets the cursor image and merges with the staging texture. GetCursor(StagingTexture, info, frame); } //Get the desktop capture texture. var data = Device.ImmediateContext.MapSubresource(StagingTexture, 0, MapMode.Read, MapFlags.None); if (data.IsEmpty) { //frame.WasDropped = true; //BlockingCollection.Add(frame); Device.ImmediateContext.UnmapSubresource(StagingTexture, 0); resource.Dispose(); return(FrameCount); } #region Get image data var bitmap = new System.Drawing.Bitmap(Width, Height, PixelFormat.Format32bppArgb); var boundsRect = new System.Drawing.Rectangle(0, 0, Width, Height); //Copy pixels from screen capture Texture to the GDI bitmap. var mapDest = bitmap.LockBits(boundsRect, ImageLockMode.WriteOnly, bitmap.PixelFormat); var sourcePtr = data.DataPointer; var destPtr = mapDest.Scan0; for (var y = 0; y < Height; y++) { //Copy a single line. Utilities.CopyMemory(destPtr, sourcePtr, Width * 4); //Advance pointers. sourcePtr = IntPtr.Add(sourcePtr, data.RowPitch); destPtr = IntPtr.Add(destPtr, mapDest.Stride); } //Release source and dest locks. bitmap.UnlockBits(mapDest); //Set frame details. FrameCount++; frame.Path = $"{Project.FullPath}{FrameCount}.png"; frame.Delay = FrameRate.GetMilliseconds(SnapDelay); frame.Image = bitmap; BlockingCollection.Add(frame); #endregion Device.ImmediateContext.UnmapSubresource(StagingTexture, 0); resource.Dispose(); return(FrameCount); } catch (SharpDXException se) when(se.ResultCode.Code == SharpDX.DXGI.ResultCode.WaitTimeout.Result.Code) { return(FrameCount); } catch (Exception ex) { LogWriter.Log(ex, "It was not possible to finish capturing the frame with DirectX."); OnError.Invoke(ex); return(FrameCount); } finally { try { //Only release the frame if there was a sucess in capturing it. if (res.Success) { DuplicatedOutput.ReleaseFrame(); } } catch (Exception e) { LogWriter.Log(e, "It was not possible to release the frame."); } } }
public override int CaptureWithCursor(FrameInfo frame) { var res = new Result(-1); try { //Try to get the duplicated output frame within given time. res = DuplicatedOutput.TryAcquireNextFrame(0, out var info, out var resource); //Checks how to proceed with the capture. It could have failed, or the screen, cursor or both could have been captured. if (FrameCount == 0 && info.LastMouseUpdateTime == 0 && (res.Failure || resource == null)) { //Somehow, it was not possible to retrieve the resource, frame or metadata. resource?.Dispose(); return(FrameCount); } else if (FrameCount == 0 && info.TotalMetadataBufferSize == 0 && info.LastMouseUpdateTime > 0) { //Sometimes, the first frame has cursor info, but no screen changes. GetCursor(null, info, frame); resource?.Dispose(); return(FrameCount); } #region Process changes //Something on screen was moved or changed. if (info.TotalMetadataBufferSize > 0) { //Copies the screen data into memory that can be accessed by the CPU. using (var screenTexture = resource.QueryInterface <Texture2D>()) { #region Moved rectangles var movedRectangles = new OutputDuplicateMoveRectangle[info.TotalMetadataBufferSize]; DuplicatedOutput.GetFrameMoveRects(movedRectangles.Length, movedRectangles, out var movedRegionsLength); for (var movedIndex = 0; movedIndex < movedRegionsLength / Marshal.SizeOf(typeof(OutputDuplicateMoveRectangle)); movedIndex++) { //Crop the destination rectangle to the screen area rectangle. var left = Math.Max(movedRectangles[movedIndex].DestinationRect.Left, Left - OffsetLeft); var right = Math.Min(movedRectangles[movedIndex].DestinationRect.Right, Left + Width - OffsetLeft); var top = Math.Max(movedRectangles[movedIndex].DestinationRect.Top, Top - OffsetTop); var bottom = Math.Min(movedRectangles[movedIndex].DestinationRect.Bottom, Top + Height - OffsetTop); //Copies from the screen texture only the area which the user wants to capture. if (right > left && bottom > top) { //Limit the source rectangle to the available size within the destination rectangle. var sourceWidth = movedRectangles[movedIndex].SourcePoint.X + (right - left); var sourceHeight = movedRectangles[movedIndex].SourcePoint.Y + (bottom - top); Device.ImmediateContext.CopySubresourceRegion(screenTexture, 0, new ResourceRegion(movedRectangles[movedIndex].SourcePoint.X, movedRectangles[movedIndex].SourcePoint.Y, 0, sourceWidth, sourceHeight, 1), BackingTexture, 0, left - (Left - OffsetLeft), top - (Top - OffsetTop)); } } #endregion #region Dirty rectangles var dirtyRectangles = new RawRectangle[info.TotalMetadataBufferSize]; DuplicatedOutput.GetFrameDirtyRects(dirtyRectangles.Length, dirtyRectangles, out var dirtyRegionsLength); for (var dirtyIndex = 0; dirtyIndex < dirtyRegionsLength / Marshal.SizeOf(typeof(RawRectangle)); dirtyIndex++) { //Crop screen positions and size to frame sizes. var left = Math.Max(dirtyRectangles[dirtyIndex].Left, Left - OffsetLeft); var right = Math.Min(dirtyRectangles[dirtyIndex].Right, Left + Width - OffsetLeft); var top = Math.Max(dirtyRectangles[dirtyIndex].Top, Top - OffsetTop); var bottom = Math.Min(dirtyRectangles[dirtyIndex].Bottom, Top + Height - OffsetTop); //Copies from the screen texture only the area which the user wants to capture. if (right > left && bottom > top) { Device.ImmediateContext.CopySubresourceRegion(screenTexture, 0, new ResourceRegion(left, top, 0, right, bottom, 1), BackingTexture, 0, left - (Left - OffsetLeft), top - (Top - OffsetTop)); } } #endregion } } if (info.TotalMetadataBufferSize > 0 || info.LastMouseUpdateTime > 0) { //Copy the captured desktop texture into a staging texture, in order to show the mouse cursor and not make the captured texture dirty with it. Device.ImmediateContext.CopyResource(BackingTexture, StagingTexture); //Gets the cursor image and merges with the staging texture. GetCursor(StagingTexture, info, frame); } //Saves the most recent capture time. LastProcessTime = Math.Max(info.LastPresentTime, info.LastMouseUpdateTime); #endregion #region Gets the image data //Get the desktop capture texture. var data = Device.ImmediateContext.MapSubresource(StagingTexture, 0, MapMode.Read, MapFlags.None); if (data.IsEmpty) { Device.ImmediateContext.UnmapSubresource(StagingTexture, 0); resource?.Dispose(); return(FrameCount); } var bitmap = new System.Drawing.Bitmap(Width, Height, PixelFormat.Format32bppArgb); var boundsRect = new System.Drawing.Rectangle(0, 0, Width, Height); //Copy pixels from screen capture Texture to the GDI bitmap. var mapDest = bitmap.LockBits(boundsRect, ImageLockMode.WriteOnly, bitmap.PixelFormat); var sourcePtr = data.DataPointer; var destPtr = mapDest.Scan0; for (var y = 0; y < Height; y++) { //Copy a single line. Utilities.CopyMemory(destPtr, sourcePtr, Width * 4); //Advance pointers. sourcePtr = IntPtr.Add(sourcePtr, data.RowPitch); destPtr = IntPtr.Add(destPtr, mapDest.Stride); } //Releases the source and dest locks. bitmap.UnlockBits(mapDest); //Set frame details. FrameCount++; frame.Path = $"{Project.FullPath}{FrameCount}.png"; frame.Delay = FrameRate.GetMilliseconds(); frame.Image = bitmap; if (IsAcceptingFrames) { BlockingCollection.Add(frame); } #endregion Device.ImmediateContext?.UnmapSubresource(StagingTexture, 0); resource?.Dispose(); return(FrameCount); } catch (SharpDXException se) when(se.ResultCode.Code == SharpDX.DXGI.ResultCode.WaitTimeout.Result.Code) { return(FrameCount); } catch (SharpDXException se) when(se.ResultCode.Code == SharpDX.DXGI.ResultCode.DeviceRemoved.Result.Code || se.ResultCode.Code == SharpDX.DXGI.ResultCode.DeviceReset.Result.Code) { //When the device gets lost or reset, the resources should be instantiated again. DisposeInternal(); Initialize(); return(FrameCount); } catch (Exception ex) { LogWriter.Log(ex, "It was not possible to finish capturing the frame with DirectX."); MajorCrashHappened = true; if (IsAcceptingFrames) { Application.Current.Dispatcher.Invoke(() => OnError.Invoke(ex)); } return(FrameCount); } finally { try { //Only release the frame if there was a success in capturing it. if (res.Success) { DuplicatedOutput.ReleaseFrame(); } } catch (Exception e) { LogWriter.Log(e, "It was not possible to release the frame."); } } }
public override int CaptureWithCursor(FrameInfo frame) { var res = Result.Ok; try { //Try to get the duplicated output frame within given time. res = DuplicatedOutput.TryAcquireNextFrame(0, out var info, out var resource); //Checks how to proceed with the capture. It could have failed, or the screen, cursor or both could have been captured. if (res.Failure || resource == null || (info.AccumulatedFrames == 0 && info.LastMouseUpdateTime <= LastProcessTime)) { //Somehow, it was not possible to retrieve the resource, frame or metadata. resource?.Dispose(); return(FrameCount); } else if (info.AccumulatedFrames == 0 && info.LastMouseUpdateTime > LastProcessTime) { //Gets the cursor shape if the screen hasn't changed in between, so the cursor will be available for the next frame. GetCursor(null, info, frame); return(FrameCount); //TODO: if only the mouse changed, but there's no frame accumulated, but there's data in the texture from the previous frame, I need to merge with the cursor and add to the list. } //Saves the most recent capture time. LastProcessTime = Math.Max(info.LastPresentTime, info.LastMouseUpdateTime); //Copy resource into memory that can be accessed by the CPU. using (var screenTexture = resource.QueryInterface <Texture2D>()) { //Copies from the screen texture only the area which the user wants to capture. Device.ImmediateContext.CopySubresourceRegion(screenTexture, 0, new ResourceRegion(TrueLeft, TrueTop, 0, TrueRight, TrueBottom, 1), BackingTexture, 0); //Copy the captured desktop texture into a staging texture, in order to show the mouse cursor and not make the captured texture dirty with it. Device.ImmediateContext.CopyResource(BackingTexture, StagingTexture); //Gets the cursor image and merges with the staging texture. GetCursor(StagingTexture, info, frame); } //Get the desktop capture texture. var data = Device.ImmediateContext.MapSubresource(StagingTexture, 0, MapMode.Read, MapFlags.None, out var stream); if (data.IsEmpty) { Device.ImmediateContext.UnmapSubresource(StagingTexture, 0); stream?.Dispose(); resource.Dispose(); return(FrameCount); } #region Get image data //Set frame details. FrameCount++; frame.Path = $"{Project.FullPath}{FrameCount}.png"; frame.Delay = FrameRate.GetMilliseconds(SnapDelay); frame.DataLength = stream.Length; frame.Data = new byte[stream.Length]; //BGRA32 is 4 bytes. for (var height = 0; height < Height; height++) { stream.Position = height * data.RowPitch; Marshal.Copy(new IntPtr(stream.DataPointer.ToInt64() + height * data.RowPitch), frame.Data, height * Width * 4, Width * 4); } BlockingCollection.Add(frame); #endregion Device.ImmediateContext.UnmapSubresource(StagingTexture, 0); stream.Dispose(); resource.Dispose(); return(FrameCount); } catch (SharpDXException se) when(se.ResultCode.Code == SharpDX.DXGI.ResultCode.WaitTimeout.Result.Code) { return(FrameCount); } catch (SharpDXException se) when(se.ResultCode.Code == SharpDX.DXGI.ResultCode.DeviceRemoved.Result.Code || se.ResultCode.Code == SharpDX.DXGI.ResultCode.DeviceReset.Result.Code) { //When the device gets lost or reset, the resources should be instantiated again. DisposeInternal(); Initialize(); return(FrameCount); } catch (Exception ex) { LogWriter.Log(ex, "It was not possible to finish capturing the frame with DirectX."); OnError.Invoke(ex); return(FrameCount); } finally { try { //Only release the frame if there was a sucess in capturing it. if (res.Success) { DuplicatedOutput.ReleaseFrame(); } } catch (Exception e) { LogWriter.Log(e, "It was not possible to release the frame."); } } }
public override int CaptureWithCursor(FrameInfo frame) { try { new System.Security.Permissions.UIPermission(System.Security.Permissions.UIPermissionWindow.AllWindows).Demand(); var success = Native.BitBlt(CompatibleDeviceContext, 0, 0, Width, Height, WindowDeviceContext, Left, Top, Native.CopyPixelOperation.SourceCopy | Native.CopyPixelOperation.CaptureBlt); if (!success) { return(FrameCount); } #region Cursor try { var cursorInfo = new Native.CursorInfo(); cursorInfo.cbSize = Marshal.SizeOf(cursorInfo); if (Native.GetCursorInfo(out cursorInfo)) { if (cursorInfo.flags == Native.CursorShowing) { var hicon = Native.CopyIcon(cursorInfo.hCursor); if (hicon != IntPtr.Zero) { if (Native.GetIconInfo(hicon, out var iconInfo)) { frame.CursorX = cursorInfo.ptScreenPos.X - Left; frame.CursorY = cursorInfo.ptScreenPos.Y - Top; var ok = Native.DrawIconEx(CompatibleDeviceContext, frame.CursorX - iconInfo.xHotspot, frame.CursorY - iconInfo.yHotspot, cursorInfo.hCursor, 0, 0, CursorStep, IntPtr.Zero, 0x0003); if (!ok) { CursorStep = 0; Native.DrawIconEx(CompatibleDeviceContext, frame.CursorX - iconInfo.xHotspot, frame.CursorY - iconInfo.yHotspot, cursorInfo.hCursor, 0, 0, CursorStep, IntPtr.Zero, 0x0003); } else { CursorStep++; } } Native.DeleteObject(iconInfo.hbmColor); Native.DeleteObject(iconInfo.hbmMask); } Native.DestroyIcon(hicon); } Native.DeleteObject(cursorInfo.hCursor); } } catch (Exception) { //LogWriter.Log(e, "Impossible to get the cursor"); } #endregion //Set frame details. FrameCount++; frame.Path = $"{Project.FullPath}{FrameCount}.png"; frame.Delay = FrameRate.GetMilliseconds(SnapDelay); frame.Image = Image.FromHbitmap(CompatibleBitmap); BlockingCollection.Add(frame); } catch (Exception) { //LogWriter.Log(ex, "Impossible to get the screenshot of the screen"); } return(FrameCount); }
public override int Capture(FrameInfo frame) { var res = new Result(-1); try { //Try to get the duplicated output frame within given time. res = DuplicatedOutput.TryAcquireNextFrame(0, out var info, out var resource); //Somehow, it was not possible to retrieve the resource or any frame. if (res.Failure || resource == null || info.AccumulatedFrames == 0) { resource?.Dispose(); return(FrameCount); } //Copy resource into memory that can be accessed by the CPU. using (var screenTexture = resource.QueryInterface <Texture2D>()) { //Copies from the screen texture only the area which the user wants to capture. Device.ImmediateContext.CopySubresourceRegion(screenTexture, 0, new ResourceRegion(TrueLeft, TrueTop, 0, TrueRight, TrueBottom, 1), StagingTexture, 0); } //Get the desktop capture texture. var data = Device.ImmediateContext.MapSubresource(StagingTexture, 0, MapMode.Read, MapFlags.None); if (data.IsEmpty) { //frame.WasDropped = true; //BlockingCollection.Add(frame); Device.ImmediateContext.UnmapSubresource(StagingTexture, 0); resource.Dispose(); return(FrameCount); } #region Get image data var bitmap = new System.Drawing.Bitmap(Width, Height, PixelFormat.Format32bppArgb); var boundsRect = new System.Drawing.Rectangle(0, 0, Width, Height); //Copy pixels from screen capture Texture to the GDI bitmap. var mapDest = bitmap.LockBits(boundsRect, ImageLockMode.WriteOnly, bitmap.PixelFormat); var sourcePtr = data.DataPointer; var destPtr = mapDest.Scan0; for (var y = 0; y < Height; y++) { //Copy a single line. Utilities.CopyMemory(destPtr, sourcePtr, Width * 4); //Advance pointers. sourcePtr = IntPtr.Add(sourcePtr, data.RowPitch); destPtr = IntPtr.Add(destPtr, mapDest.Stride); } //Release source and dest locks. bitmap.UnlockBits(mapDest); //Set frame details. FrameCount++; frame.Path = $"{Project.FullPath}{FrameCount}.png"; frame.Delay = FrameRate.GetMilliseconds(SnapDelay); frame.Image = bitmap; BlockingCollection.Add(frame); #endregion Device.ImmediateContext.UnmapSubresource(StagingTexture, 0); resource.Dispose(); return(FrameCount); } catch (SharpDXException se) when(se.ResultCode.Code == SharpDX.DXGI.ResultCode.WaitTimeout.Result.Code) { return(FrameCount); } catch (SharpDXException se) when(se.ResultCode.Code == SharpDX.DXGI.ResultCode.DeviceRemoved.Result.Code || se.ResultCode.Code == SharpDX.DXGI.ResultCode.DeviceReset.Result.Code) { //When the device gets lost or reset, the resources should be instantiated again. DisposeInternal(); Initialize(); return(FrameCount); } catch (Exception ex) { LogWriter.Log(ex, "It was not possible to finish capturing the frame with DirectX."); OnError.Invoke(ex); return(FrameCount); } finally { try { //Only release the frame if there was a sucess in capturing it. if (res.Success) { DuplicatedOutput.ReleaseFrame(); } } catch (Exception e) { LogWriter.Log(e, "It was not possible to release the frame."); } } }
public override int Capture(FrameInfo frame) { var res = new Result(-1); try { //Try to get the duplicated output frame within given time. res = DuplicatedOutput.TryAcquireNextFrame(0, out var info, out var resource); if (FrameCount == 0 && (res.Failure || resource == null)) { //Somehow, it was not possible to retrieve the resource, frame or metadata. resource?.Dispose(); return(FrameCount); } #region Process changes //Something on screen was moved or changed. if (info.TotalMetadataBufferSize > 0) { //Copy resource into memory that can be accessed by the CPU. using (var screenTexture = resource.QueryInterface <Texture2D>()) { #region Moved rectangles var movedRectangles = new OutputDuplicateMoveRectangle[info.TotalMetadataBufferSize]; DuplicatedOutput.GetFrameMoveRects(movedRectangles.Length, movedRectangles, out var movedRegionsLength); for (var movedIndex = 0; movedIndex < movedRegionsLength / Marshal.SizeOf(typeof(OutputDuplicateMoveRectangle)); movedIndex++) { //Crop the destination rectangle to the screen area rectangle. var left = Math.Max(movedRectangles[movedIndex].DestinationRect.Left, Left - OffsetLeft); var right = Math.Min(movedRectangles[movedIndex].DestinationRect.Right, Left + Width - OffsetLeft); var top = Math.Max(movedRectangles[movedIndex].DestinationRect.Top, Top - OffsetTop); var bottom = Math.Min(movedRectangles[movedIndex].DestinationRect.Bottom, Top + Height - OffsetTop); //Copies from the screen texture only the area which the user wants to capture. if (right > left && bottom > top) { //Limit the source rectangle to the available size within the destination rectangle. var sourceWidth = movedRectangles[movedIndex].SourcePoint.X + (right - left); var sourceHeight = movedRectangles[movedIndex].SourcePoint.Y + (bottom - top); Device.ImmediateContext.CopySubresourceRegion(screenTexture, 0, new ResourceRegion(movedRectangles[movedIndex].SourcePoint.X, movedRectangles[movedIndex].SourcePoint.Y, 0, sourceWidth, sourceHeight, 1), StagingTexture, 0, left - (Left - OffsetLeft), top - (Top - OffsetTop)); } } #endregion #region Dirty rectangles var dirtyRectangles = new RawRectangle[info.TotalMetadataBufferSize]; DuplicatedOutput.GetFrameDirtyRects(dirtyRectangles.Length, dirtyRectangles, out var dirtyRegionsLength); for (var dirtyIndex = 0; dirtyIndex < dirtyRegionsLength / Marshal.SizeOf(typeof(RawRectangle)); dirtyIndex++) { //Crop screen positions and size to frame sizes. var left = Math.Max(dirtyRectangles[dirtyIndex].Left, Left - OffsetLeft); var right = Math.Min(dirtyRectangles[dirtyIndex].Right, Left + Width - OffsetLeft); var top = Math.Max(dirtyRectangles[dirtyIndex].Top, Top - OffsetTop); var bottom = Math.Min(dirtyRectangles[dirtyIndex].Bottom, Top + Height - OffsetTop); //Copies from the screen texture only the area which the user wants to capture. if (right > left && bottom > top) { Device.ImmediateContext.CopySubresourceRegion(screenTexture, 0, new ResourceRegion(left, top, 0, right, bottom, 1), StagingTexture, 0, left - (Left - OffsetLeft), top - (Top - OffsetTop)); } } #endregion } } #endregion #region Gets the image data //Gets the staging texture as a stream. var data = Device.ImmediateContext.MapSubresource(StagingTexture, 0, MapMode.Read, MapFlags.None, out var stream); if (data.IsEmpty) { Device.ImmediateContext.UnmapSubresource(StagingTexture, 0); stream?.Dispose(); resource?.Dispose(); return(FrameCount); } //Set frame details. FrameCount++; frame.Path = $"{Project.FullPath}{FrameCount}.png"; frame.Delay = FrameRate.GetMilliseconds(); frame.DataLength = stream.Length; frame.Data = new byte[stream.Length]; //BGRA32 is 4 bytes. for (var height = 0; height < Height; height++) { stream.Position = height * data.RowPitch; Marshal.Copy(new IntPtr(stream.DataPointer.ToInt64() + height * data.RowPitch), frame.Data, height * Width * 4, Width * 4); } if (IsAcceptingFrames) { BlockingCollection.Add(frame); } #endregion Device.ImmediateContext?.UnmapSubresource(StagingTexture, 0); resource?.Dispose(); return(FrameCount); } catch (SharpDXException se) when(se.ResultCode.Code == SharpDX.DXGI.ResultCode.WaitTimeout.Result.Code) { return(FrameCount); } catch (SharpDXException se) when(se.ResultCode.Code == SharpDX.DXGI.ResultCode.DeviceRemoved.Result.Code || se.ResultCode.Code == SharpDX.DXGI.ResultCode.DeviceReset.Result.Code) { //When the device gets lost or reset, the resources should be instantiated again. DisposeInternal(); Initialize(); return(FrameCount); } catch (Exception ex) { LogWriter.Log(ex, "It was not possible to finish capturing the frame with DirectX."); MajorCrashHappened = true; if (IsAcceptingFrames) { Application.Current.Dispatcher.Invoke(() => OnError.Invoke(ex)); } return(FrameCount); } finally { try { //Only release the frame if there was a sucess in capturing it. if (res.Success) { DuplicatedOutput.ReleaseFrame(); } } catch (Exception e) { LogWriter.Log(e, "It was not possible to release the frame."); } } }
public override int CaptureWithCursor(FrameInfo frame) { try { new System.Security.Permissions.UIPermission(System.Security.Permissions.UIPermissionWindow.AllWindows).Demand(); //var success = Native.BitBlt(CompatibleDeviceContext, 0, 0, Width, Height, WindowDeviceContext, Left, Top, Native.CopyPixelOperation.SourceCopy | Native.CopyPixelOperation.CaptureBlt); var success = Native.StretchBlt(CompatibleDeviceContext, 0, 0, StartWidth, StartHeight, WindowDeviceContext, Left, Top, Width, Height, Native.CopyPixelOperation.SourceCopy | Native.CopyPixelOperation.CaptureBlt); if (!success) { return(FrameCount); } #region Cursor try { var cursorInfo = new Native.CursorInfo(); cursorInfo.cbSize = Marshal.SizeOf(cursorInfo); if (Native.GetCursorInfo(out cursorInfo)) { if (cursorInfo.flags == Native.CursorShowing) { var hicon = Native.CopyIcon(cursorInfo.hCursor); if (hicon != IntPtr.Zero) { if (Native.GetIconInfo(hicon, out var iconInfo)) { frame.CursorX = cursorInfo.ptScreenPos.X - Left; frame.CursorY = cursorInfo.ptScreenPos.Y - Top; //If the cursor rate needs to be precisely captured. //https://source.winehq.org/source/dlls/user32/cursoricon.c#2325 //int rate = 0, num = 0; //var ok1 = Native.GetCursorFrameInfo(cursorInfo.hCursor, IntPtr.Zero, 17, ref rate, ref num); //CursorStep var ok = Native.DrawIconEx(CompatibleDeviceContext, frame.CursorX - iconInfo.xHotspot, frame.CursorY - iconInfo.yHotspot, cursorInfo.hCursor, 0, 0, CursorStep, IntPtr.Zero, 0x0003); if (!ok) { CursorStep = 0; Native.DrawIconEx(CompatibleDeviceContext, frame.CursorX - iconInfo.xHotspot, frame.CursorY - iconInfo.yHotspot, cursorInfo.hCursor, 0, 0, CursorStep, IntPtr.Zero, 0x0003); } else { CursorStep++; } //Set to fix all alpha bits back to 255. //frame.RemoveAnyTransparency = iconInfo.hbmMask != IntPtr.Zero; } Native.DeleteObject(iconInfo.hbmColor); Native.DeleteObject(iconInfo.hbmMask); } Native.DestroyIcon(hicon); } Native.DeleteObject(cursorInfo.hCursor); } } catch (Exception e) { //LogWriter.Log(e, "Impossible to get the cursor"); } #endregion //Set frame details. FrameCount++; frame.Path = $"{Project.FullPath}{FrameCount}.png"; frame.Delay = FrameRate.GetMilliseconds(SnapDelay); frame.DataLength = _byteLength; frame.Data = new byte[_byteLength]; Native.GetDIBits(WindowDeviceContext, CompatibleBitmap, 0, (uint)StartHeight, frame.Data, ref _infoHeader, Native.DibColorMode.DibRgbColors); BlockingCollection.Add(frame); } catch (Exception e) { //LogWriter.Log(ex, "Impossible to get the screenshot of the screen"); } return(FrameCount); }
public override int CaptureWithCursor(FrameInfo frame) { try { //var success = Native.BitBlt(CompatibleDeviceContext, 0, 0, Width, Height, WindowDeviceContext, Left, Top, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt); var success = Gdi32.StretchBlt(CompatibleDeviceContext, 0, 0, StartWidth, StartHeight, WindowDeviceContext, Left, Top, Width, Height, PixelOperations); if (!success) { return(FrameCount); } #region Cursor try { var cursorInfo = new CursorInfo(); cursorInfo.cbSize = Marshal.SizeOf(cursorInfo); if (User32.GetCursorInfo(out cursorInfo)) { if (cursorInfo.flags == Native.Constants.CursorShowing) { var hicon = User32.CopyIcon(cursorInfo.hCursor); if (hicon != IntPtr.Zero) { if (User32.GetIconInfo(hicon, out var iconInfo)) { frame.CursorX = cursorInfo.ptScreenPos.X - Left; frame.CursorY = cursorInfo.ptScreenPos.Y - Top; //(int)(SystemParameters.CursorHeight * Scale) //(int)(SystemParameters.CursorHeight * Scale) var ok = User32.DrawIconEx(CompatibleDeviceContext, frame.CursorX - iconInfo.xHotspot, frame.CursorY - iconInfo.yHotspot, cursorInfo.hCursor, 0, 0, CursorStep, IntPtr.Zero, 0x0003); if (!ok) { CursorStep = 0; User32.DrawIconEx(CompatibleDeviceContext, frame.CursorX - iconInfo.xHotspot, frame.CursorY - iconInfo.yHotspot, cursorInfo.hCursor, 0, 0, CursorStep, IntPtr.Zero, 0x0003); } else { CursorStep++; } } Gdi32.DeleteObject(iconInfo.hbmColor); Gdi32.DeleteObject(iconInfo.hbmMask); } User32.DestroyIcon(hicon); } Gdi32.DeleteObject(cursorInfo.hCursor); } } catch (Exception) { //LogWriter.Log(e, "Impossible to get the cursor"); } #endregion //Set frame details. FrameCount++; frame.Path = $"{Project.FullPath}{FrameCount}.png"; frame.Delay = FrameRate.GetMilliseconds(); frame.Image = Image.FromHbitmap(CompatibleBitmap); if (IsAcceptingFrames) { BlockingCollection.Add(frame); } } catch (Exception) { //LogWriter.Log(ex, "Impossible to get the screenshot of the screen"); } return(FrameCount); }