您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

429 行
14 KiB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Runtime.InteropServices;
  4. using EasyHook;
  5. using System.IO;
  6. using System.Runtime.Remoting;
  7. using System.Drawing;
  8. using System.Drawing.Imaging;
  9. using System.Diagnostics;
  10. using RenderHookAPI.Interface;
  11. using System.Threading;
  12. namespace RenderHookAPI.Hook
  13. {
  14. internal abstract class BaseDXHook: SharpDX.Component, IDXHook
  15. {
  16. protected readonly ClientCaptureInterfaceEventProxy InterfaceEventProxy = new ClientCaptureInterfaceEventProxy();
  17. public BaseDXHook(CaptureInterface ssInterface)
  18. {
  19. this.Interface = ssInterface;
  20. this.Timer = new Stopwatch();
  21. this.Timer.Start();
  22. this.FPS = new FramesPerSecond();
  23. Interface.ScreenshotRequested += InterfaceEventProxy.ScreenshotRequestedProxyHandler;
  24. Interface.DisplayText += InterfaceEventProxy.DisplayTextProxyHandler;
  25. Interface.DrawOverlay += InterfaceEventProxy.DrawOverlayProxyHandler;
  26. InterfaceEventProxy.ScreenshotRequested += new ScreenshotRequestedEvent(InterfaceEventProxy_ScreenshotRequested);
  27. InterfaceEventProxy.DisplayText += new DisplayTextEvent(InterfaceEventProxy_DisplayText);
  28. InterfaceEventProxy.DrawOverlay += InterfaceEventProxy_DrawOverlay;
  29. }
  30. ~BaseDXHook()
  31. {
  32. Dispose(false);
  33. }
  34. void InterfaceEventProxy_DisplayText(DisplayTextEventArgs args)
  35. {
  36. TextDisplay = new TextDisplay()
  37. {
  38. Text = args.Text,
  39. Duration = args.Duration
  40. };
  41. }
  42. protected virtual void InterfaceEventProxy_ScreenshotRequested(ScreenshotRequest request)
  43. {
  44. this.Request = request;
  45. }
  46. private void InterfaceEventProxy_DrawOverlay(DrawOverlayEventArgs args)
  47. {
  48. Overlays = new List<Common.IOverlay>();
  49. if (args.Overlay != null)
  50. Overlays.Add(args.Overlay);
  51. IsOverlayUpdatePending = true;
  52. }
  53. protected Stopwatch Timer { get; set; }
  54. /// <summary>
  55. /// Frames Per second counter, FPS.Frame() must be called each frame
  56. /// </summary>
  57. protected FramesPerSecond FPS { get; set; }
  58. protected TextDisplay TextDisplay { get; set; }
  59. protected List<Common.IOverlay> Overlays { get; set; }
  60. protected bool IsOverlayUpdatePending { get; set; }
  61. int _processId = 0;
  62. protected int ProcessId
  63. {
  64. get
  65. {
  66. if (_processId == 0)
  67. {
  68. _processId = RemoteHooking.GetCurrentProcessId();
  69. }
  70. return _processId;
  71. }
  72. }
  73. protected virtual string HookName
  74. {
  75. get
  76. {
  77. return "BaseDXHook";
  78. }
  79. }
  80. protected void Frame()
  81. {
  82. FPS.Frame();
  83. if (TextDisplay != null && TextDisplay.Display)
  84. TextDisplay.Frame();
  85. }
  86. protected void DebugMessage(string message)
  87. {
  88. #if DEBUG
  89. try
  90. {
  91. Interface.Message(MessageType.Debug, HookName + ": " + message);
  92. }
  93. catch (RemotingException)
  94. {
  95. // Ignore remoting exceptions
  96. }
  97. catch (Exception)
  98. {
  99. // Ignore all other exceptions
  100. }
  101. #endif
  102. }
  103. protected IntPtr[] GetVTblAddresses(IntPtr pointer, int numberOfMethods)
  104. {
  105. return GetVTblAddresses(pointer, 0, numberOfMethods);
  106. }
  107. protected IntPtr[] GetVTblAddresses(IntPtr pointer, int startIndex, int numberOfMethods)
  108. {
  109. List<IntPtr> vtblAddresses = new List<IntPtr>();
  110. IntPtr vTable = Marshal.ReadIntPtr(pointer);
  111. for (int i = startIndex; i < startIndex + numberOfMethods; i++)
  112. vtblAddresses.Add(Marshal.ReadIntPtr(vTable, i * IntPtr.Size)); // using IntPtr.Size allows us to support both 32 and 64-bit processes
  113. return vtblAddresses.ToArray();
  114. }
  115. protected static void CopyStream(Stream input, Stream output)
  116. {
  117. int bufferSize = 32768;
  118. byte[] buffer = new byte[bufferSize];
  119. while (true)
  120. {
  121. int read = input.Read(buffer, 0, buffer.Length);
  122. if (read <= 0)
  123. {
  124. return;
  125. }
  126. output.Write(buffer, 0, read);
  127. }
  128. }
  129. /// <summary>
  130. /// Reads data from a stream until the end is reached. The
  131. /// data is returned as a byte array. An IOException is
  132. /// thrown if any of the underlying IO calls fail.
  133. /// </summary>
  134. /// <param name="stream">The stream to read data from</param>
  135. protected static byte[] ReadFullStream(Stream stream)
  136. {
  137. if (stream is MemoryStream)
  138. {
  139. return ((MemoryStream)stream).ToArray();
  140. }
  141. else
  142. {
  143. byte[] buffer = new byte[32768];
  144. using (MemoryStream ms = new MemoryStream())
  145. {
  146. while (true)
  147. {
  148. int read = stream.Read(buffer, 0, buffer.Length);
  149. if (read > 0)
  150. ms.Write(buffer, 0, read);
  151. if (read < buffer.Length)
  152. {
  153. return ms.ToArray();
  154. }
  155. }
  156. }
  157. }
  158. }
  159. /// <summary>
  160. /// Process the capture based on the requested format.
  161. /// </summary>
  162. /// <param name="width">image width</param>
  163. /// <param name="height">image height</param>
  164. /// <param name="pitch">data pitch (bytes per row)</param>
  165. /// <param name="format">target format</param>
  166. /// <param name="pBits">IntPtr to the image data</param>
  167. /// <param name="request">The original requets</param>
  168. protected void ProcessCapture(int width, int height, int pitch, PixelFormat format, IntPtr pBits, ScreenshotRequest request)
  169. {
  170. if (request == null)
  171. return;
  172. if (format == PixelFormat.Undefined)
  173. {
  174. DebugMessage("Unsupported render target format");
  175. return;
  176. }
  177. // Copy the image data from the buffer
  178. int size = height * pitch;
  179. var data = new byte[size];
  180. Marshal.Copy(pBits, data, 0, size);
  181. // Prepare the response
  182. Screenshot response = null;
  183. if (request.Format == RenderHookAPI.Interface.ImageFormat.PixelData)
  184. {
  185. // Return the raw data
  186. response = new Screenshot(request.RequestId, data)
  187. {
  188. Format = request.Format,
  189. PixelFormat = format,
  190. Height = height,
  191. Width = width,
  192. Stride = pitch
  193. };
  194. }
  195. else
  196. {
  197. // Return an image
  198. using (var bm = data.ToBitmap(width, height, pitch, format))
  199. {
  200. System.Drawing.Imaging.ImageFormat imgFormat = System.Drawing.Imaging.ImageFormat.Bmp;
  201. switch (request.Format)
  202. {
  203. case RenderHookAPI.Interface.ImageFormat.Jpeg:
  204. imgFormat = System.Drawing.Imaging.ImageFormat.Jpeg;
  205. break;
  206. case RenderHookAPI.Interface.ImageFormat.Png:
  207. imgFormat = System.Drawing.Imaging.ImageFormat.Png;
  208. break;
  209. }
  210. response = new Screenshot(request.RequestId, bm.ToByteArray(imgFormat))
  211. {
  212. Format = request.Format,
  213. Height = bm.Height,
  214. Width = bm.Width
  215. };
  216. }
  217. }
  218. // Send the response
  219. SendResponse(response);
  220. }
  221. protected void SendResponse(Screenshot response)
  222. {
  223. System.Threading.Tasks.Task.Factory.StartNew(() =>
  224. {
  225. try
  226. {
  227. Interface.SendScreenshotResponse(response);
  228. LastCaptureTime = Timer.Elapsed;
  229. }
  230. catch (RemotingException)
  231. {
  232. // Ignore remoting exceptions
  233. // .NET Remoting will throw an exception if the host application is unreachable
  234. }
  235. catch (Exception e)
  236. {
  237. DebugMessage(e.ToString());
  238. }
  239. });
  240. }
  241. protected void ProcessCapture(Stream stream, ScreenshotRequest request)
  242. {
  243. ProcessCapture(ReadFullStream(stream), request);
  244. }
  245. protected void ProcessCapture(byte[] bitmapData, ScreenshotRequest request)
  246. {
  247. try
  248. {
  249. if (request != null)
  250. {
  251. Interface.SendScreenshotResponse(new Screenshot(request.RequestId, bitmapData)
  252. {
  253. Format = request.Format,
  254. });
  255. }
  256. LastCaptureTime = Timer.Elapsed;
  257. }
  258. catch (RemotingException)
  259. {
  260. // Ignore remoting exceptions
  261. // .NET Remoting will throw an exception if the host application is unreachable
  262. }
  263. catch (Exception e)
  264. {
  265. DebugMessage(e.ToString());
  266. }
  267. }
  268. private ImageCodecInfo GetEncoder(System.Drawing.Imaging.ImageFormat format)
  269. {
  270. ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
  271. foreach (ImageCodecInfo codec in codecs)
  272. {
  273. if (codec.FormatID == format.Guid)
  274. {
  275. return codec;
  276. }
  277. }
  278. return null;
  279. }
  280. private Bitmap BitmapFromBytes(byte[] bitmapData)
  281. {
  282. using (MemoryStream ms = new MemoryStream(bitmapData))
  283. {
  284. return (Bitmap)Image.FromStream(ms);
  285. }
  286. }
  287. protected TimeSpan LastCaptureTime
  288. {
  289. get;
  290. set;
  291. }
  292. protected bool CaptureThisFrame
  293. {
  294. get
  295. {
  296. return ((Timer.Elapsed - LastCaptureTime) > CaptureDelay) || Request != null;
  297. }
  298. }
  299. protected TimeSpan CaptureDelay { get; set; }
  300. #region IDXHook Members
  301. public CaptureInterface Interface
  302. {
  303. get;
  304. set;
  305. }
  306. private CaptureConfig _config;
  307. public CaptureConfig Config
  308. {
  309. get { return _config; }
  310. set
  311. {
  312. _config = value;
  313. CaptureDelay = new TimeSpan(0, 0, 0, 0, (int)((1.0 / (double)_config.TargetFramesPerSecond) * 1000.0));
  314. }
  315. }
  316. private ScreenshotRequest _request;
  317. public ScreenshotRequest Request
  318. {
  319. get { return _request; }
  320. set { Interlocked.Exchange(ref _request, value); }
  321. }
  322. protected List<Hook> Hooks = new List<Hook>();
  323. public abstract void Hook();
  324. public abstract void Cleanup();
  325. #endregion
  326. #region IDispose Implementation
  327. protected override void Dispose(bool disposeManagedResources)
  328. {
  329. // Only clean up managed objects if disposing (i.e. not called from destructor)
  330. if (disposeManagedResources)
  331. {
  332. try
  333. {
  334. Cleanup();
  335. }
  336. catch { }
  337. try
  338. {
  339. // Uninstall Hooks
  340. if (Hooks.Count > 0)
  341. {
  342. // First disable the hook (by excluding all threads) and wait long enough to ensure that all hooks are not active
  343. foreach (var hook in Hooks)
  344. {
  345. // Lets ensure that no threads will be intercepted again
  346. hook.Deactivate();
  347. }
  348. System.Threading.Thread.Sleep(100);
  349. // Now we can dispose of the hooks (which triggers the removal of the hook)
  350. foreach (var hook in Hooks)
  351. {
  352. hook.Dispose();
  353. }
  354. Hooks.Clear();
  355. }
  356. try
  357. {
  358. // Remove the event handlers
  359. Interface.ScreenshotRequested -= InterfaceEventProxy.ScreenshotRequestedProxyHandler;
  360. Interface.DisplayText -= InterfaceEventProxy.DisplayTextProxyHandler;
  361. Interface.DrawOverlay -= InterfaceEventProxy_DrawOverlay;
  362. }
  363. catch (RemotingException) { } // Ignore remoting exceptions (host process may have been closed)
  364. }
  365. catch
  366. {
  367. }
  368. }
  369. base.Dispose(disposeManagedResources);
  370. }
  371. #endregion
  372. }
  373. }