在托管C ++上使用非托管指针来回调函数 [英] Using unmanaged pointer to callback function on managed C++

查看:219
本文介绍了在托管C ++上使用非托管指针来回调函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为控制工业计算机上的数字I / O引脚的某些C ++库(静态.lib,无源代码)编写C ++ / CLI的包装。



我的目标是使用.NET CLR处理现有C#应用程序上DIO引脚的事件。



我唯一想到的可行选择是使用委托当引脚状态更改时(由现有的lib通知),在C ++ / CLI中触发事件,然后在C#部分中处理这些事件。我已经在这里使用简单的模拟对象尝试了基本功能:我的问题是,当引脚更改时,库使用的函数来注册回调,这会导致在C ++中触发事件并在C#中处理事件



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

我尝试从非托管代码中调用EventHandler委托,但是这当然行不通:

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

使用名称空间System;
使用名称空间System :: Runtime :: InteropServices;

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

公共引用类CppDIOHandler
{
私有:

公共:
CppDIOHandler(){

} ;
void StartDIO(){
//步骤1,通过调用InitDIO()
if(!InitDIO())
{
Console :: WriteLine初始化DIO库( InitDIO->失败);
的回报;
}
Console :: WriteLine( InitDIO->通过);

//步骤2,设置状态变化中断屏蔽和电平/边沿模式
COS_INT_SETUP设置;
memset(& setup,0,sizeof(setup));
setup.portMask = 0x0f; // 00001111b,启用ch.0〜3
setup.edgeMode = 0x00; //在级别更改
setup.edgeType = 0x00;时产生中断//上升/下降沿,仅在edgeMode = 1

if(!SetupDICOS(& setup,sizeof(setup)))
{
Console :: WriteLine( SetupDICOS->失败);
的回报;
}
Console :: WriteLine( SetupDICOS->通过);

//第3步,注册回调函数
if(!RegisterCallbackDICOS(callback_function))
{
Console :: WriteLine( RegisterCallbackDICOS->失败);
的回报;
}
Console :: WriteLine( RegisterCallbackDICOS->通过);
//步骤4,如果(!StartDICOS())
{
Console :: WriteLine( StartDICOS-> FAILED失败,则启动DI状态更改中断
);
的回报;
}
Console :: WriteLine( StartDICOS->通过);
}

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

事件EventHandler< PinStateChangedEventArgs ^> ^ PinStateChanged;

受保护:
虚拟void OnPinStateChanged(PinStateChangedEventArgs ^ e)
{
PinStateChanged(this,e);
}
};

#pragma unmanaged
//步骤0,定义状态改变中断回调函数
void __stdcall callback_function(COS_INT_CALLBACK_ARG * arg)
{
printf(数据= 0x%02x,标志= 0x%02x,seq =%02d\n,
arg-> portData,arg-> intrFlag,arg-> intrSeq);
//这里应该添加一些代码来触发托管委托人的事件
//以下内容不能用于非托管代码
CppDIOHandler ^ dh = gcnew CppDIOHandler();
dh-> TriggerEvent(1);
}
#pragma管理的

void main()
{
return;
}

是否有适当的方法?绕开先前的代码可以工作,或者使用我的 PinStateChanged委托作为回调函数的任何方式对我都有效。

解决方案

使用Hans Passant提供的信息和链接,我可以正常工作。代码结束如下(可能需要清理):

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

使用名称空间System;
使用名称空间System :: Runtime :: InteropServices;

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

公共引用类CppDIOHandler
{
public:
CppDIOHandler(){}

代表无效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(){
//第1步,通过调用InitDIO()
if(!InitDIO()来初始化DIO库)
{
Console :: WriteLine( InitDIO-> FAILED);
的回报;
}
Console :: WriteLine( InitDIO->通过);

//步骤2,设置状态变化中断屏蔽和电平/边沿模式
COS_INT_SETUP设置;
memset(& setup,0,sizeof(setup));
setup.portMask = 0x0f; // 00001111b,启用ch.0〜3
setup.edgeMode = 0x00; //在级别更改
setup.edgeType = 0x00;时产生中断//上升/下降沿,仅在edgeMode = 1

if(!SetupDICOS(& setup,sizeof(setup)))
{
Console :: WriteLine( SetupDICOS->失败);
的回报;
}
Console :: WriteLine( SetupDICOS->通过);

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

//第3步,注册回调函数
if(!RegisterCallbackDICOS(functionPointer))
{
Console :: WriteLine( RegisterCallbackDICOS->失败);
的回报;
}
Console :: WriteLine( RegisterCallbackDICOS->通过);
//步骤4,如果(!StartDICOS())
{
Console :: WriteLine( StartDICOS-> FAILED失败,则启动DI状态更改中断
);
的回报;
}
Console :: WriteLine( StartDICOS->通过);
}

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

事件EventHandler< PinStateChangedEventArgs ^> ^ PinStateChanged;

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

void main()
{
return;
}


I am writing C++/CLI a wrapper for some C++ libraries (static .lib, no source code available) controlling Digital I/O pins on an industrial computer.

My objective is to handle the events of the DIO pins on an existing C# application using .NET CLR.

The only viable option I could think of is to use a delegate in C++/CLI to fire events when the pin state changes (informed by the existing lib), and then handle these events in the C# part. I have tried the basic functionality using simpler mock objects here: Firing events in C++ and handling them in C#

My problem is that the function used by the libraries to register the callback when a pin changes expects a (unmanaged) pointer to function, and I don't know how to glue all this out. The signature of this function (from the header file that accompany the libraries) is as follows:

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

I tried calling an EventHandler delegate from unmanaged code, but of course this can't work:

#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;
}

Is there any proper way to do this? Either a bypass for the previous code to work or any way of using my "PinStateChanged" delegate as callback function is valid to me.

解决方案

Using the information and the link provided by Hans Passant I got it working. The code ended as follows (might need some cleanup):

#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;
}

这篇关于在托管C ++上使用非托管指针来回调函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆