//
// This code is the managed pdb reader interface for PERWAPI.
// Written by John Gough, April 2007.
// Copyright(c) 2007-2010 John Gough, and Queensland University of Technology
//
// NOTES:
// Q: Where does this code come from?
// A: At the heart of the code is some COM interop that accesses the
// facilities of mscoree.dll. Unfortunately, the type library for
// this component is incomplete, and does not cover the symbol store
// functionality. There is some MIDL, and the C++ header files
// cor.h and corsym.h. From here comes the guid values, for
// example, and critically the order of the interface methods.
// The managed interfaces, such as ISymbolReader, are defined in
// [mscorlib]System.Diagnostics.SymbolStore.
//
// This file provides managed wrappers for the unmanaged interfaces.
// These wrappers implement the managed interfaces.
// Not all methods are implemented. Those that are not will throw
// NotImplementedException.
//
// Some of the functionality currently not used by PERWAPI is left
// unimplemented, and throws a NotImplementedException if called.
//
// THANKS:
// Wayne Kelly, whose SimpleWriter, in C++ pointed the way.
// Adam Nathan, whose ".NET and COM" tells you more than you want to know
// Microsoft's Iron Python and MDBG samples answered remaining questions
//
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Diagnostics.SymbolStore;
namespace QUT.Symbols {
public class SymbolReader : ISymbolReader {
private ISymUnmanagedReader private_reader;
///
/// Constructor for SymbolReader.
/// Just creates a reference to the unmanaged reader
/// from the COM world. This Reader only implements
/// the method(s) required by PERWAPI.
///
///
public SymbolReader(string filename) {
object ppb = null;
object pUnk = null;
object pBnd = null;
IntPtr importer = IntPtr.Zero;
object retVal = null;
try {
OLE32.CoCreateInstance(ref XGuid.dispenserClassID, null, 1, ref XGuid.dispenserIID, out ppb);
OLE32.CoCreateInstance(ref XGuid.binderCLSID, null, 1, ref XGuid.binderIID, out pBnd);
// Get the metadata dispenser from mscoree.dll
Util.ComCheck(ppb != null, "Failed to create metadata dispenser");
((IMetaDataDispenserSubset)ppb).OpenScope(filename, 0, ref XGuid.importerIID, out pUnk);
Util.ComCheck(pUnk != null, "Failed to open scope");
importer = Marshal.GetComInterfaceForObject(pUnk, typeof(IMetadataImport));
((ISymUnmanagedBinder)pBnd).GetReaderForFile(importer, filename, null, out retVal);
private_reader = (ISymUnmanagedReader)retVal;
}
catch (Exception x) {
Console.WriteLine(x.Message);
}
finally {
if (importer != IntPtr.Zero)
Marshal.Release(importer);
}
}
// ============================================================================
// ========================== ISymbolReader Methods ===========================
// ============================================================================
///
/// This is the only SymbolReader method required by PERWAPI
/// at this stage.
///
/// The metadata token
/// The SymbolMethod object for the token
public ISymbolMethod GetMethod(SymbolToken tok) {
// MIDL is ...
//[PreserveSig]
//int GetMethod(
// SymbolToken methodToken,
// [MarshalAs(UnmanagedType.Interface)] out ISymUnmanagedMethod retVal);
ISymUnmanagedMethod unMeth = null;
int hr = private_reader.GetMethod(tok, out unMeth);
if (hr == OLE32.hr_E_FAIL) // could be empty method
return null;
else // any other abnormal HRESULT
Marshal.ThrowExceptionForHR(hr);
// All ok ...
return new SymbolMethod(unMeth);
}
// This one not used by PERWAPI yet.
public ISymbolMethod GetMethod(SymbolToken tok, int ver) {
ISymUnmanagedMethod unMeth = null;
int hr = private_reader.GetMethodByVersion(tok, ver, out unMeth);
if (hr == OLE32.hr_E_FAIL) // could be empty method
return null;
else // any other abnormal HRESULT
Marshal.ThrowExceptionForHR(hr);
// All ok ...
return new SymbolMethod(unMeth);
}
///
/// Gets user entry point for a reader.
/// Returns null if reader is attached to a PE-file
/// that does not have an entry point.
///
public SymbolToken UserEntryPoint {
get {
SymbolToken retVal;
int hr = private_reader.GetUserEntryPoint(out retVal);
if (hr == OLE32.hr_E_FAIL)
return new SymbolToken(0);
else
Marshal.ThrowExceptionForHR(hr);
return retVal;
}
}
#region unimplemented methods
// Summary:
// Gets a document specified by the language, vendor, and type.
//
// Parameters:
// url:
// The URL that identifies the document.
//
// documentType:
// The type of the document. You can specify this parameter as System.Guid.Empty.
//
// languageVendor:
// The identity of the vendor for the document language. You can specify this
// parameter as System.Guid.Empty.
//
// language:
// The document language. You can specify this parameter as System.Guid.Empty.
//
// Returns:
// The specified document.
public ISymbolDocument GetDocument(
string url,
Guid language,
Guid languageVendor,
Guid documentType) {
throw new NotImplementedException("No QUT.SymbolReader.GetDocument");
}
//
// Summary:
// Gets an array of all documents defined in the symbol store.
//
// Returns:
// An array of all documents defined in the symbol store.
public ISymbolDocument[] GetDocuments() {
throw new NotImplementedException("No QUT.SymbolReader.GetDocuments");
}
//
// Summary:
// Gets all global variables in the module.
//
// Returns:
// An array of all variables in the module.
public ISymbolVariable[] GetGlobalVariables() {
throw new NotImplementedException("No QUT.SymbolReader.GetGetGlobalVariables");
}
//
// Summary:
// Gets the namespaces that are defined in the global scope within the current
// symbol store.
//
// Returns:
// The namespaces defined in the global scope within the current symbol store.
public ISymbolNamespace[] GetNamespaces() {
throw new NotImplementedException("No QUT.SymbolReader.GetNamespaces");
}
//
// Summary:
// Gets an attribute value when given the attribute name.
//
// Parameters:
// name:
// The attribute name.
//
// parent:
// The metadata token for the object for which the attribute is requested.
//
// Returns:
// The value of the attribute.
public byte[] GetSymAttribute(SymbolToken parent, string name) {
throw new NotImplementedException("No QUT.SymbolReader.GetSymAttribute");
}
#endregion unimplemented methods
///
/// Gets the method that contains a specified position of the document
///
/// The document object
/// Source line number
/// Source column number
/// The chosen method
public ISymbolMethod GetMethodFromDocumentPosition(ISymbolDocument doc, int line, int column) {
ISymUnmanagedMethod unMeth = null;
private_reader.GetMethodFromDocumentPosition(
((SymbolDocument)doc).WrappedDoc, line, column, out unMeth);
return new SymbolMethod(unMeth);
}
public ISymbolVariable[] GetVariables(SymbolToken parent) {
int varNm = 0;
private_reader.GetVariables(parent, 0, out varNm, null);
ISymUnmanagedVariable[] unVars = new ISymUnmanagedVariable[varNm];
SymbolVariable[] retVal = new SymbolVariable[varNm];
private_reader.GetVariables(parent, varNm, out varNm, unVars);
for (int i = 0; i < varNm; i++)
retVal[i] = new SymbolVariable(unVars[i]);
return retVal;
}
}
// ============================ End of SymbolReader ===========================
///
/// This is the managed wrapper for the unmanaged
/// Method descriptor. This implements the interface
/// System.Diagnostics.SymbolStore.ISymbolMethod
///
public class SymbolMethod : ISymbolMethod {
private const int INVALID = -1;
private ISymUnmanagedMethod private_method;
internal SymbolMethod(ISymUnmanagedMethod unMeth) {
private_method = unMeth;
}
///
/// Gets the root scope of the method
///
public ISymbolScope RootScope { // Needed by PERWAPI
get {
ISymUnmanagedScope retVal = null;
private_method.GetRootScope(out retVal);
return new SymbolScope(retVal);
}
}
///
/// Gets the sequence point count for the method
///
public int SequencePointCount { // Needed by PERWAPI
get {
int retVal = 0;
private_method.GetSequencePointCount(out retVal);
return retVal;
}
}
///
/// Gets the symbol token for the method
///
public SymbolToken Token {
get {
SymbolToken tok;
private_method.GetToken(out tok);
return tok;
}
}
///
/// Gets the namespace for the method
///
/// The namespace object
public ISymbolNamespace GetNamespace() {
ISymUnmanagedNamespace retVal = null;
private_method.GetNamespace(out retVal);
return new SymbolNamespace(retVal);
}
///
/// Gets the parameters of the method
///
/// The method parameters
public ISymbolVariable[] GetParameters() {
int pNum = 0;
// Call GetParameters just to get pNum
private_method.GetParameters(0, out pNum, null);
ISymUnmanagedVariable[] unVars = new ISymUnmanagedVariable[pNum];
ISymbolVariable[] manVars = new ISymbolVariable[pNum];
// Now call again to get the real information
private_method.GetParameters(pNum, out pNum, unVars);
for (int i = 0; i < pNum; i++)
manVars[i] = new SymbolVariable(unVars[i]);
return manVars;
}
#region unimplemented methods
public int[] GetRanges(ISymbolDocument document, int line, int column) {
throw new NotImplementedException("No QUT.SymbolMethod.GetRanges");
}
public ISymbolScope GetScope(int offset) {
throw new NotImplementedException("No QUT.SymbolMethod.GetScope");
}
public int GetOffset(
ISymbolDocument document,
int line,
int column) {
throw new NotImplementedException("No QUT.SymbolMethod.GetOffset");
}
private void GetAndCheckLength(int[] arr, ref int num) {
if (arr != null) {
if (num == INVALID)
num = arr.Length;
else
Util.ArgCheck(num == arr.Length, "Invalid arg to GetSequencePoints");
}
}
public bool GetSourceStartEnd(ISymbolDocument[] docs, int[] lines, int[] columns) {
throw new NotImplementedException("No QUT.SymbolMethod.GetSourceStartEnd");
}
#endregion unimplemented methods
///
/// Gets the sequence points defined for this method
///
/// array of IL offsets
/// array of documents
/// start line number array
/// start column number array
/// end line number array
/// start line number array
public void GetSequencePoints( // This method needed by PERWAPI
int[] offsets,
ISymbolDocument[] documents,
int[] lines,
int[] columns,
int[] endLines,
int[] endColumns) {
int spCount = INVALID;
GetAndCheckLength(offsets, ref spCount);
GetAndCheckLength(lines, ref spCount);
GetAndCheckLength(columns, ref spCount);
GetAndCheckLength(endLines, ref spCount);
GetAndCheckLength(endColumns, ref spCount);
if (spCount == INVALID)
spCount = 0;
int dcCount = documents.Length;
ISymUnmanagedDocument[] unDocs = new ISymUnmanagedDocument[dcCount];
private_method.GetSequencePoints(
dcCount, out spCount, offsets, unDocs, lines, columns, endLines, endColumns);
for (int i = 0; i < dcCount; i++)
documents[i] = new SymbolDocument(unDocs[i]);
return;
}
}
// ============================ End of SymbolMethod ===========================
///
/// This class is a managed wrapper for the unmanaged
/// Scope descriptor. The defintions for ISymbolScope
/// come from metadata.
///
public class SymbolScope : ISymbolScope {
private ISymUnmanagedScope private_scope;
internal SymbolScope(ISymUnmanagedScope unScope) {
private_scope = unScope;
}
///
/// Returns the end offset of the wrapped scope
///
public int EndOffset {
get {
int offset;
private_scope.GetEndOffset(out offset);
return offset;
}
}
///
/// Returns the method that contains the current lexical scope
///
public ISymbolMethod Method {
get {
ISymUnmanagedMethod unMeth = null;
private_scope.GetMethod(out unMeth);
return new SymbolMethod(unMeth);
}
}
///
/// Returns the parent lexical scope of the current scope
///
public ISymbolScope Parent {
get {
ISymUnmanagedScope unScope = null;
private_scope.GetParent(out unScope);
return new SymbolScope(unScope);
}
}
///
/// Returns the start offset of the current lexical scope
///
public int StartOffset {
get {
int offset;
private_scope.GetStartOffset(out offset);
return offset;
}
}
///
/// Returns the child lexical scopes of the current lexical scope.
///
///
public ISymbolScope[] GetChildren() {
int chNum = 0;
private_scope.GetChildren(0, out chNum, null);
ISymUnmanagedScope[] unScps = new ISymUnmanagedScope[chNum];
ISymbolScope[] manScps = new ISymbolScope[chNum];
private_scope.GetChildren(chNum, out chNum, unScps);
for (int i = 0; i < chNum; i++)
manScps[i] = new SymbolScope(unScps[i]);
return manScps;
}
///
/// Gets the local variables within the current lexical scope
///
/// The local variables of the current scope
public ISymbolVariable[] GetLocals() {
int lcNum = 0;
private_scope.GetLocals(0, out lcNum, null);
ISymUnmanagedVariable[] unVars = new ISymUnmanagedVariable[lcNum];
ISymbolVariable[] manVars = new ISymbolVariable[lcNum];
private_scope.GetLocals(lcNum, out lcNum, unVars);
for (int i = 0; i < lcNum; i++)
manVars[i] = new SymbolVariable(unVars[i]);
return manVars;
}
///
/// Gets the namespaces that are used within the current scope
///
/// The namespaces that are used within the current scope
public ISymbolNamespace[] GetNamespaces() {
int nmNum = 0;
private_scope.GetNamespaces(0, out nmNum, null);
ISymUnmanagedNamespace[] unNams = new ISymUnmanagedNamespace[nmNum];
ISymbolNamespace[] manNams = new ISymbolNamespace[nmNum];
private_scope.GetNamespaces(nmNum, out nmNum, unNams);
for (int i = 0; i < nmNum; i++)
manNams[i] = new SymbolNamespace(unNams[i]);
return manNams;
}
}
// ============================ End of SymbolScope ===========================
///
/// Managed wrapper for ISymUnmanagedNamespace
///
public class SymbolNamespace : ISymbolNamespace {
private ISymUnmanagedNamespace private_namespace;
internal SymbolNamespace(ISymUnmanagedNamespace unNmsp) {
private_namespace = unNmsp;
}
// Summary:
// Gets the current namespace.
//
// Returns:
// The current namespace.
public string Name {
get {
StringBuilder bldr;
int nmLen = 0;
private_namespace.GetName(0, out nmLen, null);
bldr = new StringBuilder(nmLen);
private_namespace.GetName(nmLen, out nmLen, bldr);
return bldr.ToString();
}
}
// Summary:
// Gets the child members of the current namespace.
//
// Returns:
// The child members of the current namespace.
public ISymbolNamespace[] GetNamespaces() {
throw new NotImplementedException("No QUT.SymbolNamespace.GetNamespaces");
}
//
// Summary:
// Gets all the variables defined at global scope within the current namespace.
//
// Returns:
// The variables defined at global scope within the current namespace.
public ISymbolVariable[] GetVariables() {
throw new NotImplementedException("No QUT.SymbolNamespace.GetVariables");
}
}
// ============================ End of SymbolReader ===========================
///
/// Managed wrapper for ISymUnmanagedDocument
///
public class SymbolDocument : ISymbolDocument {
private ISymUnmanagedDocument private_document;
///
/// Constructor for SymbolDocument
///
///
internal SymbolDocument(ISymUnmanagedDocument unDoc) {
private_document = unDoc;
}
///
/// Gets the wrapped document
///
internal ISymUnmanagedDocument WrappedDoc {
get { return private_document; }
}
///
/// Returns a GUID identifying the checksum algorithm.
/// Returns Guid.Zero if there is no checksum.
///
public Guid CheckSumAlgorithmId {
get {
Guid retVal = new Guid();
private_document.GetCheckSumAlgorithmId(ref retVal);
return retVal;
}
}
///
/// Gets the document type guid
///
public Guid DocumentType {
get {
Guid retVal = new Guid();
private_document.GetDocumentType(ref retVal);
return retVal;
}
}
///
/// Value is true if document is stored in the symbol store, otherwise false.
///
public bool HasEmbeddedSource {
get {
bool retVal = false;
private_document.HasEmbeddedSource(out retVal);
return retVal;
}
}
///
/// Gets the language guid
///
public Guid Language {
get {
Guid retVal = new Guid();
private_document.GetLanguage(ref retVal);
return retVal;
}
}
///
/// Gets the language vendor guid
///
public Guid LanguageVendor {
get {
Guid retVal = new Guid();
private_document.GetLanguageVendor(ref retVal);
return retVal;
}
}
///
/// Gets the source length of the current document
///
public int SourceLength {
get {
int retVal = 0;
private_document.GetSourceLength(out retVal);
return retVal;
}
}
///
/// Gets the URL of the current document
///
public string URL {
get {
int strLen;
private_document.GetURL(0, out strLen, null);
StringBuilder retVal = new StringBuilder(strLen);
private_document.GetURL(strLen, out strLen, retVal);
return retVal.ToString();
}
}
///
/// Gets the closest line that has a sequence point
///
/// A line in the document
/// The closest line with a sequence point
public int FindClosestLine(int line) {
int retVal;
private_document.FindClosestLine(line, out retVal);
return retVal;
}
#region unimplemented methods
///
/// Gets the checksum
///
/// The checksum
public byte[] GetCheckSum() {
throw new NotImplementedException("No QUT.SymbolDocument.GetCheckSum");
}
//
// Summary:
// Gets the embedded document source for the specified range.
//
// Parameters:
// startLine:
// The starting line in the current document.
//
// endLine:
// The ending line in the current document.
//
// startColumn:
// The starting column in the current document.
//
// endColumn:
// The ending column in the current document.
//
// Returns:
// The document source for the specified range.
public byte[] GetSourceRange(int startLine, int startColumn, int endLine, int endColumn) {
throw new NotImplementedException("No QUT.SymbolDocument.GetSourceRange");
}
#endregion unimplemented methods
}
// ========================== End of SymbolDocument ===========================
///
/// Managed wrapper for ISymUnmanagedVariable
///
public class SymbolVariable : ISymbolVariable {
private ISymUnmanagedVariable private_variable;
internal SymbolVariable(ISymUnmanagedVariable unVar) {
private_variable = unVar;
}
///
/// Gets the first address of the variable
///
public int AddressField1 {
get {
int retVal;
private_variable.GetAddressField1(out retVal);
return retVal;
}
}
///
/// Gets the second address of the variable
///
public int AddressField2 {
get {
int retVal;
private_variable.GetAddressField2(out retVal);
return retVal;
}
}
///
/// Gets the third address of the variable
///
public int AddressField3 {
get {
int retVal;
private_variable.GetAddressField3(out retVal);
return retVal;
}
}
///
/// Gets the type of the address. The result is a
/// System.Diagnostics.SymbolStore.SymAddressKind
/// enumeration value.
///
public SymAddressKind AddressKind {
get {
int retVal;
private_variable.GetAddressKind(out retVal);
return (SymAddressKind)retVal;
}
}
///
/// Gets the variable attributes
///
public object Attributes {
get {
int retVal;
private_variable.GetAttributes(out retVal);
return retVal;
}
}
///
/// Gets the end offset of the variable
///
public int EndOffset {
get {
int retVal;
private_variable.GetEndOffset(out retVal);
return retVal;
}
}
///
/// Gets the name of the variable
///
public string Name {
get {
StringBuilder bldr;
int nmLen = 0;
private_variable.GetName(0, out nmLen, null);
bldr = new StringBuilder(nmLen);
private_variable.GetName(nmLen, out nmLen, bldr);
return bldr.ToString();
}
}
///
/// Gets the start offset of the variable
///
public int StartOffset {
get {
int retVal;
private_variable.GetStartOffset(out retVal);
return retVal;
}
}
///
/// Gets the variable signature
///
/// The signature blob
public byte[] GetSignature() {
int sgLen;
private_variable.GetSignature(0, out sgLen, null);
byte[] retVal = new byte[sgLen];
private_variable.GetSignature(sgLen, out sgLen, retVal);
return retVal;
}
}
// ========================== End of SymbolVariable ===========================
}