Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How to load / unload DLL

1,870 views
Skip to first unread message

l0b0

unread,
Sep 20, 2007, 9:41:17 AM9/20/07
to
Hi all,

I'm creating a custom DLL, but using
[System.Reflection.Assembly]::LoadFile("filename.dll")
results in a lock on the DLL file which is not released until I quit
PowerShell. This is a bit unfortunate, so I've had a look around (for
hours) trying to find a way to be able to unlock the file once the
script is done using it.

I've found three promising C# articles (none for PowerShell) on how to
do it. Here are the methods and results:
Sandboxing via application domains - <http://nickgrattan.wordpress.com/
2007/07/13/sandboxing-an-assembly/>.
Seems fine until I get to the step where remoteWorker is created. I've
got the following code, and I've no idea how to convert it to the
right type as in C#:
$instance = $domain.CreateInstanceFromAndUnwrap("${tempDir}
\CERNSharepointAdmin.dll", 'Admin', $false, 0, $null, $null, $null,
$null, $null)

Also,
gm -i $instance
does not list the methods which should be available in this object.

Custom application domains - <http://dotnetslackers.com/CSharp/
re-56561_App_Domains_and_dynamic_loading_the_lost_columns.aspx>.
Quite vague, and also relies on type casting the object from
CreateInstanceFromAndUnwrap.

Shadow copying - <http://bartdesmet.net/blogs/bart/archive/
2006/07/29/4146.aspx>.
I got stuck at
$assembly = $domain.Load('CERNSharepointAdmin', $null)
, where I invariably get one of the error messages "The system cannot
find the file specified" (when running as above) or "The given
assembly name or codebase was invalid" (when running with the absolute
file path instead of the file prefix).

If it's any help, the source file starts like this:
<%@ WebService Language="C#"
Class="CERN.SharePoint.Administration.Admin" %>

Have you successfully created code to load and unload DLLs in
PowerShell? Any help would be greatly appreciated!

Jeff

unread,
Sep 20, 2007, 10:19:41 AM9/20/07
to

Unfortunately, you are running into a limitation of the .NET
framework. Once an assembly is loaded into an AppDomain, it can't be
unloaded. If you really need to unload an assembly, you will need to
create another AppDomain, load the assembly into it, do what you need
to do, and then unload the AppDomain. The MSDN documentation on the
AppDomain can get you started:

http://msdn2.microsoft.com/en-us/library/system.appdomain.aspx

Good luck.

Jeff

Jeff

unread,
Sep 20, 2007, 10:35:23 AM9/20/07
to

Sorry, I didn't read your post very carefully. I didn't see that you
were already looking into sandboxing using an AppDomain. I shouldn't
try to post this late in the day...

Jeff

l0b0

unread,
Sep 20, 2007, 10:38:40 AM9/20/07
to
On Sep 20, 4:19 pm, Jeff <jeff.hill...@gmail.com> wrote:

> Unfortunately, you are running into a limitation of the .NET
> framework. Once an assembly is loaded into an AppDomain, it can't be
> unloaded. If you really need to unload an assembly, you will need to
> create another AppDomain, load the assembly into it, do what you need
> to do, and then unload the AppDomain. The MSDN documentation on the
> AppDomain can get you started:
>
> http://msdn2.microsoft.com/en-us/library/system.appdomain.aspx

I'm sorry if it wasn't clear: I've tried using custom application
domains, without luck. I've not found any PS code for how to do this,
and I've been unable to transfer any of the C# examples provided to PS.

Jeff

unread,
Sep 20, 2007, 10:49:16 AM9/20/07
to

Your post was plenty clear; my initial response was based mostly on
your subject. I'm sorry if I got your hopes up. I'm interested to
see if anyone has had any success with this.

Jeff

Jeff

unread,
Sep 21, 2007, 5:07:16 AM9/21/07
to
I decided to give this a shot. I managed to come up with an example
that loads an assembly into a new AppDomain, calls a method on a class
in the assembly, and then unloads the new AppDomain. The whole
example is self-contained; it creates the assembly with a strong name,
adds it to the GAC, and, after the method is called, removes the
assembly from the GAC and deletes the assembly. Giving the assembly a
strong name and putting it in the GAC seemed to be the key to getting
it to work for me.

## Example Code ##
$assemblyDirectory = ( Get-Location )
$sdkPath = "C:\Program Files\Microsoft.NET\SDK\v2.0\Bin"
$assemblyPath = "$assemblyDirectory\DynamicAssembly.dll"

# create the test assembly if it doesn't exist
if ( !( Test-Path $assemblyPath ) )
{
# create a key/pair for the assembly's strong name
& "$sdkPath\sn.exe" -q -k "$assemblyDirectory\keyPair.snk"

$cSharpCodeProvider = `
New-Object Microsoft.CSharp.CSharpCodeProvider

$parameters = `
New-Object System.CodeDom.Compiler.CompilerParameters
$parameters.GenerateInMemory = $true
$parameters.GenerateExecutable = $false
$parameters.OutputAssembly = $assemblyPath

# create a strongly-named assembly
$cSharpCodeProvider.CompileAssemblyFromSource($parameters, @"
using System;
using System.Reflection;

[assembly: AssemblyKeyFile("keyPair.snk")]

namespace DynamicNamespace
{
public class DynamicClass : MarshalByRefObject
{
public void Speak()
{
Console.WriteLine( "I'm Dynamic!" );
}
}
}
"@ ) | Out-Null

# put the assembly into the GAC
& "$sdkPath\gacutil.exe" /silent /i $assemblyPath
}

if ( Test-Path $assemblyPath )
{
# create a new AppDomain
$appDomain = `
[System.AppDomain]::CreateDomain("DynamicAppDomain")

# define an AssemblyName
$assemblyName = New-Object System.Reflection.AssemblyName
$assemblyName.Name = "DynamicAssembly"
$assemblyName.CodeBase = $assemblyPath

# load the assembly into the new AppDomain
$assembly = $appDomain.Load( $assemblyName )

# create an instance of our class
$dynamicClass = $appDomain.CreateInstanceFromAndUnwrap( `
$assemblyPath, "DynamicNamespace.DynamicClass" )

# call a method
$dynamicClass.Speak()

# unload the new AppDomain
[System.AppDomain]::Unload( $appDomain )

# remove the assembly from the GAC
& "$sdkPath\gacutil.exe" /silent /u "DynamicAssembly"

# clean up
Remove-Item $assemblyPath
Remove-Item "$assemblyDirectory\keyPair.snk"
}
## Example Code ##

I hope this helps. I didn't have any trouble calling my method after
calling AppDomain.CreateInstanceFromAndUnwrap like you described
above. If you are still having trouble seeing the methods you need,
you might try something like this, once you have loaded the assembly:

foreach ($type in $assembly.GetTypes())
{
if ( $type.Name -eq "DesiredClassName" )
{
# cast to the type you want
$myClass = $appDomain.CreateInstanceFromAndUnwrap( `
$assemblyPath, "FullDesiredClassName" ) -as $type

# call your methods...
}
}

Good luck.

Jeff

Message has been deleted
Message has been deleted

Jeff

unread,
Sep 23, 2007, 9:51:36 PM9/23/07
to
On Sep 20, 8:41 pm, l0b0 <victor.engm...@gmail.com> wrote:

> Have you successfully created code to load and unload DLLs in
> PowerShell? Any help would be greatly appreciated!

Did you have any luck with this? Google Groups says there are 8
messages on this topic, and that you are the last author, but I only
see 6 messages with my attempt at the problem as the last one. I am
curious about what you ended up doing.

Thanks.
Jeff

l0b0

unread,
Sep 24, 2007, 5:10:49 AM9/24/07
to

I just reverted to using the single line approach. Ugly, but using 75
lines of code with embedded C# is a bit too much to maintain.

PS: I tried replying on this twice; both times Google Groups said it'd
been saved, but it never showed up. Hope it works now.

--
Victor Engmark

Jeff

unread,
Sep 24, 2007, 6:33:52 AM9/24/07
to

Thanks for the reply, Victor. By "single line approach" do you mean
just loading the assembly into the current AppDomain? The embedded C#
in my example is only there so the example doesn't have any external
dependencies. Compiling C# at runtime is not necessary to load an
assembly into a new AppDomain. Once your assembly is given a strong
name and put into the GAC, the only code you need is these 19 lines,
which could be only 7 lines without comments and whitespace:

# create a new AppDomain

$appDomain = [System.AppDomain]::CreateDomain("NewAppDomain")

# define an AssemblyName
$assemblyName = New-Object System.Reflection.AssemblyName

$assemblyName.CodeBase = $assemblyPath

# load the assembly into the new AppDomain
$assembly = $appDomain.Load( $assemblyName )

# create an instance of your class
$classInstance = $appDomain.CreateInstanceFromAndUnwrap( `
$assemblyPath, "YourNamespace.YourClass" )

# call method(s)
$classInstance.YourMethod()

# unload the new AppDomain
[System.AppDomain]::Unload( $appDomain )


Whether you can use it or not, I certainly learned something new with
this. Good luck with everything.

Thank you.
Jeff

0 new messages