Browse Source

Fix #2086: Check that window belongs to ILSpy before sending it a WM_COPYDATA message.

pull/2087/head
Daniel Grunwald 5 years ago
parent
commit
5c949e6e20
  1. 28
      ILSpy.AddIn/ILSpyInstance.cs
  2. 95
      ILSpy.AddIn/Utils.cs
  3. 24
      ILSpy/App.xaml.cs
  4. 36
      ILSpy/NativeMethods.cs

28
ILSpy.AddIn/ILSpyInstance.cs

@ -38,10 +38,10 @@ namespace ICSharpCode.ILSpy.AddIn
public void Start() public void Start()
{ {
var commandLineArguments = parameters?.AssemblyFileNames?.Concat(parameters.Arguments); var commandLineArguments = parameters?.AssemblyFileNames?.Concat(parameters.Arguments);
string ilSpyExe = GetILSpyPath();
var process = new Process() { var process = new Process() {
StartInfo = new ProcessStartInfo() { StartInfo = new ProcessStartInfo() {
FileName = GetILSpyPath(), FileName = ilSpyExe,
UseShellExecute = false, UseShellExecute = false,
Arguments = "/navigateTo:none" Arguments = "/navigateTo:none"
} }
@ -50,13 +50,14 @@ namespace ICSharpCode.ILSpy.AddIn
if ((commandLineArguments != null) && commandLineArguments.Any()) { if ((commandLineArguments != null) && commandLineArguments.Any()) {
// Only need a message to started process if there are any parameters to pass // Only need a message to started process if there are any parameters to pass
SendMessage(process, "ILSpy:\r\n" + string.Join(Environment.NewLine, commandLineArguments), true); SendMessage(ilSpyExe, "ILSpy:\r\n" + string.Join(Environment.NewLine, commandLineArguments), true);
} }
} }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD110:Observe result of async calls", Justification = "<Pending>")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD110:Observe result of async calls", Justification = "<Pending>")]
void SendMessage(Process ilspyProcess, string message, bool activate) 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 // We wait asynchronously until target window can be found and try to find it multiple times
Task.Run(async () => { Task.Run(async () => {
bool success = false; bool success = false;
@ -66,14 +67,17 @@ namespace ICSharpCode.ILSpy.AddIn
(hWnd, lParam) => { (hWnd, lParam) => {
string windowTitle = NativeMethods.GetWindowText(hWnd, 100); string windowTitle = NativeMethods.GetWindowText(hWnd, 100);
if (windowTitle.StartsWith("ILSpy", StringComparison.Ordinal)) { if (windowTitle.StartsWith("ILSpy", StringComparison.Ordinal)) {
Debug.WriteLine("Found {0:x4}: {1}", hWnd, windowTitle); string processName = NativeMethods.GetProcessNameFromWindow(hWnd);
IntPtr result = Send(hWnd, message); Debug.WriteLine("Found {0:x4}: '{1}' in '{2}'", hWnd, windowTitle, processName);
Debug.WriteLine("WM_COPYDATA result: {0:x8}", result); if (string.Equals(processName, expectedProcessName, StringComparison.OrdinalIgnoreCase)) {
if (result == (IntPtr)1) { IntPtr result = Send(hWnd, message);
if (activate) Debug.WriteLine("WM_COPYDATA result: {0:x8}", result);
NativeMethods.SetForegroundWindow(hWnd); if (result == (IntPtr)1) {
success = true; if (activate)
return false; // stop enumeration NativeMethods.SetForegroundWindow(hWnd);
success = true;
return false; // stop enumeration
}
} }
} }
return true; // continue enumeration return true; // continue enumeration

95
ILSpy.AddIn/Utils.cs

@ -30,101 +30,6 @@ namespace ICSharpCode.ILSpy.AddIn
static class Utils static class Utils
{ {
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")]
[DllImport("shell32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern unsafe char** CommandLineToArgvW([MarshalAs(UnmanagedType.LPWStr)] string lpCmdLine, out int pNumArgs);
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")]
[DllImport("kernel32.dll")]
static extern IntPtr LocalFree(IntPtr hMem);
#region CommandLine <-> Argument Array
/// <summary>
/// Decodes a command line into an array of arguments according to the CommandLineToArgvW rules.
/// </summary>
/// <remarks>
/// Command line parsing rules:
/// - 2n backslashes followed by a quotation mark produce n backslashes, and the quotation mark is considered to be the end of the argument.
/// - (2n) + 1 backslashes followed by a quotation mark again produce n backslashes followed by a quotation mark.
/// - n backslashes not followed by a quotation mark simply produce n backslashes.
/// </remarks>
public static unsafe string[] CommandLineToArgumentArray(string commandLine)
{
if (string.IsNullOrEmpty(commandLine))
return new string[0];
int numberOfArgs;
char** arr = CommandLineToArgvW(commandLine, out numberOfArgs);
if (arr == null)
throw new Win32Exception();
try {
string[] result = new string[numberOfArgs];
for (int i = 0; i < numberOfArgs; i++) {
result[i] = new string(arr[i]);
}
return result;
} finally {
// Free memory obtained by CommandLineToArgW.
LocalFree(new IntPtr(arr));
}
}
static readonly char[] charsNeedingQuoting = { ' ', '\t', '\n', '\v', '"' };
/// <summary>
/// Escapes a set of arguments according to the CommandLineToArgvW rules.
/// </summary>
/// <remarks>
/// Command line parsing rules:
/// - 2n backslashes followed by a quotation mark produce n backslashes, and the quotation mark is considered to be the end of the argument.
/// - (2n) + 1 backslashes followed by a quotation mark again produce n backslashes followed by a quotation mark.
/// - n backslashes not followed by a quotation mark simply produce n backslashes.
/// </remarks>
public static string ArgumentArrayToCommandLine(params string[] arguments)
{
if (arguments == null)
return null;
StringBuilder b = new StringBuilder();
for (int i = 0; i < arguments.Length; i++) {
if (i > 0)
b.Append(' ');
AppendArgument(b, arguments[i]);
}
return b.ToString();
}
static void AppendArgument(StringBuilder b, string arg)
{
if (arg == null) {
return;
}
if (arg.Length > 0 && arg.IndexOfAny(charsNeedingQuoting) < 0) {
b.Append(arg);
} else {
b.Append('"');
for (int j = 0; ; j++) {
int backslashCount = 0;
while (j < arg.Length && arg[j] == '\\') {
backslashCount++;
j++;
}
if (j == arg.Length) {
b.Append('\\', backslashCount * 2);
break;
} else if (arg[j] == '"') {
b.Append('\\', backslashCount * 2 + 1);
b.Append('"');
} else {
b.Append('\\', backslashCount);
b.Append(arg[j]);
}
}
b.Append('"');
}
}
#endregion
public static byte[] HexStringToBytes(string hex) public static byte[] HexStringToBytes(string hex)
{ {
if (hex == null) if (hex == null)

24
ILSpy/App.xaml.cs

@ -196,19 +196,27 @@ namespace ICSharpCode.ILSpy
#region Pass Command Line Arguments to previous instance #region Pass Command Line Arguments to previous instance
bool SendToPreviousInstance(string message, bool activate) bool SendToPreviousInstance(string message, bool activate)
{ {
string ownProcessName;
using (var ownProcess = Process.GetCurrentProcess()) {
ownProcessName = ownProcess.ProcessName;
}
bool success = false; bool success = false;
NativeMethods.EnumWindows( NativeMethods.EnumWindows(
(hWnd, lParam) => { (hWnd, lParam) => {
string windowTitle = NativeMethods.GetWindowText(hWnd, 100); string windowTitle = NativeMethods.GetWindowText(hWnd, 100);
if (windowTitle.StartsWith("ILSpy", StringComparison.Ordinal)) { if (windowTitle.StartsWith("ILSpy", StringComparison.Ordinal)) {
Debug.WriteLine("Found {0:x4}: {1}", hWnd, windowTitle); string processName = NativeMethods.GetProcessNameFromWindow(hWnd);
IntPtr result = Send(hWnd, message); Debug.WriteLine("Found {0:x4}: '{1}' in '{2}'", hWnd, windowTitle, processName);
Debug.WriteLine("WM_COPYDATA result: {0:x8}", result); if (string.Equals(processName, ownProcessName, StringComparison.OrdinalIgnoreCase)) {
if (result == (IntPtr)1) { IntPtr result = Send(hWnd, message);
if (activate) Debug.WriteLine("WM_COPYDATA result: {0:x8}", result);
NativeMethods.SetForegroundWindow(hWnd); if (result == (IntPtr)1) {
success = true; if (activate)
return false; // stop enumeration NativeMethods.SetForegroundWindow(hWnd);
success = true;
return false; // stop enumeration
}
} }
} }
return true; // continue enumeration return true; // continue enumeration

36
ILSpy/NativeMethods.cs

@ -20,20 +20,24 @@ using System;
using System.Text; using System.Text;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics;
namespace ICSharpCode.ILSpy namespace ICSharpCode.ILSpy
{ {
static class NativeMethods static class NativeMethods
{ {
public const uint WM_COPYDATA = 0x4a; public const uint WM_COPYDATA = 0x4a;
[DllImport("user32.dll", CharSet = CharSet.Auto)] [DllImport("user32.dll", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)] [return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam); internal static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern unsafe int GetWindowThreadProcessId(IntPtr hWnd, int* lpdwProcessId);
[DllImport("user32.dll", CharSet = CharSet.Auto)] [DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern int GetWindowText(IntPtr hWnd, [Out] StringBuilder title, int size); static extern int GetWindowText(IntPtr hWnd, [Out] StringBuilder title, int size);
public static string GetWindowText(IntPtr hWnd, int maxLength) public static string GetWindowText(IntPtr hWnd, int maxLength)
{ {
StringBuilder b = new StringBuilder(maxLength + 1); StringBuilder b = new StringBuilder(maxLength + 1);
@ -42,12 +46,12 @@ namespace ICSharpCode.ILSpy
else else
return string.Empty; return string.Empty;
} }
[DllImport("user32.dll", CharSet = CharSet.Auto)] [DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern IntPtr SendMessageTimeout( internal static extern IntPtr SendMessageTimeout(
IntPtr hWnd, uint msg, IntPtr wParam, ref CopyDataStruct lParam, IntPtr hWnd, uint msg, IntPtr wParam, ref CopyDataStruct lParam,
uint flags, uint timeout, out IntPtr result); uint flags, uint timeout, out IntPtr result);
[DllImport("user32.dll", CharSet = CharSet.Auto)] [DllImport("user32.dll", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)] [return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool SetForegroundWindow(IntPtr hWnd); internal static extern bool SetForegroundWindow(IntPtr hWnd);
@ -145,11 +149,31 @@ namespace ICSharpCode.ILSpy
} }
} }
#endregion #endregion
public unsafe static string GetProcessNameFromWindow(IntPtr hWnd)
{
int processId;
GetWindowThreadProcessId(hWnd, &processId);
try {
using (var p = Process.GetProcessById(processId)) {
return p.ProcessName;
}
} catch (ArgumentException ex) {
Debug.WriteLine(ex.Message);
return null;
} catch (InvalidOperationException ex) {
Debug.WriteLine(ex.Message);
return null;
} catch (Win32Exception ex) {
Debug.WriteLine(ex.Message);
return null;
}
}
} }
[return: MarshalAs(UnmanagedType.Bool)] [return: MarshalAs(UnmanagedType.Bool)]
delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
struct CopyDataStruct struct CopyDataStruct
{ {

Loading…
Cancel
Save