Browse Source

Fix #1676: Add pdb support to powershell and command-line frontends

derived from work by @an-dr-eas-k in #1679
pull/1857/head
Siegfried Pammer 6 years ago
parent
commit
597ee991a2
  1. 10
      ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj
  2. 23
      ICSharpCode.Decompiler.Console/IlspyCmdProgram.cs
  3. 23
      ICSharpCode.Decompiler.Console/ValidationAttributes.cs
  4. 2
      ICSharpCode.Decompiler.PdbProvider.Cecil/MonoCecilDebugInfoProvider.cs
  5. 15
      ICSharpCode.Decompiler.PowerShell/GetDecompilerCmdlet.cs
  6. 8
      ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj
  7. 113
      ILSpy/DebugInfo/DebugInfoUtils.cs
  8. 3
      ILSpy/DebugInfo/PortableDebugInfoProvider.cs
  9. 1
      ILSpy/ILSpy.csproj
  10. 69
      ILSpy/LoadedAssembly.cs

10
ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj

@ -3,6 +3,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<IsPackable>true</IsPackable> <IsPackable>true</IsPackable>
<PackAsTool>true</PackAsTool> <PackAsTool>true</PackAsTool>
<AssemblyName>ilspycmd</AssemblyName> <AssemblyName>ilspycmd</AssemblyName>
@ -12,7 +13,7 @@
<Copyright>Copyright 2011-2019 AlphaSierraPapa</Copyright> <Copyright>Copyright 2011-2019 AlphaSierraPapa</Copyright>
<PackageProjectUrl>https://github.com/icsharpcode/ILSpy/</PackageProjectUrl> <PackageProjectUrl>https://github.com/icsharpcode/ILSpy/</PackageProjectUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageIcon>ILSpyCmdNuGetPackageIcon.png</PackageIcon> <PackageIcon>ILSpyCmdNuGetPackageIcon.png</PackageIcon>
<RepositoryUrl>https://github.com/icsharpcode/ILSpy/</RepositoryUrl> <RepositoryUrl>https://github.com/icsharpcode/ILSpy/</RepositoryUrl>
<Company /> <Company />
<AssemblyVersion>6.0.0.0</AssemblyVersion> <AssemblyVersion>6.0.0.0</AssemblyVersion>
@ -26,6 +27,12 @@
<WarningsAsErrors>NU1605</WarningsAsErrors> <WarningsAsErrors>NU1605</WarningsAsErrors>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<Compile Include="..\ICSharpCode.Decompiler.PdbProvider.Cecil\MonoCecilDebugInfoProvider.cs" Link="MonoCecilDebugInfoProvider.cs" />
<Compile Include="..\ILSpy\DebugInfo\PortableDebugInfoProvider.cs" Link="PortableDebugInfoProvider.cs" />
<Compile Include="..\ILSpy\DebugInfo\DebugInfoUtils.cs" Link="DebugInfoUtils.cs" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="ILSpyCmdNuGetPackageIcon.png" Pack="true" PackagePath="\" /> <None Include="ILSpyCmdNuGetPackageIcon.png" Pack="true" PackagePath="\" />
</ItemGroup> </ItemGroup>
@ -45,6 +52,7 @@
<PackageReference Include="System.Runtime.Handles" Version="4.3.0" /> <PackageReference Include="System.Runtime.Handles" Version="4.3.0" />
<PackageReference Include="System.Runtime.InteropServices" Version="4.3.0" /> <PackageReference Include="System.Runtime.InteropServices" Version="4.3.0" />
<PackageReference Include="System.Text.Encoding.Extensions" Version="4.3.0" /> <PackageReference Include="System.Text.Encoding.Extensions" Version="4.3.0" />
<PackageReference Include="Mono.Cecil" Version="0.10.3" />
</ItemGroup> </ItemGroup>
</Project> </Project>

23
ICSharpCode.Decompiler.Console/IlspyCmdProgram.cs

@ -12,6 +12,7 @@ using System.Threading;
using System.Reflection.Metadata; using System.Reflection.Metadata;
using System.Reflection.PortableExecutable; using System.Reflection.PortableExecutable;
using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.DebugInfo;
using ICSharpCode.Decompiler.PdbProvider;
// ReSharper disable All // ReSharper disable All
namespace ICSharpCode.Decompiler.Console namespace ICSharpCode.Decompiler.Console
@ -45,9 +46,13 @@ Remarks:
[Option("-il|--ilcode", "Show IL code.", CommandOptionType.NoValue)] [Option("-il|--ilcode", "Show IL code.", CommandOptionType.NoValue)]
public bool ShowILCodeFlag { get; } public bool ShowILCodeFlag { get; }
[Option("-d|--debuginfo", "Generate PDB.", CommandOptionType.NoValue)] [Option("-genpdb", "Generate PDB.", CommandOptionType.NoValue)]
public bool CreateDebugInfoFlag { get; } public bool CreateDebugInfoFlag { get; }
[FileExistsOrNull]
[Option("-usepdb", "Use PDB.", CommandOptionType.SingleOrNoValue)]
public (bool IsSet, string Value) InputPDBFile { get; }
[Option("-l|--list <entity-type(s)>", "Lists all entities of the specified type(s). Valid types: c(lass), i(interface), s(truct), d(elegate), e(num)", CommandOptionType.MultipleValue)] [Option("-l|--list <entity-type(s)>", "Lists all entities of the specified type(s). Valid types: c(lass), i(interface), s(truct), d(elegate), e(num)", CommandOptionType.MultipleValue)]
public string[] EntityTypes { get; } = new string[0]; public string[] EntityTypes { get; } = new string[0];
@ -142,7 +147,9 @@ Remarks:
foreach (var path in ReferencePaths) { foreach (var path in ReferencePaths) {
resolver.AddSearchDirectory(path); resolver.AddSearchDirectory(path);
} }
return new CSharpDecompiler(assemblyFileName, resolver, GetSettings()); return new CSharpDecompiler(assemblyFileName, resolver, GetSettings()) {
DebugInfoProvider = TryLoadPDB(module)
};
} }
int ListContent(string assemblyFileName, TextWriter output, ISet<TypeKind> kinds) int ListContent(string assemblyFileName, TextWriter output, ISet<TypeKind> kinds)
@ -175,6 +182,7 @@ Remarks:
resolver.AddSearchDirectory(path); resolver.AddSearchDirectory(path);
} }
decompiler.AssemblyResolver = resolver; decompiler.AssemblyResolver = resolver;
decompiler.DebugInfoProvider = TryLoadPDB(module);
decompiler.DecompileProject(module, outputDirectory); decompiler.DecompileProject(module, outputDirectory);
return 0; return 0;
} }
@ -211,5 +219,16 @@ Remarks:
return 0; return 0;
} }
IDebugInfoProvider TryLoadPDB(PEFile module)
{
if (InputPDBFile.IsSet) {
if (InputPDBFile.Value == null)
return DebugInfoUtils.LoadSymbols(module);
return DebugInfoUtils.FromFile(module, InputPDBFile.Value);
}
return null;
}
} }
} }

23
ICSharpCode.Decompiler.Console/ValidationAttributes.cs

@ -1,19 +1,38 @@
using System; using System;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.IO;
namespace ICSharpCode.Decompiler.Console namespace ICSharpCode.Decompiler.Console
{ {
[AttributeUsage(AttributeTargets.Class)] [AttributeUsage(AttributeTargets.Class)]
public class ProjectOptionRequiresOutputDirectoryValidationAttribute : ValidationAttribute public sealed class ProjectOptionRequiresOutputDirectoryValidationAttribute : ValidationAttribute
{ {
public ProjectOptionRequiresOutputDirectoryValidationAttribute()
{
}
protected override ValidationResult IsValid(object value, ValidationContext context) protected override ValidationResult IsValid(object value, ValidationContext context)
{ {
if (value is ILSpyCmdProgram obj) { if (value is ILSpyCmdProgram obj) {
if (obj.CreateCompilableProjectFlag && String.IsNullOrEmpty(obj.OutputDirectory)) { if (obj.CreateCompilableProjectFlag && string.IsNullOrEmpty(obj.OutputDirectory)) {
return new ValidationResult("--project cannot be used unless --outputdir is also specified"); return new ValidationResult("--project cannot be used unless --outputdir is also specified");
} }
} }
return ValidationResult.Success; return ValidationResult.Success;
} }
} }
[AttributeUsage(AttributeTargets.Property)]
public sealed class FileExistsOrNullAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext context)
{
var s = value as string;
if (string.IsNullOrEmpty(s))
return ValidationResult.Success;
if (File.Exists(s))
return ValidationResult.Success;
return new ValidationResult($"File '{s}' does not exist!");
}
}
} }

2
ICSharpCode.Decompiler.PdbProvider.Cecil/MonoCecilDebugInfoProvider.cs

@ -30,7 +30,7 @@ using Mono.Cecil;
using Mono.Cecil.Pdb; using Mono.Cecil.Pdb;
using SRM = System.Reflection.Metadata; using SRM = System.Reflection.Metadata;
namespace ICSharpCode.Decompiler.PdbProvider.Cecil namespace ICSharpCode.Decompiler.PdbProvider
{ {
public class MonoCecilDebugInfoProvider : IDebugInfoProvider public class MonoCecilDebugInfoProvider : IDebugInfoProvider
{ {

15
ICSharpCode.Decompiler.PowerShell/GetDecompilerCmdlet.cs

@ -1,8 +1,12 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Management.Automation; using System.Management.Automation;
using System.Reflection.PortableExecutable;
using System.Text; using System.Text;
using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.PdbProvider;
namespace ICSharpCode.Decompiler.PowerShell namespace ICSharpCode.Decompiler.PowerShell
{ {
@ -24,18 +28,25 @@ namespace ICSharpCode.Decompiler.PowerShell
[Parameter(HelpMessage = "Remove dead code")] [Parameter(HelpMessage = "Remove dead code")]
public bool RemoveDeadCode { get; set; } public bool RemoveDeadCode { get; set; }
[Parameter(HelpMessage = "Use PDB")]
public string PDBFilePath { get; set; }
protected override void ProcessRecord() protected override void ProcessRecord()
{ {
string path = GetUnresolvedProviderPathFromPSPath(LiteralPath); string path = GetUnresolvedProviderPathFromPSPath(LiteralPath);
try { try {
var module = new PEFile(LiteralPath, new FileStream(LiteralPath, FileMode.Open, FileAccess.Read), PEStreamOptions.Default);
var debugInfo = DebugInfoUtils.FromFile(module, PDBFilePath);
var decompiler = new CSharpDecompiler(path, new DecompilerSettings(LanguageVersion) { var decompiler = new CSharpDecompiler(path, new DecompilerSettings(LanguageVersion) {
ThrowOnAssemblyResolveErrors = false, ThrowOnAssemblyResolveErrors = false,
RemoveDeadCode = RemoveDeadCode, RemoveDeadCode = RemoveDeadCode,
RemoveDeadStores = RemoveDeadStores RemoveDeadStores = RemoveDeadStores,
UseDebugSymbols = debugInfo != null,
ShowDebugInfo = debugInfo != null,
}); });
decompiler.DebugInfoProvider = debugInfo;
WriteObject(decompiler); WriteObject(decompiler);
} catch (Exception e) { } catch (Exception e) {
WriteVerbose(e.ToString()); WriteVerbose(e.ToString());
WriteError(new ErrorRecord(e, ErrorIds.AssemblyLoadFailed, ErrorCategory.OperationStopped, null)); WriteError(new ErrorRecord(e, ErrorIds.AssemblyLoadFailed, ErrorCategory.OperationStopped, null));

8
ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj

@ -2,6 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<RootNamespace>ICSharpCode.Decompiler.PowerShell</RootNamespace> <RootNamespace>ICSharpCode.Decompiler.PowerShell</RootNamespace>
</PropertyGroup> </PropertyGroup>
@ -9,6 +10,13 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="PowerShellStandard.Library" Version="5.1.0" /> <PackageReference Include="PowerShellStandard.Library" Version="5.1.0" />
<PackageReference Include="ICSharpCode.Decompiler" Version="6.0.0.5420-preview1" /> <PackageReference Include="ICSharpCode.Decompiler" Version="6.0.0.5420-preview1" />
<PackageReference Include="Mono.Cecil" Version="0.10.3" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\ICSharpCode.Decompiler.PdbProvider.Cecil\MonoCecilDebugInfoProvider.cs" Link="MonoCecilDebugInfoProvider.cs" />
<Compile Include="..\ILSpy\DebugInfo\PortableDebugInfoProvider.cs" Link="PortableDebugInfoProvider.cs" />
<Compile Include="..\ILSpy\DebugInfo\DebugInfoUtils.cs" Link="DebugInfoUtils.cs" />
</ItemGroup> </ItemGroup>
</Project> </Project>

113
ILSpy/DebugInfo/DebugInfoUtils.cs

@ -0,0 +1,113 @@
// Copyright (c) 2018 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.Linq;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Runtime.InteropServices;
using ICSharpCode.Decompiler.DebugInfo;
using ICSharpCode.Decompiler.Metadata;
namespace ICSharpCode.Decompiler.PdbProvider
{
static class DebugInfoUtils
{
public static IDebugInfoProvider LoadSymbols(PEFile module)
{
try {
var reader = module.Reader;
// try to open portable pdb file/embedded pdb info:
if (TryOpenPortablePdb(module, out var provider, out var pdbFileName)) {
return new PortableDebugInfoProvider(pdbFileName, provider);
} else {
// search for pdb in same directory as dll
pdbFileName = Path.Combine(Path.GetDirectoryName(module.FileName), Path.GetFileNameWithoutExtension(module.FileName) + ".pdb");
if (File.Exists(pdbFileName)) {
return new MonoCecilDebugInfoProvider(module, pdbFileName);
}
}
} catch (Exception ex) when (ex is BadImageFormatException || ex is COMException) {
// Ignore PDB load errors
}
return null;
}
public static IDebugInfoProvider FromFile(PEFile module, string pdbFileName)
{
if (string.IsNullOrEmpty(pdbFileName))
return null;
Stream stream = OpenStream(pdbFileName);
if (stream == null)
return null;
if (stream.Read(buffer, 0, buffer.Length) == LegacyPDBPrefix.Length
&& System.Text.Encoding.ASCII.GetString(buffer) == LegacyPDBPrefix) {
stream.Position = 0;
return new MonoCecilDebugInfoProvider(module, pdbFileName);
} else {
stream.Position = 0;
var provider = MetadataReaderProvider.FromPortablePdbStream(stream);
return new PortableDebugInfoProvider(pdbFileName, provider);
}
}
const string LegacyPDBPrefix = "Microsoft C/C++ MSF 7.00";
static readonly byte[] buffer = new byte[LegacyPDBPrefix.Length];
static bool TryOpenPortablePdb(PEFile module, out MetadataReaderProvider provider, out string pdbFileName)
{
provider = null;
pdbFileName = null;
var reader = module.Reader;
foreach (var entry in reader.ReadDebugDirectory()) {
if (entry.IsPortableCodeView) {
return reader.TryOpenAssociatedPortablePdb(module.FileName, OpenStream, out provider, out pdbFileName);
}
if (entry.Type == DebugDirectoryEntryType.CodeView) {
string pdbDirectory = Path.GetDirectoryName(module.FileName);
pdbFileName = Path.Combine(pdbDirectory, Path.GetFileNameWithoutExtension(module.FileName) + ".pdb");
if (File.Exists(pdbFileName)) {
Stream stream = OpenStream(pdbFileName);
if (stream.Read(buffer, 0, buffer.Length) == LegacyPDBPrefix.Length
&& System.Text.Encoding.ASCII.GetString(buffer) == LegacyPDBPrefix) {
return false;
}
stream.Position = 0;
provider = MetadataReaderProvider.FromPortablePdbStream(stream);
return true;
}
}
}
return false;
}
static Stream OpenStream(string fileName)
{
if (!File.Exists(fileName))
return null;
var memory = new MemoryStream();
using (var stream = File.OpenRead(fileName))
stream.CopyTo(memory);
memory.Position = 0;
return memory;
}
}
}

3
ILSpy/DebugInfo/PortableDebugInfoProvider.cs

@ -20,9 +20,8 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection.Metadata; using System.Reflection.Metadata;
using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.DebugInfo;
using ICSharpCode.Decompiler.Metadata;
namespace ICSharpCode.ILSpy.DebugInfo namespace ICSharpCode.Decompiler.PdbProvider
{ {
class PortableDebugInfoProvider : IDebugInfoProvider class PortableDebugInfoProvider : IDebugInfoProvider
{ {

1
ILSpy/ILSpy.csproj

@ -135,6 +135,7 @@
<Compile Include="CreateListDialog.xaml.cs"> <Compile Include="CreateListDialog.xaml.cs">
<DependentUpon>CreateListDialog.xaml</DependentUpon> <DependentUpon>CreateListDialog.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="DebugInfo\DebugInfoUtils.cs" />
<Compile Include="DebugInfo\PortableDebugInfoProvider.cs" /> <Compile Include="DebugInfo\PortableDebugInfoProvider.cs" />
<Compile Include="DebugSteps.xaml.cs"> <Compile Include="DebugSteps.xaml.cs">
<DependentUpon>DebugSteps.xaml</DependentUpon> <DependentUpon>DebugSteps.xaml</DependentUpon>

69
ILSpy/LoadedAssembly.cs

@ -28,10 +28,9 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.DebugInfo;
using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.PdbProvider.Cecil; using ICSharpCode.Decompiler.PdbProvider;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.TypeSystem.Implementation;
using ICSharpCode.ILSpy.DebugInfo;
using ICSharpCode.ILSpy.Options; using ICSharpCode.ILSpy.Options;
namespace ICSharpCode.ILSpy namespace ICSharpCode.ILSpy
@ -167,7 +166,7 @@ namespace ICSharpCode.ILSpy
if (DecompilerSettingsPanel.CurrentDecompilerSettings.UseDebugSymbols) { if (DecompilerSettingsPanel.CurrentDecompilerSettings.UseDebugSymbols) {
try { try {
LoadSymbols(module); debugInfoProvider = DebugInfoUtils.LoadSymbols(module);
} catch (IOException) { } catch (IOException) {
} catch (UnauthorizedAccessException) { } catch (UnauthorizedAccessException) {
} catch (InvalidOperationException) { } catch (InvalidOperationException) {
@ -180,70 +179,6 @@ namespace ICSharpCode.ILSpy
return module; return module;
} }
void LoadSymbols(PEFile module)
{
try {
var reader = module.Reader;
// try to open portable pdb file/embedded pdb info:
if (TryOpenPortablePdb(module, out var provider, out var pdbFileName)) {
debugInfoProvider = new PortableDebugInfoProvider(pdbFileName, provider);
} else {
// search for pdb in same directory as dll
string pdbDirectory = Path.GetDirectoryName(fileName);
pdbFileName = Path.Combine(pdbDirectory, Path.GetFileNameWithoutExtension(fileName) + ".pdb");
if (File.Exists(pdbFileName)) {
debugInfoProvider = new MonoCecilDebugInfoProvider(module, pdbFileName);
return;
}
// TODO: use symbol cache, get symbols from microsoft
}
} catch (Exception ex) when (ex is BadImageFormatException || ex is COMException) {
// Ignore PDB load errors
}
}
const string LegacyPDBPrefix = "Microsoft C/C++ MSF 7.00";
byte[] buffer = new byte[LegacyPDBPrefix.Length];
bool TryOpenPortablePdb(PEFile module, out MetadataReaderProvider provider, out string pdbFileName)
{
provider = null;
pdbFileName = null;
var reader = module.Reader;
foreach (var entry in reader.ReadDebugDirectory()) {
if (entry.IsPortableCodeView) {
return reader.TryOpenAssociatedPortablePdb(fileName, OpenStream, out provider, out pdbFileName);
}
if (entry.Type == DebugDirectoryEntryType.CodeView) {
string pdbDirectory = Path.GetDirectoryName(fileName);
pdbFileName = Path.Combine(pdbDirectory, Path.GetFileNameWithoutExtension(fileName) + ".pdb");
if (File.Exists(pdbFileName)) {
Stream stream = OpenStream(pdbFileName);
if (stream.Read(buffer, 0, buffer.Length) == LegacyPDBPrefix.Length
&& System.Text.Encoding.ASCII.GetString(buffer) == LegacyPDBPrefix) {
return false;
}
stream.Position = 0;
provider = MetadataReaderProvider.FromPortablePdbStream(stream);
return true;
}
}
}
return false;
}
Stream OpenStream(string fileName)
{
if (!File.Exists(fileName))
return null;
var memory = new MemoryStream();
using (var stream = File.OpenRead(fileName))
stream.CopyTo(memory);
memory.Position = 0;
return memory;
}
[ThreadStatic] [ThreadStatic]
static int assemblyLoadDisableCount; static int assemblyLoadDisableCount;

Loading…
Cancel
Save