问题描述
|
我目前正在尝试从IL字节码和PDB文件中检索源代码,
我到了可以从IL和反射生成源代码的地步
我知道局部变量名称的名称包含在pdb文件中。
我的问题是如何找到它?我应该使用哪些库来处理pdb文件(如果有),或者我应该自己编写代码?在哪里可以找到有关pdb文件格式的信息?
目前在生成的源代码中,我正在使用本地变量的自动生成的值,但是我想更改它,因为我相信如果您有pdb文件可供使用,则可以找到该信息。
我试图在Google上查看,但没有找到任何有用的信息。
预先感谢您的答复;)
解决方法
这是使用
System.Diagnostics.SymbolStore
中的类型从MethodInfo读取局部变量名称的方法:
public class LocalVariableNameReader
{
Dictionary<int,string> _names = new Dictionary<int,string> ();
public string this [int index]
{
get
{
if (!_names.ContainsKey (index)) return null;
return _names [index];
}
}
public LocalVariableNameReader (MethodInfo m)
{
ISymbolReader symReader = SymUtil.GetSymbolReaderForFile (m.DeclaringType.Assembly.Location,null);
ISymbolMethod met = symReader.GetMethod (new SymbolToken (m.MetadataToken));
VisitLocals (met.RootScope);
}
void VisitLocals (ISymbolScope iSymbolScope)
{
foreach (var s in iSymbolScope.GetLocals ()) _names [s.AddressField1] = s.Name;
foreach (var c in iSymbolScope.GetChildren ()) VisitLocals (c);
}
}
SymUtil
类来自此示例。
编辑:上面的链接已损坏。从谷歌缓存:
很久以前,我在研究如何处理pdb文件,以便
获得反射根本无法提供的其他信息。
现在我绊倒了一个5岁的Mike Stall的职位
(这里)
突然之间一切都变得清晰了。我整理了一个小例子
使用.net 4.0读取方法主体代码,就像在源代码中一样
从汇编的位置开始。请注意这一点
要工作,您必须具有程序集的pdb。也
请确保您添加对ISymWrapper和您的项目的引用
针对.Net 4.0框架,而不是.Net 4.0 Client。
using System;
using System.Diagnostics.SymbolStore;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
namespace PdbTest
{
class Program
{
static void Main(string[] args)
{
Assembly ass = Assembly.GetExecutingAssembly();
ISymbolReader symreader = SymUtil.GetSymbolReaderForFile(ass.Location,null);
MethodInfo m = ass.GetType(\"PdbTest.TestClass\").GetMethod(\"GetStringRepresentation\");
ISymbolMethod met = symreader.GetMethod(new SymbolToken(m.MetadataToken));
int count = met.SequencePointCount;
ISymbolDocument[] docs = new ISymbolDocument[count];
int[] offsets = new int[count];
int[] lines = new int[count];
int[] columns = new int[count];
int[] endlines = new int[count];
int[] endcolumns = new int[count];
met.GetSequencePoints(offsets,docs,lines,columns,endlines,endcolumns);
StreamReader reader = new StreamReader(docs[0].URL);
string[] linesOfCode = reader.ReadToEnd().Split(\'n\');
reader.Close();
Console.WriteLine(\"The content of method PdbTest.TestClass.GetStringRepresentation\");
for (int i = lines[0]; i < endlines[count - 1] - 1; i++)
{
Console.WriteLine(linesOfCode[i]);
}
}
}
#region test class
public enum MyEnum
{
Apples,Oranges
}
public partial class TestClass
{
public string GetStringRepresentation(MyEnum e)
{
MyEnum e2 = MyEnum.Apples;
return e.ToString() + e2.ToString();
}
}
#endregion test class
#region Get a symbol reader for the given module
// Encapsulate a set of helper classes to get a symbol reader from a file.
// The symbol interfaces require an unmanaged metadata interface.
static class SymUtil
{
static class NativeMethods
{
[DllImport(\"ole32.dll\")]
public static extern int CoCreateInstance(
[In] ref Guid rclsid,[In,MarshalAs(UnmanagedType.IUnknown)] Object pUnkOuter,[In] uint dwClsContext,[In] ref Guid riid,[Out,MarshalAs(UnmanagedType.Interface)] out Object ppv);
}
// Wrapper.
public static ISymbolReader GetSymbolReaderForFile(string pathModule,string searchPath)
{
return SymUtil.GetSymbolReaderForFile(
new System.Diagnostics.SymbolStore.SymBinder(),pathModule,searchPath);
}
// We demand Unmanaged code permissions because we\'re reading from the file
// system and calling out to the Symbol Reader
// @TODO - make this more specific.
[System.Security.Permissions.SecurityPermission(
System.Security.Permissions.SecurityAction.Demand,Flags = System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode)]
public static ISymbolReader GetSymbolReaderForFile(
System.Diagnostics.SymbolStore.SymBinder binder,string pathModule,string searchPath)
{
// Guids for imported metadata interfaces.
Guid dispenserClassID = new Guid(0xe5cb7a31,0x7512,0x11d2,0x89,0xce,0x00,0x80,0xc7,0x92,0xe5,0xd8); // CLSID_CorMetaDataDispenser
Guid dispenserIID = new Guid(0x809c652e,0x7396,0x97,0x71,0xa0,0xc9,0xb4,0xd5,0x0c); // IID_IMetaDataDispenser
Guid importerIID = new Guid(0x7dac8207,0xd3ae,0x4c75,0x9b,0x67,0x1a,0x49,0x7d,0x44); // IID_IMetaDataImport
// First create the Metadata dispenser.
object objDispenser;
NativeMethods.CoCreateInstance(ref dispenserClassID,null,1,ref dispenserIID,out objDispenser);
// Now open an Importer on the given filename. We\'ll end up passing this importer
// straight through to the Binder.
object objImporter;
IMetaDataDispenser dispenser = (IMetaDataDispenser)objDispenser;
dispenser.OpenScope(pathModule,ref importerIID,out objImporter);
IntPtr importerPtr = IntPtr.Zero;
ISymbolReader reader;
try
{
// This will manually AddRef the underlying object,so we need to
// be very careful to Release it.
importerPtr = Marshal.GetComInterfaceForObject(objImporter,typeof(IMetadataImport));
reader = binder.GetReader(importerPtr,searchPath);
}
finally
{
if (importerPtr != IntPtr.Zero)
{
Marshal.Release(importerPtr);
}
}
return reader;
}
}
#region Metadata Imports
// We can use reflection-only load context to use reflection to query for
// metadata information rather
// than painfully import the com-classic metadata interfaces.
[Guid(\"809c652e-7396-11d2-9771-00a0c9b4d50c\"),InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComVisible(true)]
interface IMetaDataDispenser
{
// We need to be able to call OpenScope,which is the 2nd vtable slot.
// Thus we need this one placeholder here to occupy the first slot..
void DefineScope_Placeholder();
//STDMETHOD(OpenScope)( // Return code.
//LPCWSTR szScope,// [in] The scope to open.
// DWORD dwOpenFlags,// [in] Open mode flags.
// REFIID riid,// [in] The interface desired.
// IUnknown **ppIUnk) PURE; // [out] Return interface on success.
void OpenScope([In,MarshalAs(UnmanagedType.LPWStr)] String szScope,[In] Int32 dwOpenFlags,MarshalAs(UnmanagedType.IUnknown)] out Object punk);
// Don\'t need any other methods.
}
// Since we\'re just blindly passing this interface through managed code to the Symbinder,// we don\'t care about actually importing the specific methods.
// This needs to be public so that we can call Marshal.GetComInterfaceForObject() on
// it to get the underlying metadata pointer.
[Guid(\"7DAC8207-D3AE-4c75-9B67-92801A497D44\"),InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComVisible(true)]
[CLSCompliant(true)]
public interface IMetadataImport
{
// Just need a single placeholder method so that it doesn\'t complain
// about an empty interface.
void Placeholder();
}
#endregion
#endregion Get a symbol reader for the given module
}
, 我看到这个问题是在2011年提出的。现在是2019年,有2种方法可以从一种方法中检索局部变量。
首先让我们定义ѭ4来保留局部变量参数:
public class VariableInfo : IEquatable<VariableInfo>
{
public int Index { get; }
public string Name { get; }
public Type Type { get; }
public VariableInfo(int index,Type type,string name) =>
(Index,Type,Name) = (index,type,name);
public override bool Equals(object obj) =>
Equals(obj as VariableInfo);
public bool Equals(VariableInfo info) =>
info != null &&
Index.Equals(info.Index) &&
Name.Equals(info.Name) &&
Type.Equals(info.Type);
public override int GetHashCode()
{
unchecked
{
var hash = 17;
hash = 23 * hash + Index.GetHashCode();
hash = 23 * hash + Name.GetHashCode();
hash = 23 * hash + Type.GetHashCode();
return hash;
}
}
public override string ToString() =>
$\"Index {Index},Type {Type},Name {Name}\";
}
然后让我们将“ 6”接口定义如下:
public interface ILocalsReader
{
VariableInfo[] Read(MethodBase info);
}
现在可以像这样用Microsoft.Samples.Debugging.CorApi
实现它:
public class MicrosoftDebuggingReader : ILocalsReader
{
public VariableInfo[] Read(MethodBase info)
{
var il = info.GetMethodBody().LocalVariables.ToArray();
return SymbolAccess
.GetReaderForFile(info.DeclaringType.Assembly.Location)
.GetMethod(new SymbolToken(info.MetadataToken))
.RootScope
.GetInnerScopesRecursive()
.SelectMany(scope => scope.GetLocals())
.Select(local =>
new VariableInfo(local.AddressField1,il[local.AddressField1].LocalType,local.Name))
.ToArray();
}
}
其中“ 10”是扩展方法:
internal static class SymbolScopeExtensions
{
public static IEnumerable<ISymbolScope> GetInnerScopesRecursive(this ISymbolScope scope)
{
yield return scope;
foreach (var innerScope in scope.GetChildren()
.SelectMany(innerScope => innerScope.GetInnerScopesRecursive()))
yield return innerScope;
}
}
记住要反对ѭ12build。
另一个选择是使用Mono.Cecil
像这样:
public class MonoCecilReader : ILocalsReader
{
public VariableInfo[] Read(MethodBase info)
{
var method = info.GetMethodDefinition();
method.Module.ReadSymbols();
var pdb = Path.ChangeExtension(info.DeclaringType.Assembly.Location,\"pdb\");
new PdbReaderProvider().GetSymbolReader(method.Module,pdb)
.Read(method);
var il = info.GetMethodBody().LocalVariables;
return Read(method,il);
}
public VariableInfo[] Read(MethodDefinition method,IList<LocalVariableInfo> il)
{
return method
.DebugInformation
.Scope
.GetInnerScopesRecursive()
.SelectMany(scope => scope.Variables)
.Select(local =>
new VariableInfo(local.Index,il[local.Index].LocalType,local.Name))
.ToArray();
}
}
其中“ 15”是扩展方法:
public static class MethodDefinitionExtensions
{
public static MethodDefinition GetMethodDefinition(this MethodBase info) =>
AssemblyDefinition
.ReadAssembly(info.DeclaringType.Assembly.Location)
.Modules
.SelectMany(module => module.GetTypes())
.Single(type => type.FullNameMatches(info.DeclaringType))
.Methods
.FirstOrDefault(method =>
method.Name.Equals(info.Name) &&
method.ReturnType.FullName.Equals(info.GetReturnType().FullName) &&
method.Parameters.Select(parameter => parameter.ParameterType.FullName)
.SequenceEqual(info.GetParameters().Select(parameter => parameter.ParameterType.FullName)));
}
GetReturnType
是扩展方法:
public static class MethodBaseExtensions
{
public static Type GetReturnType(this MethodBase method)
{
if (method is MethodInfo info)
return info.ReturnType;
if (method is ConstructorInfo ctor)
return typeof(void);
throw new ArgumentException($\"Argument {nameof(method)} has unsupported type {method.GetType()}.\");
}
}
FullNameMatches
是扩展方法:
internal static class TypeDefinitionExtensions
{
public static bool FullNameMatches(this TypeDefinition typeDefinition,Type type) =>
typeDefinition.FullName.Replace(\"/\",\"\").Equals(type.FullName.Replace(\"+\",\"\"));
}
GetInnerScopesRecursive
是扩展方法:
internal static class ScopeDebugInformationExtensions
{
public static IEnumerable<ScopeDebugInformation> GetInnerScopesRecursive(this ScopeDebugInformation scope)
{
yield return scope;
foreach (var innerScope in scope.Scopes
.SelectMany(innerScope => innerScope.GetInnerScopesRecursive()))
yield return innerScope;
}
}
用法:
class Program
{
static void Main(string[] args)
{
var info = new Action<string>(Foo).GetMethodInfo();
Console.WriteLine(\"\\tMicrosoft.Samples.Debugging.CorSymbolStore\");
foreach (var v in new MicrosoftDebuggingReader().Read(info))
Console.WriteLine(v);
Console.WriteLine(\"\\tMono.Cecil\");
foreach (var v in new MonoCecilReader().Read(info))
Console.WriteLine(v);
}
public static void Foo(string s)
{
for (int i; ;)
for (double j; ;)
for (bool k; ;)
for (object m = 0; ;)
for (DateTime n; ;) { }
}
}
给出:
Microsoft.Samples.Debugging.CorSymbolStore
Index 0,Type System.Int32,Name i
Index 1,Type System.Double,Name j
Index 2,Type System.Boolean,Name k
Index 3,Type System.Object,Name m
Index 4,Type System.DateTime,Name n
Mono.Cecil
Index 0,Name n
注意:
Microsoft.Samples.Debugging.CorApi
下载量约9000,最新更新于2011年10月10日
Mono.Cecil
共有〜3415k下载,最新提交时间为05.08.2019
, 查看Codeplex上的CCI项目。它具有一个PDBReader项目。