How to load / unload DLL
Jeff posted on Thursday, September 20, 2007 10:19 AM
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 |
|
How to load / unload DLL
Jeff posted on Thursday, September 20, 2007 10:35 AM
Sorry, I did not read your post very carefully. I did not see that you
were already looking into sandboxing using an AppDomain. I should not
try to post this late in the day...
Jeff |
|
How to load / unload DLL
l0b0 posted on Thursday, September 20, 2007 10:38 AM
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. |
|
How to load / unload DLL
Jeff posted on Thursday, September 20, 2007 10:49 AM
Your post was plenty clear; my initial response was based mostly on
your subject. I am sorry if I got your hopes up. I am interested to
see if anyone has had any success with this.
Jeff |
|
I decided to give this a shot.
Jeff posted on Friday, September 21, 2007 5:07 AM
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.
$assemblyDirectory = ( Get-Location )
$sdkPath = "C:\Program Files\Microsoft.NET\SDK\v2.0\Bin"
$assemblyPath = "$assemblyDirectory\DynamicAssembly.dll"
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!" );
}
}
}
# 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"
}
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 |
|
How to load / unload DLL
l0b0 posted on Friday, September 21, 2007 8:16 AM
Whoah, thanks for the work! Unfortunately, using dynamic C# is getting
a bit complex, so I think I'll go for the short but bad code.
Do you know if it's at all possible to do this kind of thing in native
PowerShell?
--
Victor Engmark |
|
How to load / unload DLL
l0b0 posted on Friday, September 21, 2007 8:54 AM
Whoah, thanks! Unfortunately, it looks like the solution is more
complex than I'd like to maintain (especially with the embedded C#
code), so I'll stick with the single line approach for now. Too bad
error handling and proper object orientation is so shoddily handled by
PowerShell.
--
Victor Engmark |
|
How to load / unload DLL
Jeff posted on Sunday, September 23, 2007 9:51 PM
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 |
|
How to load / unload DLL
l0b0 posted on Monday, September 24, 2007 5:10 AM
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 |
|
How to load / unload DLL
Jeff posted on Monday, September 24, 2007 6:33 AM
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:
$appDomain = [System.AppDomain]::CreateDomain("NewAppDomain")
$assemblyName = New-Object System.Reflection.AssemblyName
$assemblyName.CodeBase = $assemblyPath
$assembly = $appDomain.Load( $assemblyName )
$classInstance = $appDomain.CreateInstanceFromAndUnwrap( `
$assemblyPath, "YourNamespace.YourClass" )
$classInstance.YourMethod()
[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 |
|
|
|
|
Didn't Find The Answer You Were Looking For? |
| EggHeadCafe has experts online right now that may know the answer to your question. We pay them a bonus for answering as many questions as they can. So, why not help them and yourself by becoming a member (free) and ask them your question right now? |
| Create Account & Ask Question In Live Forum |
|
|