3

I want to load a DLL in C# dynamically so i can unload it at execution time. I found some articles but none really helped me. I need to unload the DLL because it does not provide any functions to release/clean up the memory. The actual problem: Its a hardware driver (CAN-USB) and i need to restart my application if the hardware gets disconnected, which is very annoying. If i could load it into an assembly (or something like that) i could "unload" respectively "reload" it. I would need a short and minimalistic, but relating example how to load my dll and how to import and use the dll functions. I attached a screenshot how i am using them right now.

What I need summarized:

  1. Example on how to load a dll (of the attached type) dynamically and how to use its functions.

  2. How to unlead/reload the dll in case of hardware disconnection.

I would appreciate any suggestions.

dll functions

EDIT 1: I have now implemented it like it is described in this approach but it does not solve my problem. The CAN-USB-Hardware blocks until i close my application and start it again. Did i do something wrong or does anyone have any other suggestions how to solve my initial problem?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace InDiPro
{
    public struct canData
    {
        public uint id;
        public uint length;
        public byte data0;
        public byte data1;
        public byte data2;
        public byte data3;
        public byte data4;
        public byte data5;
        public byte data6;
        public byte data7;
    }

    public class EsdCanDriver
    {
        private IntPtr pDll;
        private string dllPath;

        private IntPtr fptrCanOpen;
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate int CanOpen(int net, int mode, int txqueueSize, int rxqueueSize, int txTimeout, int rxTimeout, ref int handle);
        private static CanOpen _canOpen;
        public int canOpen(int net, int mode, int txqueueSize, int rxqueueSize, int txTimeout, int rxTimeout, ref int handle)
        {
            return _canOpen(net, mode, txqueueSize, rxqueueSize, txTimeout, rxTimeout, ref handle);
        }

        private IntPtr fptrCanClose;
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate int CanClose(int handle);
        private static CanClose _canClose;
        public int canClose(int handle)
        {
            return _canClose(handle);
        }

        private IntPtr fptrCanSetBaudrate;
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate int CanSetBaudrate(int handle, int baudrate);
        private static CanSetBaudrate _canSetBaudrate;
        public int canSetBaudrate(int handle, int baudrate)
        {
            return _canSetBaudrate(handle, baudrate);
        }

        private IntPtr fptrCanGetBaudrate;
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate int CanGetBaudrate(int handle, ref int baudrate);
        private static CanGetBaudrate _canGetBaudrate;
        public int canGetBaudrate(int handle, ref int baudrate)
        {
            return _canGetBaudrate(handle, ref baudrate);
        }

        private IntPtr fptrCanIdAdd;
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate int CanIdAdd(int handle, int id);
        private static CanIdAdd _canIdAdd;
        public int canIdAdd(int handle, int id)
        {
            return _canIdAdd(handle, id);
        }

        private IntPtr fptrCanIdDelete;
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate int CanIdDelete(int handle, int id);
        private static CanIdDelete _canIdDelete;
        public int canIdDelete(int handle, int id)
        {
            return _canIdDelete(handle, id);
        }

        private IntPtr fptrCanSend;
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate int CanSend(int handle, ref canData msg, ref int length);
        private static CanSend _canSend;
        public int canSend(int handle, ref canData msg, ref int length)
        {
            return _canSend(handle, ref msg, ref length);
        }

        private IntPtr fptrCanTake;
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate int CanTake(int handle, ref canData msg, ref int length);
        private static CanTake _canTake;
        public int canTake(int handle, ref canData msg, ref int length)
        {
            return _canTake(handle, ref msg, ref length);
        }

        private IntPtr fptrCanWrite;
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate int CanWrite(int handle, ref canData msg, ref int length, ref int dummy);
        private static CanWrite _canWrite;
        public int canWrite(int handle, ref canData msg, ref int length, ref int dummy)
        {
            return _canWrite(handle, ref msg, ref length, ref dummy);
        }

        private IntPtr fptrCanRead;
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate int CanRead(int handle, ref canData msg, ref int length, ref int dummy);
        private static CanRead _canRead;
        public int canRead(int handle, ref canData msg, ref int length, ref int dummy)
        {
            return _canRead(handle, ref msg, ref length, ref dummy);
        }

        public EsdCanDriver(string dllPath)
        {
            this.dllPath = dllPath;
        }

        public bool LoadDriver()
        {
            pDll = NativeMethods.LoadLibrary(this.dllPath);
            if(pDll == IntPtr.Zero)
            {
                return false;
            }
            else
            {
                fptrCanOpen = NativeMethods.GetProcAddress(pDll, "__canOpen@28");
                if (fptrCanOpen == IntPtr.Zero) return false;
                _canOpen = (CanOpen)Marshal.GetDelegateForFunctionPointer(fptrCanOpen, typeof(CanOpen));

                fptrCanClose = NativeMethods.GetProcAddress(pDll, "__canClose@4");
                if (fptrCanClose == IntPtr.Zero) return false;
                _canClose = (CanClose)Marshal.GetDelegateForFunctionPointer(fptrCanClose, typeof(CanClose));

                fptrCanSetBaudrate = NativeMethods.GetProcAddress(pDll, "__canSetBaudrate@8");
                if (fptrCanSetBaudrate == IntPtr.Zero) return false;
                _canSetBaudrate = (CanSetBaudrate)Marshal.GetDelegateForFunctionPointer(fptrCanSetBaudrate, typeof(CanSetBaudrate));

                fptrCanGetBaudrate = NativeMethods.GetProcAddress(pDll, "__canGetBaudrate@8");
                if (fptrCanGetBaudrate == IntPtr.Zero) return false;
                _canGetBaudrate = (CanGetBaudrate)Marshal.GetDelegateForFunctionPointer(fptrCanGetBaudrate, typeof(CanGetBaudrate));

                fptrCanIdAdd = NativeMethods.GetProcAddress(pDll, "__canIdAdd@8");
                if (fptrCanIdAdd == IntPtr.Zero) return false;
                _canIdAdd = (CanIdAdd)Marshal.GetDelegateForFunctionPointer(fptrCanIdAdd, typeof(CanIdAdd));

                fptrCanIdDelete = NativeMethods.GetProcAddress(pDll, "__canIdDelete@8");
                if (fptrCanIdDelete == IntPtr.Zero) return false;
                _canIdDelete = (CanIdDelete)Marshal.GetDelegateForFunctionPointer(fptrCanIdDelete, typeof(CanIdDelete));

                fptrCanSend = NativeMethods.GetProcAddress(pDll, "__canSend@12");
                if (fptrCanSend == IntPtr.Zero) return false;
                _canSend = (CanSend)Marshal.GetDelegateForFunctionPointer(fptrCanSend, typeof(CanSend));

                fptrCanTake = NativeMethods.GetProcAddress(pDll, "__canTake@12");
                if (fptrCanTake == IntPtr.Zero) return false;
                _canTake = (CanTake)Marshal.GetDelegateForFunctionPointer(fptrCanTake, typeof(CanTake));

                fptrCanWrite = NativeMethods.GetProcAddress(pDll, "__canWrite@16");
                if (fptrCanWrite == IntPtr.Zero) return false;
                _canWrite = (CanWrite)Marshal.GetDelegateForFunctionPointer(fptrCanWrite, typeof(CanWrite));

                fptrCanRead = NativeMethods.GetProcAddress(pDll, "__canRead@16");
                if (fptrCanRead == IntPtr.Zero) return false;
                _canRead = (CanRead)Marshal.GetDelegateForFunctionPointer(fptrCanRead, typeof(CanRead));

                return true;
            }         
        }

        public bool FreeDriver()
        {
            return NativeMethods.FreeLibrary(pDll);
        }
    }
}
nimp0u
  • 123
  • 2
  • 6
  • Did you try to search for existing code? The important functions you need to call are LoadLibrary and GetProcAddress, here's an example: http://blogs.msdn.com/b/jonathanswift/archive/2006/10/03/dynamically-calling-an-unmanaged-dll-from-.net-_2800_c_23002900_.aspx – Lasse V. Karlsen Jan 09 '15 at 11:23
  • @LasseV.Karlsen I'm not able to see the page - the link as provided doesn't seem to work. – Thorsten Dittmar Jan 09 '15 at 11:25
  • 3
    Too late for me to edit that comment, but let's try again: [Dynamically calling an unmanaged dll from .NET (C#)](http://blogs.msdn.com/b/jonathanswift/archive/2006/10/03/dynamically-calling-an-unmanaged-dll-from-.net-_2800_c_23002900_.aspx) Some dots and similar mangled the link but this seems to work. – Lasse V. Karlsen Jan 09 '15 at 11:30
  • @LasseV.Karlsen Great link! I didn't actually know you could do that and it looks even quite simple. – Thorsten Dittmar Jan 09 '15 at 11:45
  • @LasseV.Karlsen Thx! Can you explain how to usw that for my functions? Where do i have to put the entry point etc.? Would be nice if you can (fully) explain for one of my functions from the screenshot! – nimp0u Jan 12 '15 at 08:52
  • I have implemented that approach but it does not solve my problem. Read Edit 1 please. – nimp0u Jan 12 '15 at 11:43

2 Answers2

2

You are using DLLImport, so you are importing function from a native assembly, so you need to use the same function that you will use in native languages : LoadLibrary, GetProcAddress, FreeLibrary. These functions perform the following tasks:

LoadLibrary : Loads the DLL into memory and returns a raw address of the DLL’s handle. If the DLL cannot be found, returns IntPtr.Zero.

GetProcAddress : Loads a function by name from the DLL. Returns a raw address of the function. If the function cannot be found, returns IntPtr.Zero.

FreeLibrary : Releases the DLL loaded by the LoadLibrary function.

An example of use of LoadLibrary could be :

 IntPtr address = win32.GetProcAddress(m_dll, moduleName);
 System.Delegate fn_ptr = Marshal.GetDelegateForFunctionPointer(address, typeof(T));

While an example of FreeLibrary could be :

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
static extern IntPtr GetModuleHandle(string moduleName); 
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
[return: MarshalAs(UnmanagedType.Bool)]  
static extern bool FreeLibrary(IntPtr hModule); 
// Unload the DLL by calling GetModuleHandle and FreeLibrary.  
FreeLibrary(GetModuleHandle(moduleName));
Community
  • 1
  • 1
aleroot
  • 71,077
  • 30
  • 176
  • 213
1

You can not really unload a DLL in .NET. Neither an assembly nor a native DLL that's loaded when accessing a function imported vial DllImport.

You can, however, remove application domains. So the usual way of unloading a DLL is to load it in a separate application domain and then throw that away if the DLL is no longer needed.

I've actually not tried this, but you could try to generate an interface .NET DLL that exposes the DllImports as a class and then load this DLL into a separate application domain. If you free the domain, in theory, the native DLL should be unloaded, too.


Of course, another way could be to use LoadLibrary and FreeLibrary to load/unload the DLL, but I don't know how to access DLL functions then.

Thorsten Dittmar
  • 55,956
  • 8
  • 91
  • 139
  • Why can't you unload a dll that is loaded dynamically with LoadLibrary? Doesn't FreeLibrary unload it when the reference count reaches 0? – Lasse V. Karlsen Jan 09 '15 at 11:31
  • That's not what I'm trying to say. Of course you can use `LoadLibrary` and `FreeLibrary`, but not together with `DllImport`. I'll clarify. I've not yet seen DLL functions being used from DLLs loaded with `LoadLibrary`, so I can not give an example for that. – Thorsten Dittmar Jan 09 '15 at 11:38
  • I have read about your approach but i dont understand how to use it respectively initialize my function prototypes. Thats the reason why i asked for an related example or preferably an explenation with my functions from my screenshot. But i appreciate your suggestion. – nimp0u Jan 12 '15 at 08:22
  • Well, all you need to do is create a .NET DLL that uses `DllImport` normally as you have done in your screenshot. Create a public class that exposes a method per `DllImport`-function that calls the function. You just don't reference the DLL from your project, but load it into a new application domain as described here: http://stackoverflow.com/questions/88717/loading-dlls-into-a-separate-appdomain. You can later unload the application domain using `AppDomain.Unload(domain);`. This should also unload the native DLL. – Thorsten Dittmar Jan 12 '15 at 08:43