123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 |
- using EasyHook;
- using FISLib;
- using FISSDKDemo.Managers;
- using SharpDX;
- using SharpDX.Direct3D9;
- using System;
- using System.Collections.Concurrent;
- using System.Collections.Generic;
- using System.Runtime.InteropServices;
- namespace FISSDKDemo
- {
- public unsafe class D3D9WindowCapturer
- {
- [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)]
- private delegate int PresentFunc(IntPtr swapChainPtr, Rectangle* pSourceRect, Rectangle* pDestRect,
- IntPtr hDestWindowOverride, IntPtr pDirtyRegion, Present dwFlags);
- private static PresentFunc _originPresentFunc;
- private static readonly Dictionary<SurfaceDescription, Surface> _dstSurfaces = new Dictionary<SurfaceDescription, Surface>();
- private static readonly ConcurrentDictionary<IntPtr, LocalHook> _registerCaptureHooks = new ConcurrentDictionary<IntPtr, LocalHook>();
- private static readonly object LockObject = new object();
- private static bool _startCapture;
- private static LocalHook _localhook;
- /// <summary>
- /// Raised when the target window is changed, the image data of the window will be included.
- /// </summary>
- public static event EventHandler<FISCaptureDataEventArgs> Captured;
- static D3D9WindowCapturer()
- {
- }
- [DllImport("User32.dll")]
- private static extern IntPtr GetDesktopWindow();
- public static void RegisterWindow(IntPtr windowHandle)
- {
- _registerCaptureHooks.TryAdd(windowHandle, _localhook);
- }
- public static void UnRegisterWindow(IntPtr windowHandle)
- {
- _registerCaptureHooks.TryRemove(windowHandle, out var localHook);
- Logger.WriteLineInfo($"Vcloud Service D3DWindowCapturer UnRegisterWindow windowHandle:{windowHandle} end");
- }
- private static void CopySurface(SwapChain swapChain, IntPtr hDestWindowOverride)
- {
- try
- {
- if (!_startCapture)
- {
- return;
- }
- if (!_registerCaptureHooks.ContainsKey(hDestWindowOverride))
- {
- return;
- }
- using (var srcSurface = swapChain.GetBackBuffer(0))
- using (var device = swapChain.Device)
- {
- var desc = srcSurface.Description;
- _dstSurfaces.TryGetValue(desc, out var dstSurface);
- var width = desc.Width;
- var height = desc.Height;
- for (var i = 0; ; i++)
- {
- const int maxRetry = 3;
- if (i >= maxRetry)
- {
- Logger.WriteLineError($"D3D9WindowCapturer.CopySurface handle:{hDestWindowOverride} i >= maxRetry");
- return;
- }
- if (dstSurface != null && TryGetRenderTargetData(device, srcSurface, dstSurface))
- break;
- dstSurface?.Dispose();
- _dstSurfaces[desc] =
- dstSurface =
- Surface.CreateOffscreenPlain(device, width, height, desc.Format, Pool.SystemMemory);
- Logger.WriteLineInfo($"D3D9WindowCapturer.CopySurface Add dstSurface handle:{hDestWindowOverride}");
- }
- var data = dstSurface.LockRectangle(LockFlags.NoDirtyUpdate | LockFlags.ReadOnly).DataPointer;
- OnCaptured(new FISCaptureDataEventArgs(hDestWindowOverride, width, height, data));
- dstSurface.UnlockRectangle();
- //Logger.WriteLineInfo($"D3D9WindowCapturer.CopySurface _dstSurfacesCount:{_dstSurfaces.Count} end size:{width}*{height}");
- }
- }
- catch (Exception ex)
- {
- Logger.WriteLineError($"D3D9WindowCapturer.{ex}");
- }
- }
- private static int HookedPresent(IntPtr swapChainPtr, Rectangle* pSourceRect, Rectangle* pDestRect,
- IntPtr hDestWindowOverride, IntPtr pDirtyRegion, Present dwFlags)
- {
- try
- {
- var swapChain = (SwapChain)swapChainPtr;
- CopySurface(swapChain, hDestWindowOverride);
- }
- catch (Exception e)
- {
- Logger.WriteLineError($"D3D9WindowCapturer HookedPresent: {e}");
- }
- return _originPresentFunc(swapChainPtr, pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion, dwFlags);
- }
- private static LocalHook InitLocalHook(IntPtr windowHandle)
- {
- LocalHook localHook = null;
- try
- {
- Logger.WriteLineInfo($"D3D9WindowCapturer.Init windowHandle:{windowHandle} Begin");
- var parameters = new PresentParameters
- {
- Windowed = true,
- SwapEffect = SwapEffect.Discard,
- DeviceWindowHandle = windowHandle
- };
- const CreateFlags flags = CreateFlags.HardwareVertexProcessing | CreateFlags.Multithreaded |
- CreateFlags.FpuPreserve;
- using (var direct3D = new Direct3D())
- using (var device = new SharpDX.Direct3D9.Device(direct3D, 0, DeviceType.Hardware, IntPtr.Zero, flags, parameters))
- using (var swapChain = new SwapChain(device, parameters))
- {
- var vTable = Marshal.ReadIntPtr(swapChain.NativePointer);
- var inTargetProc = Marshal.ReadIntPtr(vTable, 3 * IntPtr.Size);
- _originPresentFunc = Marshal.GetDelegateForFunctionPointer<PresentFunc>(inTargetProc);
- var hookedPresentFunc = new PresentFunc(HookedPresent);
- localHook = LocalHook.Create(inTargetProc, hookedPresentFunc, null);
- localHook.ThreadACL.SetExclusiveACL(new[] { 0 });
- Logger.WriteLineInfo($"D3D9WindowCapturer.Init windowHandle:{windowHandle} End");
- }
- }
- catch (Exception e)
- {
- Logger.WriteLineError($"D3D9WindowCapturer.Init windowHandle:{windowHandle} ,{e}");
- }
- return localHook;
- }
- public static void Start()
- {
- lock (LockObject)
- {
- if (!_startCapture)
- {
- _startCapture = true;
- }
- if (_localhook == null)
- {
- _localhook = InitLocalHook(GetDesktopWindow());
- }
- }
- }
- public static void Stop()
- {
- lock (LockObject)
- {
- if (_startCapture)
- {
- _startCapture = false;
- }
- }
- Logger.WriteLineInfo($"Vcloud Service D3DWindowCapturer Stop end");
- }
- private static bool TryGetRenderTargetData(Device device, Surface srcSurface, Surface dstSurface)
- {
- try
- {
- device.GetRenderTargetData(srcSurface, dstSurface);
- return true;
- }
- catch (SharpDXException e)
- {
- if (e.Descriptor != ResultCode.InvalidCall)
- throw;
- // Maybe display resolution changed
- Logger.WriteLineInfo("D3D9WindowCapturer.TryGetRenderTargetData e.Descriptor == ResultCode.InvalidCall");
- }
- return false;
- }
- private static void OnCaptured(FISCaptureDataEventArgs e)
- {
- Captured?.Invoke(null, e);
- }
- }
- }
|