[.NET] Dynamisch DLL's inladen

Pagina: 1
Acties:

  • MacWolf
  • Registratie: Januari 2004
  • Laatst online: 06-09-2024
Voor een project moet ik dynamisch DLL's zien in te laden. De volgende opzet: er is 1 map aanwezig waar ik DLL's in kan stoppen die ik dynamisch wil inladen / uitvoeren.

Nu kwam ik via deze url op het volgende stuk code:

Visual Basic .NET:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Private Sub loadModules()
    'Output
    txtOutput.AppendText("Loading modules from " + moduleLocation + vbCrLf)

    'Assembly to load the file
    Dim assembly As System.Reflection.Assembly
    Dim textProcessorModule As apcc.textprocessor.ITextFile

    'For each .dll file in the module directory
    For Each file As String In System.IO.Directory.GetFiles(modul­eLocation, "*.dll")
        'Load the assembly
        assembly = System.Reflection.Assembly.LoadFro­m(file)

        'Loop through each of the assemeblies type
        For Each fileType As Type In assembly.GetTypes
            'If the type is of ITestFile
            If GetType(apcc.textprocessor.ITextFi­le).IsAssignableFrom(fileType) Then
                'Activate the located module
                textProcessorModule = CType(Activator.CreateInstance(fil­eType), apcc.textprocessor.ITextFile)
                'Add the activated module to the arraylist
                textProcessingModules.Add(textProcessorModule)
                'Output
                txtOutput.AppendText("Loaded module: " + textProcessorModule.jobName + " " + "v" + textProcessorModule.moduleVersion ­ + vbCrLf)
            End If
        Next
    Next
End Sub


Tevens heb ik de volgende tips in gedachten gehouden
I don't think it matters too much whether you use interfaces or abstract classes, but I've seen cases where casting doesn't quite work because the two objects, even though they're the same type, are considered "different" types by the runtime (because they were loaded from different assemblies, or they reference different assemblies).

For this reason I tend to err on the side of interfaces for dynamic loading, but I use abstract classes for implementation. As an example:

IFileFormatter -> BaseFileFormatter -> XmlFileFormatter

This way you use interfaces for the runtime/reflection stuff, but you can still share and make use of the abstract classes when inheriting/implementing the concrete classes. For an example of this in the .NET framework, checkout ICollection and CollectionBase - ICollection is used for the work, CollectionBase is just designed to make inheritance easier.

--- --- --- --- --- --- --- ---

Within my main "File Processor" project I now have the interface "ITextFile" and the abstract class "TextFileProcessor" that implements this interface. I was wondering, what is the best way to access this abstract class from the modules that will inherit it considering that they will all be in different projects.

--- --- --- --- --- --- --- ---

Create a seperate DLL project which contains your interfaces. Reference this project/dll for each implementation.
Mijn code is iets verschillend (ik gebruik andere types), maar het principe is hetzelfde. Volgende probleem doet zich voor: ik probeer op regel 19 een instance te maken (van een object met als type de interface) en dan de enige functie aan te roepen die ik zelf heb gedeclareerd in de interface.

Mijn code ziet er ongeveer zo uit:

Visual Basic .NET:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
        'Assembly to load the file
        Dim Assembly As System.Reflection.Assembly
        Dim ModData As ModDatabase.ModDatabase.IModDatabase
        Dim ObjType As Type
        Dim ObjPlugin As Object

        'For each .dll file in the module directory
        For Each file As String In System.IO.Directory.GetFiles(DllDir, "*.dll")
            Assembly = System.Reflection.Assembly.LoadFrom(file)

            For Each ObjType In Assembly.GetTypes
                If GetType(ModDatabase.ModDatabase.IModDatabase).IsAssignableFrom(ObjType) Then
                    ObjPlugin = Activator.CreateInstance(ObjType)
                    ModData = DirectCast(ObjPlugin, ModDatabase.ModDatabase.IModDatabase)
                    ModData.ModifyData(My.Settings.DatabaseConnection)

                End If
            Next
        Next


Nu faalt mijn code op regel 15, ik kan de functie niet aanroepen en krijg een NullReferenceException. Toch lijkt er wel een instance gemaakt te zijn, tenminste in Visual Studio bevat ModData een waarde (<> Nothing), maar blijkbaar is er geen instance van de methode?

Iemand enig idee waar het mis kan gaan?

Microsoft Windows: A thirty-two bit extension and graphical shell to a sixteen-bit patch to an eight-bit operating system originally coded for a four-bit microprocessor which was written by a two-bit company that can't stand one bit of competition.


Verwijderd

Volgens mij moet je een interface schrijven van het object en die meenemen in je solution, door dan het object te casten naar de interface kun je de methodes wel aanroepen.

  • MacWolf
  • Registratie: Januari 2004
  • Laatst online: 06-09-2024
Verwijderd schreef op woensdag 28 maart 2007 @ 15:41:
Volgens mij moet je een interface schrijven van het object en die meenemen in je solution, door dan het object te casten naar de interface kun je de methodes wel aanroepen.
In mijn huidige Visual Studio Solution heb ik dus 3 projecten:
* 1 applicatie (database tekst importer)
* 1 library project (interface declaraties, abstracte class)
* 1 test plug-in gebaseerd op de library, maar worden er in de toekomst meer.

Het vreemde is dat na het casten de methodes niet "zichtbaar" worden in de Visual Studio debugger (als je over het object scrolled en het object "uiklapt"). Dit verklaart de Null Exception (immers, er is blijkbaar geen instantie van de methodes, maar blijkbaar wel van het object?), maar hoe los ik dat op?

Edit: zoals zichtbaar in bovenstaande code cast ik het object al naar de interface.

[ Voor 14% gewijzigd door MacWolf op 28-03-2007 15:56 ]

Microsoft Windows: A thirty-two bit extension and graphical shell to a sixteen-bit patch to an eight-bit operating system originally coded for a four-bit microprocessor which was written by a two-bit company that can't stand one bit of competition.


  • whoami
  • Registratie: December 2000
  • Laatst online: 00:54
Heb je al eens gedebugged ? Waar exact treedt die NullRef exceptie op ?
Als het creeëren en het casten goed gaat, dan treedt die exceptie ws ergens binnen de ModifyData method op.

https://fgheysels.github.io/


  • MacWolf
  • Registratie: Januari 2004
  • Laatst online: 06-09-2024
whoami schreef op woensdag 28 maart 2007 @ 16:08:
Heb je al eens gedebugged ? Waar exact treedt die NullRef exceptie op ?
Als het creeëren en het casten goed gaat, dan treedt die exceptie ws ergens binnen de ModifyData method op.
Zucht, wordt moe van mezelf, maar bedankt voor je hulp.

Ik ging ervan uit dat de code in mijn 1e plug-in foutloos was, maar het tegenovergestelde is waar, er treed inderdaad een exception op in de plug-in, de rest werkt goed.

Wil eventueel nog wel ff de volledige code van het plug-in systeem posten als mensen daar nog behoefte aan hebben.

Microsoft Windows: A thirty-two bit extension and graphical shell to a sixteen-bit patch to an eight-bit operating system originally coded for a four-bit microprocessor which was written by a two-bit company that can't stand one bit of competition.


  • E.Greidanus
  • Registratie: November 2000
  • Laatst online: 14-11-2017
MacWolf schreef op woensdag 28 maart 2007 @ 16:22:
[...]

Zucht, wordt moe van mezelf, maar bedankt voor je hulp.

Ik ging ervan uit dat de code in mijn 1e plug-in foutloos was, maar het tegenovergestelde is waar, er treed inderdaad een exception op in de plug-in, de rest werkt goed.

Wil eventueel nog wel ff de volledige code van het plug-in systeem posten als mensen daar nog behoefte aan hebben.
Dat zou wel een meer waarde zijn voor deze thread >:)

  • MacWolf
  • Registratie: Januari 2004
  • Laatst online: 06-09-2024
Even een code listing over een eenvoudige manier om een systeempje te maken dat dynamisch DLL's kan inladen.

Maak een solution met 3 projecten:
* 1 applicatie
* 1 prototype voor iedere plug-in (DLL) in de vorm van een library project
* 1 voorbeeld DLL

Maak referenties van zowel de applicatie als de voorbeeld DLL naar de prototype DLL.

In de applicatie de volgende code toevoegen:

Visual Basic .NET:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    Private WithEvents ModData As Lucrasoft.ModDatabaseData.IModDatabase
    Private DllDir As String = My.Application.Info.DirectoryPath & "\DLL"

    Public Sub DllException(ByVal Ex As Exception) Handles ModData.ExceptionOccured
        MsgBox("Exception occured in: " & ModData.AssemblyName & Environment.NewLine & "StackTrace: " & Ex.ToString)
    End Sub

    Private Sub ExecuteDlls
        Dim Assembly As System.Reflection.Assembly
        Dim ObjType As Type
        Dim ObjPlugin As Object

        'For each .dll file in the module directory
        For Each File As String In System.IO.Directory.GetFiles(DllDir, "*.dll")
            Assembly = System.Reflection.Assembly.LoadFrom(file)

            For Each ObjType In Assembly.GetTypes
                If GetType(Lucrasoft.ModDatabaseData.IModDatabase).IsAssignableFrom(ObjType) Then
                    ObjPlugin = Activator.CreateInstance(ObjType)
                    ModData = DirectCast(ObjPlugin, Lucrasoft.ModDatabaseData.IModDatabase)
                    ModData.ModifyData(My.Settings.DatabaseConnection)
                End If
            Next
        Next
    End Sub


Code voor de prototype plug-in (het library project)

Visual Basic .NET:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Public Class ModDatabaseData
    Interface IModDatabase
        Function ModifyData(ByVal ConnectionString As String) As Boolean
        Event ExceptionOccured(ByVal Ex As Exception)
        ReadOnly Property AssemblyName()
    End Interface

    MustInherit Class ModDatabase
        Implements IModDatabase

        Private _AssemblyName As String

        Public Sub New(ByVal MyAssemblyName As String)
            _AssemblyName = MyAssemblyName
        End Sub

        Public MustOverride Function ModifyData(ByVal ConnectionString As String) As Boolean Implements IModDatabase.ModifyData

        Protected Sub RaiseException(ByVal Ex As Exception)
            RaiseEvent ExceptionOccured(Ex)
        End Sub

        Public Event ExceptionOccured(ByVal Ex As System.Exception) Implements IModDatabase.ExceptionOccured

        Public ReadOnly Property AssemblyName() As Object Implements IModDatabase.AssemblyName
            Get
                Return _AssemblyName
            End Get
        End Property
    End Class
End Class


En als laatste code voor een voorbeeld plug-in:

Visual Basic .NET:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Public Class Test
    Inherits Lucrasoft.ModDatabaseData.ModDatabase

    Public Sub New()
        MyBase.New(My.Application.Info.AssemblyName)
    End Sub

    Public Overrides Function ModifyData(ByVal ConnectionString As String) As Boolean
        Dim MyDataTable As New DataTable
        Dim MySqlConnection As New SqlClient.SqlConnection(ConnectionString)
        Dim MySqlCommand As New SqlClient.SqlCommand("SELECT * FROM MATMAS1", MySqlConnection)
        Dim MySqlDataAdapter As New SqlClient.SqlDataAdapter(MySqlCommand)

        Dim MyCommandBuilder As New SqlClient.SqlCommandBuilder(MySqlDataAdapter)
        MyCommandBuilder.GetUpdateCommand(True)

        Try
            MySqlDataAdapter.Fill(MyDataTable)
            For i As Integer = 0 To MyDataTable.Rows.Count - 1
                MyDataTable.Rows(i)("Sales_org") = "test"
            Next
            MySqlDataAdapter.Update(MyDataTable)
        Catch ex As Exception
            RaiseException(ex)
            Return False
        End Try

        Return True
    End Function
End Class


Dit is puur een eenvoudig voorbeeld (met wel een hele simpele methode om met Exceptions om te gaan) maar volstaat waar ik het voor nodig heb.

Alle DLL's in een bepaalde map kunnen op deze manier eenvoudig gestart worden, maar er kan zeker een hoop worden verbeterd / toegevoegd.

Microsoft Windows: A thirty-two bit extension and graphical shell to a sixteen-bit patch to an eight-bit operating system originally coded for a four-bit microprocessor which was written by a two-bit company that can't stand one bit of competition.


  • gorgi_19
  • Registratie: Mei 2002
  • Laatst online: 10:24

gorgi_19

Kruimeltjes zijn weer op :9

Je zou iig de assemblies ook kunnen cachen in HttpRuntime.Cache :) Scheelt een hoop performance in de volgende calls.

Digitaal onderwijsmateriaal, leermateriaal voor hbo


  • whoami
  • Registratie: December 2000
  • Laatst online: 00:54
* whoami mept gorgi die er altijd van uit gaat dat het over ASP.NET gaat.
Dat werd nergens vermeld. :P

Kan het trouwens niet zijn dat het framework zelf al voor enige vorm van caching zorgt; bv, als de assembly reeds ingeladen is in het AppDomain, wordt deze niet meer geladen...
(Dit is een veronderstelling, maar ik denk dat dit goed mogelijk is. Zou het eens moeten opzoeken)

https://fgheysels.github.io/


  • MacWolf
  • Registratie: Januari 2004
  • Laatst online: 06-09-2024
whoami schreef op donderdag 29 maart 2007 @ 09:59:
* whoami mept gorgi die er altijd van uit gaat dat het over ASP.NET gaat.
Dat werd nergens vermeld. :P

Kan het trouwens niet zijn dat het framework zelf al voor enige vorm van caching zorgt; bv, als de assembly reeds ingeladen is in het AppDomain, wordt deze niet meer geladen...
(Dit is een veronderstelling, maar ik denk dat dit goed mogelijk is. Zou het eens moeten opzoeken)
Toen ik liep te googlen met betrekking tot dit probleem kwam ik volgens mij ergens tegen dat de .NET runtime dynamisch geladen DLL's weer gelijk uit het geheugen haalt wanneer ze niet meer gebruikt worden, tenzij je gebruik zou maken van een AppDomain oid. Weet voor de rest ook geen details (en voor mijn huidige project ook niet nodig :))

Microsoft Windows: A thirty-two bit extension and graphical shell to a sixteen-bit patch to an eight-bit operating system originally coded for a four-bit microprocessor which was written by a two-bit company that can't stand one bit of competition.

Pagina: 1