Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

powershell 2 new-object "Cannot find type..." exception when instantiate .net class implemented interface from external library

I have found an issue that is quite easy to reproduce. Please advise if there is any work around for this?

There are two .Net libraries libraryA.dll and libraryB.dll. And each library has one interface InterfaceA and InterfaceB. ClassAA implements InterfaceA and lives in libraryA. ClassAB implements InterfaceB and lives in the same libraryA. The same way ClassBB - LibraryB - InterfaceB; ClassBA - LibraryB - InterfaceA

New-Object works correctly when ClassAA and ClassBB are instantiated but not ClassAB or ClassBA. They constantly fail to instantiate.

Here you are powershell code

[System.Reflection.Assembly]::LoadFile(‘c:\LibraryA.dll’)
[System.Reflection.Assembly]::LoadFile(‘c:\LibraryB.dll’)

$obj1 = new-object -typeName ClassAA   (IT WORKS)
$obj2 = new-object -typeName ClassBB   (IT WORKS)
$obj3 = new-object -typeName ClassAB   (EXCEPTION THROWN)
$obj4 = new-object -typeName ClassBA   (EXCEPTION THROWN)

Many Thanks,

Andrey

like image 915
Andrey Avatar asked Jan 21 '23 02:01

Andrey


2 Answers

Instead of ::LoadFile, use:

[System.Reflection.Assembly]::LoadFrom(‘c:\LibraryA.dll’)
[System.Reflection.Assembly]::LoadFrom(‘c:\LibraryB.dll’)

When you use ::LoadFrom the assembly will be loaded into a context with the directory it was loaded from, referenced assemblies in that same directory will automatically be resolved. ::LoadFile is meant for loading assemblies that share an identity but are in different directories and does not keep any load context, so referenced assemblies are not resolved.

like image 111
joshperry Avatar answered Jan 25 '23 22:01

joshperry


The answer to this question solves your issue: How can I get PowerShell Added-Types to use Added Types

The key is to use the AppDomain.CurrentDomain.AssemblyResolve event.

You can, for example, add the AssemblyResolver class (from the post above) to your LibraryA and then use [Utils.AssemblyResolver]::AddAssemblyLocation("LibraryB.dll") to pull in the LibraryB reference when needed.

Or, just to prove the point:

[System.AppDomain]::CurrentDomain.add_assemblyResolve({
    If ($args[1].Name.StartsWith("LibraryB"))
    {
        Return [System.Reflection.Assembly]::LoadFile("C:\LibraryB.dll")
    }
    Else
    {
        Return $Null
    }
})

Note that you have a circular dependency in your example above: LibraryA is referencing LibraryB and LibraryB references LibraryA. You will probably want to fix that first - assuming you have the same in your real project...

like image 44
Johan Broberg Avatar answered Jan 25 '23 22:01

Johan Broberg