[Closed] .NET: how to unload assembly ?
Hi !
I was wondering if anyone here has some information on unloading assemblies ? I would like to use some functionality of a .NET dll in MAXScript and when I recompile in VS it obviously cannot overwrite the DLL because it was loaded before via
dotNet.loadAssembly "lib.dll"
and thus is still being used by MAX. Is there a way to unload / release a .NET dll without restarting MAX ? Or is there a certain strategy that .NET developers use for recompiling ?
I really do not want to go the way of creating a stub that dynamically loads my DLL so if anybody had some info on how to comfortably program and test I would be very grateful !
Thanks !
Markus
Ok, after hours of searching the web I think I am able to answer my own question…
I found this amazing thread on the area where Mike Biddlecombe explains how to dynamically compile sources and load them into memory. I modified it so it will read a .cs file and it works like a charm !
http://area.autodesk.com/index.php/forums/viewthread/13286/
No visual studio compile necessary !
Hi Markus,
Mike’s post was really helpful to me too, however I changed the top line to compile Visual Basic instead –
fn CreateAssembly src =
(
/*
code compilation based on an example from Kim David Hauser
[click on the link]( http://www.codeproject.com/KB/cs/evalcscode.aspx)
altered Mike B's codeprovider to compile visual basic
*/
VBProvider = dotnetobject "Microsoft.VisualBasic.VBCodeProvider"
compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
compilerParams.GenerateInMemory = true
compilerResults = VBProvider.CompileAssemblyFromSource compilerParams #(src)
if (compilerResults.Errors.Count > 0 ) then
(
errs = stringstream ""
for i = 0 to (compilerResults.Errors.Count-1) do
(
err = compilerResults.Errors.Item[i]
format "Error:% Line:% Column:% %
" err.ErrorNumber err.Line \
err.Column err.ErrorText to:errs
)
MessageBox (errs as string) title: "Errors encountered while compiling VB code"
return undefined
)
a = compilerResults.CompiledAssembly
)
fn StreamToByteArray =
(
vbf = "Imports System.IO
"
vbf +="Public Class StreamToByteArray
"
vbf +="Public Function GetStreamAsByteArray(ByVal stream As System.IO.Stream) As Byte()
"
vbf +="Dim streamLength As Integer = System.Convert.ToInt32(stream.Length)
"
vbf +="Dim fileData As Byte() = New Byte(streamLength) {}
"
vbf += "stream.Read(fileData, 0, streamLength)
"
vbf += "stream.Close()
"
vbf += "Return fileData
"
vbf += "End Function
"
vbf +="End Class"
a = CreateAssembly vbf
a.CreateInstance "StreamToByteArray"
)
global StreamByteArray = StreamToByteArray()
Yea it is really big problem with .net assembly which can not be unloaded once it loaded. Only AppDomain can be unloaded. Have a look about it on MSDN. If you check softimage .net-integration then you will see, you can dynamically load/unload .net assembly with out any kind of hack. 3dsMax / MaxScript should provide similar kind of functionality by default, so users not have to play with cross AppDomain communication.
Incase of dynamic compilation, which is technically not the replacement of unloading assembly by unloading AppDomain.
From the use I can assume they have used internally AppDomain unloading but not sure as it is not exposed in user level. Incase of Appdomain, load the assembely in a separate AppDomain and then when need to unload assembly, unload this particular AppDomain instead of a single assembly. But then we also have to take care about cross AppDomain communication. Another hacky way, In every loading call create a separate temp copy of the assembly and load that instead of the original one to avoid dll locking problem. The new loaded definition will replace the old one (but not sure whether old definition get cleaned from memory or not). In case of dynamic compilation to overcome dll lock problem either in temp folder or in memory you are basically doing same copy hack.
I’ve not managed to get this roll-your-own shadow copying to work. On the second call of loadAssembly, the original version stubbornly remains in memory? Maybe I’m doing something else wrong.
I think what he meant was that you should start out (after a fresh max start, say) by copying the DLL to a (temp) location and loadAssembly that file – to prevent a lock on the original file (so you can, say, overwrite it with a new compile). After that, the next time you want to load the assembly, again create a copy and loadAssembly that new copy.
Now…
- the original file that you just recompiled remains unlocked
- your first copy is still locked, but its definition in memory overwritten by the 2nd copy
- your second copy is locked
You’d end up with quite a few ‘shadow copies’, but then… loadAssembly should be used sparingly – I suppose you’d only really run into this situation when developing an assembly.
Ok, thanks, ZB. It’s klunkier than I thought. But perhaps OK for development.
EDIT: Thanks, too, susanta … this one will send me back to my books!
ZeBoxx2, yea that is easy way to overcome this problem…
Sorry, the hard way I have actually not dicussed the process in deatail in my previous post.
Here is the way to load multiple versions of the assembly (diffrent version number)
http://infosysblogs.com/microsoft/2007/04/loading_multiple_versions_of_s.html
if you need to autoincrement during development quickly in visual studio here it is:
http://www.codeplex.com/autobuildversion
i guess currently you have to get used to restarting max when testing a custom assembly. :hmm:
I copy everything from Visual Studio to a launch directory, still have to restart. boo.