Adrian
Adrian

Reputation: 10911

How can I query a .pdb file in PowerShell?

From Querying the .Pdb File I can see that there is a COM interface to access the contents of a .pdb file. Now, how would I access that interface from PowerShell? I know I need to use New-Object -ComObject ... but I don't know what I need to specify in the ... section.

Upvotes: 4

Views: 1450

Answers (2)

Leon Bouquiet
Leon Bouquiet

Reputation: 4372

The problem with DIA2 interfaces is that it doesn't provide late-binding support that would make it easily usable from scripting languages like Powershell.
In COM terms, it doesn't provide dual interfaces that derive from IDispatch, only interfaces that derive from IUnknown. Without IDispatch, it is impossible for the caller to figure out at run time which methods are available from any given object.

Step 1: Generating a Type Library

In the COM world, you would use a Type Library (.tlb file) to communicate this metadata, and although the DIA SDK doesn't provide such a type library out of the box, you can generate it from the dia2.idl file that is part of the DIA SDK.

This step requires that you have the MIDL compiler available, which is installed as a part of Visual Studio 2015 if you've installed the Visual C++ common tools (under "Programming Languages/Visual C++/Common Tools for Visual C++ 2015" in the installer menu).

Open an elevated (i.e. with Run as adminstrator) Visual Studio Command Prompt and navigate to the DIA SDK:

cd C:\Program Files (x86)\Microsoft Visual Studio 14.0\DIA SDK

From here, run the Microsoft IDL compiler on the idl\dia2.idl file:

midl idl\dia2.idl /tlb dia2.tlb /I .\include

This will generate the file dia2.tlb, which contains the coclass and interface metadata.

Step 2: Generating a COM class wrapper

To be able to use this metadata from .NET, microsoft provides a Type Library importer tool that generates managed wrappers based on a .tlb file. For more information, see https://msdn.microsoft.com/en-us/library/aa645736(VS.71).aspx. Invoke it from the same directory as follows:

tlbimp dia2.tlb

It should respond with:

TlbImp : Type library imported to Dia2Lib.dll

In other words, you now have the .NET type definitions for the DIA SDK. Check it out with ILSpy if you like.

Step 3: Use these types from Powershell.

Using these types is now a breeze. First load in the wrapper dll (assuming it is located in the current working directory):

[void][System.Reflection.Assembly]::LoadFile("$pwd\Dia2Lib.dll")

Now, simply instantiate one of the CoClasses and start using them:

$ds = new-object Dia2Lib.DiaSourceClass
$ds.lastError

Edit: I'm having trouble getting a useful result when calling openSession from Powershell. That is, Powershell knows that it is a IDiaSession object

$sessionObj = $null
$ds.openSession([ref]$sessionObj)
$sessionObj -is [Dia2Lib.IDiaSession]
True

But somehow doesn't provide access to the members this interface exposes:

$sessionObj | Get-Member

TypeName: System.__ComObject

Name                      MemberType Definition
----                      ---------- ----------
CreateObjRef              Method     System.Runtime.Remoting.ObjRef CreateObjRef(type requestedType)
Equals                    Method     bool Equals(System.Object obj)
GetHashCode               Method     int GetHashCode()
GetLifetimeService        Method     System.Object GetLifetimeService()
GetType                   Method     type GetType()
InitializeLifetimeService Method     System.Object InitializeLifetimeService()
ToString                  Method     string ToString()

Edit 2: Apparently, as x0n says here "You can't make this work. PowerShell uses a transparent "com adapter" layer that prevents this from working, but enables late binding in script. For the majority of cases this a Good Thing, but not in yours."

Since the problem lies within Powershell, not .NET in general, you can still use C# code from within Powershell to access the SDK:

# Declare a C# helper class inline, and make it available in the current Powershell session:
$diaSessionHelperClassDef = @"
    using Dia2Lib;

    public class DiaSessionHelper
    {
        public IDiaSession Session { get; private set; }

        public DiaSessionHelper(IDiaDataSource dataSource)
        {
            IDiaSession result = null;
            dataSource.openSession(out result);
            Session = result;
        }

        public string GlobalScopeName
        {
            get{ return Session.globalScope.name; }
        }
    }

"@

Add-Type -TypeDefinition $diaSessionHelperClassDef -ReferencedAssemblies "$pwd\Dia2Lib.dll"

# Use this class to access the SDK's functionality:
$diaHelper = new-object DiaSessionHelper($ds)
$diaHelper.GlobalScopeName
Acme

Upvotes: 3

Burt_Harris
Burt_Harris

Reputation: 6874

From the looks of the documentation, the Debug Interface Access SDK is intended to be used from C++, not .NET languages. For this reason I think you'll have a very hard time using it from PowerShell.

Strictly speaking, what you need to know to use New-Object -COM is a PROGID that is equivalent to CLSID_DiaSource. Unfortunately some COM classes don't register a PROGID and provide their binding metadata in C/C++ specific form (".h" and ".lib" files, rather than in language-independent form like ITypeLib, ITypeComp or even IDispatch.) As a result, getting past the New-Object hurdle is just the beginning.

See this post for an example of the sort of COM operations (as basic as QueryInterface) that are hard to address from any .NET language. Another shows the related sort of limitations that occur even on ADSI COM objects which have built-in support in PowerShell.

If you are experienced in C++/COM development it will almost certainly save you time to write in C++ for this part of your project.

Upvotes: 1

Related Questions