Ошибка вызова неуправляемой функции DLL из управляемого кода - PullRequest
3 голосов
/ 22 января 2011

Я написал DLL на неуправляемом Visual C ++, и у меня возникли небольшие проблемы с тем, чтобы заставить его работать как с приложениями C #, так и C ++.Вот как выглядит прототип в C ++ DLL:

extern "C" __declspec(dllexport) int WINAPI ZBNConnect( UCHAR dev, LPARAM hWnd, ZBCallbackFn rfn, ZBCallbackFn nfn, int DevType, byte * DevAddr, ZBCallbackFn dfn );

Мое приложение C # может связываться с функцией, нет проблем, но при попытке вызвать функцию выдается исключение:

catch (Exception e) { /* ... */ }

e.Message = "Ссылка на объект не установлена ​​для экземпляра объекта."

Странно, если я возьму WINAPI из прототипа в DLL и перекомпилирую, вызовет приложение C #функция без проблем.К сожалению, WINAPI должен остаться, потому что именно так определяется функция в приложении C ++.

Функция в настоящее время прототипируется в приложении C # следующим образом:

public delegate int ZBNConnectDelegate(uint dev, IntPtr hWnd, USBCallbackDelegate rfn, NotifyCallbackDelegate nfn, uint DevType, byte[] DevAddr, ZBdebugCallbackDelegate dfn);
public ZBNConnectDelegate ZBNConnect;

procName = "ZBNConnect";
fUintPtr = Kernel32.GetProcAddress(dllHandle, procName);

if (fUintPtr == UIntPtr.Zero)
{
    throw new ArgumentException(procName);
}

fIntPtr = unchecked((IntPtr)(long)(ulong)fUintPtr);
ZBNConnect = (ZBNConnectDelegate)Marshal.GetDelegateForFunctionPointer(fIntPtr, typeof(ZBNConnectDelegate));

Как можноЯ изменить приложение C #, чтобы это работало?Спасибо.

РЕДАКТИРОВАТЬ: Дополнительная информация

Статическая ссылка ([DllImport...]) не вариант, потому что в зависимости от того, какое оборудование подключено к системе, различные библиотеки DLL, которыеподдерживает подключенное оборудование загружается во время выполнения.Обе библиотеки DLL имеют одинаковые вызовы API.

Ответы [ 5 ]

3 голосов
/ 22 января 2011

Что-то в принципе не так.Вы объявили делегата, как будто функция является обратным вызовом.Совсем не похоже на обратный вызов, оно похоже на то, что вы должны объявить с помощью [DllImport].Чтобы заставить его работать так, как вы, вам нужно было бы вызвать LoadLibrary и GetProcAddress ().Что [DllImport] делает под капотом.Я не вижу, чтобы вы это использовали.

0 голосов
/ 25 января 2011

Оказалось, что добавление WINAPI в объявление и определение функции приводило к искажению имени функции в DLL.К сожалению, WINAPI требовалось для обеспечения совместимости с уже развернутыми приложениями.Исправление заключалось в добавлении дополнительного экспорта в компоновщик:

#pragma comment(linker, "/EXPORT:ZBNConnect=_ZBNConnect@28")
0 голосов
/ 24 января 2011

Если вы можете определить правильную DLL во время выполнения со стороны C #, я бы использовал два объявления DllImport:

[DllImport("Dll1.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "ZBNConnect")]
extern static int ZBNConnect1(...)
[DllImport("Dll2.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "ZBNConnect")]
extern static int ZBNConnect2(...)

public static int ZBNConnect(...)
{
    if (UseDll1)
        return ZBNConnect1(...);
   return ZBNConnect2(...);
}

Примечание

P / Invoke isпередается при динамической загрузке.Если вы никогда не звоните ZBNConnect1, Dll1.dll никогда не загружается и наоборот.

Обновлено

Добавлен EntryPoint = в DllImport.

0 голосов
/ 24 января 2011

extern "C" __declspec(dllexport) int WINAPI ZBNConnect( UCHAR dev, LPARAM hWnd, ZBCallbackFn rfn, ZBCallbackFn nfn, int DevType, byte * DevAddr, ZBCallbackFn dfn );

public delegate int ZBNConnectDelegate(uint dev, IntPtr hWnd, USBCallbackDelegate rfn, NotifyCallbackDelegate nfn, uint DevType, byte[] DevAddr, ZBdebugCallbackDelegate dfn);

C ++ UCHAR (dev) являются однобайтовыми значениями, а C # uint - 4. Вы разрушаете стек, когда выполняете собственный вызов, и по какой-то причине WINAPI позволяет вам сойти с рук. Кроме того, использование такого делегата вместо обычного P / Invoke DllImport, скорее всего, вызовет у вас проблемы, поскольку вы не можете настроить способ маршалинга.

0 голосов
/ 24 января 2011

Макрос WINAPI изменяет соглашение о вызовах.

Попробуйте

[UnmanagedFunctionPointer(CallingConvention = CallingConvention.StdCall]
public delegate Int32 ZBNConnectDelegate(Byte dev, IntPtr hWnd, USBCallbackDelegate rfn, NotifyCallbackDelegate nfn, Int32 DevType, Byte[] DevAddr, ZBdebugCallbackDelegate dfn);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...