You would think that it would be relatively easy for one managed code assembly to get a reference to another managed code assembly at runtime that might be running under a different application domain, but it's not. In order to do that you have to do several things:
- Make the server .Net Assembly Com-visible and Com-Compatible
- Register the server assembly in the RunTime Object Table (ROT) so that the client assembly can find it at run time
The technique for exposing a .Net assembly to COM is not too difficult and is covered in a number of locations on the web. The task of registering a COM object in the ROT and retrieving a reference to that COM object from another application is a bit tricky. Here’s some code that I’ve found works for that:
Option Strict On
Option Explicit On
Imports System.Runtime.InteropServices
Imports System.Runtime.InteropServices.ComTypes
Imports System.Runtime.InteropServices.Marshal
Public Class ROTHook
Private Const ROTFLAGS_REGISTRATIONKEEPSALIVE As Integer = 1
Private Const ROTFLAGS_ALLOWANYCLIENT As Integer = 2
<DllImport("ole32.dll", ExactSpelling:=True, PreserveSig:=False)> _
Private Shared Function GetRunningObjectTable(ByVal reserved As Int32) As IRunningObjectTable
End Function
<DllImport("ole32.dll", CharSet:=CharSet.Unicode, ExactSpelling:=True, PreserveSig:=False)> _
Private Shared Function CreateItemMoniker(ByVal lpszDelim As String, ByVal lpszItem As String) As IMoniker
End Function
<DllImport("ole32.dll", ExactSpelling:=True, PreserveSig:=False)> _
Private Shared Function CreateBindCtx(ByVal reserved As Integer) As IBindCtx
End Function
Public Shared Function AddToROT(ByVal obj As Object, ByVal classID As String) As Integer
'----------------------------------------------------------------------------------
' AddToROT
'
' Abstract - Adds a Reference to This Object to the Runtime Object Table
'
' Parameters
' obj Object to register
' classID Class ID of object to register
'
' Return Value cookie to revoke ROT registration
'----------------------------------------------------------------------------------
Dim cookieValue As Integer
Dim rot As IRunningObjectTable = Nothing
Dim moniker As IMoniker = Nothing
Try
'---- Get the ROT -------------------------------------------------------------
rot = GetRunningObjectTable(0)
'--- Create a moniker ---------------------------------------------------------
Dim objName As String = "{" + classID + "}"
moniker = CreateItemMoniker("!", objName)
'--- Registers the object in the running object table -------------------------
cookieValue = rot.Register(ROTFLAGS_REGISTRATIONKEEPSALIVE, obj, moniker)
Return cookieValue
Catch Exc As Exception
Throw Exc
Finally
'--- Releases the COM objects -----------------------------------------------
If Not (moniker Is Nothing) Then
Try
While (Marshal.ReleaseComObject(moniker) > 0)
End While
Catch ex As Exception
Exit Try
End Try
End If
If Not (rot Is Nothing) Then
Try
While (Marshal.ReleaseComObject(rot) > 0)
End While
Catch ex As Exception
Exit Try
End Try
End If
End Try
End Function
Public Shared Function GetObjectByClassName(ByVal objClassName As String) As Object
Return GetActiveObject("!" + objClassName)
End Function
Public Shared Function GetObjectByClassID(ByVal objClassID As String) As Object
Return GetActiveObject("!{" + objClassID + "}")
End Function
Public Shared Function GetActiveObject(ByVal objName As String) _
As Object
'----------------------------------------------------------------------------------
' GetActiveObject
'
' Abstract - Gets an Instance of this Object from the Runtime Object Table
'
' Parameters
'
' Return Value Object found or Nothing if Not Found
'----------------------------------------------------------------------------------
Try
Dim ROTObject As Object = Nothing
Dim runningObjectTable As IRunningObjectTable
Dim monikerEnumerator As IEnumMoniker = Nothing
Dim monikers(1) As IMoniker
runningObjectTable = GetRunningObjectTable(0)
runningObjectTable.EnumRunning(monikerEnumerator)
monikerEnumerator.Reset()
Dim numFetched As IntPtr = New IntPtr()
While (monikerEnumerator.Next(1, monikers, numFetched) = 0)
Dim ctx As IBindCtx
ctx = CreateBindCtx(0)
Dim runningObjectName As String = ""
monikers(0).GetDisplayName(ctx, Nothing, runningObjectName)
runningObjectName = runningObjectName.ToUpper
If (runningObjectName.StartsWith(objName.ToUpper)) Then
Dim runningObjectVal As Object = Nothing
runningObjectTable.GetObject(monikers(0), runningObjectVal)
ROTObject = CType(runningObjectVal, Object)
Return ROTObject
End If
End While
Return ROTObject
Catch Exc As Exception
Throw Exc
End Try
End Function
Public Shared Sub RemoveFromROT(ByVal myCookie As Integer)
'----------------------------------------------------------------------------------
' RemoveFromROT
'
' Abstract - Removes a Reference to This Object From the Runtime Object Table
'
' Parameters
'
' Return Value
'----------------------------------------------------------------------------------
If (myCookie = 0) Then Exit Sub
Dim rot As IRunningObjectTable = Nothing
Try
'--- Get the running object table and revoke the cookie ----------------
rot = GetRunningObjectTable(0)
rot.Revoke(myCookie)
myCookie = 0
Catch Exc As Exception
Throw Exc
Finally
If Not (rot Is Nothing) Then
While (Marshal.ReleaseComObject(rot) > 0)
End While
End If
End Try
End Sub
End Class
You can use the “AddToROT” method to register an object in the ROT by classID – that method will return a cookie that can be used later to remove the object. The “GetObjectByClassID” can be used to retrieve the object from another process.
Chuck