2

I want to enumerate all the AppDomains in the current process from PowerShell. The process happens to be Visual Studio, which is hosting StudioShell. To do that I need to instantiate CorRuntimHost, which is part of mscoree.tlb, so I can adapt this C# code..

I tried to get the proper name of CorRunTimeHost and pass it to New-Object -COMObject "objectName". Based on this forum posting, I searched the registry and I think the correct name is CLRMetaData.CorRuntimeHost. However, while New-Object -ComObject 'CLRMetaData.CorRuntimeHost' -Strict does return an object, it only exposes the methods intrinsic to a COM object.

Based on this stackoverflow question I tried [Activator]::CreateInstance(). However, the following two statements give me the same problem as New-Object, namely I can't call the ICorRuntimeHost::EnumDomains() method.

$corRuntimeHost = [Activator]::CreateInstance([Type]::GetTypeFromProgID('CLRMetaData.CorRuntimeHost'));
$enumerator = $null;
$corRuntimeHost.EnumDomains([ref]$enumerator);

Method invocation failed because [System.__ComObject] doesn't contain a method named 'EnumDomains'.
At line:1 char:1
+ $corRuntimeHost.EnumDomains([ref]$enumerator)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : MethodNotFound
Community
  • 1
  • 1
Justin Dearing
  • 14,270
  • 22
  • 88
  • 161
  • You could embed the C# code directly in your powershell script, like described here in step 4: http://blogs.msdn.com/b/mattbie/archive/2010/02/23/how-to-call-net-and-win32-methods-from-powershell-and-your-troubleshooting-packs.aspx – Simon Mourier May 13 '13 at 08:17
  • @SimonMourier That was what I was attempting for step 2. However, [Add-Type](http://technet.microsoft.com/en-us/library/hh849914.aspx) won't compile the code I need without the COM equivalent of `[Reflection.Assembly]::LoadFromFile()`. – Justin Dearing May 13 '13 at 11:32
  • You can't load a TLB using LoadFromFile, you'll have to convert the C# to "pure" C# declaration instead of adding a .TLB as a reference. – Simon Mourier May 13 '13 at 11:51

1 Answers1

2

To get it working in PowerShell 3.0 I ended up having to use an AssemblyBuilder. Below is the working code:

The problem seems to be that there is no public constructor for mscoree.CorRuntimeHostClass in .NET 4.0 but there is in 3.5.

I later tested this on a Windows 7 VM with powershell 2.0 and now this code will work in PowerShell 2.0 and 3.0.

$tlbName = Split-Path -Parent ([AppDomain]::CurrentDomain.GetAssemblies() | Where { $_.Location -Match '\\mscorlib.dll$' }).Location 
$tlbName = Join-Path $tlbName 'mscoree.tlb';

$csharpString = @"
//adapted from here http://blog.semanticsworks.com/2008/04/enumerating-appdomains.html
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;

public class ListProcessAppDomains
{

    [DllImport( `"oleaut32.dll`", CharSet = CharSet.Unicode, PreserveSig = false )]
    private static extern void LoadTypeLibEx
        (String strTypeLibName, RegKind regKind, 
         [MarshalAs( UnmanagedType.Interface )] out Object typeLib);

    private enum RegKind
    {
        Default = 0,
        Register = 1,
        None = 2
    }

    private class ConversionEventHandler : ITypeLibImporterNotifySink
    {
        public void ReportEvent( ImporterEventKind eventKind, int eventCode, string eventMsg )
        {
            Console.Error.WriteLine("Kind: {0} Code: {1} Message");
        }

        public Assembly ResolveRef( object typeLib )
        {
            string stackTrace = System.Environment.StackTrace;
            Console.WriteLine("ResolveRef ({0})", typeLib);
            Console.WriteLine(stackTrace);
          return null; 
        }    
    }

    public static AssemblyBuilder LoadMsCoreeDll( ref Object typeLib ) {
        ConversionEventHandler eventHandler = new ConversionEventHandler();
        string assemblyName = "PoshComWrapper.dll";
        LoadTypeLibEx( @"$($tlbName)", RegKind.None, out typeLib ); 
        TypeLibConverter typeLibConverter = new TypeLibConverter();
        return typeLibConverter.ConvertTypeLibToAssembly( typeLib, assemblyName, 0, eventHandler, null, null, null, null );
    }
}
"@

# So we can run this scipt multiple times
try { [ListProcessAppDomains] } catch {  Add-Type -TypeDefinition $csharpString }

function Get-AppDomain {
    $typeLib = $null;
    $assemblyBuilder = [ListProcessAppDomains]::LoadMsCoreeDll([ref] $typeLib)
    $corRuntimeHostClass = $assemblyBuilder.CreateInstance('PoshComWrapper.CorRuntimeHostClass')
    $enumHandle = [IntPtr]::Zero
    $corRuntimeHostClass.EnumDomains([ref] $enumHandle);
    $appDomain = $null;
    do
    {
        $corRuntimeHostClass.NextDomain($enumHandle, [ref] $appDomain);
        if ($appDomain -ne $null -and $appDomain.GetType() -eq [AppDomain]) { $appDomain; }
    } while ($appDomain -ne $null)
}

Get-AppDomain 
Justin Dearing
  • 14,270
  • 22
  • 88
  • 161