Ken Kolda
10/25/2004 9:55:00 PM
There are two issues that are causing the ObjectHandleAssembly assembly to
be loaded into the main AppDomain:
1) When you call ObjectHandle.Unwrap(), the code running in the main
AppDomain gets an reference to a MyType instance. Since MyType is defined in
the ObjectHandleAssembly, the assembly must be loaded at that point.
2) When you enumerate the loaded assemblies in the second AppDomain, you are
actually causing each assembly loaded in that domain to also be loaded into
the main AppDomain!
First, let me explain issue #1. When you call CreateInstance() and get the
ObjectHandle object, at that point you're OK. The MyType instance is loaded
into the secondary AppDomain and your primaty AppDomain mintains no direct
reference to it. But, when you call Unwrap(), you're causing the MyType
instance to be marshalled to your primary AppDomain. In order for that to
occur, the primary AppDomain needs to know the definition of the MyType
class, so it loads your custom assembly. Here's the key: you cannot
reference the MyType type or obtain a reference to an instance of a MyType
object in your primary AppDomain. As soon asyou break either rule, the
assembly gets loaded.
As for #2, the key is to understand that when you call GetAssemblies() on
the second AppDomain, it marshals an array of Assembly objects from the
secondary AppDomain to the first. When an Assembly instance is marshalled
across AppDomains, that assembly will get loaded into the target AppDomain.
Thus, just by virtue of inquiring as to what assemblies are loaded into the
second AppDomain you are actually causing each of those to be loaded in the
main AppDomain!!
So, the main questions are:
1) How do you create an object in a second AppDomain, invoke its methods in
the main AppDomain but NOT have the class's assembly loaded into the main
AppDomain. This is exactly the same issue you'd fase if doing cross-app
remoting and you wanted to prevent your client from having to load the
server classes' implementation. The key is to use interfaces and
MarshalByRefObjects. For example, create an assembly that will be loaded in
both the primary and secondary AppDomains. This assembly will define the
interface for your plugins, e.g.
public interface IPlugin
{
string DoSomething();
}
2) In your ObjectHandleAssembly, derive MyType from MarhsalByRefObject and
implement the IPlugin interface.
3) From your main AppDomain, you would obtains your instance using:
IPlugin plugin = (IPlugin) AD3.CreateInstance("ObjectHandleAssembly",
"ObjectHandleAssembly.MyType").Unwrap();
You will now be able to call the methods on the IPlugin interface of the
object without having the ObjectHandleAssembly loaded into the main
AppDomain. The reason this works is twofold:
1) You haven't referenced the MyType type in your code.
2) No instance of MyType has been passed across the AppDomain boundary.
Because the type is marshal-by-ref, an ObjRef is all that crosses the
AppDomain boundary.
Now, if you really want to know what assemblies are loaded into your
secondary AppDomain without causing them to be loaded into the main
AppDomain, try using the AppDomain.DoCallback() method. This allows you to
invoke a method in the other AppDomain. Just take your code that dumps the
Assembly list, put it in a method and use AD3.DoCallback() to invoke that
method. Here's any example:
// Somewhere in your Main code
AD3.DoCallBack(new CrossAppDomainDelegate(DumpAssemblies));
// Elsewhere in the class
public static void DumpAssemblies()
{
Assembly[] asms = AppDomain.CurrentDomain.GetAssemblies();
for (int i = 0; i < asms.Length; i++)
Console.WriteLine("Loaded: " + asms[i].FullName);
}
By using this technique, you've prevented the actual Assembly object from
crossing the AppDomain boundary, so it's never loaded in the main AppDomain.
Hope that helps -
Ken
"DFReed" <darren@yahoo.com> wrote in message
news:EA4EC03F-90EB-4A52-94DB-022604813735@microsoft.com...
>
> The following is built as a Class Library with the assembly name
> "ObjectHandleAssembly" I then copied the "ObjectHandleAssembly.dll" to
> "C:\projects\UseAppDomain\UseAppDomain\bin\Assemblies"
>
> For this to work I have to add a referenct to the ObjectHandleAssembly.dll
> in my project.
>
> Without unwraping the objecthandle how can access the properties or
methods
> that associated with "ObjectHandleAssembly.MyType"
>
> Thanks for all the help so far I understand time is valuable. I have been
> working on this for a week now and read everything I can find on
AppDomains.
>
>
> '************************** OBJECTHANDLEASSEMBLY
> *************************************
>
> Imports System
> Imports System.Runtime.Remoting
> Imports System.MarshalByRefObject
>
> <Serializable()> Public Class MyType
>
> Inherits MarshalByRefObject
>
> Public ReadOnly iTestOne As String = "Test Property"
>
> Public Function GetAppDomainHashCode() As Integer
> Return AppDomain.CurrentDomain.GetHashCode()
> End Function 'GetAppDomainHashCode
>
> End Class 'MyType
>
> '*************************** END OBJECTHANDLEASSEMBLY
> ***********************************
>
> '*************************** START TEST APPLICATION
> *************************************
>
> Imports System.Reflection
> Imports System.Reflection.Emit
> Imports System
> Imports System.IO
> Imports System.Runtime.InteropServices.Marshal
> Imports System.Runtime.Remoting
>
>
> Public Class Main
>
> Private Sub CreateAD3_Click(ByVal sender As System.Object, ByVal e As
> System.EventArgs) Handles CreateAD3.Click
> sOutput += "----------AppDomain AD3 Data-----------" & vbCrLf
>
> ' Create AppDomain AD3 Setup.
>
> Dim ads As New AppDomainSetup
> ads.ApplicationName = "NewApplication"
> ads.ApplicationBase = "C:\projects\UseAppDomain\UseAppDomain\bin\"
> ads.PrivateBinPath = "bins;assemblies"
> ads.ShadowCopyFiles = False
>
> ' CREATES APPDOMAIN AD3
>
> Dim AD3 As AppDomain = AppDomain.CreateDomain("AD3", Nothing, ads)
>
> ' CREATES AN INSTANCE OF "MyType" DEFINED IN THE ASSEMBLY CALLED
> OBJECTHANDLEASSEMBLY.
>
> Dim obj As ObjectHandle =
AD3.CreateInstance("ObjectHandleAssembly",
> "ObjectHandleAssembly.MyType")
>
> ' UNRAPP THE PROXY TO THE MyType OBJECT CREATED IN THE AD3 APPDOMAIN
>
> Dim TestObj As Object = obj.Unwrap
>
> ' TEST ONE OF THE PROPERTYS OF "MYTYPE" IN THE OBJCECTHANDLEASSEMBLY.DLL
>
> sOutput += "Test property =" & TestObj.iTestOne & vbCrLf
>
> ' OUTPUT RESULTS
>
> sOutput += "The hash code of AppDomain AD3 is " & AD3.GetHashCode()
&
> vbCrLf
>
> ' CHECK FOR PROXY
>
> If RemotingServices.IsTransparentProxy(TestObj) Then
> sOutput += "The unwrapped object is a proxy." & vbCrLf
> Else
> sOutput += "The unwrapped object is not a proxy!" & vbCrLf
> End If
>
>
> ' PRINT AD3 DATA
>
> sOutput += "Calling a method on the object located in an AppDomain
> with the hash code "
> sOutput += TestObj.GetAppDomainHashCode() & vbCrLf
> sOutput += "BaseDirectory = " & AD3.BaseDirectory & vbCrLf
> sOutput += "FriendlyName = " & AD3.FriendlyName & vbCrLf
> sOutput += "RelativeSearchPath =" & AD3.RelativeSearchPath &
vbCrLf
> sOutput += "ShadowCopyFiles = " & AD3.ShadowCopyFiles & vbCrLf
> sOutput += "ApplicationName = " & ads.ApplicationName & vbCrLf
> sOutput += "CachePath = " & ads.CachePath & vbCrLf
> sOutput += "ConfigurationFile = " & ads.ConfigurationFile & vbCrLf
> sOutput += "DisallowPublisherPolicy = " &
> ads.DisallowPublisherPolicy & vbCrLf
> sOutput += "LoaderOptimization = " & ads.LoaderOptimization &
vbCrLf
> sOutput += "PrivateBinPath = " & ads.PrivateBinPath & vbCrLf
> sOutput += "ShadowCopyFiles = " & ads.ShadowCopyFiles & vbCrLf
>
> ' PRINT THE LIST OF LOADED ASSEMBLIES
>
> sOutput += "Assemblies loaded in AppDomain AD3 " & vbCrLf
> Dim asm As Reflection.Assembly
> For Each asm In AD3.GetAssemblies
> sOutput += " " & asm.FullName & vbCrLf
> Next
> sOutput += "----- End AppDomain AD3 Data ------------" & vbCrLf
> sOutput += "" & vbCrLf
> Results.Text = sOutput
>
> ' UNLOAD THE AD3 APPDOMAIN
> 'testobj = Nothing
> 'AppDomain.Unload(AD3)
> 'AD3 = Nothing
> 'GC.Collect()
>
> End Sub
>
> ' TEST ASSEMBLIES LOADED IN THE DEFAULT DOMAIN
>
> Private Sub TestCurrent_Click(ByVal sender As System.Object, ByVal
e
> As System.EventArgs) Handles TestCurrent.Click
>
> ' GET A REFERENCE TO THE DEFAULT DOMAIN.
>
> ad1 = AppDomain.CurrentDomain
>
> ' PRINT DATA FOR DEAULT APPDOMAIN
>
> Dim ads As AppDomainSetup = ad1.SetupInformation
> Dim asm As Reflection.Assembly
>
> sOutput += "----- AppDomain Data ------------------" & vbCrLf
> sOutput += "BaseDirectory = " & ad1.BaseDirectory & vbCrLf
> sOutput += "FriendlyName = " & ad1.FriendlyName & vbCrLf
> sOutput += "RelativeSearchPath =" & ad1.RelativeSearchPath &
vbCrLf
> sOutput += "ShadowCopyFiles = " & ad1.ShadowCopyFiles & vbCrLf
> sOutput += "CurrentThreadId = " & AppDomain.GetCurrentThreadId &
> vbCrLf
> sOutput += "CurrentHashCode = " &
> AppDomain.CurrentDomain.GetHashCode() & vbCrLf
> sOutput += "ApplicationName = " & ads.ApplicationName & vbCrLf
> sOutput += "CachePath = " & ads.CachePath & vbCrLf
> sOutput += "ConfigurationFile = " & ads.ConfigurationFile & vbCrLf
> sOutput += "DisallowPublisherPolicy = " &
> ads.DisallowPublisherPolicy & vbCrLf
> sOutput += "LoaderOptimization = " & ads.LoaderOptimization &
vbCrLf
> sOutput += "PrivateBinPath = " & ads.PrivateBinPath & vbCrLf
> sOutput += "ShadowCopyFiles = " & ads.ShadowCopyFiles & vbCrLf
>
> 'PRINT ASSEMBLIES LOADED IN DEAULT APP DOMAIN
>
> sOutput += "Assemblies loaded in current domain" & vbCrLf
> For Each asm In ad1.GetAssemblies
> Console.WriteLine(" " & asm.FullName)
> sOutput += " " & asm.FullName & vbCrLf
> Next
> sOutput += "----- End of Parant AppDomain Data ------------" &
vbCrLf
> sOutput += "" & vbCrLf
> Results.Text = sOutput
> End Sub
>
> End Class
>
>
> "Ken Kolda" wrote:
>
> > That's really strange -- I ran some sample code and was able to get the
> > assembly to load in the second AppDomain only. Go ahead and post a small
> > example that reproduces this problem and I'll take a look.
> >
> > Ken
> >
> >
> > "DFReed" <DFReed@discussions.microsoft.com> wrote in message
> > news:7B8FFD9F-62AF-40C6-97BD-CF6B61687AD0@microsoft.com...
> > > On further testing if I comment out #3 below
> > > 3. create testObj
> > > ' Dim testObj As MyType = CType(obj.Unwrap(), MyType)
> > > the "ObjectHandleAssembly" still appears to be loaded in the default
> > > AppDomain. I can copy all the code if you think it would help or email
the
> > > test App to you.
> > >
> > > "DFReed" wrote:
> > >
> > > > If I don't call the unwrap method how can I access a function inside
> > MyType
> > > > like:
> > > > sOutput += "Calc = " & testObj.InitializeCalc
> > > >
> > > > "Ken Kolda" wrote:
> > > >
> > > > > The problem is that you call the Unwrap() method on the returned
> > > > > ObjectHandle. Once you do that, the main AppDomain needs to have
the
> > > > > definition of the object's Type, so it's forced to load the
assembly.
> > If you
> > > > > omit step 3 then I believe you'll get the desired behavior.
> > > > >
> > > > > Ken
> > > > >
> > > > >
> > > > > "DFReed" <DFReed@discussions.microsoft.com> wrote in message
> > > > > news:E8B15E90-4F37-454C-8EF3-562622E03233@microsoft.com...
> > > > > > I am trying to load an assembly (ObjectHandleAssembly.dll) in
its
> > own
> > > > > > AppDomain so I can unload it, but it seems to be loading itself
in
> > the
> > > > > > default AppDomain as well. To do this I have.
> > > > > >
> > > > > > 1. Created a new AppDomain
> > > > > > Dim ad3 As AppDomain = AppDomain.CreateDomain("AD3",
Nothing,
> > ads)
> > > > > > 2. Created an ObjectHandls
> > > > > > Dim obj As ObjectHandle = ad3.CreateInstance
> > > > > > ("ObjectHandleAssembly", "ObjectHandleAssembly.MyType")
> > > > > > 3. create testObj
> > > > > > Dim testObj As MyType = CType(obj.Unwrap(), MyType)
> > > > > > 4. Unload AppDomain
> > > > > > AppDomain.Unload(ad3)
> > > > > >
> > > > > > The problem is when I check the Default AppDomain for assemblys
the
> > > > > > ObjectHandleAssembly is loaded.
> > > > > >
> > > > > > Dim asm As Reflection.Assembly
> > > > > > For Each asm In ad2.GetAssemblies
> > > > > > Console.WriteLine(" " & asm.FullName)
> > > > > > sOutput += " " & asm.FullName & vbCrLf
> > > > > > Next
> > > > > >
> > > > > > Even though the AppDomain AD3 is unloaded.
> > > > > >
> > > > > > Private Sub TestAppDomainUnload(ByVal AD As AppDomain, ByVal
> > Name As
> > > > > > String)
> > > > > > Try
> > > > > > sOutput += "----- Test" & Name &
> > "Unload ------------------" &
> > > > > > vbCrLf
> > > > > > sOutput += "BaseDirectory = " & AD.BaseDirectory &
> > vbCrLf
> > > > > > sOutput += "FriendlyName = " & AD.FriendlyName &
vbCrLf
> > > > > > Catch ex As Exception
> > > > > > sOutput += "AppDomain " & Name & " has been
Unloaded" &
> > vbCrLf
> > > > > > sOutput += "" & vbCrLf
> > > > > > Results.Text = sOutput
> > > > > > End Try
> > > > > > End Sub
> > > > >
> > > > >
> > > > >
> >
> >
> >