Browse Source

Add /navigateTo command line argument.

pull/105/head
Daniel Grunwald 15 years ago
parent
commit
dce7d13146
  1. 7
      ILSpy.sln
  2. 29
      ILSpy/App.xaml.cs
  3. 15
      ILSpy/CommandLineArguments.cs
  4. 5
      ILSpy/ILSpy.csproj
  5. 72
      ILSpy/MainWindow.xaml.cs
  6. 0
      ILSpy/TreeNodes/XamlResourceNode.cs
  7. 168
      ILSpy/XmlDocKeyProvider.cs
  8. 52
      doc/Command Line.txt

7
ILSpy.sln

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
# SharpDevelop 4.0.1.7100
# SharpDevelop 4.0.1.7126
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpy", "ILSpy\ILSpy.csproj", "{1E85EFF9-E370-4683-83E4-8A3D063FF791}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.TreeView", "SharpTreeView\ICSharpCode.TreeView.csproj", "{DDE2A481-8271-4EAC-A330-8FA6A38D13D1}"
@ -16,6 +16,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.NRefactory", "N @@ -16,6 +16,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.NRefactory", "N
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Decompiler.Tests", "ICSharpCode.Decompiler\Tests\ICSharpCode.Decompiler.Tests.csproj", "{FEC0DA52-C4A6-4710-BE36-B484A20C5E22}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{F45DB999-7E72-4000-B5AD-3A7B485A0896}"
ProjectSection(SolutionItems) = postProject
doc\Command Line.txt = doc\Command Line.txt
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU

29
ILSpy/App.xaml.cs

@ -20,6 +20,7 @@ using System; @@ -20,6 +20,7 @@ using System;
using System.Collections.Generic;
using System.ComponentModel.Composition.Hosting;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows;
@ -44,10 +45,12 @@ namespace ICSharpCode.ILSpy @@ -44,10 +45,12 @@ namespace ICSharpCode.ILSpy
public App()
{
App.CommandLineArguments = new CommandLineArguments(Environment.GetCommandLineArgs().Skip(1));
if (App.CommandLineArguments.SharedInstance) {
string message = string.Join(Environment.NewLine, Environment.GetCommandLineArgs().Skip(1));
if (SendToPreviousInstance("ILSpy:\r\n" + message)) {
var cmdArgs = Environment.GetCommandLineArgs().Skip(1);
App.CommandLineArguments = new CommandLineArguments(cmdArgs);
if (App.CommandLineArguments.SingleInstance ?? true) {
cmdArgs = cmdArgs.Select(FullyQualifyPath);
string message = string.Join(Environment.NewLine, cmdArgs);
if (SendToPreviousInstance("ILSpy:\r\n" + message, !App.CommandLineArguments.NoActivate)) {
Environment.Exit(0);
}
}
@ -71,6 +74,19 @@ namespace ICSharpCode.ILSpy @@ -71,6 +74,19 @@ namespace ICSharpCode.ILSpy
new RequestNavigateEventHandler(Window_RequestNavigate));
}
string FullyQualifyPath(string argument)
{
// Fully qualify the paths before passing them to another process,
// because that process might use a different current directory.
if (string.IsNullOrEmpty(argument) || argument[0] == '/')
return argument;
try {
return Path.Combine(Environment.CurrentDirectory, argument);
} catch (ArgumentException) {
return argument;
}
}
#region Exception Handling
static void Dispatcher_UnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
@ -90,7 +106,7 @@ namespace ICSharpCode.ILSpy @@ -90,7 +106,7 @@ namespace ICSharpCode.ILSpy
#endregion
#region Pass Command Line Arguments to previous instance
bool SendToPreviousInstance(string message)
bool SendToPreviousInstance(string message, bool activate)
{
bool success = false;
NativeMethods.EnumWindows(
@ -101,7 +117,8 @@ namespace ICSharpCode.ILSpy @@ -101,7 +117,8 @@ namespace ICSharpCode.ILSpy
IntPtr result = Send(hWnd, message);
Debug.WriteLine("WM_COPYDATA result: {0:x8}", result);
if (result == (IntPtr)1) {
NativeMethods.SetForegroundWindow(hWnd);
if (activate)
NativeMethods.SetForegroundWindow(hWnd);
success = true;
return false; // stop enumeration
}

15
ILSpy/CommandLineArguments.cs

@ -8,9 +8,12 @@ namespace ICSharpCode.ILSpy @@ -8,9 +8,12 @@ namespace ICSharpCode.ILSpy
{
sealed class CommandLineArguments
{
// see /doc/Command Line.txt for details
public List<string> AssembliesToLoad = new List<string>();
public bool? SingleInstance;
public string NavigateTo;
public bool SharedInstance = true;
public string Language;
public bool NoActivate;
public CommandLineArguments(IEnumerable<string> arguments)
{
@ -18,12 +21,16 @@ namespace ICSharpCode.ILSpy @@ -18,12 +21,16 @@ namespace ICSharpCode.ILSpy
if (arg.Length == 0)
continue;
if (arg[0] == '/') {
if (arg.Equals("/sharedInstance", StringComparison.OrdinalIgnoreCase))
this.SharedInstance = true;
if (arg.Equals("/singleInstance", StringComparison.OrdinalIgnoreCase))
this.SingleInstance = true;
else if (arg.Equals("/separate", StringComparison.OrdinalIgnoreCase))
this.SharedInstance = false;
this.SingleInstance = false;
else if (arg.StartsWith("/navigateTo:", StringComparison.OrdinalIgnoreCase))
this.NavigateTo = arg.Substring("/navigateTo:".Length);
else if (arg.StartsWith("/language:", StringComparison.OrdinalIgnoreCase))
this.Language = arg.Substring("/language:".Length);
else if (arg.Equals("/noActivate", StringComparison.OrdinalIgnoreCase))
this.NoActivate = true;
} else {
this.AssembliesToLoad.Add(arg);
}

5
ILSpy/ILSpy.csproj

@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<ApplicationIcon>Images\ILSpy.ico</ApplicationIcon>
<RunCodeAnalysis>False</RunCodeAnalysis>
</PropertyGroup>
<PropertyGroup Condition=" '$(Platform)' == 'x86' ">
<PlatformTarget>x86</PlatformTarget>
@ -38,6 +39,7 @@ @@ -38,6 +39,7 @@
<Optimize>False</Optimize>
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<StartAction>Project</StartAction>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<OutputPath>bin\Release\</OutputPath>
@ -125,7 +127,8 @@ @@ -125,7 +127,8 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzeContextMenuEntry.cs" />
<Compile Include="TreeNodes\IMemberTreeNode.cs" />
<Compile Include="XamlResourceNode.cs" />
<Compile Include="TreeNodes\XamlResourceNode.cs" />
<Compile Include="XmlDocKeyProvider.cs" />
<EmbeddedResource Include="..\README.txt">
<Link>README.txt</Link>
</EmbeddedResource>

72
ILSpy/MainWindow.xaml.cs

@ -200,18 +200,52 @@ namespace ICSharpCode.ILSpy @@ -200,18 +200,52 @@ namespace ICSharpCode.ILSpy
while ((line = r.ReadLine()) != null)
lines.Add(line);
}
HandleCommandLineArguments(new CommandLineArguments(lines));
handled = true;
return (IntPtr)1;
var args = new CommandLineArguments(lines);
if (HandleCommandLineArguments(args)) {
HandleCommandLineArgumentsAfterShowList(args);
handled = true;
return (IntPtr)1;
}
}
}
return IntPtr.Zero;
}
#endregion
void HandleCommandLineArguments(CommandLineArguments args)
List<LoadedAssembly> commandLineLoadedAssemblies = new List<LoadedAssembly>();
bool HandleCommandLineArguments(CommandLineArguments args)
{
OpenFiles(args.AssembliesToLoad.ToArray());
foreach (string file in args.AssembliesToLoad) {
commandLineLoadedAssemblies.Add(assemblyList.OpenAssembly(file));
}
if (args.Language != null)
sessionSettings.FilterSettings.Language = Languages.GetLanguage(args.Language);
return true;
}
void HandleCommandLineArgumentsAfterShowList(CommandLineArguments args)
{
if (args.NavigateTo != null) {
bool found = false;
foreach (LoadedAssembly asm in commandLineLoadedAssemblies) {
AssemblyDefinition def = asm.AssemblyDefinition;
if (def != null) {
MemberReference mr = XmlDocKeyProvider.FindMemberByKey(def.MainModule, args.NavigateTo);
if (mr != null) {
found = true;
JumpToReference(mr);
break;
}
}
}
if (!found) {
AvalonEditTextOutput output = new AvalonEditTextOutput();
output.Write("Cannot find " + args.NavigateTo);
decompilerTextView.Show(output);
}
}
commandLineLoadedAssemblies.Clear(); // clear references once we don't need them anymore
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
@ -224,19 +258,27 @@ namespace ICSharpCode.ILSpy @@ -224,19 +258,27 @@ namespace ICSharpCode.ILSpy
this.assemblyList = assemblyListManager.LoadList(spySettings, sessionSettings.ActiveAssemblyList);
HandleCommandLineArguments(App.CommandLineArguments);
ShowAssemblyList(this.assemblyList);
if (assemblyList.GetAssemblies().Length == 0)
if (assemblyList.GetAssemblies().Length == 0
&& assemblyList.ListName == AssemblyListManager.DefaultListName)
{
LoadInitialAssemblies();
}
ShowAssemblyList(this.assemblyList);
SharpTreeNode node = FindNodeByPath(sessionSettings.ActiveTreeViewPath, true);
if (node != null) {
SelectNode(node);
// only if not showing the about page, perform the update check:
ShowMessageIfUpdatesAvailableAsync(spySettings);
} else {
AboutPage.Display(decompilerTextView);
HandleCommandLineArgumentsAfterShowList(App.CommandLineArguments);
if (App.CommandLineArguments.NavigateTo == null) {
SharpTreeNode node = FindNodeByPath(sessionSettings.ActiveTreeViewPath, true);
if (node != null) {
SelectNode(node);
// only if not showing the about page, perform the update check:
ShowMessageIfUpdatesAvailableAsync(spySettings);
} else {
AboutPage.Display(decompilerTextView);
}
}
}

0
ILSpy/XamlResourceNode.cs → ILSpy/TreeNodes/XamlResourceNode.cs

168
ILSpy/XmlDocKeyProvider.cs

@ -0,0 +1,168 @@ @@ -0,0 +1,168 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mono.Cecil;
namespace ICSharpCode.ILSpy
{
/// <summary>
/// Provides XML documentation tags.
/// </summary>
sealed class XmlDocKeyProvider
{
#region GetKey
public static string GetKey(MemberReference member)
{
StringBuilder b = new StringBuilder();
if (member is TypeReference) {
b.Append("T:");
AppendTypeName(b, (TypeDefinition)member);
} else {
if (member is FieldReference)
b.Append("F:");
else if (member is PropertyDefinition)
b.Append("P:");
else if (member is EventDefinition)
b.Append("E:");
else if (member is MethodReference)
b.Append("M:");
AppendTypeName(b, member.DeclaringType);
b.Append('.');
b.Append(member.Name);
IList<ParameterDefinition> parameters;
if (member is PropertyDefinition) {
parameters = ((PropertyDefinition)member).Parameters;
} else if (member is MethodReference) {
parameters = ((MethodReference)member).Parameters;
} else {
parameters = null;
}
if (parameters != null && parameters.Count > 0) {
b.Append('(');
for (int i = 0; i < parameters.Count; i++) {
if (i > 0) b.Append(',');
AppendTypeName(b, parameters[i].ParameterType);
}
b.Append(')');
}
}
return b.ToString();
}
static void AppendTypeName(StringBuilder b, TypeReference type)
{
if (type is TypeSpecification) {
AppendTypeName(b, ((TypeSpecification)type).ElementType);
ArrayType arrayType = type as ArrayType;
if (arrayType != null) {
b.Append('[');
for (int i = 1; i < arrayType.Dimensions.Count; i++) {
b.Append(',');
}
b.Append(']');
}
ByReferenceType refType = type as ByReferenceType;
if (refType != null) {
b.Append('@');
}
GenericInstanceType giType = type as GenericInstanceType;
if (giType != null) {
b.Append('{');
for (int i = 0; i < giType.GenericArguments.Count; i++) {
if (i > 0) b.Append(',');
AppendTypeName(b, giType.GenericArguments[i]);
}
b.Append('}');
}
PointerType ptrType = type as PointerType;
if (ptrType != null) {
b.Append('*'); // TODO: is this correct?
}
} else {
GenericParameter gp = type as GenericParameter;
if (gp != null) {
b.Append('`');
if (gp.Owner.GenericParameterType == GenericParameterType.Method) {
b.Append('`');
}
b.Append(gp.Position);
} else if (type.DeclaringType != null) {
AppendTypeName(b, type.DeclaringType);
b.Append('.');
b.Append(type.Name);
} else {
b.Append(type.FullName);
}
}
}
#endregion
#region FindMemberByKey
public static MemberReference FindMemberByKey(ModuleDefinition module, string key)
{
if (module == null)
throw new ArgumentNullException("module");
if (key == null || key.Length < 2 || key[1] != ':')
return null;
switch (key[0]) {
case 'T':
return FindType(module, key.Substring(2));
case 'F':
return FindMember(module, key, type => type.Fields);
case 'P':
return FindMember(module, key, type => type.Properties);
case 'E':
return FindMember(module, key, type => type.Events);
case 'M':
return FindMember(module, key, type => type.Methods);
default:
return null;
}
}
static MemberReference FindMember(ModuleDefinition module, string key, Func<TypeDefinition, IEnumerable<MemberReference>> memberSelector)
{
int pos = key.IndexOf('(');
int dotPos;
if (pos > 0) {
dotPos = key.LastIndexOf('.', 0, pos);
} else {
dotPos = key.LastIndexOf('.');
}
TypeDefinition type = FindType(module, key.Substring(2, dotPos - 2));
if (type == null)
return null;
foreach (MemberReference member in memberSelector(type)) {
if (GetKey(member) == key)
return member;
}
return null;
}
static TypeDefinition FindType(ModuleDefinition module, string name)
{
int pos = name.LastIndexOf('.');
string ns;
if (pos >= 0) {
ns = name.Substring(0, pos);
name = name.Substring(pos + 1);
} else {
ns = string.Empty;
}
TypeDefinition type = module.GetType(ns, name);
if (type == null && ns.Length > 0) {
// try if this is a nested type
type = FindType(module, ns);
if (type != null) {
type = type.NestedTypes.FirstOrDefault(t => t.Name == name);
}
}
return type;
}
#endregion
}
}

52
doc/Command Line.txt

@ -0,0 +1,52 @@ @@ -0,0 +1,52 @@
ILSpy Command Line Arguments
Command line arguments can be either options or file names.
If an argument is a file name, the file will be opened as assembly and added to the current assembly list.
Available options:
/singleInstance If ILSpy is already running, activates the existing instance
and passes command line arguments to that instance.
This is the default value if /list is not used.
/separate Start up a separate ILSpy instance even if it is already running.
/noActivate Do not activate the existing ILSpy instance. This option has no effec
if a new ILSpy instance is being started.
/list:listname Specifies the name of the assembly list that is loaded initially.
When this option is not specified, ILSpy loads the previously opened list.
Specify "/list" (without value) to open the default list.
When this option is used, ILSpy will activate an existing instance
only if it uses the same list as specified.
[Note: Assembly Lists are not yet implemented]
/clearList Clears the assembly list before loading the specified assemblies.
[Note: Assembly Lists are not yet implemented]
/navigateTo:tag Navigates to the member specified by the given XML documentation tag.
The member is searched for only in the assemblies specified on the command line.
Example: 'ILSpy ILSpy.exe /navigateTo:T:ICSharpCode.ILSpy.CommandLineArguments'
/language:name Selects the specified language.
Example: 'ILSpy /language:C#' or 'ILSpy /language:IL'
WM_COPYDATA (SendMessage API):
ILSpy can be controlled by other programs that send a WM_COPYDATA message to its main window.
The message data must be an Unicode (UTF-16) string starting with "ILSpy:\r\n".
All lines except the first ("ILSpy:") in that string are handled as command-line arguments.
There must be exactly one argument per line.
That is, by sending this message:
ILSpy:
C:\Assembly.dll
/navigateTo T:Type
The target ILSpy instance will open C:\Assembly.dll and navigate to the specified type.
ILSpy will return TRUE (1) if it handles the message, and FALSE (0) otherwise.
The /separate option will be ignored; WM_COPYDATA will never start up a new instance.
The /noActivate option has no effect, sending WM_COPYDATA will never activate the window.
Instead, the calling process should use SetForegroundWindow().
If you use /list with WM_COPYDATA, you need to specify /singleInstance as well, otherwise
ILSpy will not handle the message if it has opened a different assembly list.
Loading…
Cancel
Save