none
VB.Net How to Use Dynamic Link Libraries ? RRS feed

  • Question

  • Hi Guys

    I'm looking for some guidance for best method/practices developing an application that uses dynamic link libraries to provide specific functionality.

    I currently have a VB.net application that has grown so much over the years with functionality that the source code is rapidly becoming unmanageable.

    I've had the idea of splitting this up into separate dynamic link libraries to facilitate the ability of making changes to the functionality supplied by these libraries when needed that won't affect the main application.

    To keep things simple, I'm looking at using a treeview in the main application form that gets populated on startup with all the dll's in the library folder and the functionality each contains e.g.  

    + Load.dl

        Excel

        Word

    + Save.dll

        Excel

        Word

    + View.dll

        Excel

        Word

    The above tree would get the root nodes from the contained dll's and the sub-nodes would be populated from the public functions the dll contains, so clicking Excel in the View.dll node would display an excel spread sheet using the Excel subrouting from the View.dll.

    Note* the dll functionality may contain additional sub functionality as in View.dll Excel may have a version sub function. Currently this is done using a class - Public _View as c_View() which is then used  _View .Excel() to display the spread sheet or _View .Excel.Version() to display the spread sheet version.

    The end goal is to have the ability to add remove specific functionality purely by changing/adding/removing a dll file.

    I've tried to keep this explanation as simple as possible that hopefully someone will be able to assist me with a solution instead of the barrage of the usual  'Why do you want to do this?' and "I'd do something totally irrelevant" responses!

    Note* I've used Excel and Word in my example, however my application will be supporting a variety of custom industry file types from different vendors in the Controls & Automation sector each having different methods of storing/retrieving data. The dll method seems like a good method of keeping the independent vendor functionality separate from all the others and allowing the application to be distributed with only the required vendor support.

    Thanks for any assistance in Advance.

    Monday, August 20, 2018 9:42 AM

All replies

  • It's called Seperation of Concerns.

    https://en.wikipedia.org/wiki/Separation_of_concerns

    You can implement SoC by having classlib projects that have specific functionality.

    You can implement SoC by implementing the Repository pattern, a  Repository classlib project.

    However, I wouldn't call it a Repository in the traditional sense of calling it a Repository in your case that sits between the Business Logic Layer and the Data Persistence Layer.

    https://deviq.com/repository-pattern/

    I have seen it called an Engine instead of a Repository. The Engine classlib project can produce an object,  a little machine that can take care of its needs. The classes/objects in the Engine classlib project should implement an Interface.

    You see the concept in play in the Data Access Layer code where the class the DAO (Data Access Object) using the DAO pattern implements its Interface with an Interface being a contract between the calling object/class and the class/object being called that exposes the public methods and properties exposed to the outside world on the called class/object.

    The Controller class/object is making calls to the DAO object in the DAL classlib project based on the public methods implemented in the public Interface for the class/object.

    The DAO is based on the DAO design pattern. You can see in the Delete() method in the DaoProject how it calls on the DaoTask to delete tasks for a given project.

    https://www.tutorialspoint.com/design_pattern/data_access_object_pattern.htm

    So you should be able to see how design patterns can be fashioned by you to meet your needs.

    https://www.dofactory.com/net/design-patterns

    <copied>

    Design patterns are solutions to software design problems you find again and again in real-world application development. Patterns are about reusable designs and interactions of objects.

    <end>

    Imports DAL Imports Entities Imports System.Web.Http Namespace Controllers <CustomExceptionFilter> Public Class ProjectController Inherits ApiController Private ReadOnly _daoproject As IDaoProject public sub New (daoproject As IDaoProject) _daoproject = daoproject End sub <HttpGet> <ActionName("GetProjectById")> public Function GetProjectById(ByVal id As Int32) As DtoProject return _daoproject.GetProjectById(id) End Function <HttpGet> <ActionName("GetProjectsByUserId")> public Function GetProjectsByUserId(ByVal userid As String) As List(Of DtoProject) return _daoproject.GetProjectsByUserId(userid) End Function <HttpPost> <ActionName("CreateProject")> public sub CreateProject(ByVal dto As DtoProject) Call _daoproject.CreateProject(dto) End sub <HttpPost> <ActionName("UpdateProject")> public sub UpdateProject(ByVal dto As DtoProject) Call _daoproject.UpdateProject(dto) End sub <HttpPost> <ActionName("DeleteProject")> public sub DeleteProject(ByVal dto As DtoId) Call _daoproject.DeleteProject(dto.Id) End sub End Class End Namespace ================================================== Imports Entities Public Interface IDaoProject Function GetProjectById(ByVal id As Int32) As DtoProject Function GetProjectsByUserId(ByVal userid As String) As List(Of DtoProject) Sub CreateProject(ByVal dto As DtoProject) Sub UpdateProject(ByVal dto As DtoProject) Sub DeleteProject(ByVal id As Int32) End Interface ===================================================== Imports System.Data.Entity Imports System.Transactions Imports Entities Public Class DaoProject Implements IDaoProject Public Function GetProjectById(ByVal id As Int32) As DtoProject Implements IDaoProject.GetProjectById Dim dto = New DtoProject() Using context As New ProjectManagementEntities Dim project = (context.Projects.Where(Function(a) a.ProjectId = id)).SingleOrDefault() If IsNothing(project) Then Return dto End If dto.ProjectId = project.ProjectId dto.ClientName = project.ClientName dto.ProjectName = project.ProjectName dto.Technology = project.Technology dto.ProjectType = project.ProjectType dto.UserId = project.UserId dto.StartDate = project.StartDate dto.EndDate = project.EndDate dto.Cost = project.Cost End Using Return dto End Function Public Function GetProjectsByUserId(ByVal userid As String) As List(Of DtoProject) Implements IDaoProject.GetProjectsByUserId Dim dtos = New List(Of DtoProject) Using context As New ProjectManagementEntities dtos = (From a In context.Projects.Where(Function(a) a.UserId.Contains(userid)) Select New DtoProject With {.ProjectId = a.ProjectId, .ClientName = a.ClientName, .ProjectName = a.ProjectName, .Technology = a.Technology, .ProjectType = a.ProjectType, .UserId = a.UserId, .StartDate = a.StartDate, .EndDate = a.EndDate, .Cost = a.Cost}).ToList() End Using Return dtos End Function Public Sub CreateProject(ByVal dto As DtoProject) Implements IDaoProject.CreateProject Using context As New ProjectManagementEntities Dim project = New Project() With {.ClientName = dto.ClientName, .ProjectName = dto.ProjectName, .Technology = dto.Technology, .ProjectType = dto.ProjectType, .UserId = dto.UserId, .StartDate = dto.StartDate, .EndDate = dto.EndDate, .Cost = dto.Cost} context.Projects.Add(project) context.SaveChanges() End Using End Sub Public Sub UpdateProject(ByVal dto As DtoProject) Implements IDaoProject.UpdateProject Dim project = New Project() Using context As New ProjectManagementEntities project = (context.Projects.Where(Function(a) a.ProjectId = dto.ProjectId)).SingleOrDefault() End Using If Not IsNothing(project) Then project.ClientName = dto.ClientName project.ProjectName = dto.ProjectName project.Technology = dto.Technology project.ProjectType = dto.ProjectType project.UserId = dto.UserId project.StartDate = dto.StartDate project.EndDate = dto.EndDate project.Cost = dto.Cost End If Using dbcontext As New ProjectManagementEntities If IsNothing(project) Then Exit Sub End If dbcontext.Entry(project).State = EntityState.Modified dbcontext.SaveChanges() End Using End Sub Public Sub DeleteProject(ByVal id As Int32) Implements IDaoProject.DeleteProject Dim project As Project Using context As New ProjectManagementEntities project = (context.Projects.Where(Function(a) a.ProjectId = id)).SingleOrDefault() End Using If IsNothing(project) Then Exit Sub End If Using newcontext As New ProjectManagementEntities Dim tasks = New DaoTask().GetTasksByProjectId(project.ProjectId) Using scope As New TransactionScope() for each dto As DtoTask In tasks Call New DaoTask().DeleteTask(dto.TaskId) Next newContext.Entry(project).State = EntityState.Deleted newContext.SaveChanges() scope.Complete() End Using End Using End Sub End Class


     

     


    • Edited by DA924x Tuesday, August 21, 2018 2:44 AM correction
    Tuesday, August 21, 2018 2:41 AM
  • Whoa! that look's way to complex code for my requirements...

    I only used excel and word as an example, the issue I'm having is detecting the public functions contained within the individual dll's, here is some basic code that I'm working with at the moment. Once I have this working the way I want, I'll upgrade the dll's to provide the actual functionality they'll be used for.

    '******************************************************************************* '* 1st Dynamic Link Library '******************************************************************************* Namespace dll_A '***************************************************************************** Public Class c_dll_A '*************************************************************************** Private Structure s_Info Dim Author as String Dim Version as String Dim Name as String Dim Description as String End Structure '*************************************************************************** Public Interface i_dll ReadOnly Property Info as s_Info End Interface '*************************************************************************** Public Property myProp() as string return Something '*** SOMETHING *** End Sub '*************************************************************************** Public Sub mySub() '*** DO SOMETHING *** End Sub '*************************************************************************** Public Function myFunc() as string return Something '*** SOMETHING *** End Sub '*************************************************************************** Public myTest_A as c_Test_A Public myTest_B as c_Test_B '*************************************************************************** End Class '***************************************************************************** End namespace '******************************************************************************* '******************************************************************************* '* ADDITIONAL CLASS CONATINED IN dll_A '******************************************************************************* Public Class c_Test_A '***************************************************************************** Public Sub Test_0 '*** DO SOMETHING *** End Sub '***************************************************************************** Public Sub Test_1 '*** DO SOMETHING *** End Sub '***************************************************************************** End Class '******************************************************************************* '******************************************************************************* '* ADDITIONAL CLASS CONATINED IN dll_A '******************************************************************************* Public Class c_Test_B '***************************************************************************** Public Sub Test_2 '*** DO SOMETHING *** End Sub '***************************************************************************** Public Sub Test_3 '*** DO SOMETHING *** End Sub '***************************************************************************** End Class '******************************************************************************* '******************************************************************************* '* 2nd Dynamic Link Library '******************************************************************************* Namespace dll_B '***************************************************************************** Public Class c_dll_B '*************************************************************************** Private Structure s_Info Dim Author as String Dim Version as String Dim Name as String Dim Description as String End Structure '*************************************************************************** Public Interface i_dll ReadOnly Property Info as s_Info End Interface '*************************************************************************** Public Property myProp() as string return Something '*** SOMETHING *** End Sub '*************************************************************************** Public Sub mySub() '*** DO SOMETHING *** End Sub '*************************************************************************** Public Function myFunc() as string return Something '*** SOMETHING *** End Sub End Class '***************************************************************************** End namespace '******************************************************************************* '******************************************************************************* '* Main Application '******************************************************************************* Public Class frm_Main '***************************************************************************** Private Sub frm_Main_Load(Sender as Object, e as EventArgs) Handles Me.Load Treeview.Nodes.Clear() Dim _node as Treenode = nothing '*** ADD ALL FOUND DLL's TO TREEVIEW *************************************** for each _dll as string in System.IO.Directory.Getfiles(System.Windows.Forms.Application.StartupPath, "*.dll") _node = new Treenode(_dll) _node.Text = System.IO.Path.GetFilenameWithoutExtension(_dll) Treeview.Nodes.Add(_node) '*** Load Assembly into node ********************************************* loadAssembly(_node, _dll) Next End Sub '***************************************************************************** Private Sub loadAssembly(Byval _node as Treenode, byval _dll as String) Dim _assembly as System.Reflection.Assembly = System.Reflection.Assembly.LoadFrom(_dll) Dim _subnode as Treenode = nothing '*** Add dll Functions to node ********************************************* For each _type as Type in _assembly.GetTypes() _subnode = new Treenode(_type.Name) _subnode.Text =_type.Name _node.Nodes.Add(_subnode) Next '***************************************************************************** End Class

    The purpose is to populate a TreeView for now, I'll implement context menus later on; with all the defined dll functions as follows. Whenever a treview or menu item get clicked the application will perform the dll function

    + dll_A  <- THis part works ok but would prefer to use dll_A.info.name
      + myProp
      + mySub
      + myFunc
      + myTest_A
        + Test_1
        + Test_2
      + myTest_B
        + Test_3
        + Test_4
    + dll_B  <- THis part works ok but would prefer to use dll_B.info.name
      + myProp
      + mySub
      + myFunc


    The problen is retrieveing the functions and sub functions within the dll's
     I appear to be getting loads of additional modules and information? and not the sub-class types.


    Tuesday, August 21, 2018 8:29 AM
  • The struct can be  converted into a class and placed into a classlib project called Entities that all projects have project reference to Entities. Entities contain DTO(s).

     

    Public Class DtoInfo
          public property Author as String
          public property Version as String
          public property Name as String
          public property Description as String
    End Class

    https://en.wikipedia.org/wiki/Data_transfer_object

    https://www.codeguru.com/vb/gen/vb_misc/oop/article.php/c7063/Data-Transfer-Object-Pattern-Goes-VBNET.htm

       Public Class c_dll_A
        implements Ic_dll_A
       
        Public Sub mySub()
    	'*** DO SOMETHING ***
        End Sub
    
        Public Function myFunc() as string
           return Something '*** SOMETHING ***
        End Sub
    
        Public myTest_A as c_Test_A
        Public myTest_B as c_Test_B
        End Class

    I don't know what myTest_A thing is about, and maybe it shouldn't be there at all. 

    Public Property myProp() as string
         Get
           return DoSomethingAndReturnResult()
         end get
    end property 

    I don't see why you cannot have a given class implement an Interface.

    An Interface a class implements can have public properties and/or methods a class can implement that are exposed publicly on the class by the Interface the class implements. 

    https://docs.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/interfaces/

    A classlib project is a container the holds classes, the classes can be instanced into objects and used by another classes/objects or used within itself, like a Form class the has objects on the Form called controls where a method in a class in the in the classlib project is called upon to get the data and populate a control on the form

     

    OOP follows the basic principals of class vs instance vs object. It doesn't matter what the language platform is of .NET or Java.

    https://alfredjava.wordpress.com/2008/07/08/class-vs-object-vs-instance/ 

    The problen is retrieveing the functions and sub functions within the dll's
    I appear to be getting loads of additional modules and information? and not the sub-class types.

    You shouldn't be trying to retrieve any functions or methods. You should be instancing an object in the classlib project,  and the methods the behavior acts upon objects that may or may not return results.

    The objects that have methods in a given class that has been instanced into an object would take a DTO and create the DTO, load it into a List(of T), a collection, and the method/behavior returns the collection of DTO(s)  as a result  to the Form control  and use the DTO or DTO(s), a custom type,  to populate the control.

    control.Datasource = new classlib().GetData()

    I don't know what you are doing up at the form. Well, I kind of know, but  what you are doing is not optimal programming using a classlib project with objects and using their behaviors.  

    I don't even know how one could even possibly conceive of such programming logic. It's no offense to you, but it's questionable to say the least about it.

    Something else you should consider, becuase it helps in Speration of Duty at the UI. The UI should be dumb as dumb as possible.

    https://www.codeproject.com/Articles/228214/Understanding-Basics-of-UI-Design-Pattern-MVC-MVP

     



     

     
    Tuesday, August 21, 2018 11:55 AM
  • Thanks for the replies DA924x

    Interfaces don't seem to like Public myVariable as myClass 

    in response to your query...

      'I don't know what myTest_A thing is about, and maybe it shouldn't be there at all. '

    My Library.dll contains a main class called c_Library and a selection of sub classes c_subClass, these sub classes contain a variety of functions which are used to perform certain tasks.

    Lets say I have a sub class function called Load, in the main class I have a variable called File defined as the sub class type. In my main application I can then perform a File.Load() function

    When My application starts, it search its library folder for dll's and adds then to a treeview, while doing this it adds all the members it finds in the dll (I managed to figure out how to limit this to the defined members and variables)

    I'm can't get the File methods() to be added to the file node, and I can't get the dll to ignore all it's sub class modules, I've tried every combination of assembly,module,ExportedTypes I can think of with no success!

    Thanks Again for the feedback.

    Tuesday, August 21, 2018 4:48 PM
  • Interfaces don't seem to like Public myVariable as myClass 

    No it doesn't,  and I don't see what that would accomplish either. A class is a type that has behavior that becomes an object when it is instantiated. A class is a blueprint that defines an object. 

    dim myobject = new myclass()

    dim somestringvaue as string  = myobject.GetStringData(). 

    Now you can have object within a class you can do that

    dim thisismyobj as myclass

    thisismyobj = new myclass()

    dim somestringvaue as string = thisismyobj.GetStringData()

    My Library.dll contains a main class called c_Library and a selection of sub classes c_subClass, these sub classes contain a variety of functions which are used to perform certain tasks.

    As I recall, becuase I have not done it in a long time, a single class/object  can implement multiple Interfaces  belonging to other classes. 

    When My application starts, it search its library folder for dll'sand adds then to a treeview, while doing this it adds all the members it finds in the dll (I managed to figure out how to limit this to the defined members and variables)

    What is the purpose of adding DLL(s) to a treeview node. This makes no sense to me. Are you expecting that you can execute some method in a class contained is a classlib project becuase you clicked on a treeview node?

    I'm can't get the File methods() to be added to the file node, and I can't get the dll to ignore all it's sub class modules, I've tried every combination of assembly,module,ExportedTypes I can think of with no success!

     This makes no sense to me as to what you are trying to accomplish. 



    • Edited by DA924x Tuesday, August 21, 2018 8:58 PM correction
    • Marked as answer by Alan_Barclay_Uk Wednesday, August 22, 2018 3:58 PM
    • Unmarked as answer by Alan_Barclay_Uk Wednesday, August 22, 2018 3:58 PM
    Tuesday, August 21, 2018 8:57 PM
  • Finally I figured it all out (I think)

       

    Private Sub loadLibrary(p_Node as TreeNode, p_dllFile as string)

    Dim _node0 as TreeNode = Nothing

    Dim _node1 as TreeNode = Nothing

    Dim _assembly As System.Reflection.Assembly = System.Reflection.Assembly.LoadFile(_dllFile) For Each _eType In _assembly.GetExportedTypes() If _eType.IsPublic Then For Each _field In _eType.GetFields() _node0 = New TreeNode(_field.Name) p_Node.Nodes.Add(_node0) For Each _method In _field.FieldType.GetMethods() If Not _method.IsHideBySig Then _node1 = New TreeNode(_method.Name) _node0.Nodes.Add(_node1) End If Next Next For Each _method In _eType.GetMethods() If _method.DeclaringType.Name = _eType.Assembly.GetName.Name Then If Not _method.IsHideBySig Then _node0 = New TreeNode(_method.Name) p_Node.Nodes.Add(_node0) End If End If Next End If Next

    End Sub

    It's a rough draught, and it only supports three tree levels, the first level being the dll filename (outwith this code!)

    I will probably implement some sort of recursive method to populate the tree to any number of levels and change the node Name, Tag, Text and tooltip to return useful information that I'll use in the tree click routine to dynamically call the appropriate function when required.

     Thanks for the help DA924x it was much appreciated.

    Tuesday, August 21, 2018 10:42 PM
  • I'll just say this. If you presented what you have and are doing for code review by VB.NET professional programmers, you would never make it out of the code review.

    You would be told to go back and redo it.  

    Wednesday, August 22, 2018 5:56 AM
  • Gee Thanks

    At least my code delivers what was required and in such a small a code footprint instead of pages and pages of meaningless code.

    Wednesday, August 22, 2018 8:29 AM
  • Gee Thanks

    At least my code delivers what was required and in such a small a code footprint instead of pages and pages of meaningless code.

    Anyone can write some code. But can one architecture a solution from the frontend to the backend  knowing what technologies to use and do it effectively.

    Maybe, one day you'll learn it.

    https://www.dofactory.com/net/design-patterns

    Wednesday, August 22, 2018 9:42 AM
  • So since your so clever what is the issue have with my solution, if my solution serves the purpose it was designed for?

    after all, I did post this on the forum as a question looking for some guidance on what was a clearly marked idea, you initially posted a selection of code that clearly didn't match what was being asked.

    The fact that you have become negative, only goes to show how immature you are, and maybe one day you'll grow up!

    Wednesday, August 22, 2018 3:08 PM
  • So since your so clever what is the issue have with my solution, if my solution serves the purpose it was designed for?

    after all, I did post this on the forum as a question looking for some guidance on what was a clearly marked idea, you initially posted a selection of code that clearly didn't match what was being asked.

    The fact that you have become negative, only goes to show how immature you are, and maybe one day you'll grow up!

    I have gone negative? I am just telling you the truth. You're right and everyone else is wrong, right? I have only been doing this for about 30 some years  or more programming professionally.

    Your problem is that you already know it all, but you don't know anything is your problem. You don't know the first thing about Object Oriented Programming you really don't. You're out here playing programmer a hobbyist. 

    Wednesday, August 22, 2018 4:20 PM
  • Actually that's what I could say about you. If you'd take the time to back up a little you may notice that I was initially asking a question not posting a solution.

    I haven't said there was anything wrong with your reply except that it was overly complex for the question asked, so obviously 30 years experience hasn't helped you determine what's being asked and how to respond at a level required by the person asking the question in the first place, instead of just copy pasting what appears to be a totally obscure piece of code that doesn't clearly do what is required.

    You then go on to trash my coding capabilities without knowing what experience I have or how long I have been programming, Your problem is that after 30 years being an a-hole you haven't learned a thing about how to be helpful in any aspect of programming. I haven't stated at any time that I know everything, in fact the whole reason I'm on this forum clearly proves that which seems to have escaped your vastly superior (self overrated) brain.

    Just to put you straight, I've also been programming 30+ years in a professional environment with my software being used globally in several industries, I generally don't use VB as a rule but this is a special request. I heavily use OOP in all my projects to facilitate re-usable code.

    Now as to my initial question, I have developed a working solution and your initial reply although overly complex did give me a few alternate ideas to try out, IInterface as an example although in this instance I have no need to use this for now.

    Your assistance has been appreciated however your negativity clearly identifies you as someone who needs to get out in the real world a little bit more and to possibly get laid once in a while. Why give anyone undeserved negative feedback unless you are afraid of being bettered by someone less experienced!

    Be nice it makes the world a better place.

    Friday, August 31, 2018 10:00 AM
  • I didn't read it, go on now and stand in the corner and face the wall. 
    Friday, August 31, 2018 10:19 AM
  • Obviously...
    Sunday, September 2, 2018 12:10 PM
  • Obviously...

    Alan_Barclay_Uk


    Yeah that you are demented clown. 
    • Edited by DA924x Sunday, September 2, 2018 12:29 PM
    Sunday, September 2, 2018 12:28 PM