Не могу перезагрузить C++ DLL в C# Project
Я надеюсь, что есть кто-то, кто может помочь мне с загрузкой и перезагрузкой DLL C++ в проект C#.
Я загружаю C++ dll в свой проект с помощью средства выбора файлов, а затем копирую его в файл с именем Process.dll. Затем я использую эту dll для выполнения своей задачи, а затем мне понадобится возможность загрузить новую dll и выполнить ее.
Итак, это выглядит так:
- Пользователь выбирает dll
- DLL копируется в Processor.dll
- сделать мой класс Processor, который использует импорт DLL, чтобы использовать DLL
- Используйте dll и вызывайте ее различные функции и т. Д.
- Вернитесь к 1.
У меня 2 класса
- ProcessTab - класс C#, имеющий графический интерфейс и вызывающий классы в ProcessPlugin
- 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);
Затем повторите все эти шаги для второй библиотеки.
Очевидно, я не могу проверить это, потому что у меня нет твоих библиотек, но это должно поставить тебя на правильный путь.
Я оставил добавление проверок ошибок для краткости, но вы определенно должны добавить их.