c# - 无法在 C# 项目中重新加载 C++ dll

标签 c# c++ .net dll

我希望有人可以帮助我将 C++ dll 加载和重新加载到 C# 项目中。

我正在使用文件选取器将 C++ dll 加载到我的项目中,然后将其复制到名为 Process.dll 的文件中。然后我使用这个 dll 来做它的事情,然后我需要能够加载一个新的 dll 并让它做它的事情。

所以它是这样的:

  1. 用户选择一个dll
  2. dll 被复制到 Processor.dll
  3. 制作我的处理器类,它使用 dll 导入来使用 dll
  4. 使用dll并调用其各种函数等
  5. 回到 1。

我有两个类(class)

  1. ProcessTab - 具有 GUI 并调用 ProcessPlugin 中的类的 C# 类
  2. ProcessPlugin - 调用 C++ 类的 C# 类,它使用 dllImport 等

代码如下所示:

class ProcessorTab
{
    private void buttonLoadProcDll_Click(object sender, RoutedEventArgs e)
    {
        // Open dll and copy it to "\\Processor.dll"

        processorPlugIn = new ProcessorPlugIn(this);
        return;
    }

    private void DestroyProcessor_Click(object sender, RoutedEventArgs e)
    {
        processorPlugIn.UnloadModule("Processor.dll");
        processorPlugIn = null;
    }
}

class ProcessorPlugIn
{ 
    public const string PluginName = "Processor.dll";

    public LibraryInfo info;
    ErrorCode err;
    private IntPtr pRxClass;
    private IntPtr pTxClass;

    [DllImport("kernel32", SetLastError = true)]
    static extern bool FreeLibrary(IntPtr hModule);

    [System.Runtime.InteropServices.DllImportAttribute(PluginName, CallingConvention = CallingConvention.Cdecl)]
    public static extern ErrorCode VE_ProcessorPluginLib_Rx_API_Constructor(ref IntPtr pRx);

    [System.Runtime.InteropServices.DllImportAttribute(PluginName, CallingConvention = CallingConvention.Cdecl)]
    public static extern ErrorCode VE_ProcessorPluginLib_Rx_API_Destructor(ref IntPtr pRx);

    [System.Runtime.InteropServices.DllImportAttribute(PluginName, CallingConvention = CallingConvention.Cdecl)]
    public static extern ErrorCode VE_ProcessorPluginLib_Tx_API_Constructor(ref IntPtr pTx);

    [System.Runtime.InteropServices.DllImportAttribute(PluginName, CallingConvention = CallingConvention.Cdecl)]
    public static extern ErrorCode VE_ProcessorPluginLib_Tx_API_Destructor(ref IntPtr pTx);

    public ProcessorPlugIn(MainWindow mWindow)
    {
        pRxClass = new IntPtr();
        pTxClass = new IntPtr();

        if (VE_ProcessorPluginLib_Rx_API_Constructor(ref pRxClass) != ErrorCode.EC_OK) // ERROR HERE: AccessViolationException was unhandled
            MessageBox.Show("Error constructing Rx");

        if (VE_ProcessorPluginLib_Tx_API_Constructor(ref pTxClass) != ErrorCode.EC_OK)
            MessageBox.Show("Error constructing Tx");
    }

    public void UnloadModule(string moduleName)
    {
        if (VE_ProcessorPluginLib_Rx_API_Destructor(ref pRxClass) != ErrorCode.EC_OK)
            MessageBox.Show("Error destropying Rx");

        if (VE_ProcessorPluginLib_Tx_API_Destructor(ref pTxClass) != ErrorCode.EC_OK)
            MessageBox.Show("Error destropying Rx");

        foreach (ProcessModule mod in Process.GetCurrentProcess().Modules)
        {
            if (mod.ModuleName == moduleName)
            {
                FreeLibrary(mod.BaseAddress);
            }
        }
    }     
}

一切正常,我调用 C++ dll 中的方法并获得预期的输出等,但是当我尝试销毁它并重新加载它时,我得到一个错误 AccessViolationException was unhandled,如代码中的注释所示。

有人知道我该如何解决这个问题吗?

提前致谢

最佳答案

P/Invoke 并不意味着处理这种情况。它假设库永远不会改变,我敢肯定它不希望您在它导入的模块上调用 FreeLibrary

如果你想动态地使用dlls,你将不得不手动做相当于P/Invoke在幕后做的事情:使用LoadLibrary/GetProcAddress :

private static class UnsafeNativeMethods
{
    [DllImport("kernel32", SetLastError=true, CharSet = CharSet.Ansi)]
    static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)]string lpFileName);

    [DllImport("kernel32", CharSet=CharSet.Ansi, ExactSpelling=true, SetLastError=true)]
    static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

    [DllImport("kernel32", SetLastError=true)]
    static extern bool FreeLibrary(IntPtr hModule);
}

你可以像这样使用它们:

  • 定义委托(delegate):

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    private delegate ErrorCode ProcessorFunction(ref IntPtr pRx);
    
  • 加载库:

    IntPtr hLib = UnsafeNativeMethods.LoadLibrary("Processor.dll");
    
  • 获得委托(delegate):

    IntPtr ctorPtr = UnsafeNativeMethods.GetProcAddress(hLib, "VE_ProcessorPluginLib_Rx_API_Constructor");
    ProcessorFunction constructorFn = (ProcessorFunction)Marshal.GetDelegateForFunctionPointer(ctorPtr, typeof(ProcessorFunction));
    
  • 调用它:

    conctructorFn(ref pRxClass);
    
  • 完成后,释放库(最好在 finally block 、SafeHandle 或其他确保此步骤完成的东西中):

    UnsafeNativeMethods.FreeLibrary(hLib);
    
  • 然后对第二个库重复所有这些步骤。

显然我无法测试这个,因为我没有你的库,但它应该会让你走上正轨。
为了简洁起见,我没有添加失败检查,但您绝对应该添加这些。

关于c# - 无法在 C# 项目中重新加载 C++ dll,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26873309/

相关文章:

c# - 使用 GetProcAddress 从 C# 调用 C# DLL

c# - KnownType 属性不能使用类型参数

c++ - 基于 MSVC 10 范围的 for 循环

.net - RxJS/Rx.Net 可观察订阅与事件 - 性能/线程

c# - 请推荐.NET ORM进行N层开发

c# - 如何获取具有我的 MEF 插件的每个 DLL 的版本号?

c# - 在网站上处理信用卡订阅的最佳方式是什么?

c# - 为什么 C# 将整数类型实现为结构而不是原始类型?

c++ - C++ 中是否有等效的 realloc?

c++ - 为什么这段代码打印的是字符串而不是地址?