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 @@ -38,10 +38,10 @@ namespace ICSharpCode.ILSpy.AddIn
public void Start()
{
var commandLineArguments = parameters?.AssemblyFileNames?.Concat(parameters.Arguments);
string ilSpyExe = GetILSpyPath();
var process = new Process() {
StartInfo = new ProcessStartInfo() {
FileName = GetILSpyPath(),
FileName = ilSpyExe,
UseShellExecute = false,
Arguments = "/navigateTo:none"
}
@ -50,13 +50,14 @@ namespace ICSharpCode.ILSpy.AddIn @@ -50,13 +50,14 @@ namespace ICSharpCode.ILSpy.AddIn
if ((commandLineArguments != null) && commandLineArguments.Any()) {
// 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>")]
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
Task.Run(async () => {
bool success = false;
@ -66,14 +67,17 @@ namespace ICSharpCode.ILSpy.AddIn @@ -66,14 +67,17 @@ namespace ICSharpCode.ILSpy.AddIn
(hWnd, lParam) => {
string windowTitle = NativeMethods.GetWindowText(hWnd, 100);
if (windowTitle.StartsWith("ILSpy", StringComparison.Ordinal)) {
Debug.WriteLine("Found {0:x4}: {1}", hWnd, windowTitle);
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
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

95
ILSpy.AddIn/Utils.cs

@ -30,101 +30,6 @@ namespace ICSharpCode.ILSpy.AddIn @@ -30,101 +30,6 @@ namespace ICSharpCode.ILSpy.AddIn
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)
{
if (hex == null)

24
ILSpy/App.xaml.cs

@ -196,19 +196,27 @@ namespace ICSharpCode.ILSpy @@ -196,19 +196,27 @@ namespace ICSharpCode.ILSpy
#region Pass Command Line Arguments to previous instance
bool SendToPreviousInstance(string message, bool activate)
{
string ownProcessName;
using (var ownProcess = Process.GetCurrentProcess()) {
ownProcessName = ownProcess.ProcessName;
}
bool success = false;
NativeMethods.EnumWindows(
(hWnd, lParam) => {
string windowTitle = NativeMethods.GetWindowText(hWnd, 100);
if (windowTitle.StartsWith("ILSpy", StringComparison.Ordinal)) {
Debug.WriteLine("Found {0:x4}: {1}", hWnd, windowTitle);
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
string processName = NativeMethods.GetProcessNameFromWindow(hWnd);
Debug.WriteLine("Found {0:x4}: '{1}' in '{2}'", hWnd, windowTitle, processName);
if (string.Equals(processName, ownProcessName, 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

36
ILSpy/NativeMethods.cs

@ -20,20 +20,24 @@ using System; @@ -20,20 +20,24 @@ using System;
using System.Text;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Diagnostics;
namespace ICSharpCode.ILSpy
{
static class NativeMethods
{
public const uint WM_COPYDATA = 0x4a;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
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)]
static extern int GetWindowText(IntPtr hWnd, [Out] StringBuilder title, int size);
public static string GetWindowText(IntPtr hWnd, int maxLength)
{
StringBuilder b = new StringBuilder(maxLength + 1);
@ -42,12 +46,12 @@ namespace ICSharpCode.ILSpy @@ -42,12 +46,12 @@ namespace ICSharpCode.ILSpy
else
return string.Empty;
}
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern IntPtr SendMessageTimeout(
IntPtr hWnd, uint msg, IntPtr wParam, ref CopyDataStruct lParam,
uint flags, uint timeout, out IntPtr result);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool SetForegroundWindow(IntPtr hWnd);
@ -145,11 +149,31 @@ namespace ICSharpCode.ILSpy @@ -145,11 +149,31 @@ namespace ICSharpCode.ILSpy
}
}
#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)]
delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[StructLayout(LayoutKind.Sequential)]
struct CopyDataStruct
{

Loading…
Cancel
Save