Правильный способ вызова метода C DLL из C # - PullRequest
1 голос
/ 28 декабря 2011

Я пытаюсь выполнить некоторые методы (в данном конкретном случае, rdOnAllDone) из сторонней библиотеки DLL, написанной на C, и просматривая файлы заголовков, я нашел это:

#ifndef TDECLSDONE
#ifdef STDCALL
#define     CCON        __stdcall
#else
#define     CCON        __cdecl
#endif
#define TDECLSDONE
#endif
#define     DLLIMP      __declspec (dllimport)
DLLIMP int CCON rdOnAllDone (void(CCON *)(int));

Послепытаясь найти способ вызова этого метода, я сделал это:

[DllImport("sb6lib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int rdOnAllDone(Delegate d);
public delegate void rdOnAllDoneCallbackDelegate();

private static void rdOnAllDoneCallback()
{
    Console.WriteLine("rdOnAllDoneCallback invoked");
}

Метод был вызван правильно, за исключением того, что я не смог получить параметр int.Поэтому я попытался добавить входной параметр int следующим образом

[DllImport("sb6lib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int rdOnAllDone(Delegate d);
public delegate void rdOnAllDoneCallbackDelegate(int number);

private static void rdOnAllDoneCallback(int number)
{
    Console.WriteLine("rdOnAllDoneCallback invoked " + number);
}

Но теперь делегат вызывается дважды, и он вылетает из программы со следующей ошибкой «vshosts32.exe перестал работать»

Чтоправильный способ вызова этого метода DLL?

РЕДАКТИРОВАТЬ: Забыли добавить метод Main:

public static void Main()
{
    rdOnAllDoneCallbackDelegate del3 = new rdOnAllDoneCallbackDelegate(rdOnAllDoneCallback);
rdOnAllDone(del3);

    while (true)
    {
        Thread.Sleep(1000);
    }
}

Ответы [ 2 ]

4 голосов
/ 28 декабря 2011

Три вещи, которые нужно сделать, чтобы сделать эту работу правильной:

  • вам нужно сообщить маршаллеру pinvoke о фактическом типе делегата, но использование делегата недостаточно. Это создаст неправильный толчок, который не будет правильно распределять аргумент. То, что вы видели, происходило.
  • вам нужно сообщить маршаллеру о соглашении о вызовах, если оно не __stdcall с атрибутом [UnmanagedFunctionPointer]. Неправильное восприятие приводит к дисбалансу стека с хорошими шансами на серьезный сбой.
  • вам нужно сохранить ссылку на объект делегата, чтобы сборщик мусора не собирал его. Он не может видеть ссылки, хранящиеся в собственном коде. Из-за неправильной настройки нативный код завершается сбоем после следующей сборки мусора.

Так что это должно работать лучше, настройте по необходимости:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void rdOnAllDoneCallbackDelegate(int parameter);

[DllImport("sb6lib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int rdOnAllDone(rdOnAllDoneCallbackDelegate d);

class Foo {
    private static rdOnAllDoneCallbackDelegate callback;   // Keeps it referenced

    public static void SetupCallback() {
       callback = new rdOnAllDoneCallbackDelegate(rdOnAllDoneCallback);
       rdOnAllDone(callback);
    }

    private static void rdOnAllDoneCallback(int parameter) {
       Console.WriteLine("rdOnAllDoneCallback invoked, parameter={0}", parameter);
    }
}
1 голос
/ 28 декабря 2011

Ваша подпись делегата должна совпадать с подписью собственного обратного вызова, а также должна быть правильно установлена ​​UnmanagedFunctionPointerAttribute.

В вашем случае так:

[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
public delegate void rdOnAllDoneCallbackDelegate(int parameter);

[DllImport("sb6lib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int rdOnAllDone(rdOnAllDoneCallbackDelegate callback);

Использование:

{
    rdOnAllDone(rdOnAllDoneCallback);
}

private static void rdOnAllDoneCallback(int parameter)
{
    Console.WriteLine("rdOnAllDoneCallback invoked, parameter={0}", parameter);
}
...