private void SafeInvokeRecordingStarted(CaptureConfig config) { if (RecordingStarted == null) { return; //No Listeners } RecordingStartedEvent listener = null; Delegate[] dels = RecordingStarted.GetInvocationList(); foreach (Delegate del in dels) { try { listener = (RecordingStartedEvent)del; listener.Invoke(config); } catch (Exception) { //Could not reach the destination, so remove it //from the list RecordingStarted -= listener; } } }
public void RecordingStartedProxyHandler(CaptureConfig config) { if (RecordingStarted != null) { RecordingStarted(config); } }
public EntryPoint( EasyHook.RemoteHooking.IContext context, String channelName, CaptureConfig config) { // Get reference to IPC to host application // Note: any methods called or events triggered against _interface will execute in the host process. _interface = EasyHook.RemoteHooking.IpcConnectClient<CaptureInterface>(channelName); // We try to ping immediately, if it fails then injection fails _interface.Ping(); #region Allow client event handlers (bi-directional IPC) // Attempt to create a IpcServerChannel so that any event handlers on the client will function correctly System.Collections.IDictionary properties = new System.Collections.Hashtable(); properties["name"] = channelName; properties["portName"] = channelName + Guid.NewGuid().ToString("N"); // random portName so no conflict with existing channels of channelName System.Runtime.Remoting.Channels.BinaryServerFormatterSinkProvider binaryProv = new System.Runtime.Remoting.Channels.BinaryServerFormatterSinkProvider(); binaryProv.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full; System.Runtime.Remoting.Channels.Ipc.IpcServerChannel _clientServerChannel = new System.Runtime.Remoting.Channels.Ipc.IpcServerChannel(properties, binaryProv); System.Runtime.Remoting.Channels.ChannelServices.RegisterChannel(_clientServerChannel, false); #endregion }
/// <summary> /// If not <see cref="IsRecording"/> will invoke the <see cref="RecordingStarted"/> event, starting a new recording. /// </summary> /// <param name="config">The configuration for the recording</param> /// <remarks>Handlers in the server and remote process will be be invoked.</remarks> public void StartRecording(CaptureConfig config) { if (IsRecording) { return; } SafeInvokeRecordingStarted(config); IsRecording = true; }
/// <summary> /// Prepares capturing in the target process. Note that the process must not already be hooked, and must have a <see cref="Process.MainWindowHandle"/>. /// </summary> /// <param name="process">The process to inject into</param> /// <exception cref="ProcessHasNoWindowHandleException">Thrown if the <paramref name="process"/> does not have a window handle. This could mean that the process does not have a UI, or that the process has not yet finished starting.</exception> /// <exception cref="ProcessAlreadyHookedException">Thrown if the <paramref name="process"/> is already hooked</exception> /// <exception cref="InjectionFailedException">Thrown if the injection failed - see the InnerException for more details.</exception> /// <remarks>The target process will have its main window brought to the foreground after successful injection.</remarks> public CaptureProcess(Process process, CaptureConfig config, CaptureInterface captureInterface) { // If the process doesn't have a mainwindowhandle yet, skip it (we need to be able to get the hwnd to set foreground etc) //if (process.MainWindowHandle == IntPtr.Zero) //{ // throw new ProcessHasNoWindowHandleException(); //} // Skip if the process is already hooked (and we want to hook multiple applications) if (HookManager.IsHooked(process.Id)) { throw new ProcessAlreadyHookedException(); } captureInterface.ProcessId = process.Id; this._serverInterface = captureInterface; //_serverInterface = new CaptureInterface() { ProcessId = process.Id }; // Initialise the IPC server (with our instance of _serverInterface) this._screenshotServer = RemoteHooking.IpcCreateServer<CaptureInterface>( ref this._channelName, WellKnownObjectMode.Singleton, this._serverInterface); try { var location = typeof(CaptureInterface).Assembly.Location; if (String.IsNullOrEmpty(location)) { var dllName = typeof(CaptureInterface).Assembly.GetName().Name + ".dll"; location = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, dllName); } // Inject DLL into target process RemoteHooking.Inject( process.Id, InjectionOptions.Default, location, //"Capture.dll", // 32-bit version (the same because AnyCPU) could use different assembly that links to 32-bit C++ helper dll location, //"Capture.dll", // 64-bit version (the same because AnyCPU) could use different assembly that links to 64-bit C++ helper dll // the optional parameter list... this._channelName, // The name of the IPC channel for the injected assembly to connect to config ); } catch (Exception e) { throw new InjectionFailedException(e); } HookManager.AddHookedProcess(process.Id); this.Process = process; }
/// <summary> /// Prepares capturing in the target process. Note that the process must not already be hooked, and must have a <see cref="Process.MainWindowHandle"/>. /// </summary> /// <param name="process">The process to inject into</param> /// <exception cref="ProcessHasNoWindowHandleException">Thrown if the <paramref name="process"/> does not have a window handle. This could mean that the process does not have a UI, or that the process has not yet finished starting.</exception> /// <exception cref="ProcessAlreadyHookedException">Thrown if the <paramref name="process"/> is already hooked</exception> /// <exception cref="InjectionFailedException">Thrown if the injection failed - see the InnerException for more details.</exception> /// <remarks>The target process will have its main window brought to the foreground after successful injection.</remarks> public CaptureProcess(Process process, CaptureConfig config, CaptureInterface captureInterface) { // If the process doesn't have a mainwindowhandle yet, skip it (we need to be able to get the hwnd to set foreground etc) if (process.MainWindowHandle == IntPtr.Zero) { throw new ProcessHasNoWindowHandleException(); } // Skip if the process is already hooked (and we want to hook multiple applications) if (HookManager.IsHooked(process.Id)) { throw new ProcessAlreadyHookedException(); } captureInterface.ProcessId = process.Id; _serverInterface = captureInterface; //_serverInterface = new CaptureInterface() { ProcessId = process.Id }; // Initialise the IPC server (with our instance of _serverInterface) _screenshotServer = RemoteHooking.IpcCreateServer<CaptureInterface>( ref _channelName, WellKnownObjectMode.Singleton, _serverInterface); try { // Inject DLL into target process RemoteHooking.Inject( process.Id, InjectionOptions.Default, typeof(CaptureInterface).Assembly.Location,//"Capture.dll", // 32-bit version (the same because AnyCPU) could use different assembly that links to 32-bit C++ helper dll typeof(CaptureInterface).Assembly.Location, //"Capture.dll", // 64-bit version (the same because AnyCPU) could use different assembly that links to 64-bit C++ helper dll // the optional parameter list... _channelName, // The name of the IPC channel for the injected assembly to connect to config ); } catch (Exception e) { throw new InjectionFailedException(e); } HookManager.AddHookedProcess(process.Id); Process = process; // Ensure the target process is in the foreground, // this prevents an issue where the target app appears to be in // the foreground but does not receive any user inputs. // Note: the first Alt+Tab out of the target application after injection // may still be an issue - switching between windowed and // fullscreen fixes the issue however (see ScreenshotInjection.cs for another option) BringProcessWindowToFront(); }
public void Run( EasyHook.RemoteHooking.IContext context, String channelName, CaptureConfig config) { // When not using GAC there can be issues with remoting assemblies resolving correctly // this is a workaround that ensures that the current assembly is correctly associated AppDomain currentDomain = AppDomain.CurrentDomain; currentDomain.AssemblyResolve += (sender, args) => { return this.GetType().Assembly.FullName == args.Name ? this.GetType().Assembly : null; }; // NOTE: This is running in the target process _interface.Message(MessageType.Information, "Injected into process Id:{0}.", EasyHook.RemoteHooking.GetCurrentProcessId()); _runWait = new System.Threading.ManualResetEvent(false); _runWait.Reset(); try { // Initialise the Hook if (!InitialiseDirectXHook(config)) { return; } _interface.Disconnected += _clientEventProxy.DisconnectedProxyHandler; // Important Note: // accessing the _interface from within a _clientEventProxy event handler must always // be done on a different thread otherwise it will cause a deadlock _clientEventProxy.Disconnected += () => { // We can now signal the exit of the Run method _runWait.Set(); }; // We start a thread here to periodically check if the host is still running // If the host process stops then we will automatically uninstall the hooks StartCheckHostIsAliveThread(); // Wait until signaled for exit either when a Disconnect message from the host // or if the the check is alive has failed to Ping the host. _runWait.WaitOne(); // we need to tell the check host thread to exit (if it hasn't already) StopCheckHostIsAliveThread(); // Dispose of the DXHook so any installed hooks are removed correctly DisposeDirectXHook(); } catch (Exception e) { _interface.Message(MessageType.Error, "An unexpected error occured: {0}", e.ToString()); } finally { try { _interface.Message(MessageType.Information, "Disconnecting from process {0}", EasyHook.RemoteHooking.GetCurrentProcessId()); } catch { } // Remove the client server channel (that allows client event handlers) System.Runtime.Remoting.Channels.ChannelServices.UnregisterChannel(_clientServerChannel); // Always sleep long enough for any remaining messages to complete sending System.Threading.Thread.Sleep(100); } }
private bool InitialiseDirectXHook(CaptureConfig config) { Direct3DVersion version = config.Direct3DVersion; List<Direct3DVersion> loadedVersions = new List<Direct3DVersion>(); bool isX64Process = EasyHook.RemoteHooking.IsX64Process(EasyHook.RemoteHooking.GetCurrentProcessId()); _interface.Message(MessageType.Information, "Remote process is a {0}-bit process.", isX64Process ? "64" : "32"); try { if (version == Direct3DVersion.AutoDetect) { // Attempt to determine the correct version based on loaded module. // In most cases this will work fine, however it is perfectly ok for an application to use a D3D10 device along with D3D11 devices // so the version might matched might not be the one you want to use IntPtr d3D9Loaded = IntPtr.Zero; IntPtr d3D10Loaded = IntPtr.Zero; IntPtr d3D10_1Loaded = IntPtr.Zero; IntPtr d3D11Loaded = IntPtr.Zero; IntPtr d3D11_1Loaded = IntPtr.Zero; int delayTime = 100; int retryCount = 0; while (d3D9Loaded == IntPtr.Zero && d3D10Loaded == IntPtr.Zero && d3D10_1Loaded == IntPtr.Zero && d3D11Loaded == IntPtr.Zero && d3D11_1Loaded == IntPtr.Zero) { retryCount++; d3D9Loaded = NativeMethods.GetModuleHandle("d3d9.dll"); d3D10Loaded = NativeMethods.GetModuleHandle("d3d10.dll"); d3D10_1Loaded = NativeMethods.GetModuleHandle("d3d10_1.dll"); d3D11Loaded = NativeMethods.GetModuleHandle("d3d11.dll"); d3D11_1Loaded = NativeMethods.GetModuleHandle("d3d11_1.dll"); System.Threading.Thread.Sleep(delayTime); if (retryCount * delayTime > 5000) { _interface.Message(MessageType.Error, "Unsupported Direct3D version, or Direct3D DLL not loaded within 5 seconds."); return false; } } version = Direct3DVersion.Unknown; if (d3D11_1Loaded != IntPtr.Zero) { _interface.Message(MessageType.Debug, "Autodetect found Direct3D 11.1"); version = Direct3DVersion.Direct3D11_1; loadedVersions.Add(version); } if (d3D11Loaded != IntPtr.Zero) { _interface.Message(MessageType.Debug, "Autodetect found Direct3D 11"); version = Direct3DVersion.Direct3D11; loadedVersions.Add(version); } if (d3D10_1Loaded != IntPtr.Zero) { _interface.Message(MessageType.Debug, "Autodetect found Direct3D 10.1"); version = Direct3DVersion.Direct3D10_1; loadedVersions.Add(version); } if (d3D10Loaded != IntPtr.Zero) { _interface.Message(MessageType.Debug, "Autodetect found Direct3D 10"); version = Direct3DVersion.Direct3D10; loadedVersions.Add(version); } if (d3D9Loaded != IntPtr.Zero) { _interface.Message(MessageType.Debug, "Autodetect found Direct3D 9"); version = Direct3DVersion.Direct3D9; loadedVersions.Add(version); } } foreach (var dxVersion in loadedVersions) { version = dxVersion; switch (version) { case Direct3DVersion.Direct3D9: _directXHook = new DXHookD3D9(_interface); break; case Direct3DVersion.Direct3D10: _directXHook = new DXHookD3D10(_interface); break; case Direct3DVersion.Direct3D10_1: _directXHook = new DXHookD3D10_1(_interface); break; case Direct3DVersion.Direct3D11: _directXHook = new DXHookD3D11(_interface); break; //case Direct3DVersion.Direct3D11_1: // _directXHook = new DXHookD3D11_1(_interface); // return; default: _interface.Message(MessageType.Error, "Unsupported Direct3D version: {0}", version); return false; } _directXHook.Config = config; _directXHook.Hook(); _directXHooks.Add(_directXHook); } return true; } catch (Exception e) { // Notify the host/server application about this error _interface.Message(MessageType.Error, "Error in InitialiseHook: {0}", e.ToString()); return false; } }
private void AttachProcess() { string exeName = Path.GetFileNameWithoutExtension(textBox1.Text); Process[] processes = Process.GetProcessesByName(exeName); foreach (Process process in processes) { // Simply attach to the first one found. // If the process doesn't have a mainwindowhandle yet, skip it (we need to be able to get the hwnd to set foreground etc) if (process.MainWindowHandle == IntPtr.Zero) { continue; } // Skip if the process is already hooked (and we want to hook multiple applications) if (HookManager.IsHooked(process.Id)) { continue; } Direct3DVersion direct3DVersion = Direct3DVersion.Direct3D10; if (rbDirect3D11.Checked) { direct3DVersion = Direct3DVersion.Direct3D11; } else if (rbDirect3D10_1.Checked) { direct3DVersion = Direct3DVersion.Direct3D10_1; } else if (rbDirect3D10.Checked) { direct3DVersion = Direct3DVersion.Direct3D10; } else if (rbDirect3D9.Checked) { direct3DVersion = Direct3DVersion.Direct3D9; } else if (rbAutodetect.Checked) { direct3DVersion = Direct3DVersion.AutoDetect; } CaptureConfig cc = new CaptureConfig() { Direct3DVersion = direct3DVersion, ShowOverlay = cbDrawOverlay.Checked }; processId = process.Id; _process = process; var captureInterface = new CaptureInterface(); captureInterface.RemoteMessage += new MessageReceivedEvent(CaptureInterface_RemoteMessage); _captureProcess = new CaptureProcess(process, cc, captureInterface); break; } Thread.Sleep(10); if (_captureProcess == null) { MessageBox.Show("No executable found matching: '" + exeName + "'"); } else { btnLoadTest.Enabled = true; btnCapture.Enabled = true; } }
private void SafeInvokeRecordingStarted(CaptureConfig config) { if (RecordingStarted == null) return; //No Listeners RecordingStartedEvent listener = null; Delegate[] dels = RecordingStarted.GetInvocationList(); foreach (Delegate del in dels) { try { listener = (RecordingStartedEvent)del; listener.Invoke(config); } catch (Exception) { //Could not reach the destination, so remove it //from the list RecordingStarted -= listener; } } }
/// <summary> /// If not <see cref="IsRecording"/> will invoke the <see cref="RecordingStarted"/> event, starting a new recording. /// </summary> /// <param name="config">The configuration for the recording</param> /// <remarks>Handlers in the server and remote process will be be invoked.</remarks> public void StartRecording(CaptureConfig config) { if (IsRecording) return; SafeInvokeRecordingStarted(config); IsRecording = true; }
public void RecordingStartedProxyHandler(CaptureConfig config) { if (RecordingStarted != null) RecordingStarted(config); }
private void CreateCaptureProcess(Process process) { _process = process; Direct3DVersion direct3DVersion = Direct3DVersion.Direct3D10; if (rbDirect3D11.Checked) { direct3DVersion = Direct3DVersion.Direct3D11; } else if (rbDirect3D10_1.Checked) { direct3DVersion = Direct3DVersion.Direct3D10_1; } else if (rbDirect3D10.Checked) { direct3DVersion = Direct3DVersion.Direct3D10; } else if (rbDirect3D9.Checked) { direct3DVersion = Direct3DVersion.Direct3D9; } else if (rbAutodetect.Checked) { direct3DVersion = Direct3DVersion.AutoDetect; } var cc = new CaptureConfig { Direct3DVersion = direct3DVersion, ShowOverlay = cbDrawOverlay.Checked, TestThisShit = 300 }; var captureInterface = new CaptureInterface(); if (frm2 != null) { frm2.Close(); frm2.Dispose(); } frm2 = new form2JT(captureInterface); captureInterface.RemoteMessage += CaptureInterface_RemoteMessage; _captureProcess = new CaptureProcess(process, cc, captureInterface); }
public AutoCaptureEngine( IEventAggregator events, [ImportMany] IEnumerable<IImageScanner> imageScanners) { this.events = events; this.imageScanners = imageScanners; screenCapture = new ScreenCapture(); var direct3DVersion = Direct3DVersion.Direct3D9SharedMem; CaptureMethod = CaptureMethod.AutoDetect; captureConfig = new CaptureConfig { Direct3DVersion = direct3DVersion }; var security = new MutexSecurity(); security.AddAccessRule(new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.Synchronize | MutexRights.Modify, AccessControlType.Allow)); bool created; sharedMemMutexes = new[] { new Mutex(false, "Global\\DXHookD3D9Shared0", out created, security), new Mutex(false, "Global\\DXHookD3D9Shared1", out created, security) }; var ewsecurity = new EventWaitHandleSecurity(); ewsecurity.AddAccessRule(new EventWaitHandleAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), EventWaitHandleRights.FullControl, AccessControlType.Allow)); captureDxWaitHandle = new EventWaitHandle(false, EventResetMode.ManualReset, "Global\\DXHookD3D9Capture", out created, ewsecurity); hookReadyWaitHandle = new EventWaitHandle(false, EventResetMode.ManualReset, "Global\\DXHookD3D9CaptureReady", out created, ewsecurity); }
void Interface_RecordingStarted(CaptureConfig config) { this.targetFps = config.TargetFramesPerSecond; isCapturing = true; }