snakealpha
12/22/2017 - 9:44 AM

A Pdb file explorer

A code snippet used to explorer symbols of a pdb file.

using System;
using System.Collections.Generic;
using System.Linq;
using Dia2Lib;

namespace PDBParser
{
    class PDBExplorer
    {
        private Stack<IDiaSymbol> _accessStack = new Stack<IDiaSymbol>();

        public IDiaSymbol RootSymbol { get; private set; }

        public IDiaSession Session { get; private set; }

        public string PdbPath { get; private set; }

        public IDiaSymbol CurrentSymbol { get; private set; }

        public PDBExplorer(string path)
        {
            PdbPath = path;
        }

        public int Deepth
        {
            get => _accessStack.Count;
        }

        public void Open()
        {
            var proxy = new DiaSourceClass();
            proxy.loadDataFromPdb(PdbPath);
            proxy.openSession(out var session);
            RootSymbol = session.globalScope;
            Session = session;
            CurrentSymbol = RootSymbol;
        }

        public IDiaEnumSymbols List(SymTagEnum type, string name, bool adapt = true)
        {
            try
            {
                if (adapt)
                {
                    CurrentSymbol.findChildrenEx(type, name, name == null ? 0u : 8u, out var symbols);
                    return symbols;
                }
                else
                {
                    CurrentSymbol.findChildrenEx(type, name, 0u, out var symbols);
                    return symbols;
                }
            }
            catch (NotImplementedException)
            {
                return null;
            }
        }

        public IDiaSymbol ReturnLastLayer()
        {
            if (_accessStack.Count > 0)
            {
                CurrentSymbol = _accessStack.Pop();
            }

            return CurrentSymbol;
        }

        public bool GetInSymbol(SymTagEnum type, string name)
        {
            var symbols = List(type, name, false);

            if (symbols is null)
            {
                return false;
            }

            foreach (IDiaSymbol diaSymbol in symbols)
            {
                if ((SymTagEnum)diaSymbol.symTag == type && diaSymbol.name == name)
                {
                    _accessStack.Push(CurrentSymbol);
                    CurrentSymbol = diaSymbol;
                    return true;
                }
            }

            return false;
        }

        public bool GetInSymbol(uint id)
        {
            try
            {
                Session.symbolById(id, out var symbol);

                if (symbol is null)
                    return false;
                else
                {
                    _accessStack.Push(CurrentSymbol);
                    CurrentSymbol = symbol;
                    return true;
                }
            }
            catch (Exception)
            {
                return false;
            }
        }

        public IList<IDiaSymbol> GetSymbolStack()
        {
            Stack<IDiaSymbol> temp = new Stack<IDiaSymbol>();
            List<IDiaSymbol> result = new List<IDiaSymbol>();
            while (_accessStack.Count > 0)
            {
                temp.Push(_accessStack.Pop());
            }
            while (temp.Count > 0)
            {
                result.Add(temp.Peek());
                _accessStack.Push(temp.Pop());
            }

            result.Add(CurrentSymbol);

            return result;
        }
    }

    class PdbMonitor
    {
        private PDBExplorer explorer;

        public PdbMonitor(PDBExplorer explorer)
        {
            this.explorer = explorer;
        }

        public void StartLoop()
        {
            explorer.Open();
            DisplayItem(explorer.RootSymbol);

            foreach (var b in loopBody())
            {
                if (b) return;
            }
        }

        private IEnumerable<bool> loopBody()
        {
            while (true)
            {
                yield return ProcessCommand(ReadCommand());
            }
        }

        public void DisplayItem(IDiaSymbol symbol)
        {
            Console.WriteLine(FormatSymbol(symbol));
        }

        public string FormatSymbol(IDiaSymbol symbol)
        {
            return $"[{(SymTagEnum)symbol.symTag}:{symbol.symIndexId}]    {symbol.name}";
        }

        public IList<string> ReadCommand()
        {
            Console.ForegroundColor = ConsoleColor.DarkYellow;
            Console.Write('>');
            Console.ForegroundColor = ConsoleColor.DarkGreen;
            var commandContent = Console.ReadLine();
            Console.ForegroundColor = ConsoleColor.Gray;
            return (from subString in commandContent.Split(' ') select subString.Trim()).ToArray();
        }

        private bool ProcessCommand(IList<string> command)
        {
            switch (command[0])
            {
                case "current":
                case "ps":
                {
                    var symbols = explorer.GetSymbolStack();
                    for (var i = 0; i != symbols.Count; i++)
                    {
                        for(var j = 0; j != i; j++)
                            Console.Write("----");
                        DisplayItem(symbols[i]);
                    }

                    break;
                }
                case "upward":
                case "back":
                {
                    DisplayItem(explorer.ReturnLastLayer());

                    break;
                }
                case "sc":
                case "cd":
                    {
                    if (command.Count < 3)
                    {
                        if (command.Count == 2 && command[1] == "..")
                        {
                            ProcessCommand(new []{"back"});
                            break;
                        }

                        Console.WriteLine("Must pass type and name of symbol you want to enter.");
                        break;
                    }

                    if (!Enum.TryParse(command[1], true, out SymTagEnum tagEnum))
                    {
                        if (!Enum.TryParse("SymTag" + command[1], true, out tagEnum))
                            tagEnum = SymTagEnum.SymTagNull;
                    }

                    var succeed = explorer.GetInSymbol(tagEnum, command[2]);
                    if(succeed)
                        DisplayItem(explorer.CurrentSymbol);
                    else
                        Console.WriteLine("No symbol found.");

                    break;
                }
                case "sci":
                case "cdi":
                {
                    if (command.Count < 2)
                    {
                        Console.WriteLine("Must pass index of symbol you want to enter.");
                        break;
                    }

                    if (uint.TryParse(command[1], out uint id))
                    {
                        var succeed = explorer.GetInSymbol(id);
                        if (succeed)
                            DisplayItem(explorer.CurrentSymbol);
                        else
                            Console.WriteLine("No symbol found.");
                        }
                    else
                    {
                        Console.WriteLine("No symbol found.");
                    }

                    break;
                }
                case "list":
                case "ls":
                {
                    var tagEnum = SymTagEnum.SymTagNull;
                    if (command.Count > 1)
                    {
                        if (!Enum.TryParse(command[1], true, out tagEnum))
                        {
                            if (!Enum.TryParse("SymTag" + command[1], true, out tagEnum))
                                tagEnum = SymTagEnum.SymTagNull;
                        }
                    }

                    string name = null;
                    if (command.Count > 2)
                        name = command[2].ToLower();

                    var symbols = explorer.List(tagEnum, name);
                    if (symbols is null)
                    {
                        Console.WriteLine("No Children Node Symbol Found.");
                    }
                    else
                    {
                        int num = 0;
                        foreach (IDiaSymbol symbol in symbols)
                        {
                            DisplayItem(symbol);
                            num++;
                        }
                        Console.WriteLine(num > 0 ? $"{num} Symbols Found." : "No Children Node Symbol Found.");
                    }

                    break;
                }
                case "quit":
                {
                    return true;
                }
                default:
                {
                    Console.WriteLine("Cannot parse command.");

                    break;
                }
            }

            return false;
        }
    }
}