Не могу перезагрузить C++ DLL в C# Project

Я надеюсь, что есть кто-то, кто может помочь мне с загрузкой и перезагрузкой DLL C++ в проект C#.

Я загружаю C++ dll в свой проект с помощью средства выбора файлов, а затем копирую его в файл с именем Process.dll. Затем я использую эту dll для выполнения своей задачи, а затем мне понадобится возможность загрузить новую dll и выполнить ее.

Итак, это выглядит так:

  1. Пользователь выбирает dll
  2. DLL копируется в Processor.dll
  3. сделать мой класс Processor, который использует импорт DLL, чтобы использовать DLL
  4. Используйте dll и вызывайте ее различные функции и т. Д.
  5. Вернитесь к 1.

У меня 2 класса

  1. ProcessTab - класс C#, имеющий графический интерфейс и вызывающий классы в ProcessPlugin
  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, которая не обрабатывается, как показано в комментариях в коде.

У кого-нибудь есть идеи, как мне это решить?

Спасибо заранее

1 ответ

Решение

P/Invoke не предназначен для обработки этого случая. Предполагается, что библиотека никогда не меняется, и я уверен, что она не ожидает, что вы позвоните FreeLibrary на модулях это импортирует.

Если вы хотите использовать dll динамически, вам придется вручную сделать эквивалент того, что делает P/Invoke под капотом: use 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);
}

Вы используете это так:

  • Определить делегата:

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    private delegate ErrorCode ProcessorFunction(ref IntPtr pRx);
    
  • Загрузите библиотеку:

    IntPtr hLib = UnsafeNativeMethods.LoadLibrary("Processor.dll");
    
  • Получить делегата:

    IntPtr ctorPtr = UnsafeNativeMethods.GetProcAddress(hLib, "VE_ProcessorPluginLib_Rx_API_Constructor");
    ProcessorFunction constructorFn = (ProcessorFunction)Marshal.GetDelegateForFunctionPointer(ctorPtr, typeof(ProcessorFunction));
    
  • Назови это:

    conctructorFn(ref pRxClass);
    
  • Когда вы закончите, освободите библиотеку (лучше всего в finally блок, SafeHandle или что-то, чтобы этот шаг был сделан)

    UnsafeNativeMethods.FreeLibrary(hLib);
    
  • Затем повторите все эти шаги для второй библиотеки.

Очевидно, я не могу проверить это, потому что у меня нет твоих библиотек, но это должно поставить тебя на правильный путь.
Я оставил добавление проверок ошибок для краткости, но вы определенно должны добавить их.

Другие вопросы по тегам