Сбой при вызове функции обратного вызова C # из C DLL - PullRequest
4 голосов
/ 01 ноября 2011

Я пишу приложение C # в Windows CE 6 для мониторинга 3G-модема. Приложение будет вызывать функции в C DLL для доступа к модему.

При запуске приложение C # вызывает эту функцию для создания нового соединения:

[DllImport("swmodem.dll", CallingConvention = CallingConvention.Winapi)]
      public static extern int CreateDataConnection(EVENT_CALLBACK callback);

EVENT_CALLBACK определяется как:

public delegate void EVENT_CALLBACK(int e, IntPtr data);

Также определена структура данных:

[StructLayout(LayoutKind.Sequential)]      
public struct ECIO_INFO
{
        public UInt32 ecio1;    /*!< Primary scramble code */
        public UInt32 ecio2;    /*!< Received signal code power */
        public UInt32 ecio3;    /*!< Energy per chip per power density */
}

В C DLL указатель функции передается в CreateDataConnection () для обновления состояния модема.

int CreateDataConnection(EVENT_CALLBACK ecb)
{
    .
    .               
    fEventCallback = ecb;

    // Create a connection
    .
    .
}

После создания соединения DLL вызовет функцию обратного вызова для обновления состояния модема, например, EC / IO (отношение принятой энергии пилот-сигнала).

Обычно, когда изменяется ECIO, вызывается функция обратного вызова для передачи данных ECIO в приложение C #:

В C DLL:

void ProcessNotification(EVENT_CALLBACK fEventCallback)
{
    ECIO_INFO ecio_info;

        ecio_info.ecio1 = ecio_info.ecio2 = ecio_info.ecio3 = 0;
        if(data.nNumOfCells>0)
            ecio_info.ecio1 = data.arCellInfo[0].nEcIo;
        if(data.nNumOfCells>1)
            ecio_info.ecio2 = data.arCellInfo[1].nEcIo;
        if(data.nNumOfCells>2)
            ecio_info.ecio3 = data.arCellInfo[2].nEcIo;

        if(data.nNumOfCells>0)
            fEventCallback(ME_RSCP_ECIO, &ecio_info);
}

В приложении C # функция обратного вызова определяется как:

private void ModemEventCallback(int e, IntPtr data)
{
    .
    .

    Modem.ECIO_INFO new_reinfo = new Modem.ECIO_INFO();
    new_reinfo = (Modem.ECIO_INFO)Marshal.PtrToStructure(
       data, typeof(Modem.ECIO_INFO));
    .
    .
}

Теперь проблема приходит. Когда программа запускается, все в порядке, соединение создается нормально и обновляется EC / IO. но после нескольких часов работы обновление EC / IO останавливается. После теста я обнаружил, что он останавливается при вызове обратного вызова:

fEventCallback(ME_RSCP_ECIO, &ecio_info);

Я не знаю, что здесь пошло не так. Возможно, передача указателя на функцию в C # DLL вызывает просто неправильный способ сделать это, или в коде скрыта какая-то ошибка?

Ответы [ 3 ]

3 голосов
/ 01 ноября 2011

Поскольку функция обратного вызова является просто указателем для C / C ++, параметр обратного вызова должен быть объявлен как IntPtr.Создайте экземпляр EVENT_CALLBACK end и убедитесь, что он остается живым все время, пока работает ваша программа.Используйте метод Marshal.GetFunctionPointerForDelegate, чтобы преобразовать экземпляр deletecate в IntPtr и передать полученный IntPtr в функцию CreateDataConnection.

[DllImport("swmodem.dll", CallingConvention = CallingConvention.Winapi)]
      public static extern int CreateDataConnection(IntPtr callback);

...
EVENT_CALLBACK c;
c = new EVENT_CALLBACK( ... );   // keep this alive !
...
CreateDataConnection(Marshal.GetFunctionPointerForDelegate(c));
3 голосов
/ 08 августа 2013

Попробуйте это

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void EVENT_CALLBACK(int e, IntPtr data);

Это решило мою проблему.

1 голос
/ 01 ноября 2011

Я думаю, что вы должны использовать GCHandl.Alloc с GCHandleType.Pinned , тем самым вы скажете gc, что этот объект должен оставаться в памяти, даже если у него нет «корней»в приложении, которое ссылается на этот объект, и память для этого объекта не может быть сжата

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...