在 C# 中动态加载和使用 DLL

Posted

技术标签:

【中文标题】在 C# 中动态加载和使用 DLL【英文标题】:Dynamic loading and using a DLL in c# 【发布时间】:2015-01-09 11:19:36 【问题描述】:

我想在 C# 中动态加载一个 DLL,这样我就可以在执行时卸载它。我找到了一些文章,但没有一个真正帮助我。我需要卸载 DLL,因为它不提供任何释放/清理内存的功能。实际问题:它是一个硬件驱动程序(CAN-USB),如果硬件断开连接,我需要重新启动我的应用程序,这很烦人。如果我可以将它加载到程序集中(或类似的东西),我可以“卸载”分别“重新加载”它。我需要一个简短但相关的示例,如何加载我的 dll 以及如何导入和使用 dll 函数。我附上了我现在如何使用它们的屏幕截图。

我需要总结的:

    有关如何动态加载(附加类型的)dll 以及如何使用其功能的示例。

    如何在硬件断开的情况下解除/重新加载 dll。

如果有任何建议,我将不胜感激。

编辑 1: 我现在已经实现了它,就像this 方法中描述的那样,但它确实解决了我的问题。 CAN-USB-Hardware 会阻塞,直到我关闭我的应用程序并重新启动它。我是不是做错了什么,或者有人对如何解决我最初的问题有任何其他建议吗?

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);
        
    

【问题讨论】:

您是否尝试搜索现有代码?您需要调用的重要函数是 LoadLibrary 和 GetProcAddress,这是一个示例:blogs.msdn.com/b/jonathanswift/archive/2006/10/03/…2800_c_23002900.aspx @LasseV.Karlsen 我看不到该页面 - 提供的链接似乎不起作用。 我来不及编辑该评论,但让我们再试一次:Dynamically calling an unmanaged dll from .NET (C#) 一些点和类似内容损坏了链接,但这似乎有效。 @LasseV.Karlsen 很棒的链接!我实际上不知道你能做到这一点,它看起来甚至很简单。 @LasseV.Karlsen 谢谢!你能解释一下如何为我的功能使用它吗?我必须在哪里放置入口点等?如果您能(完全)从屏幕截图中解释我的一项功能,那就太好了! 【参考方案1】:

您正在使用 DLLImport,因此您正在从本机程序集中导入函数,因此您需要使用将在本机语言中使用的相同函数:LoadLibrary、GetProcAddress、FreeLibrary。 这些函数执行以下任务:

LoadLibrary : 将 DLL 加载到内存中并返回原始地址 DLL 的句柄。如果找不到 DLL,则返回 IntPtr.Zero。

GetProcAddress :按名称从 DLL 加载函数。返回一个原始的 函数的地址。如果找不到该函数,则返回 IntPtr.Zero.

FreeLibrary : 释放​​ LoadLibrary 加载的 DLL 功能。

LoadLibrary 的使用示例如下:

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

虽然 FreeLibrary 的示例可能是:

[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));

【讨论】:

您能否提供一个示例,说明如何通过LoadLibrary 方法使用上述canOpen 函数(参见原始屏幕截图)? 我将不胜感激! 我已经实现了这种方法,但它并没有解决我的问题。请阅读编辑 1。【参考方案2】:

您不能真正在 .NET 中卸载 DLL。访问函数导入的小瓶 DllImport 时,既不是程序集也不是本机 DLL。

但是,您可以删除应用程序域。因此,卸载 DLL 的常用方法是将其加载到单独的应用程序域中,然后在不再需要 DLL 时将其丢弃。

我实际上没有尝试过,但您可以尝试生成一个接口 .NET DLL,将DllImports 作为一个类公开,然后将此 DLL 加载到单独的应用程序域中。如果你释放域,理论上,本地 DLL 也应该被卸载。


当然,另一种方法是使用LoadLibraryFreeLibrary 加载/卸载DLL,但我不知道如何访问DLL 函数。

【讨论】:

为什么不能卸载用LoadLibrary动态加载的dll?引用计数为0时FreeLibrary不卸载吗? 这不是我想说的。当然你可以使用LoadLibraryFreeLibrary,但不能和DllImport一起使用。我会澄清的。我还没有看到使用LoadLibrary 加载的 DLL 中使用的 DLL 函数,所以我不能举个例子。 我已经阅读了您的方法,但我不明白如何使用它分别初始化我的函数原型。这就是为什么我要一个相关的例子,或者最好是从我的屏幕截图中对我的功能进行解释。但我很欣赏你的建议。 好吧,您需要做的就是创建一个使用DllImport 的.NET DLL,就像您在屏幕截图中所做的那样。创建一个公共类,为每个调用该函数的DllImport-function 公开一个方法。您只是不从项目中引用 DLL,而是将其加载到新的应用程序域中,如下所述:***.com/questions/88717/…。您可以稍后使用AppDomain.Unload(domain); 卸载应用程序域。这也应该卸载本机 DLL。

以上是关于在 C# 中动态加载和使用 DLL的主要内容,如果未能解决你的问题,请参考以下文章

C#中如何动态加载和卸载DLL

C# 写一个动态加载DLL的程序 怎么写?

从另一个 C# DotNet DLL 动态加载和调用 C# DotNet DLL

AppDomain 详解二-C#中动态加载和卸载DLL

C#动态dll系统问题

Unity中动态加载dll文件