views:

54

answers:

3

Hi all,

In my application, I would like to load Assemblies that reside on a network share (The application itself is on that share, too) without having to add them to the project's output path. What I'd like to have is an output directory that contains next to nothing but the application.

Merely adding the target directory to my project's reference path's won't work.

I tried setting the probing directory in the App.Config, but that didn't work (even tested it on my local diks instead of the network share)

Thanks in advance for any hints!

EDIT : Seems the way this is going leads through reflection. Isn't there a more elegant way using Dependency Injection containers like NInject?

+2  A: 

Unfortunately you can't add an arbitrary directory to your probing path: only a subdirectory within your application.

However, it's possible to provide full paths to assemblies using the <codeBase> element within an <assemblyBinding>. There's an example on the MSDN page for <assemblyBinding>. The downside is that you'll need to explicitly list paths for all the assemblies your application references.

Another option is to load assemblies at run time through Assembly.LoadFrom and invoke methods through reflection. If you go down this route, you must read Suzanne Cook's blog. If you read nothing else, read Choosing a Binding Context.

Tim Robinson
+2  A: 

I'd take a look at Assembly.LoadFrom. You be able to write some code like this:

Assembly myAssembly = Assembly.LoadFrom(assemblyPath);

Note that you'll also need to load any dependant assemblies as well. Once you have this you can create as object using:

object myObject = myAssembly.CreateInstance(typeName);

And cast it to the type specified by typeName

John Sibly
A: 

If I understand correctly, you want to load classes from an assembly without having to reference the assembly's project ?

Then you could use this class to get all types that implements / inherits a certain type :

Imports System.IO
Imports System.Threading
Imports Exceptions
Imports System.Reflection
Imports ModuleInterfaces

Public Class StartupLoader
    Private ReadOnly syncroot As New Object

    Private _handler As ExceptionHandler ' custom class to handle exceptions'
    Private _typeToLoad As Type

    Public Sub New(ByVal typeToLoad As Type, _
                   ByVal handler As ExceptionHandler)
        _handler = handler
        _typeToLoad = typeToLoad
    End Sub

    Public Function LoadDLLs() As List(Of Type)
        Dim threads As New List(Of Thread)
        Dim types As New List(Of Type)
        Dim exceptions As New List(Of Exception)
        Dim folders As New Stack(Of String)
        Dim t As Thread

        folders.Push(Directory.GetCurrentDirectory) ' change to your dir here, could use a member var'
        While Not folders.Count = 0
            For Each f In Directory.GetFiles(folders.Peek)
                Dim tmp As String = f
                If tmp.ToLower.EndsWith(".dll") OrElse _
                   tmp.ToLower.EndsWith(".exe") Then
                    t = New Thread(AddressOf LoadDLLsThread)

                    t.Start(New Object() {tmp, types, exceptions})
                    threads.Add(t)
                End If
            Next

            For Each d In Directory.GetDirectories(folders.Peek)
                folders.Push(d)
            Next

            folders.Pop()
        End While

        For Each t In threads
            t.Join()
        Next

        If exceptions.Count > 0 Then
            Throw New ThreadedException(exceptions) ' Custom exception containing a List(Of Exception)'
        End If

        Return types

    End Function

    Private Sub LoadDLLsThread(ByVal vObj As Object)
        Dim objs As Object() = CType(vObj, Object())
        Dim fileName As String = CStr(objs(0))
        Dim globalTypes As List(Of Type) = CType(objs(1), Global.System.Collections.Generic.List(Of Global.System.Type))
        Dim exceptions As List(Of Exception) = CType(objs(2), Global.System.Collections.Generic.List(Of Global.System.Exception))

        Dim types As New List(Of Type)

        Try
            Dim myAssembly As Assembly = Assembly.LoadFrom(fileName)

            For Each t In myAssembly.GetTypes()
                If _typeToLoad.IsAssignableFrom(t) AndAlso _
                   t.IsClass = True AndAlso _
                   t.IsAbstract = False Then
                    types.Add(t)
                End If
            Next

            SyncLock syncroot
                globalTypes.AddRange(types)
            End SyncLock

        Catch ex As Exception
            SyncLock syncroot
                exceptions.Add(ex)
            End SyncLock
        End Try

    End Sub

End Class

After that, use John Sibly's answer to create an object of any of the types on the fly

Cheers !

Alex Rouillard