How to load / unload DLL

L

l0b0

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!
 

My Computer

J

Jeff

On Sep 20, 8:41 pm, l0b0 <[email protected]> wrote:

> 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!

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
 

My Computer

J

Jeff

On Sep 20, 9:19 pm, Jeff <[email protected]> wrote:

> On Sep 20, 8:41 pm, l0b0 <[email protected]> wrote:
>
>
>

> > 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!
>
> 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

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
 

My Computer

L

l0b0

On Sep 20, 4:19 pm, Jeff <[email protected]> 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.
 

My Computer

J

Jeff

On Sep 20, 9:38 pm, l0b0 <[email protected]> wrote:

> On Sep 20, 4:19 pm, Jeff <[email protected]> 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:
>
>
> 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.

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
 

My Computer

J

Jeff

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
 

My Computer

L

l0b0

On Sep 21, 11:07 am, Jeff <[email protected]> wrote:

> 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.

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
 

My Computer

L

l0b0

On Sep 21, 11:07 am, Jeff <[email protected]> wrote:

> 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.

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
 

My Computer

J

Jeff

On Sep 20, 8:41 pm, l0b0 <[email protected]> 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
 

My Computer

L

l0b0

On Sep 24, 3:51 am, Jeff <[email protected]> wrote:

> On Sep 20, 8:41 pm, l0b0 <[email protected]> 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.

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
 

My Computer

J

Jeff

On Sep 24, 4:10 pm, l0b0 <[email protected]> wrote:

> On Sep 24, 3:51 am, Jeff <[email protected]> wrote:
>

> > On Sep 20, 8:41 pm, l0b0 <[email protected]> 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.
>
> 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

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
 

My Computer

Top