public void RecordingStartedProxyHandler(CaptureConfig config) { if (RecordingStarted != null) { RecordingStarted(config); } }
public EntryPoint( 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 = 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 IDictionary properties = new Hashtable(); properties["name"] = channelName; properties["portName"] = channelName + Guid.NewGuid().ToString("N"); // random portName so no conflict with existing channels of channelName var binaryProv = new BinaryServerFormatterSinkProvider(); binaryProv.TypeFilterLevel = TypeFilterLevel.Full; var _clientServerChannel = new IpcServerChannel(properties, binaryProv); 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; CaptureInterface = captureInterface; //_serverInterface = new CaptureInterface() { ProcessId = process.Id }; // Initialise the IPC server (with our instance of _serverInterface) _screenshotServer = RemoteHooking.IpcCreateServer( ref _channelName, WellKnownObjectMode.Singleton, CaptureInterface); 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(); }
private void SafeInvokeRecordingStarted(CaptureConfig config) { if (RecordingStarted == null) { return; //No Listeners } RecordingStartedEvent listener = null; var dels = RecordingStarted.GetInvocationList(); foreach (var 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; } } }
private void AttachProcess() { var exeName = Path.GetFileNameWithoutExtension(textBox1.Text); var processes = Process.GetProcessesByName(exeName); foreach (var 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; } var 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 }; processId = process.Id; _process = process; var captureInterface = new CaptureInterface(); captureInterface.RemoteMessage += 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; } }
public void RecordingStartedProxyHandler(CaptureConfig config) { if (RecordingStarted != null) RecordingStarted(config); }
private void SafeInvokeRecordingStarted(CaptureConfig config) { if (RecordingStarted == null) return; //No Listeners RecordingStartedEvent listener = null; var dels = RecordingStarted.GetInvocationList(); foreach (var 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 Run( 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 var currentDomain = AppDomain.CurrentDomain; currentDomain.AssemblyResolve += (sender, args) => { return GetType().Assembly.FullName == args.Name ? GetType().Assembly : null; }; // NOTE: This is running in the target process _interface.Message(MessageType.Information, "Injected into process Id:{0}.", RemoteHooking.GetCurrentProcessId()); _runWait = new 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}", RemoteHooking.GetCurrentProcessId()); } catch { } // Remove the client server channel (that allows client event handlers) ChannelServices.UnregisterChannel(_clientServerChannel); // Always sleep long enough for any remaining messages to complete sending Thread.Sleep(100); } }
private bool InitialiseDirectXHook(CaptureConfig config) { var version = config.Direct3DVersion; var loadedVersions = new List<Direct3DVersion>(); var isX64Process = RemoteHooking.IsX64Process(RemoteHooking.GetCurrentProcessId()); _interface.Message(MessageType.Information, "Remote process is a {0}-bit process.", isX64Process ? "64" : "32"); try { if (version == Direct3DVersion.AutoDetect || version == Direct3DVersion.Unknown) { // 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 var d3D9Loaded = IntPtr.Zero; var d3D10Loaded = IntPtr.Zero; var d3D10_1Loaded = IntPtr.Zero; var d3D11Loaded = IntPtr.Zero; var d3D11_1Loaded = IntPtr.Zero; var delayTime = 100; var 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"); 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); } } else { // If not autodetect, assume specified version is loaded 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; } }