Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.

8 mesi fa
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. using System;
  2. using System.Collections.Generic;
  3. using RenderHookAPI.Hook;
  4. using RenderHookAPI.Interface;
  5. using System.Threading.Tasks;
  6. using System.Runtime.Remoting.Channels.Ipc;
  7. namespace RenderHookAPI
  8. {
  9. public class EntryPoint : EasyHook.IEntryPoint
  10. {
  11. List<IDXHook> _directXHooks = new List<IDXHook>();
  12. IDXHook _directXHook = null;
  13. private CaptureInterface _interface;
  14. private System.Threading.ManualResetEvent _runWait;
  15. ClientCaptureInterfaceEventProxy _clientEventProxy = new ClientCaptureInterfaceEventProxy();
  16. IpcServerChannel _clientServerChannel = null;
  17. public EntryPoint(
  18. EasyHook.RemoteHooking.IContext context,
  19. String channelName,
  20. CaptureConfig config)
  21. {
  22. // Get reference to IPC to host application
  23. // Note: any methods called or events triggered against _interface will execute in the host process.
  24. _interface = EasyHook.RemoteHooking.IpcConnectClient<CaptureInterface>(channelName);
  25. // We try to ping immediately, if it fails then injection fails
  26. _interface.Ping();
  27. #region Allow client event handlers (bi-directional IPC)
  28. // Attempt to create a IpcServerChannel so that any event handlers on the client will function correctly
  29. System.Collections.IDictionary properties = new System.Collections.Hashtable();
  30. properties["name"] = channelName;
  31. properties["portName"] = channelName + Guid.NewGuid().ToString("N"); // random portName so no conflict with existing channels of channelName
  32. System.Runtime.Remoting.Channels.BinaryServerFormatterSinkProvider binaryProv = new System.Runtime.Remoting.Channels.BinaryServerFormatterSinkProvider();
  33. binaryProv.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
  34. System.Runtime.Remoting.Channels.Ipc.IpcServerChannel _clientServerChannel = new System.Runtime.Remoting.Channels.Ipc.IpcServerChannel(properties, binaryProv);
  35. System.Runtime.Remoting.Channels.ChannelServices.RegisterChannel(_clientServerChannel, false);
  36. #endregion
  37. }
  38. public void Run(
  39. EasyHook.RemoteHooking.IContext context,
  40. String channelName,
  41. CaptureConfig config)
  42. {
  43. // When not using GAC there can be issues with remoting assemblies resolving correctly
  44. // this is a workaround that ensures that the current assembly is correctly associated
  45. AppDomain currentDomain = AppDomain.CurrentDomain;
  46. currentDomain.AssemblyResolve += (sender, args) =>
  47. {
  48. return this.GetType().Assembly.FullName == args.Name ? this.GetType().Assembly : null;
  49. };
  50. // NOTE: This is running in the target process
  51. _interface.Message(MessageType.Information, "Injected into process Id:{0}.", EasyHook.RemoteHooking.GetCurrentProcessId());
  52. _runWait = new System.Threading.ManualResetEvent(false);
  53. _runWait.Reset();
  54. try
  55. {
  56. // Initialise the Hook
  57. if (!InitialiseDirectXHook(config))
  58. {
  59. return;
  60. }
  61. _interface.Disconnected += _clientEventProxy.DisconnectedProxyHandler;
  62. // Important Note:
  63. // accessing the _interface from within a _clientEventProxy event handler must always
  64. // be done on a different thread otherwise it will cause a deadlock
  65. _clientEventProxy.Disconnected += () =>
  66. {
  67. // We can now signal the exit of the Run method
  68. _runWait.Set();
  69. };
  70. // We start a thread here to periodically check if the host is still running
  71. // If the host process stops then we will automatically uninstall the hooks
  72. StartCheckHostIsAliveThread();
  73. // Wait until signaled for exit either when a Disconnect message from the host
  74. // or if the the check is alive has failed to Ping the host.
  75. _runWait.WaitOne();
  76. // we need to tell the check host thread to exit (if it hasn't already)
  77. StopCheckHostIsAliveThread();
  78. // Dispose of the DXHook so any installed hooks are removed correctly
  79. DisposeDirectXHook();
  80. }
  81. catch (Exception e)
  82. {
  83. _interface.Message(MessageType.Error, "An unexpected error occured: {0}", e.ToString());
  84. }
  85. finally
  86. {
  87. try
  88. {
  89. _interface.Message(MessageType.Information, "Disconnecting from process {0}", EasyHook.RemoteHooking.GetCurrentProcessId());
  90. }
  91. catch
  92. {
  93. }
  94. // Remove the client server channel (that allows client event handlers)
  95. System.Runtime.Remoting.Channels.ChannelServices.UnregisterChannel(_clientServerChannel);
  96. // Always sleep long enough for any remaining messages to complete sending
  97. System.Threading.Thread.Sleep(100);
  98. }
  99. }
  100. private void DisposeDirectXHook()
  101. {
  102. if (_directXHooks != null)
  103. {
  104. try
  105. {
  106. _interface.Message(MessageType.Debug, "Disposing of hooks...");
  107. }
  108. catch (System.Runtime.Remoting.RemotingException) { } // Ignore channel remoting errors
  109. // Dispose of the hooks so they are removed
  110. foreach (var dxHook in _directXHooks)
  111. dxHook.Dispose();
  112. _directXHooks.Clear();
  113. }
  114. }
  115. private bool InitialiseDirectXHook(CaptureConfig config)
  116. {
  117. Direct3DVersion version = config.Direct3DVersion;
  118. List<Direct3DVersion> loadedVersions = new List<Direct3DVersion>();
  119. bool isX64Process = EasyHook.RemoteHooking.IsX64Process(EasyHook.RemoteHooking.GetCurrentProcessId());
  120. _interface.Message(MessageType.Information, "Remote process is a {0}-bit process.", isX64Process ? "64" : "32");
  121. try
  122. {
  123. if (version == Direct3DVersion.AutoDetect || version == Direct3DVersion.Unknown)
  124. {
  125. // Attempt to determine the correct version based on loaded module.
  126. // In most cases this will work fine, however it is perfectly ok for an application to use a D3D10 device along with D3D11 devices
  127. // so the version might matched might not be the one you want to use
  128. IntPtr d3D9Loaded = IntPtr.Zero;
  129. IntPtr d3D10Loaded = IntPtr.Zero;
  130. IntPtr d3D10_1Loaded = IntPtr.Zero;
  131. IntPtr d3D11Loaded = IntPtr.Zero;
  132. IntPtr d3D11_1Loaded = IntPtr.Zero;
  133. int delayTime = 100;
  134. int retryCount = 0;
  135. while (d3D9Loaded == IntPtr.Zero && d3D10Loaded == IntPtr.Zero && d3D10_1Loaded == IntPtr.Zero && d3D11Loaded == IntPtr.Zero && d3D11_1Loaded == IntPtr.Zero)
  136. {
  137. retryCount++;
  138. d3D9Loaded = NativeMethods.GetModuleHandle("d3d9.dll");
  139. d3D10Loaded = NativeMethods.GetModuleHandle("d3d10.dll");
  140. d3D10_1Loaded = NativeMethods.GetModuleHandle("d3d10_1.dll");
  141. d3D11Loaded = NativeMethods.GetModuleHandle("d3d11.dll");
  142. d3D11_1Loaded = NativeMethods.GetModuleHandle("d3d11_1.dll");
  143. System.Threading.Thread.Sleep(delayTime);
  144. if (retryCount * delayTime > 5000)
  145. {
  146. _interface.Message(MessageType.Error, "Unsupported Direct3D version, or Direct3D DLL not loaded within 5 seconds.");
  147. return false;
  148. }
  149. }
  150. version = Direct3DVersion.Unknown;
  151. if (d3D11_1Loaded != IntPtr.Zero)
  152. {
  153. _interface.Message(MessageType.Debug, "Autodetect found Direct3D 11.1");
  154. version = Direct3DVersion.Direct3D11_1;
  155. loadedVersions.Add(version);
  156. }
  157. if (d3D11Loaded != IntPtr.Zero)
  158. {
  159. _interface.Message(MessageType.Debug, "Autodetect found Direct3D 11");
  160. version = Direct3DVersion.Direct3D11;
  161. loadedVersions.Add(version);
  162. }
  163. if (d3D10_1Loaded != IntPtr.Zero)
  164. {
  165. _interface.Message(MessageType.Debug, "Autodetect found Direct3D 10.1");
  166. version = Direct3DVersion.Direct3D10_1;
  167. loadedVersions.Add(version);
  168. }
  169. if (d3D10Loaded != IntPtr.Zero)
  170. {
  171. _interface.Message(MessageType.Debug, "Autodetect found Direct3D 10");
  172. version = Direct3DVersion.Direct3D10;
  173. loadedVersions.Add(version);
  174. }
  175. if (d3D9Loaded != IntPtr.Zero)
  176. {
  177. _interface.Message(MessageType.Debug, "Autodetect found Direct3D 9");
  178. version = Direct3DVersion.Direct3D9;
  179. loadedVersions.Add(version);
  180. }
  181. }
  182. else
  183. {
  184. // If not autodetect, assume specified version is loaded
  185. loadedVersions.Add(version);
  186. }
  187. foreach (var dxVersion in loadedVersions)
  188. {
  189. version = dxVersion;
  190. switch (version)
  191. {
  192. case Direct3DVersion.Direct3D9:
  193. _directXHook = new DXHookD3D9(_interface);
  194. break;
  195. case Direct3DVersion.Direct3D10:
  196. _directXHook = new DXHookD3D10(_interface);
  197. break;
  198. case Direct3DVersion.Direct3D10_1:
  199. _directXHook = new DXHookD3D10_1(_interface);
  200. break;
  201. case Direct3DVersion.Direct3D11:
  202. _directXHook = new DXHookD3D11(_interface);
  203. break;
  204. //case Direct3DVersion.Direct3D11_1:
  205. // _directXHook = new DXHookD3D11_1(_interface);
  206. // return;
  207. default:
  208. _interface.Message(MessageType.Error, "Unsupported Direct3D version: {0}", version);
  209. return false;
  210. }
  211. _directXHook.Config = config;
  212. _directXHook.Hook();
  213. _directXHooks.Add(_directXHook);
  214. }
  215. return true;
  216. }
  217. catch (Exception e)
  218. {
  219. // Notify the host/server application about this error
  220. _interface.Message(MessageType.Error, "Error in InitialiseHook: {0}", e.ToString());
  221. return false;
  222. }
  223. }
  224. #region Check Host Is Alive
  225. Task _checkAlive;
  226. long _stopCheckAlive = 0;
  227. /// <summary>
  228. /// Begin a background thread to check periodically that the host process is still accessible on its IPC channel
  229. /// </summary>
  230. private void StartCheckHostIsAliveThread()
  231. {
  232. _checkAlive = new Task(() =>
  233. {
  234. try
  235. {
  236. while (System.Threading.Interlocked.Read(ref _stopCheckAlive) == 0)
  237. {
  238. System.Threading.Thread.Sleep(1000);
  239. // .NET Remoting exceptions will throw RemotingException
  240. _interface.Ping();
  241. }
  242. }
  243. catch // We will assume that any exception means that the hooks need to be removed.
  244. {
  245. // Signal the Run method so that it can exit
  246. _runWait.Set();
  247. }
  248. });
  249. _checkAlive.Start();
  250. }
  251. /// <summary>
  252. /// Tell the _checkAlive thread that it can exit if it hasn't already
  253. /// </summary>
  254. private void StopCheckHostIsAliveThread()
  255. {
  256. System.Threading.Interlocked.Increment(ref _stopCheckAlive);
  257. }
  258. #endregion
  259. }
  260. }