using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ICSharpCode.ILSpy.AddIn { class ILSpyParameters { public ILSpyParameters(IEnumerable assemblyFileNames, params string[] arguments) { this.AssemblyFileNames = assemblyFileNames.ToArray(); this.Arguments = arguments; } public string[] AssemblyFileNames { get; private set; } public string[] Arguments { get; private set; } } class ILSpyInstance { readonly ILSpyParameters parameters; public ILSpyInstance(ILSpyParameters parameters = null) { this.parameters = parameters; } static string GetILSpyPath() { var basePath = Path.GetDirectoryName(typeof(ILSpyAddInPackage).Assembly.Location); return Path.Combine(basePath, "ILSpy", "ILSpy.exe"); } public void Start() { var commandLineArguments = parameters?.AssemblyFileNames?.Concat(parameters.Arguments); string ilSpyExe = GetILSpyPath(); var process = new Process() { StartInfo = new ProcessStartInfo() { FileName = ilSpyExe, UseShellExecute = false, Arguments = "/navigateTo:none" } }; process.Start(); if ((commandLineArguments != null) && commandLineArguments.Any()) { // Only need a message to started process if there are any parameters to pass SendMessage(ilSpyExe, "ILSpy:\r\n" + string.Join(Environment.NewLine, commandLineArguments), true); } } [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD110:Observe result of async calls", Justification = "")] void SendMessage(string ilSpyExe, string message, bool activate) { string expectedProcessName = Path.GetFileNameWithoutExtension(ilSpyExe); // We wait asynchronously until target window can be found and try to find it multiple times Task.Run(async () => { bool success = false; int remainingAttempts = 20; do { NativeMethods.EnumWindows( (hWnd, lParam) => { string windowTitle = NativeMethods.GetWindowText(hWnd, 100); if (windowTitle.StartsWith("ILSpy", StringComparison.Ordinal)) { string processName = NativeMethods.GetProcessNameFromWindow(hWnd); Debug.WriteLine("Found {0:x4}: '{1}' in '{2}'", hWnd, windowTitle, processName); if (string.Equals(processName, expectedProcessName, StringComparison.OrdinalIgnoreCase)) { IntPtr result = Send(hWnd, message); Debug.WriteLine("WM_COPYDATA result: {0:x8}", result); if (result == (IntPtr)1) { if (activate) NativeMethods.SetForegroundWindow(hWnd); success = true; return false; // stop enumeration } } } return true; // continue enumeration }, IntPtr.Zero); // Wait some time before next attempt await Task.Delay(500); remainingAttempts--; } while (!success && (remainingAttempts > 0)); }); } unsafe static IntPtr Send(IntPtr hWnd, string message) { const uint SMTO_NORMAL = 0; CopyDataStruct lParam; lParam.Padding = IntPtr.Zero; lParam.Size = message.Length * 2; fixed (char* buffer = message) { lParam.Buffer = (IntPtr)buffer; IntPtr result; // SendMessage with 3s timeout (e.g. when the target process is stopped in the debugger) if (NativeMethods.SendMessageTimeout( hWnd, NativeMethods.WM_COPYDATA, IntPtr.Zero, ref lParam, SMTO_NORMAL, 3000, out result) != IntPtr.Zero) { return result; } else { return IntPtr.Zero; } } } } }