Skip to content
This repository has been archived by the owner on Feb 11, 2023. It is now read-only.

Commit

Permalink
Added a plugin allowing to decompile the selected type with Reflector…
Browse files Browse the repository at this point in the history
… or ILSpy
  • Loading branch information
odalet committed Aug 10, 2014
1 parent 99064da commit 6e00b53
Show file tree
Hide file tree
Showing 17 changed files with 999 additions and 23 deletions.
35 changes: 13 additions & 22 deletions src/Hawkeye.sln
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hawkeye.DemoProject.N4.x86"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{3EA22717-FCBD-4834-BA48-6C13D31267E8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hawkeye.Plugins.Snapshot", "Plugins\Hawkeye.Plugins.Snapshot\Hawkeye.Plugins.Snapshot.csproj", "{AAAA7CD4-0709-4736-9361-0B116C70870F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hawkeye.Api", "Hawkeye.Api\Hawkeye.Api.csproj", "{854D919B-FABD-4D9B-ADAF-CA94ECAAD70E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hawkeye.Plugins.SearchBox", "Plugins\Hawkeye.Plugins.SearchBox\Hawkeye.Plugins.SearchBox.csproj", "{165F5B49-0F1D-428E-8201-D5D66F47C0D6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hawkeye.Core", "Hawkeye.Core\Hawkeye.Core.csproj", "{DE23092D-7853-47FC-BAFD-9DB917408717}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hawkeye", "Hawkeye\Hawkeye.csproj", "{80334CD2-7D8A-459D-80C5-C16FC660DDC7}"
Expand Down Expand Up @@ -56,6 +52,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScreenInfo", "TestsAndTools
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hawkeye.CapturePlugin", "Plugins\Hawkeye.CapturePlugin\Hawkeye.CapturePlugin.csproj", "{D42CF19F-6B4D-4019-8670-1F76AC23DF5B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hawkeye.DecompilePlugin", "Plugins\Hawkeye.DecompilePlugin\Hawkeye.DecompilePlugin.csproj", "{A18C30D4-F4AA-4A31-9CF6-9F22BA457491}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -98,14 +96,6 @@ Global
{FDF1D4EB-8AFB-4417-8396-54B6BA55A9AC}.Release|Mixed Platforms.Build.0 = Release|x86
{FDF1D4EB-8AFB-4417-8396-54B6BA55A9AC}.Release|x86.ActiveCfg = Release|x86
{FDF1D4EB-8AFB-4417-8396-54B6BA55A9AC}.Release|x86.Build.0 = Release|x86
{AAAA7CD4-0709-4736-9361-0B116C70870F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AAAA7CD4-0709-4736-9361-0B116C70870F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AAAA7CD4-0709-4736-9361-0B116C70870F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{AAAA7CD4-0709-4736-9361-0B116C70870F}.Debug|x86.ActiveCfg = Debug|Any CPU
{AAAA7CD4-0709-4736-9361-0B116C70870F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AAAA7CD4-0709-4736-9361-0B116C70870F}.Release|Any CPU.Build.0 = Release|Any CPU
{AAAA7CD4-0709-4736-9361-0B116C70870F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{AAAA7CD4-0709-4736-9361-0B116C70870F}.Release|x86.ActiveCfg = Release|Any CPU
{854D919B-FABD-4D9B-ADAF-CA94ECAAD70E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{854D919B-FABD-4D9B-ADAF-CA94ECAAD70E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{854D919B-FABD-4D9B-ADAF-CA94ECAAD70E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
Expand All @@ -116,14 +106,6 @@ Global
{854D919B-FABD-4D9B-ADAF-CA94ECAAD70E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{854D919B-FABD-4D9B-ADAF-CA94ECAAD70E}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{854D919B-FABD-4D9B-ADAF-CA94ECAAD70E}.Release|x86.ActiveCfg = Release|Any CPU
{165F5B49-0F1D-428E-8201-D5D66F47C0D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{165F5B49-0F1D-428E-8201-D5D66F47C0D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{165F5B49-0F1D-428E-8201-D5D66F47C0D6}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{165F5B49-0F1D-428E-8201-D5D66F47C0D6}.Debug|x86.ActiveCfg = Debug|Any CPU
{165F5B49-0F1D-428E-8201-D5D66F47C0D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{165F5B49-0F1D-428E-8201-D5D66F47C0D6}.Release|Any CPU.Build.0 = Release|Any CPU
{165F5B49-0F1D-428E-8201-D5D66F47C0D6}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{165F5B49-0F1D-428E-8201-D5D66F47C0D6}.Release|x86.ActiveCfg = Release|Any CPU
{DE23092D-7853-47FC-BAFD-9DB917408717}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DE23092D-7853-47FC-BAFD-9DB917408717}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DE23092D-7853-47FC-BAFD-9DB917408717}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
Expand Down Expand Up @@ -248,6 +230,16 @@ Global
{D42CF19F-6B4D-4019-8670-1F76AC23DF5B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{D42CF19F-6B4D-4019-8670-1F76AC23DF5B}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{D42CF19F-6B4D-4019-8670-1F76AC23DF5B}.Release|x86.ActiveCfg = Release|Any CPU
{A18C30D4-F4AA-4A31-9CF6-9F22BA457491}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A18C30D4-F4AA-4A31-9CF6-9F22BA457491}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A18C30D4-F4AA-4A31-9CF6-9F22BA457491}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{A18C30D4-F4AA-4A31-9CF6-9F22BA457491}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{A18C30D4-F4AA-4A31-9CF6-9F22BA457491}.Debug|x86.ActiveCfg = Debug|Any CPU
{A18C30D4-F4AA-4A31-9CF6-9F22BA457491}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A18C30D4-F4AA-4A31-9CF6-9F22BA457491}.Release|Any CPU.Build.0 = Release|Any CPU
{A18C30D4-F4AA-4A31-9CF6-9F22BA457491}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{A18C30D4-F4AA-4A31-9CF6-9F22BA457491}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{A18C30D4-F4AA-4A31-9CF6-9F22BA457491}.Release|x86.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -259,9 +251,8 @@ Global
{703E71EC-0EB6-410D-B520-62FF78655C4A} = {FD802853-6426-4B07-9759-1BF6FA00BAE7}
{00886C93-7079-48E6-A92B-0A36A69E8C3A} = {FD802853-6426-4B07-9759-1BF6FA00BAE7}
{2FAE624D-9A18-4933-8678-C64D837AB84A} = {FD802853-6426-4B07-9759-1BF6FA00BAE7}
{AAAA7CD4-0709-4736-9361-0B116C70870F} = {3EA22717-FCBD-4834-BA48-6C13D31267E8}
{165F5B49-0F1D-428E-8201-D5D66F47C0D6} = {3EA22717-FCBD-4834-BA48-6C13D31267E8}
{D42CF19F-6B4D-4019-8670-1F76AC23DF5B} = {3EA22717-FCBD-4834-BA48-6C13D31267E8}
{A18C30D4-F4AA-4A31-9CF6-9F22BA457491} = {3EA22717-FCBD-4834-BA48-6C13D31267E8}
{5403CEA2-F8E6-4B2D-B25C-4DAC65A6216C} = {0DDFADEA-5F53-40DE-B4F8-6D93E567FB0E}
{04C22ADE-B620-4753-9ED7-01A70234E25D} = {0DDFADEA-5F53-40DE-B4F8-6D93E567FB0E}
{C29817A9-3696-421D-89BD-F66681657C37} = {0DDFADEA-5F53-40DE-B4F8-6D93E567FB0E}
Expand Down
8 changes: 7 additions & 1 deletion src/History.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
Version 2 - 0.5.0 - 2014/08/10
==============================
* Plugin API available
* Capture Plugin: captures an image of the selected control and places it in the clipboard.
* Decompile Plugin (works with ILSpy and Reflector).

Version 2 - 0.4.1 - 2014/08/04
==========================
==============================
* Fixed issue #8:
* Added control's name (or type if no name) to the window title.
* Reintroduced the search box
Expand Down
148 changes: 148 additions & 0 deletions src/Plugins/Hawkeye.DecompilePlugin/BaseDecompilerController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
using System;
using System.Text;
using System.Runtime.InteropServices;

namespace Hawkeye.DecompilePlugin
{
/// <summary>
/// Allows an external program to remotely control a decompiler (Reflector or ILSpy).
/// This class is adapted from RemoteController.cs found in
/// .NET Reflector addins project (http://www.codeplex.com/reflectoraddins)
/// </summary>
/// <remarks>
/// ILSpy supports a way of being remotely controlled very similar to Reflector's
/// See https://github.com/icsharpcode/ILSpy/blob/master/doc/Command%20Line.txt.
/// </remarks>
internal abstract class BaseDecompilerController : IDecompilerController
{
private IntPtr targetWindow = IntPtr.Zero;

public BaseDecompilerController()
{
WindowMessage = WM_COPYDATA;
}

#region Win32 Interop

private const int WM_COPYDATA = 0x4A;

private delegate bool EnumWindowsCallback(IntPtr hwnd, int lparam);

[DllImport("user32.dll")]
private static extern int EnumWindows(EnumWindowsCallback callback, int lparam);

[DllImport("user32.dll")]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder title, int size);

[DllImport("user32.dll")]
private static extern bool SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, ref CopyDataStruct lParam);

[StructLayout(LayoutKind.Sequential)]
private struct CopyDataStruct
{
public IntPtr Padding;
public int Size;
public IntPtr Buffer;

public CopyDataStruct(IntPtr padding, int size, IntPtr buffer)
{
Padding = padding;
Size = size;
Buffer = buffer;
}
}

#endregion

#region IDecompilerController Members

/// <summary>
/// Gets a value indicating whether a decompiler instance is running.
/// </summary>
/// <value>
/// <c>true</c> if a running decompiler instance could be found; otherwise, <c>false</c>.
/// </value>
public abstract bool IsRunning { get; }

/// <summary>
/// Loads the type's assembly then selects the specified type declaration in the decompiler;
/// </summary>
/// <param name="type">The type to decompile.</param>
/// <returns>
/// <c>true</c> if the action succeeded; otherwise, <c>false</c>.
/// </returns>
/// <exception cref="System.NotImplementedException"></exception>
public abstract bool GotoType(Type type);

#endregion

/// <summary>
/// Gets the target window (each call re-enumerates to make sure the target exists).
/// </summary>
protected IntPtr TargetWindow
{
get
{
targetWindow = IntPtr.Zero;
EnumWindows(new EnumWindowsCallback(EnumWindow), 0);
return targetWindow;
}
}

/// <summary>
/// Determines wether the specified window title matches the actual decompiler.
/// </summary>
/// <param name="title">The window title.</param>
/// <returns><c>true</c> if matches; otherwise, <c>false</c>.</returns>
protected abstract bool DoesWindowTitleMatches(string title);

/// <summary>
/// Gets or sets the window message used to communicate with the decompiler instance.
/// </summary>
/// <value>
/// Default value is WM_COPYDATA.
/// </value>
protected virtual int WindowMessage { get; set; }

protected bool Send(string message)
{
targetWindow = IntPtr.Zero;

// We can't use a simple FindWindow, because the decompiler window title
// can vary: we must detect its window title starts with a known value;
// not simply it is equal to a known value. See the EnumWindow method.
EnumWindows(new EnumWindowsCallback(EnumWindow), 0);

if (targetWindow != IntPtr.Zero)
{
var chars = message.ToCharArray();
var data = new CopyDataStruct();
data.Padding = IntPtr.Zero;
data.Size = chars.Length * 2;
data.Buffer = Marshal.AllocHGlobal(data.Size);
Marshal.Copy(chars, 0, data.Buffer, chars.Length);

var result = SendMessage(targetWindow, WindowMessage, IntPtr.Zero, ref data);
Marshal.FreeHGlobal(data.Buffer);

return result;
}

return false;
}

private bool EnumWindow(IntPtr handle, int lparam)
{
var titleBuilder = new StringBuilder(256);
GetWindowText(handle, titleBuilder, 256);

var title = titleBuilder.ToString();
if (DoesWindowTitleMatches(title))
{
targetWindow = handle;
return false; // No need to enumerate other windows
}
else return true; // Try again
}
}
}
120 changes: 120 additions & 0 deletions src/Plugins/Hawkeye.DecompilePlugin/BaseDecompilerPluginCore.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
using System;
using System.Collections.Generic;
using System.Text;
using Hawkeye.Extensibility;
using Hawkeye.Logging;
using System.Windows.Forms;

namespace Hawkeye.DecompilePlugin
{
internal abstract class BaseDecompilerPluginCore : BaseCommandPlugin
{
private ILogService log = null;
private IWindowInfo windowInfo = null;
private IDecompilerController controller = null;

/// <summary>
/// Initializes a new instance of the <see cref="ReflectorPluginCore"/> class.
/// </summary>
/// <param name="descriptor">The descriptor.</param>
public BaseDecompilerPluginCore(IPluginDescriptor descriptor) :
base(descriptor) { }

public override string Label
{
get { return "&Decompile"; }
}

protected abstract IDecompilerController CreateDecompilerController();

protected virtual string DecompilerNotAvailable
{
get
{
return
@"A running instance of the decompiler could not be found.
Hawkeye can not show you the source code for the selected item.
Make sure it is running";
}
}

/// <summary>
/// Called when the plugin has just been initialized.
/// </summary>
protected override void OnInitialized()
{
base.OnInitialized();

controller = CreateDecompilerController();

log = base.Host.GetLogger<ReflectorPluginCore>();
base.Host.CurrentWindowInfoChanged += (s, e) =>
{
windowInfo = base.Host.CurrentWindowInfo;
base.RaiseCanExecuteChanged(this);
};

windowInfo = base.Host.CurrentWindowInfo;
base.RaiseCanExecuteChanged(this);

log.Info(string.Format("'{0}' was initialized.", base.Descriptor.Name));
}

/// <summary>
/// Determines whether this plugin command can be executed.
/// </summary>
/// <returns>
/// <c>true</c> if the command can be executed; otherwise, <c>false</c>.
/// </returns>
protected override bool CanExecuteCore()
{
return
windowInfo != null &&
windowInfo.ControlInfo != null &&
windowInfo.ControlInfo.Control != null;
}

/// <summary>
/// Executes this plugin command.
/// </summary>
protected override void ExecuteCore()
{
if (!CanExecuteCore()) return;

var savedCursor = Cursor.Current;
Cursor.Current = Cursors.WaitCursor;
try
{
OpenInDecompiler();
}
finally
{
Cursor.Current = savedCursor;
}
}

private void OpenInDecompiler()
{
if (!controller.IsRunning)
{
MessageBox.Show(DecompilerNotAvailable,
"Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}

var control = windowInfo.ControlInfo.Control;
var type = control.GetType();

// Remark: the logic here is really simplified comapred with the Hawkeye 1 Reflector facility:
// In Hawkeye 1, we tried to get Reflector to point to the exact property, member, event or method
// that was curently selected.
// Here, we only open the current type in Reflector.
// Indeed this makes more sense, because often times, in the previous version, what was selected
// was some member inherited from Control and Reflector was not loading the really inspected
// control but some member of the System.Windows.Forms.Control class.

controller.GotoType(type);
}
}
}
Loading

0 comments on commit 6e00b53

Please sign in to comment.