Использование неуправляемого указателя для функции обратного вызова на управляемом C ++ - PullRequest
0 голосов
/ 05 июня 2018

Я пишу C ++ / CLI как оболочку для некоторых библиотек C ++ (static .lib, исходный код недоступен), управляющих выводами цифрового ввода-вывода на промышленном компьютере.

Моя цель - обрабатывать событиявыводы DIO в существующем приложении C # с использованием .NET CLR.

Единственный возможный вариант, о котором я могу подумать, - это использовать делегат в C ++ / CLI для запуска событий при изменении состояния вывода (сообщается существующей библиотекой), а затем обработать эти события в части C #.Я попробовал базовую функциональность, используя более простые фиктивные объекты здесь: Запуск событий в C ++ и обработка их в C #

Моя проблема заключается в том, что функция, используемая библиотеками для регистрации обратного вызова при выводеИзменения ожидают (неуправляемый) указатель на функцию, и я не знаю, как все это склеить.Сигнатура этой функции (из заголовочного файла, прилагаемого к библиотекам) выглядит следующим образом:

typedef void (__stdcall* COS_INT_CALLBACK)(COS_INT_CALLBACK_ARG* arg);
BOOL RegisterCallbackDICOS(COS_INT_CALLBACK callback);   

Я попытался вызвать делегат EventHandler из неуправляемого кода, но, конечно, это не может работать:

#include "stdafx.h"
#include <WDT_DIO.H>
#include <string.h>
#include <stdio.h>

using namespace System;
using namespace System::Runtime::InteropServices;

public ref class PinStateChangedEventArgs : public EventArgs
{
public:
    property int Direction;
    property DateTime TimeReached;
};

public ref class CppDIOHandler
{
private:

public:
    CppDIOHandler() {

};
    void StartDIO() {
        //Step 1, initialize DIO library by invoking InitDIO()
        if (!InitDIO())
        {
            Console::WriteLine("InitDIO --> FAILED");
            return;
        }
        Console::WriteLine("InitDIO --> PASSED");

        //Step 2, setup Change-of-State Interrupt mask and level/edge mode
        COS_INT_SETUP setup;
        memset(&setup, 0, sizeof(setup));
        setup.portMask = 0x0f; // 00001111b, enable ch.0~3
        setup.edgeMode = 0x00; // generate interrupt on level change
        setup.edgeType = 0x00; // rising/falling edge, only effective when edgeMode = 1

        if (!SetupDICOS(&setup, sizeof(setup)))
        {
            Console::WriteLine("SetupDICOS --> FAILED");
            return;
        }
        Console::WriteLine("SetupDICOS --> PASSED");

        //Step 3, register the callback function
        if (!RegisterCallbackDICOS(callback_function))
        {
            Console::WriteLine("RegisterCallbackDICOS --> FAILED");
            return;
        }
        Console::WriteLine("RegisterCallbackDICOS --> PASSED");
        //Step 4, start the DI Change-of-State Interrupt
        if (!StartDICOS())
        {
            Console::WriteLine("StartDICOS --> FAILED");
            return;
        }
        Console::WriteLine("StartDICOS --> PASSED");
    }

    void TriggerEvent(int direction)
    {
            PinStateChangedEventArgs^ args = gcnew PinStateChangedEventArgs();
            args->Direction = direction;
            args->TimeReached = DateTime::Now;
            OnPinStateChanged(args);
    }

    event EventHandler<PinStateChangedEventArgs^>^ PinStateChanged;

protected:
    virtual void OnPinStateChanged(PinStateChangedEventArgs^ e)
    {
        PinStateChanged(this, e);
    }
};

#pragma unmanaged 
//Step 0, define a Change-of-State Interrupt callback function
void __stdcall callback_function(COS_INT_CALLBACK_ARG* arg)
{
    printf("data=0x%02x, flag=0x%02x, seq=%02d\n",
        arg->portData, arg->intrFlag, arg->intrSeq);
    //Here should go some code to trigger an event with the managed delegate
    //The following can't be used on unmanaged code
    CppDIOHandler^ dh = gcnew CppDIOHandler();
    dh->TriggerEvent(1);
}
#pragma managed  

void main()
{
    return;
}

Есть ли правильный способ сделать это?Либо обход предыдущего кода для работы, либо любой способ использования моего делегата "PinStateChanged" в качестве функции обратного вызова действителен для меня.

1 Ответ

0 голосов
/ 05 июня 2018

Используя информацию и ссылку, предоставленную Хансом Пассантом, я получил ее на работу.Код завершился следующим образом (может потребоваться очистка):

#include "stdafx.h"
#include <WDT_DIO.H>
#include <string.h>
#include <stdio.h>

using namespace System;
using namespace System::Runtime::InteropServices;

public ref class PinStateChangedEventArgs : public EventArgs
{
public:
    property int Direction;
    property DateTime TimeReached;
};

public ref class CppDIOHandler
{
public:
    CppDIOHandler() {}

    delegate void CallbackDelegate(COS_INT_CALLBACK_ARG*);

    void __stdcall callback_function(COS_INT_CALLBACK_ARG* arg)
    {
        printf("data=0x%02x, flag=0x%02x, seq=%02d\n",
            arg->portData, arg->intrFlag, arg->intrSeq);
        TriggerEvent(1);
    }


    static void StartDIO() {
        //Step 1, initialize DIO library by invoking InitDIO()
        if (!InitDIO())
        {
            Console::WriteLine("InitDIO --> FAILED");
            return;
        }
        Console::WriteLine("InitDIO --> PASSED");

        //Step 2, setup Change-of-State Interrupt mask and level/edge mode
        COS_INT_SETUP setup;
        memset(&setup, 0, sizeof(setup));
        setup.portMask = 0x0f; // 00001111b, enable ch.0~3
        setup.edgeMode = 0x00; // generate interrupt on level change
        setup.edgeType = 0x00; // rising/falling edge, only effective when edgeMode = 1

        if (!SetupDICOS(&setup, sizeof(setup)))
        {
            Console::WriteLine("SetupDICOS --> FAILED");
            return;
        }
        Console::WriteLine("SetupDICOS --> PASSED");

        CppDIOHandler^ cdh = gcnew CppDIOHandler();
        CallbackDelegate^ callback = gcnew CallbackDelegate(cdh, &callback_function);
        IntPtr stubPointer = Marshal::GetFunctionPointerForDelegate(callback);
        COS_INT_CALLBACK functionPointer = static_cast<COS_INT_CALLBACK>(stubPointer.ToPointer());
        GC::KeepAlive(callback);

        //Step 3, register the callback function
        if (!RegisterCallbackDICOS(functionPointer))
        {
            Console::WriteLine("RegisterCallbackDICOS --> FAILED");
            return;
        }
        Console::WriteLine("RegisterCallbackDICOS --> PASSED");
        //Step 4, start the DI Change-of-State Interrupt
        if (!StartDICOS())
        {
            Console::WriteLine("StartDICOS --> FAILED");
            return;
        }
        Console::WriteLine("StartDICOS --> PASSED");
    }

    void TriggerEvent(int direction)
    {
            PinStateChangedEventArgs^ args = gcnew PinStateChangedEventArgs();
            args->Direction = direction;
            args->TimeReached = DateTime::Now;
            OnPinStateChanged(args);
    }

    event EventHandler<PinStateChangedEventArgs^>^ PinStateChanged;

    virtual void OnPinStateChanged(PinStateChangedEventArgs^ e)
    {
        PinStateChanged(this, e);
    }
};

void main()
{
    return;
}
...