|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- using System;
- using EasyHook;
- using System.Runtime.Remoting;
- using System.Runtime.Remoting.Channels.Ipc;
- using RenderHookAPI.Interface;
- using System.Diagnostics;
- using System.Threading;
- using RenderHookAPI.Hook;
- using System.Windows.Forms;
-
- namespace RenderHookAPI
- {
- public class CaptureProcess : IDisposable
- {
- /// <summary>
- /// Must be null to allow a random channel name to be generated
- /// </summary>
- string _channelName = null;
- private IpcServerChannel _screenshotServer;
- private CaptureInterface _serverInterface;
- public Process Process { get; set; }
-
- /// <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 CaptureInterface CaptureInterface
- {
- get { return _serverInterface; }
- }
-
- ~CaptureProcess()
- {
- Dispose(false);
- }
-
-
- #region Private methods
-
- /// <summary>
- /// Bring the target window to the front and wait for it to be visible
- /// </summary>
- /// <remarks>If the window does not come to the front within approx. 30 seconds an exception is raised</remarks>
- public void BringProcessWindowToFront()
- {
- if (this.Process == null)
- return;
- IntPtr handle = this.Process.MainWindowHandle;
- int i = 0;
-
- while (!NativeMethods.IsWindowInForeground(handle))
- {
- if (i == 0)
- {
- // Initial sleep if target window is not in foreground - just to let things settle
- Thread.Sleep(250);
- }
-
- if (NativeMethods.IsIconic(handle))
- {
- // Minimized so send restore
- NativeMethods.ShowWindow(handle, NativeMethods.WindowShowStyle.Restore);
- }
- else
- {
- // Already Maximized or Restored so just bring to front
- NativeMethods.SetForegroundWindow(handle);
- }
- Thread.Sleep(250);
-
- // Check if the target process main window is now in the foreground
- if (NativeMethods.IsWindowInForeground(handle))
- {
- // Leave enough time for screen to redraw
- Thread.Sleep(1000);
- return;
- }
-
- // Prevent an infinite loop
- if (i > 120) // about 30secs
- {
- throw new Exception("Could not set process window to the foreground");
- }
- i++;
- }
- }
-
- #endregion
-
- #region IDispose
-
- private bool _disposed = false;
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- protected virtual void Dispose(bool disposing)
- {
- if (!_disposed)
- {
- if (disposing)
- {
- // Disconnect the IPC (which causes the remote entry point to exit)
- _serverInterface.Disconnect();
- }
-
- _disposed = true;
- }
- }
-
- #endregion
- }
- }
|