Browse Source

ilspycmd: add --member for single-member decompilation

Decompiling one method or property previously required decompiling its
whole type and searching the output, which is wasteful for scripted and
agent-driven use against large assemblies. The new -m|--member option
accepts an XML documentation id string (the syntax of compiler-generated
documentation files and of the UI's --navigateto option) or a metadata
token in 0x06000005 form, resolves it against the main module (with a
bounds check for tokens and distinct error messages for malformed ids,
unknown members, and members of other modules), and prints just that
member through the engine's single-member decompilation path.

The tests seed a new ICSharpCode.ILSpyCmd.Tests project (part of the
XPlat and Desktop solution filters and of the per-project CI test
steps), driving the real Main in-process via InternalsVisibleTo, so
future ilspycmd features have a dedicated home for CLI tests.

Assisted-by: Claude:claude-fable-5:Claude Code
pull/3927/head
Siegfried Pammer 2 months ago committed by Siegfried Pammer
parent
commit
74bc61b70f
  1. 7
      .github/workflows/build-ilspy.yml
  2. 27
      ICSharpCode.ILSpyCmd.Tests/ICSharpCode.ILSpyCmd.Tests.csproj
  3. 156
      ICSharpCode.ILSpyCmd.Tests/MemberOptionTests.cs
  4. 549
      ICSharpCode.ILSpyCmd.Tests/packages.lock.json
  5. 4
      ICSharpCode.ILSpyCmd/ICSharpCode.ILSpyCmd.csproj
  6. 95
      ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs
  7. 1
      ILSpy.Desktop.slnf
  8. 1
      ILSpy.XPlat.slnf
  9. 14
      ILSpy.sln

7
.github/workflows/build-ilspy.yml

@ -350,6 +350,13 @@ jobs:
--no-build --report-trx --no-build --report-trx
--results-directory test-results --results-directory test-results
- name: Execute ilspycmd tests
run: >
dotnet test --project ICSharpCode.ILSpyCmd.Tests/ICSharpCode.ILSpyCmd.Tests.csproj
--configuration Release
--no-build --report-trx
--results-directory test-results
- name: Execute BAML decompiler tests - name: Execute BAML decompiler tests
run: > run: >
dotnet test --project ILSpy.BamlDecompiler.Tests/ILSpy.BamlDecompiler.Tests.csproj dotnet test --project ILSpy.BamlDecompiler.Tests/ILSpy.BamlDecompiler.Tests.csproj

27
ICSharpCode.ILSpyCmd.Tests/ICSharpCode.ILSpyCmd.Tests.csproj

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net11.0</TargetFramework>
<LangVersion>preview</LangVersion>
<IsPackable>false</IsPackable>
<GenerateTestingPlatformEntryPoint>true</GenerateTestingPlatformEntryPoint>
<StartupObject>ICSharpCode.ILSpyCmd.Tests.MicrosoftTestingPlatformEntryPoint</StartupObject>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NUnit" />
<PackageReference Include="NUnit3TestAdapter" />
<PackageReference Include="Microsoft.Testing.Extensions.TrxReport" />
<PackageReference Include="Microsoft.Testing.Extensions.VSTestBridge" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ICSharpCode.ILSpyCmd\ICSharpCode.ILSpyCmd.csproj" />
</ItemGroup>
</Project>

156
ICSharpCode.ILSpyCmd.Tests/MemberOptionTests.cs

@ -0,0 +1,156 @@
// Copyright (c) 2026 Siegfried Pammer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.IO;
using System.Threading.Tasks;
using NUnit.Framework;
namespace ICSharpCode.ILSpyCmd.Tests
{
[TestFixture]
public class ILSpyCmdMemberOptionTests
{
static readonly string testAssemblyPath = typeof(ILSpyCmdMemberOptionTests).Assembly.Location;
static async Task<(int ExitCode, string Output, string Error)> RunAsync(params string[] args)
{
var originalOut = Console.Out;
var originalError = Console.Error;
var stdout = new StringWriter();
var stderr = new StringWriter();
try
{
Console.SetOut(stdout);
Console.SetError(stderr);
int exitCode = await ILSpyCmdProgram.Main(args);
return (exitCode, stdout.ToString(), stderr.ToString());
}
finally
{
Console.SetOut(originalOut);
Console.SetError(originalError);
}
}
[Test]
public async Task MethodByDocumentationIdString()
{
var result = await RunAsync(testAssemblyPath, "--disable-updatecheck",
"-m", "M:ICSharpCode.ILSpyCmd.Tests.MemberOptionSample.Add(System.Int32,System.Int32)");
Assert.That(result.ExitCode, Is.EqualTo(0), result.Error);
Assert.That(result.Output, Does.Contain("int Add(int a, int b)"));
Assert.That(result.Output, Does.Not.Contain("Unrelated"));
}
[Test]
public async Task PropertyByDocumentationIdString()
{
var result = await RunAsync(testAssemblyPath, "--disable-updatecheck",
"-m", "P:ICSharpCode.ILSpyCmd.Tests.MemberOptionSample.Answer");
Assert.That(result.ExitCode, Is.EqualTo(0), result.Error);
Assert.That(result.Output, Does.Contain("Answer"));
Assert.That(result.Output, Does.Not.Contain("Add(int a, int b)"));
}
[Test]
public async Task MethodByMetadataToken()
{
int token = typeof(MemberOptionSample).GetMethod(nameof(MemberOptionSample.Add))!.MetadataToken;
var result = await RunAsync(testAssemblyPath, "--disable-updatecheck", "-m", $"0x{token:x8}");
Assert.That(result.ExitCode, Is.EqualTo(0), result.Error);
Assert.That(result.Output, Does.Contain("int Add(int a, int b)"));
}
[Test]
public async Task UnknownMemberReportsError()
{
var result = await RunAsync(testAssemblyPath, "--disable-updatecheck",
"-m", "M:ICSharpCode.ILSpyCmd.Tests.MemberOptionSample.DoesNotExist");
Assert.That(result.ExitCode, Is.EqualTo(ProgramExitCodes.EX_DATAERR));
Assert.That(result.Error, Does.Contain("DoesNotExist"));
}
[Test]
public async Task MalformedTokenReportsTokenSpecificError()
{
var result = await RunAsync(testAssemblyPath, "--disable-updatecheck", "-m", "0xZZ000001");
Assert.That(result.ExitCode, Is.EqualTo(ProgramExitCodes.EX_DATAERR));
Assert.That(result.Error, Does.Contain("metadata token"));
Assert.That(result.Error, Does.Not.Contain("documentation id"));
}
[Test]
public async Task MemberOfAnotherModuleReportsDistinctError()
{
var result = await RunAsync(testAssemblyPath, "--disable-updatecheck", "-m", "M:System.Object.ToString");
Assert.That(result.ExitCode, Is.EqualTo(ProgramExitCodes.EX_DATAERR));
Assert.That(result.Error, Does.Contain("defined in"));
}
[Test]
public async Task SurroundingWhitespaceIsIgnored()
{
var result = await RunAsync(testAssemblyPath, "--disable-updatecheck",
"-m", " M:ICSharpCode.ILSpyCmd.Tests.MemberOptionSample.Add(System.Int32,System.Int32) ");
Assert.That(result.ExitCode, Is.EqualTo(0), result.Error);
Assert.That(result.Output, Does.Contain("int Add(int a, int b)"));
}
[Test]
public async Task OutOfRangeTokenReportsError()
{
var result = await RunAsync(testAssemblyPath, "--disable-updatecheck", "-m", "0x06ffffff");
Assert.That(result.ExitCode, Is.EqualTo(ProgramExitCodes.EX_DATAERR));
Assert.That(result.Error, Does.Contain("0x06ffffff"));
}
[Test]
public async Task TypeAndMemberOptionsAreMutuallyExclusive()
{
var result = await RunAsync(testAssemblyPath, "--disable-updatecheck",
"-t", "ICSharpCode.ILSpyCmd.Tests.MemberOptionSample",
"-m", "P:ICSharpCode.ILSpyCmd.Tests.MemberOptionSample.Answer");
Assert.That(result.ExitCode, Is.EqualTo(ProgramExitCodes.EX_USAGE));
}
}
public class MemberOptionSample
{
public int Add(int a, int b)
{
return a + b;
}
public string Answer => "42";
public void Unrelated()
{
}
}
}

549
ICSharpCode.ILSpyCmd.Tests/packages.lock.json

@ -0,0 +1,549 @@
{
"version": 2,
"dependencies": {
"net11.0": {
"Microsoft.Testing.Extensions.TrxReport": {
"type": "Direct",
"requested": "[2.1.0, )",
"resolved": "2.1.0",
"contentHash": "cXmP225WcMLLOSrW8xekaNhfzdBwXX3cbXbE5qSzmLbK0KZe3z8rAObKj70FWiPPPzm2W22x0ZW93gsmAfK6Mg==",
"dependencies": {
"Microsoft.Testing.Extensions.TrxReport.Abstractions": "2.1.0",
"Microsoft.Testing.Platform": "2.1.0"
}
},
"Microsoft.Testing.Extensions.VSTestBridge": {
"type": "Direct",
"requested": "[2.1.0, )",
"resolved": "2.1.0",
"contentHash": "bNRIEA2YoGr+Y+7LHdA7i1U80+7BAdf4K4Qh4Kx6eKkoBK/NV7QpoMg+GWPP0/eqAFzuUmUOIPVZ87Oo0Vyxmw==",
"dependencies": {
"Microsoft.TestPlatform.ObjectModel": "18.0.1",
"Microsoft.Testing.Extensions.Telemetry": "2.1.0",
"Microsoft.Testing.Extensions.TrxReport.Abstractions": "2.1.0",
"Microsoft.Testing.Platform": "2.1.0"
}
},
"NUnit": {
"type": "Direct",
"requested": "[4.6.1, )",
"resolved": "4.6.1",
"contentHash": "xS4+YaBFUv1r8bcAbjitSfYaRZGfMwUiMdiaRziBXZpKgVxKDSHhjUn0mV5mObHGZRZq6eNa+WFWr3g8grj66A=="
},
"NUnit3TestAdapter": {
"type": "Direct",
"requested": "[6.2.0, )",
"resolved": "6.2.0",
"contentHash": "8PMJB8za2u8S0ey/nEtvh9BHjLhv5yzUEMCmA0+VIPLKSeOAZ3TEHWDyAp0+iPQb3jnBH1o8k0PoU2FgzXLRnQ==",
"dependencies": {
"Microsoft.Extensions.DependencyModel": "8.0.2",
"Microsoft.Testing.Extensions.VSTestBridge": "2.1.0",
"Microsoft.Testing.Platform.MSBuild": "2.1.0"
}
},
"TomsToolbox.Composition.Analyzer": {
"type": "Direct",
"requested": "[2.24.0, )",
"resolved": "2.24.0",
"contentHash": "dKHqW1MeAMnDIbtx8qDTsGwy/7LUiQ3ccdzHX0PzCh1r98Lgl/1deIky9+ojZO0K5HjeA7uE+eW9/h+v7EOIBA=="
},
"Microsoft.ApplicationInsights": {
"type": "Transitive",
"resolved": "2.23.0",
"contentHash": "nWArUZTdU7iqZLycLKWe0TDms48KKGE6pONH2terYNa8REXiqixrMOkf1sk5DHGMaUTqONU2YkS4SAXBhLStgw==",
"dependencies": {
"System.Diagnostics.DiagnosticSource": "5.0.0"
}
},
"Microsoft.Extensions.Configuration.Abstractions": {
"type": "Transitive",
"resolved": "10.0.10",
"contentHash": "5Vnd2I75DmZCVEjSynIdJ/0EGafgnLQwgR3t2C2/fkjx/nRG+cLwxLLdInoHeCEpkD5K4Ov/g9ZCRYrl4TRsaA==",
"dependencies": {
"Microsoft.Extensions.Primitives": "10.0.10"
}
},
"Microsoft.Extensions.Configuration.Binder": {
"type": "Transitive",
"resolved": "10.0.10",
"contentHash": "GqmN2o1CkJvk7uWp+p4CwBYW0w/zfoEbvsiFDbO2G8l1Uz+mrDAbAcZiXhU2lufKPby1cjAUdd5GTWpebYOkOA==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.10",
"Microsoft.Extensions.Configuration.Abstractions": "10.0.10"
}
},
"Microsoft.Extensions.Configuration.CommandLine": {
"type": "Transitive",
"resolved": "10.0.10",
"contentHash": "33cBeR2HRbzHUTtmcmLdNOApneNGcymwwL4arHuotgVK9Frba8kcDTrvVTj7cSCmF1R9OiSbZH0KxNOwab3HUg==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.10",
"Microsoft.Extensions.Configuration.Abstractions": "10.0.10"
}
},
"Microsoft.Extensions.Configuration.EnvironmentVariables": {
"type": "Transitive",
"resolved": "10.0.10",
"contentHash": "KRfFSSCV58vEdU7mPED/YMzeovIWF5P0g8s9K8n9HEfy0/WzMq37SrPdXdFN5/dFT/rPMHpF7AvpoXHckbcBFg==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.10",
"Microsoft.Extensions.Configuration.Abstractions": "10.0.10"
}
},
"Microsoft.Extensions.Configuration.FileExtensions": {
"type": "Transitive",
"resolved": "10.0.10",
"contentHash": "ZOhZYwvbXGTgGVRwswIirofEMVHuWdxjdh0JeUZXwaF9cgcjXdz/t0ELtgaevw7ezTyv47yPNCgGreWtLkn3IQ==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.10",
"Microsoft.Extensions.Configuration.Abstractions": "10.0.10",
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.10",
"Microsoft.Extensions.FileProviders.Physical": "10.0.10",
"Microsoft.Extensions.Primitives": "10.0.10"
}
},
"Microsoft.Extensions.Configuration.UserSecrets": {
"type": "Transitive",
"resolved": "10.0.10",
"contentHash": "1s1sKFTk/Foam64JY6+m/diH8drL3Wx6V3gtSd5v1IEZtszZYyc1pW8uRnMblzpNiR0l0t8gGk7tXj3xHzFgdg==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "10.0.10",
"Microsoft.Extensions.Configuration.Json": "10.0.10",
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.10",
"Microsoft.Extensions.FileProviders.Physical": "10.0.10"
}
},
"Microsoft.Extensions.DependencyInjection": {
"type": "Transitive",
"resolved": "10.0.10",
"contentHash": "ANyvsgkNBRvcJh2XLgn8veGmajf+8m0AbKK+HPWdRL1yraSNVVSmQhFntLtdz/C795jxqqup+k05cs/3jZQPOA==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.10"
}
},
"Microsoft.Extensions.DependencyModel": {
"type": "Transitive",
"resolved": "8.0.2",
"contentHash": "mUBDZZRgZrSyFOsJ2qJJ9fXfqd/kXJwf3AiDoqLD9m6TjY5OO/vLNOb9fb4juC0487eq4hcGN/M2Rh/CKS7QYw=="
},
"Microsoft.Extensions.Diagnostics": {
"type": "Transitive",
"resolved": "10.0.10",
"contentHash": "Kr/e7lUf4+N8tacbqJ2Ctwe/HarKdAc9ZkgKVVqvtJDBKbez+T/KnUwu82KSlnBp/SrpBcxc7u7xkE2oUZT/5Q==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.10",
"Microsoft.Extensions.Diagnostics.Abstractions": "10.0.10",
"Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.10"
}
},
"Microsoft.Extensions.Diagnostics.Abstractions": {
"type": "Transitive",
"resolved": "10.0.10",
"contentHash": "9uWiKpeOVac355STyChWR/pliFX/5CeLqChW9kKsaxyDH4EUTZxMkT4Jwp/J/peLm0GBFmSX5c0WCse3yCnq1Q==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.10",
"Microsoft.Extensions.Options": "10.0.10"
}
},
"Microsoft.Extensions.FileProviders.Abstractions": {
"type": "Transitive",
"resolved": "10.0.10",
"contentHash": "c5zqFCY9DiIpMovLd7/d/CTiEtrMOuQ639dhv3PABtKQIKNQikSHwQt8+N679uii9q+B55lgK28Uv64FOwEu8w==",
"dependencies": {
"Microsoft.Extensions.Primitives": "10.0.10"
}
},
"Microsoft.Extensions.FileProviders.Physical": {
"type": "Transitive",
"resolved": "10.0.10",
"contentHash": "jhJAyo38kSrH3ARvWUk0h8itogVnQu2DCZuPo+s0Z+tXes0ugTxMPaHYzap85785eHQmPFqD9TYERqBbtGxn/w==",
"dependencies": {
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.10",
"Microsoft.Extensions.FileSystemGlobbing": "10.0.10",
"Microsoft.Extensions.Primitives": "10.0.10"
}
},
"Microsoft.Extensions.FileSystemGlobbing": {
"type": "Transitive",
"resolved": "10.0.10",
"contentHash": "jSOCVxEwCd4Aq925kJVz1kSO1EpX2OHYKL04qVREXkDU7Ce3pVDdHPYm+fEy8y/th2kJf/DAstRHpJAqoNWP8w=="
},
"Microsoft.Extensions.Hosting.Abstractions": {
"type": "Transitive",
"resolved": "10.0.10",
"contentHash": "5LugpYGHk+mkn0a8IZgcyfBca8PCTAU9RQFoMrTdtOOidq88M2SI5f3px6ugnzgxC+eTkvYYJi8pzlUnG5xdAQ==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "10.0.10",
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.10",
"Microsoft.Extensions.Diagnostics.Abstractions": "10.0.10",
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.10",
"Microsoft.Extensions.Logging.Abstractions": "10.0.10"
}
},
"Microsoft.Extensions.Logging": {
"type": "Transitive",
"resolved": "10.0.10",
"contentHash": "Tf6z5HsL0VDYRTfvsoNrTGHGheCwkTsZBA2FFh5ATJUbkAwug+FFNISJK2gjpUNemlAOoWllAK52HOWCjto3EQ==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "10.0.10",
"Microsoft.Extensions.Logging.Abstractions": "10.0.10",
"Microsoft.Extensions.Options": "10.0.10"
}
},
"Microsoft.Extensions.Logging.Abstractions": {
"type": "Transitive",
"resolved": "10.0.10",
"contentHash": "zkFxGYUvdxAvIKTyXHrmW+Sux53D4SezD9dMyZ6hrwwzPQJNuwCRy1f5W7AvYTqacEGhWF2XderRQG1OvbV8og==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.10"
}
},
"Microsoft.Extensions.Logging.Configuration": {
"type": "Transitive",
"resolved": "10.0.10",
"contentHash": "cLrqxkuEfcilZ8SjK+9KAnpLk9lOoMPaOokF+wRUYie+iUEcdX4/p/+gJkt0BYgWLthjpBUCkVTBI6Kxg0nsOw==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.10",
"Microsoft.Extensions.Configuration.Abstractions": "10.0.10",
"Microsoft.Extensions.Configuration.Binder": "10.0.10",
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.10",
"Microsoft.Extensions.Logging": "10.0.10",
"Microsoft.Extensions.Logging.Abstractions": "10.0.10",
"Microsoft.Extensions.Options": "10.0.10",
"Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.10"
}
},
"Microsoft.Extensions.Logging.Console": {
"type": "Transitive",
"resolved": "10.0.10",
"contentHash": "VIlNzPwPS0GeQVSmCqqo36ugryX3LpE9ul6gEkks5VLET3weH/XMLeWmclwfoGn4Nxi2mwVibB+OZBVJ9tDqvg==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.10",
"Microsoft.Extensions.Logging": "10.0.10",
"Microsoft.Extensions.Logging.Abstractions": "10.0.10",
"Microsoft.Extensions.Logging.Configuration": "10.0.10",
"Microsoft.Extensions.Options": "10.0.10"
}
},
"Microsoft.Extensions.Logging.Debug": {
"type": "Transitive",
"resolved": "10.0.10",
"contentHash": "8+TZBnV5fgBXoVNJ5ROSErUwYogk4hOgV7c2HWK1u5cqKGmiUTUn7+KqZ35iQu8e/B7Ykccyz5OTjdXcidNZ9g==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.10",
"Microsoft.Extensions.Logging": "10.0.10",
"Microsoft.Extensions.Logging.Abstractions": "10.0.10"
}
},
"Microsoft.Extensions.Logging.EventLog": {
"type": "Transitive",
"resolved": "10.0.10",
"contentHash": "0RE4951AzQ+YD4gVrvbq0BhdsiBgSDo44yM7+QBZ2mrmMJeNjY+teCIYfUjqDPVYnKs0HR6SkkhgrX1YgXZq3Q==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.10",
"Microsoft.Extensions.Logging": "10.0.10",
"Microsoft.Extensions.Logging.Abstractions": "10.0.10",
"Microsoft.Extensions.Options": "10.0.10",
"System.Diagnostics.EventLog": "10.0.10"
}
},
"Microsoft.Extensions.Logging.EventSource": {
"type": "Transitive",
"resolved": "10.0.10",
"contentHash": "85SAPwXhJtdBInzN2k7SChiFiBGh3KOWay5AfoY+GREF6P7oZA98+ST2p7Z9384iLKYjkZSKIZ/FqIO5aojtNw==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.10",
"Microsoft.Extensions.Logging": "10.0.10",
"Microsoft.Extensions.Logging.Abstractions": "10.0.10",
"Microsoft.Extensions.Options": "10.0.10",
"Microsoft.Extensions.Primitives": "10.0.10"
}
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "10.0.10",
"contentHash": "srnhnk7nE8krBiIXp71LvBmKBtraBONWSRzdjJgRv1Ko9Mp8IVNqv4vIS9hGeVteBig8aQkva9ZG+sC+o5sVcA==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.10",
"Microsoft.Extensions.Primitives": "10.0.10"
}
},
"Microsoft.Extensions.Options.ConfigurationExtensions": {
"type": "Transitive",
"resolved": "10.0.10",
"contentHash": "tnBmu/LwF25ZQK+HBNCu2xrwnkKoB/XEbJyooGGoYxHrhvxbSKi7eOFiJ4AXBy/QU4vtCvCJfoi8k9Ej72qzOQ==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "10.0.10",
"Microsoft.Extensions.Configuration.Binder": "10.0.10",
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.10",
"Microsoft.Extensions.Options": "10.0.10",
"Microsoft.Extensions.Primitives": "10.0.10"
}
},
"Microsoft.Extensions.Primitives": {
"type": "Transitive",
"resolved": "10.0.10",
"contentHash": "5wu/GrYVd8mG2DVUw3vFJzF+O336TyTGg/Kmcgw9bfwYhCoFiV5lR5QeEmKecJyrW4W54nMfD3p3589E8a7czQ=="
},
"Microsoft.Testing.Extensions.Telemetry": {
"type": "Transitive",
"resolved": "2.1.0",
"contentHash": "5TwgTx2u7k9Al/xbZ18QXq4Hdy2xewkVTI6K3sk+jY2ykqUkIKNuj7rFu3GOV5KnEUkevhw6eZcyZs77STHJIA==",
"dependencies": {
"Microsoft.ApplicationInsights": "2.23.0",
"Microsoft.Testing.Platform": "2.1.0"
}
},
"Microsoft.Testing.Extensions.TrxReport.Abstractions": {
"type": "Transitive",
"resolved": "2.1.0",
"contentHash": "D8xmIJYQFJ6D49Rx5/vPrkZZxb338Jkew+eSqZLBfBiWKw4QZKy3i1BOXiLfz0lOmaNErwDz/YWRojCdNl+B9Q==",
"dependencies": {
"Microsoft.Testing.Platform": "2.1.0"
}
},
"Microsoft.Testing.Platform": {
"type": "Transitive",
"resolved": "2.1.0",
"contentHash": "aHkjNTGIA+Zbdw6RJgSFrbDrCjO0CgqpElqYcvkRSeUhBv2bKarnvU3ep786U7UqrPlArT/B7VmImRibJD0Zrg=="
},
"Microsoft.Testing.Platform.MSBuild": {
"type": "Transitive",
"resolved": "2.1.0",
"contentHash": "UpfPebXQtHGrWz21+YLHmJSm+5zsuPE9U9pfdCtoB+67g75fDmWlNgpkH2ZmdVhSwkjNIed9Icg8Iu63z2ei5Q==",
"dependencies": {
"Microsoft.Testing.Platform": "2.1.0"
}
},
"Microsoft.TestPlatform.ObjectModel": {
"type": "Transitive",
"resolved": "18.0.1",
"contentHash": "qT/mwMcLF9BieRkzOBPL2qCopl8hQu6A1P7JWAoj/FMu5i9vds/7cjbJ/LLtaiwWevWLAeD5v5wjQJ/l6jvhWQ==",
"dependencies": {
"System.Reflection.Metadata": "8.0.0"
}
},
"Newtonsoft.Json": {
"type": "Transitive",
"resolved": "13.0.3",
"contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ=="
},
"NuGet.Common": {
"type": "Transitive",
"resolved": "7.6.0",
"contentHash": "uyXLqkbbZmkMvdHOR23l1EHW2hRmULtzoG3Ocj84VpGptnNfkODVboHGJfDfcn9Gi9oUNDs8/VjL4FZcST6zVg==",
"dependencies": {
"NuGet.Frameworks": "7.6.0"
}
},
"NuGet.Configuration": {
"type": "Transitive",
"resolved": "7.6.0",
"contentHash": "+bNj+YneC5CNg1vR+WZjLAakscJlsi0KhADZUgIJPn4pwh8/jeCUMC5ik5cpET/i+QOplDy7aJVTGkpdfrlPww==",
"dependencies": {
"NuGet.Common": "7.6.0",
"System.Security.Cryptography.ProtectedData": "8.0.0"
}
},
"NuGet.Packaging": {
"type": "Transitive",
"resolved": "7.6.0",
"contentHash": "TDp+qHzRBy1zjwiJGCbfpdO0jMG5hH/bk7p1EABLKv9p5SIykDPGnbuYXm2iZO0QJ/H+hOI/vo5LfqM17Q+G0w==",
"dependencies": {
"Newtonsoft.Json": "13.0.3",
"NuGet.Configuration": "7.6.0",
"NuGet.Versioning": "7.6.0",
"System.Security.Cryptography.Pkcs": "8.0.1"
}
},
"NuGet.Versioning": {
"type": "Transitive",
"resolved": "7.6.0",
"contentHash": "TpZxfOoQBQk/0r/2uc1A1qNYIKHkJGgOrWP+ax3nsNAUN/1BOQMDrgmGADogSA4hOXH1ZJiyeYg4Ca+vUW0sEg=="
},
"System.Diagnostics.DiagnosticSource": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "tCQTzPsGZh/A9LhhA6zrqCRV4hOHsK90/G7q3Khxmn6tnB1PuNU0cRaKANP2AWcF9bn0zsuOoZOSrHuJk6oNBA=="
},
"System.Diagnostics.EventLog": {
"type": "Transitive",
"resolved": "10.0.10",
"contentHash": "OvGz3PrzuAI/Sj7LTcXcCe3FClRI1IyRMZjNONcZtFh+Ww7nAtSh4kh08r8KVe/xxkXJPjR0Y1jF7H+N42d4xQ=="
},
"System.Security.Cryptography.ProtectedData": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "+TUFINV2q2ifyXauQXRwy4CiBhqvDEDZeVJU7qfxya4aRYOKzVBpN+4acx25VcPB9ywUN6C0n8drWl110PhZEg=="
},
"icsharpcode.bamldecompiler": {
"type": "Project",
"dependencies": {
"ICSharpCode.Decompiler": "[8.0.0-noversion, )"
}
},
"icsharpcode.decompiler": {
"type": "Project",
"dependencies": {
"System.Collections.Immutable": "[9.0.0, )",
"System.Reflection.Metadata": "[9.0.0, )"
}
},
"icsharpcode.ilspyx": {
"type": "Project",
"dependencies": {
"ICSharpCode.Decompiler": "[8.0.0-noversion, )",
"K4os.Compression.LZ4": "[1.3.8, )",
"Mono.Cecil": "[0.11.6, )",
"System.Composition.AttributedModel": "[10.0.10, )",
"System.Reflection.Metadata": "[10.0.10, )",
"System.Runtime.CompilerServices.Unsafe": "[6.1.2, )"
}
},
"ilspycmd": {
"type": "Project",
"dependencies": {
"ICSharpCode.BamlDecompiler": "[10.0.0-noversion, )",
"ICSharpCode.Decompiler": "[8.0.0-noversion, )",
"ICSharpCode.ILSpyX": "[8.0.0-noversion, )",
"McMaster.Extensions.Hosting.CommandLine": "[5.1.0, )",
"Microsoft.Extensions.Hosting": "[10.0.10, )",
"NuGet.Protocol": "[7.6.0, )",
"System.Security.Cryptography.Pkcs": "[10.0.10, )"
}
},
"K4os.Compression.LZ4": {
"type": "CentralTransitive",
"requested": "[1.3.8, )",
"resolved": "1.3.8",
"contentHash": "LhwlPa7c1zs1OV2XadMtAWdImjLIsqFJPoRcIWAadSRn0Ri1DepK65UbWLPmt4riLqx2d40xjXRk0ogpqNtK7g=="
},
"McMaster.Extensions.CommandLineUtils": {
"type": "CentralTransitive",
"requested": "[5.1.0, )",
"resolved": "5.1.0",
"contentHash": "YceiKmxdsBrlkAJpmJ2N8XkLyAH01xLsm4VLfjNp5MvtAom2wflZtEBz3CLMQret6Z7jSh3oBO1ULF1Is0tNxA=="
},
"McMaster.Extensions.Hosting.CommandLine": {
"type": "CentralTransitive",
"requested": "[5.1.0, )",
"resolved": "5.1.0",
"contentHash": "XN6s/86/bP0PSsP5p2eX49ZKmWrURSNg7nvrG5vws3abm7Py1A4bv3x3Pdvg7FqR8mLgEE0gNTI1qDtlYQIQnw==",
"dependencies": {
"McMaster.Extensions.CommandLineUtils": "5.1.0",
"Microsoft.Extensions.Hosting.Abstractions": "10.0.5",
"Microsoft.Extensions.Logging.Abstractions": "10.0.5"
}
},
"Microsoft.Extensions.Configuration": {
"type": "CentralTransitive",
"requested": "[10.0.10, )",
"resolved": "10.0.10",
"contentHash": "plJWK2zpWuuyxI8F8s2scx6Je7N1Ajjs6HvYUGKwRnDMWIVIz9FHwAkiT7ASgrvAOd10T0FPVlh9BzAJJME+jg==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "10.0.10",
"Microsoft.Extensions.Primitives": "10.0.10"
}
},
"Microsoft.Extensions.Configuration.Json": {
"type": "CentralTransitive",
"requested": "[10.0.10, )",
"resolved": "10.0.10",
"contentHash": "uvJ6sHwjgrkMEJOgiC76G0mcZGXerwyyWkwX34EOjCbxKG6TCtfAoqDKAMsCvEBf9HxjlGQEgqsSMOGCmGBf+A==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.10",
"Microsoft.Extensions.Configuration.Abstractions": "10.0.10",
"Microsoft.Extensions.Configuration.FileExtensions": "10.0.10",
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.10"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "CentralTransitive",
"requested": "[10.0.10, )",
"resolved": "10.0.10",
"contentHash": "z/2xXlFw2aLGjHyEm6E0tQ+In6VfzQzTrtArbQ2c0TQE16ZbyDCMGPvaUT9I0s8rgy9sRWlU2P9waW37qV04qA=="
},
"Microsoft.Extensions.Hosting": {
"type": "CentralTransitive",
"requested": "[10.0.10, )",
"resolved": "10.0.10",
"contentHash": "tL9FkfV64GPUDSPvwrgyw42LVzsnVAnyrqJEuZVJbODgrQ3eL63zmzEcVWoCHzfgqUhWggzbgAyUCnz/zfI3Pg==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.10",
"Microsoft.Extensions.Configuration.Abstractions": "10.0.10",
"Microsoft.Extensions.Configuration.Binder": "10.0.10",
"Microsoft.Extensions.Configuration.CommandLine": "10.0.10",
"Microsoft.Extensions.Configuration.EnvironmentVariables": "10.0.10",
"Microsoft.Extensions.Configuration.FileExtensions": "10.0.10",
"Microsoft.Extensions.Configuration.Json": "10.0.10",
"Microsoft.Extensions.Configuration.UserSecrets": "10.0.10",
"Microsoft.Extensions.DependencyInjection": "10.0.10",
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.10",
"Microsoft.Extensions.Diagnostics": "10.0.10",
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.10",
"Microsoft.Extensions.FileProviders.Physical": "10.0.10",
"Microsoft.Extensions.Hosting.Abstractions": "10.0.10",
"Microsoft.Extensions.Logging": "10.0.10",
"Microsoft.Extensions.Logging.Abstractions": "10.0.10",
"Microsoft.Extensions.Logging.Configuration": "10.0.10",
"Microsoft.Extensions.Logging.Console": "10.0.10",
"Microsoft.Extensions.Logging.Debug": "10.0.10",
"Microsoft.Extensions.Logging.EventLog": "10.0.10",
"Microsoft.Extensions.Logging.EventSource": "10.0.10",
"Microsoft.Extensions.Options": "10.0.10"
}
},
"Mono.Cecil": {
"type": "CentralTransitive",
"requested": "[0.11.6, )",
"resolved": "0.11.6",
"contentHash": "f33RkDtZO8VlGXCtmQIviOtxgnUdym9xx/b1p9h91CRGOsJFxCFOFK1FDbVt1OCf1aWwYejUFa2MOQyFWTFjbA=="
},
"NuGet.Frameworks": {
"type": "CentralTransitive",
"requested": "[7.6.0, )",
"resolved": "7.6.0",
"contentHash": "rJ7QtKN45XzLXCrMATve6eFLiUyUGEkA1rFSb6U6Fw6laM4hEAcKOrcdbgWlcFUlCK2158qP1LF00hg/ivF3nw=="
},
"NuGet.Protocol": {
"type": "CentralTransitive",
"requested": "[7.6.0, )",
"resolved": "7.6.0",
"contentHash": "Ccb9dJG9hW0FdHFjXoHmhJBBJRYSCeSJArLdZjyZj6/FEAIKezLn75KFQNrTPE5UiAp4HVrjBizufZ/0IXhfKQ==",
"dependencies": {
"NuGet.Packaging": "7.6.0"
}
},
"System.Collections.Immutable": {
"type": "CentralTransitive",
"requested": "[10.0.10, )",
"resolved": "10.0.10",
"contentHash": "Ih5zrydoDc1H5I0eNP6f4Lzw3cjsPMN4Nikd86kbyi66y0flkM9GfjVo62aUbcxeTbypzBCFAFQ+/6RF2jRORg=="
},
"System.Composition.AttributedModel": {
"type": "CentralTransitive",
"requested": "[10.0.10, )",
"resolved": "10.0.10",
"contentHash": "alK+Ew7XBsxHgwmSmiZzg7x4sLpNIaQ3QVJUkJ/issHZY0L+RHceqO27pDp0J0sDnSf8g6yJNpDnWSKL5sAa/g=="
},
"System.Reflection.Metadata": {
"type": "CentralTransitive",
"requested": "[10.0.10, )",
"resolved": "10.0.10",
"contentHash": "NZIS6/IUBh70nJfVvE577oDc81y5GfYRgjWssC8AGpSlVzSu5YfbHXEtHXyLeEY7gqqbAaASwJEP0Oy3Q54Jfw=="
},
"System.Runtime.CompilerServices.Unsafe": {
"type": "CentralTransitive",
"requested": "[6.1.2, )",
"resolved": "6.1.2",
"contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw=="
},
"System.Security.Cryptography.Pkcs": {
"type": "CentralTransitive",
"requested": "[10.0.10, )",
"resolved": "10.0.10",
"contentHash": "KG8t5RHczdgB3IWvRndpI0nE1DSo7ikDYSDbITxfgRFyIvnYiKzuPjaZOsPnwFQ1aOGUl9sEulJul9wlHLEUoA=="
}
}
}
}

4
ICSharpCode.ILSpyCmd/ICSharpCode.ILSpyCmd.csproj

@ -71,4 +71,8 @@
</ReadLinesFromFile> </ReadLinesFromFile>
</Target> </Target>
<ItemGroup>
<InternalsVisibleTo Include="ICSharpCode.ILSpyCmd.Tests" />
</ItemGroup>
</Project> </Project>

95
ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs

@ -23,7 +23,9 @@ using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.IO.MemoryMappedFiles; using System.IO.MemoryMappedFiles;
using System.Linq; using System.Linq;
using System.Globalization;
using System.Reflection.Metadata; using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable; using System.Reflection.PortableExecutable;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -33,6 +35,7 @@ using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.CSharp.ProjectDecompiler; using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.DebugInfo;
using ICSharpCode.Decompiler.Documentation;
using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Disassembler;
using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Solution; using ICSharpCode.Decompiler.Solution;
@ -105,6 +108,9 @@ Examples:
[Option("-t|--type <type-name>", "The fully qualified name of the type to decompile.", CommandOptionType.SingleValue)] [Option("-t|--type <type-name>", "The fully qualified name of the type to decompile.", CommandOptionType.SingleValue)]
public string TypeName { get; } public string TypeName { get; }
[Option("-m|--member <doc-id-or-token>", "The single member to decompile: an XML documentation id string (e.g. \"M:System.String.Concat(System.String,System.String)\", as also accepted by the UI's --navigateto option) or a metadata token (e.g. 0x06000005).", CommandOptionType.SingleValue)]
public string MemberIdString { get; }
[Option("-il|--ilcode", "Show IL code.", CommandOptionType.NoValue)] [Option("-il|--ilcode", "Show IL code.", CommandOptionType.NoValue)]
public bool ShowILCodeFlag { get; } public bool ShowILCodeFlag { get; }
@ -359,6 +365,12 @@ Examples:
} }
else else
{ {
if (MemberIdString != null && TypeName != null)
{
app.Error.WriteLine("The --type and --member options are mutually exclusive.");
return ProgramExitCodes.EX_USAGE;
}
if (outputDirectory != null) if (outputDirectory != null)
{ {
string outputName = Path.GetFileNameWithoutExtension(fileName); string outputName = Path.GetFileNameWithoutExtension(fileName);
@ -366,6 +378,11 @@ Examples:
(string.IsNullOrEmpty(TypeName) ? outputName : TypeName) + ".decompiled.cs")); (string.IsNullOrEmpty(TypeName) ? outputName : TypeName) + ".decompiled.cs"));
} }
if (MemberIdString != null)
{
return DecompileMember(fileName, output, MemberIdString);
}
return Decompile(fileName, output, TypeName); return Decompile(fileName, output, TypeName);
} }
} }
@ -619,6 +636,84 @@ Examples:
return 0; return 0;
} }
int DecompileMember(string assemblyFileName, TextWriter output, string idOrToken)
{
CSharpDecompiler decompiler = GetDecompiler(assemblyFileName);
if (!TryResolveMember(decompiler.TypeSystem, idOrToken, out EntityHandle handle, out string error))
{
Console.Error.WriteLine(error);
return ProgramExitCodes.EX_DATAERR;
}
output.Write(decompiler.DecompileAsString(handle));
return 0;
}
/// <summary>
/// Resolves a member reference supplied on the command line: either an XML
/// documentation id string ("M:...", "P:...", "F:...", "E:..." or "T:...") or a
/// metadata token in hexadecimal "0x06000005" form. Documentation id strings use
/// the same syntax as the compiler-generated documentation files and the UI's
/// --navigateto option.
/// </summary>
static bool TryResolveMember(IDecompilerTypeSystem typeSystem, string idOrToken, out EntityHandle handle, out string error)
{
handle = default;
error = null;
string trimmed = idOrToken.Trim();
if (trimmed.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
{
if (!int.TryParse(trimmed.AsSpan(2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int tokenValue))
{
error = $"'{trimmed}' is not a valid metadata token; expected a hexadecimal value like 0x06000005.";
return false;
}
var metadata = typeSystem.MainModule.MetadataFile.Metadata;
var candidate = MetadataTokens.EntityHandle(tokenValue);
int rowNumber = tokenValue & 0x00ffffff;
int rowCount = candidate.Kind switch {
HandleKind.TypeDefinition => metadata.TypeDefinitions.Count,
HandleKind.FieldDefinition => metadata.FieldDefinitions.Count,
HandleKind.MethodDefinition => metadata.MethodDefinitions.Count,
HandleKind.PropertyDefinition => metadata.PropertyDefinitions.Count,
HandleKind.EventDefinition => metadata.EventDefinitions.Count,
_ => 0,
};
if (rowNumber < 1 || rowNumber > rowCount)
{
error = $"Metadata token {trimmed} does not reference a type or member of this module.";
return false;
}
handle = candidate;
return true;
}
IEntity entity;
try
{
entity = IdStringProvider.FindEntity(trimmed, new SimpleTypeResolveContext(typeSystem.MainModule));
}
catch (ReflectionNameParseException ex)
{
error = $"'{trimmed}' is not a valid documentation id string: {ex.Message}";
return false;
}
if (entity == null || entity.MetadataToken.IsNil)
{
error = $"Member '{trimmed}' was not found in this module. Expected an XML documentation id string (e.g. \"M:System.String.Concat(System.String,System.String)\") or a metadata token (e.g. 0x06000005).";
return false;
}
if (entity.ParentModule != typeSystem.MainModule)
{
error = $"Member '{trimmed}' is defined in '{entity.ParentModule?.AssemblyName}', not in this module.";
return false;
}
handle = entity.MetadataToken;
return true;
}
/// <summary> /// <summary>
/// Resolves a type name supplied on the command line to a single type definition. /// Resolves a type name supplied on the command line to a single type definition.
/// <para> /// <para>

1
ILSpy.Desktop.slnf

@ -5,6 +5,7 @@
"ICSharpCode.BamlDecompiler\\ICSharpCode.BamlDecompiler.csproj", "ICSharpCode.BamlDecompiler\\ICSharpCode.BamlDecompiler.csproj",
"ICSharpCode.Decompiler.TestRunner\\ICSharpCode.Decompiler.TestRunner.csproj", "ICSharpCode.Decompiler.TestRunner\\ICSharpCode.Decompiler.TestRunner.csproj",
"ICSharpCode.Decompiler.Tests\\ICSharpCode.Decompiler.Tests.csproj", "ICSharpCode.Decompiler.Tests\\ICSharpCode.Decompiler.Tests.csproj",
"ICSharpCode.ILSpyCmd.Tests\\ICSharpCode.ILSpyCmd.Tests.csproj",
"ICSharpCode.Decompiler\\ICSharpCode.Decompiler.csproj", "ICSharpCode.Decompiler\\ICSharpCode.Decompiler.csproj",
"ICSharpCode.ILSpyX\\ICSharpCode.ILSpyX.csproj", "ICSharpCode.ILSpyX\\ICSharpCode.ILSpyX.csproj",
"ILSpy.BamlDecompiler.Tests\\ILSpy.BamlDecompiler.Tests.csproj", "ILSpy.BamlDecompiler.Tests\\ILSpy.BamlDecompiler.Tests.csproj",

1
ILSpy.XPlat.slnf

@ -3,6 +3,7 @@
"path": "ILSpy.sln", "path": "ILSpy.sln",
"projects": [ "projects": [
"ICSharpCode.ILSpyCmd\\ICSharpCode.ILSpyCmd.csproj", "ICSharpCode.ILSpyCmd\\ICSharpCode.ILSpyCmd.csproj",
"ICSharpCode.ILSpyCmd.Tests\\ICSharpCode.ILSpyCmd.Tests.csproj",
"ICSharpCode.Decompiler.PowerShell\\ICSharpCode.Decompiler.PowerShell.csproj", "ICSharpCode.Decompiler.PowerShell\\ICSharpCode.Decompiler.PowerShell.csproj",
"ICSharpCode.Decompiler.TestRunner\\ICSharpCode.Decompiler.TestRunner.csproj", "ICSharpCode.Decompiler.TestRunner\\ICSharpCode.Decompiler.TestRunner.csproj",
"ICSharpCode.Decompiler.Tests\\ICSharpCode.Decompiler.Tests.csproj", "ICSharpCode.Decompiler.Tests\\ICSharpCode.Decompiler.Tests.csproj",

14
ILSpy.sln

@ -49,6 +49,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpy.BamlDecompiler.Tests"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpy.BamlDecompiler.Tests.Windows", "ILSpy.BamlDecompiler.Tests.Windows\ILSpy.BamlDecompiler.Tests.Windows.csproj", "{7A5C3CEF-0744-4643-BA1E-7AE6610FF269}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpy.BamlDecompiler.Tests.Windows", "ILSpy.BamlDecompiler.Tests.Windows\ILSpy.BamlDecompiler.Tests.Windows.csproj", "{7A5C3CEF-0744-4643-BA1E-7AE6610FF269}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.ILSpyCmd.Tests", "ICSharpCode.ILSpyCmd.Tests\ICSharpCode.ILSpyCmd.Tests.csproj", "{042FE5F2-3C0A-4B99-A268-D1917CC67B47}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -239,6 +241,18 @@ Global
{7A5C3CEF-0744-4643-BA1E-7AE6610FF269}.Release|x64.Build.0 = Release|Any CPU {7A5C3CEF-0744-4643-BA1E-7AE6610FF269}.Release|x64.Build.0 = Release|Any CPU
{7A5C3CEF-0744-4643-BA1E-7AE6610FF269}.Release|x86.ActiveCfg = Release|Any CPU {7A5C3CEF-0744-4643-BA1E-7AE6610FF269}.Release|x86.ActiveCfg = Release|Any CPU
{7A5C3CEF-0744-4643-BA1E-7AE6610FF269}.Release|x86.Build.0 = Release|Any CPU {7A5C3CEF-0744-4643-BA1E-7AE6610FF269}.Release|x86.Build.0 = Release|Any CPU
{042FE5F2-3C0A-4B99-A268-D1917CC67B47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{042FE5F2-3C0A-4B99-A268-D1917CC67B47}.Debug|Any CPU.Build.0 = Debug|Any CPU
{042FE5F2-3C0A-4B99-A268-D1917CC67B47}.Debug|x64.ActiveCfg = Debug|Any CPU
{042FE5F2-3C0A-4B99-A268-D1917CC67B47}.Debug|x64.Build.0 = Debug|Any CPU
{042FE5F2-3C0A-4B99-A268-D1917CC67B47}.Debug|x86.ActiveCfg = Debug|Any CPU
{042FE5F2-3C0A-4B99-A268-D1917CC67B47}.Debug|x86.Build.0 = Debug|Any CPU
{042FE5F2-3C0A-4B99-A268-D1917CC67B47}.Release|Any CPU.ActiveCfg = Release|Any CPU
{042FE5F2-3C0A-4B99-A268-D1917CC67B47}.Release|Any CPU.Build.0 = Release|Any CPU
{042FE5F2-3C0A-4B99-A268-D1917CC67B47}.Release|x64.ActiveCfg = Release|Any CPU
{042FE5F2-3C0A-4B99-A268-D1917CC67B47}.Release|x64.Build.0 = Release|Any CPU
{042FE5F2-3C0A-4B99-A268-D1917CC67B47}.Release|x86.ActiveCfg = Release|Any CPU
{042FE5F2-3C0A-4B99-A268-D1917CC67B47}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

Loading…
Cancel
Save