【问题标题】:Callback from Unmanaged code to managed从非托管代码回调到托管
【发布时间】:2017-12-17 11:26:54
【问题描述】:

我正在触发我的托管代码并启动对非托管代码的调用。非托管代码中有回调。从非托管我在我的托管方法“DelegateMethod”中得到回调。但是我没有从非托管代码中获得正确的参数/参数值。请帮我解决这个问题

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace TestApp
{
    public class Program
    {
        public delegate void fPointer(byte[] Sendapdu, ref int Sendlen, byte[] Recvapdu, ref int Recvlen);

        public struct sCommsAbstraction
        {
            ///Function to send and receive.
            public fPointer pf_TxRx;

            ///Other functions if necessary, e.g. reset
        }

        // Create a method for a delegate. 
        public static void DelegateMethod(byte[] Sendapdu, ref int Sendlen, byte[] Recvapdu, ref int Recvlen)
        {
            //This is called from unmanaged code. I am not getting proper arguments
            Console.WriteLine(Sendlen);
        }

        [DllImport("AuthLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int CmdLib_RegisterItsIO(ref sCommsAbstraction psCommsFunctions);

        [DllImport("AuthLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int CmdLib_OpenApplication();

        [DllImport("TransparentChannel.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int TC_Transceive(byte[] writeBuf, ref int writeBufLen, byte[] readBuf, ref int pwReadBufferLen);

        static void Main(string[] args)
        {
            sCommsAbstraction psCommsFunctions = new sCommsAbstraction();
            // Instantiate the delegate.
            psCommsFunctions.pf_TxRx = DelegateMethod;

            CmdLib_RegisterItsIO(ref psCommsFunctions);
            CmdLib_OpenApplication();
        }
    }
}

我的未管理代码在这里 - CmdLib.c

//C code - unmanaged

typedef int32_t (*pFTransceive)(const uint8_t *prgbWriteBuffer, const uint16_t *pwWriteBufferLen, uint8_t *prgbReadBuffer, uint16_t *pwReadBufferLen);

typedef struct sCommsAbstraction
{
    ///Function to send and receive.
    pFTransceive pf_TxRx;

    ///Other functions if necessary, e.g. reset
}sCommsAbstraction_d

static sCommsAbstraction_d sCommsAbstraction = {0};

void CmdLib_RegisterItsIO(const sCommsAbstraction_d *psCommsFunctions)
{
    sCommsAbstraction.pf_TxRx = psCommsFunctions->pf_TxRx;    
}
void CmdLib_OpenApplication()
{
    sCommsAbstraction.pf_TxRx(rgbAPDUBuffer,&wTotalLength,rgbAPDUBuffer,&psApduData->wResponseLength);
}

【问题讨论】:

  • 一些观察。调用约定可能是错误的。我认为你的代表应该是 cdecl。您可能需要将委托 ref 存储在静态字段中以停止收集它。现在是大的。编组器不知道数组有多长,也无法编组它们。可能需要在属性中指定大小,或者封送为 IntPtr 然后使用 Marshal.Copy。
  • pFTransceive 是如何定义的? pf_TxRx 必须填充数组还是必须创建一个新数组?
  • 将 .NET 数组从 C++ 编组到 C# 很复杂(我说的是委托)...非常复杂...您可以尝试 public delegate void fPointer([MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] byte[] Sendapdu, ref int Sendlen, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=3)] byte[] Recvapdu, ref int Recvlen); 但我不确定它是否会起作用...而且它会很慢(因为缓冲区需要由 .NET 复制)
  • @codroipo 我已经更新了 pFTransceive 的定义

标签: c# pinvoke


【解决方案1】:

您的委托声明与pFTransceive 函数指针声明不匹配。这会在您的回调方法中产生垃圾参数值。

靠近:

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void fPointer(
        [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]
        byte[] Sendapdu, ref short Sendlen,
        [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)]
        byte[] Recvapdu, ref short Recvlen
    );

注意 required 属性以匹配 C/C++ 程序中使用的默认调用约定。和 short 参数类型匹配uint16_t。`

更多关于调用约定的奥秘在this post


    sCommsAbstraction psCommsFunctions = new sCommsAbstraction();

这是您必须担心的另一件事。只要本机代码可以进行回调,该对象就需要存在。现在它没有,下一次垃圾收集将销毁它,因为垃圾收集器无法发现本机代码正在使用它。 Kaboom 当本机代码进行回调时。您还没有看到该错误,调试器现在使对象保持活动状态。这不会在生产中发生。

正如所写,您需要将此语句添加到 Main() 方法的底部:

    GC.KeepAlive(psCommsFunctions);

将其存储在 static 变量中是更好的方法,只有在您知道本机代码不再进行回调时才将其设置回 null

【讨论】:

  • 很好的解释@Hans,谢谢分配:)
  • 编组器如何在不知道数组长度的情况下编组数组?
猜你喜欢
  • 2013-08-24
  • 2015-02-24
  • 1970-01-01
  • 2011-01-21
  • 2012-07-03
  • 2010-09-18
  • 1970-01-01
  • 1970-01-01
  • 2013-12-29
相关资源
最近更新 更多