托管函数作为非托管结构中的回调函数传递的问题(C ++,C#)

时间:2016-02-18 22:55:17

标签: c# c++

这是C ++结构:

typedef struct strImageAcq
{
    int                   theHandle;
    uint8_t*          theImageBuf;
    uint32_t*             theImageBufSizePtr;
    const struct timeval* theTimeout;
    int                   ImageStatus;
    CallBackFunc          AcqImageCallBack;
    void *                CallBackParameters;
}strImageAcq;

struct timeval {
    long    tv_sec;         /* seconds */
    long    tv_usec;        /* and microseconds */
};

typedef void (CALLBACK *CallBackFunc)(void *); 

这是我需要调用的C ++函数:

int AcqImage(strImageAcq* myImageAcq);

这是我构建的托管代码:

[DllImport(CameraDll, EntryPoint = "AcqImage", CallingConvention = CallingConvention.Cdecl)]
    private static extern int AcquireImage(IntPtr ImgStr);

 delegate void CallBack();

    internal struct timeval
    {
        internal int tv_sec;     //seconds
        internal int tv_usec;    //microseconds
    };

  internal struct ImageAcq
    {
        internal int handle;
        internal IntPtr ImageBuf;
        internal IntPtr ImageBufSizePtr;
        internal IntPtr Timeout;
        internal int ImageStatus;
        internal IntPtr AcqImgCallBack;
        internal IntPtr CallbackParms;
    };

private static void MyCallBackFunc()
{
        Console.WriteLine("Function MyCallBackFunc called.");
}

IntPtr SerialBufList;
IntPtr pAcqImg;
IntPtr pImgBuf;
IntPtr pImgSize;
IntPtr pTimeout;
IntPtr pCallBack;
timeval Timeout = new timeval();  
Timeout.tv_sec = 3;
ImageSize = 386000;


 //filling  the structure to be sent to AcquireImage
        pImgBuf = Marshal.AllocHGlobal( (int)ImageSize * sizeof(byte)); //initial buffer


        pImgSize = Marshal.AllocHGlobal(sizeof(uint)); //image buffer size pointer
        Marshal.WriteInt32(pImgSize, (int)ImageSize);

        pTimeout = Marshal.AllocHGlobal(Marshal.SizeOf(Timeout)); //timeout structure 
        Timeout.tv_sec = 2;
        Marshal.StructureToPtr(Timeout, pTimeout, true);

        FunctionPointer = new CallBack(MyCallBackFunc);
        pCallBack = Marshal.GetFunctionPointerForDelegate(FunctionPointer);

        AcqImg.handle = DetectorHandle[0];
        AcqImg.ImageBuf = pImgBuf;
        AcqImg.ImageBufSizePtr = pImgSize;
        AcqImg.ImageStatus = -8;
        AcqImg.Timeout = pTimeout;
        AcqImg.AcqImgCallBack = pCallBack;
        AcqImg.CallbackParms = IntPtr.Zero;

        pAcqImg = Marshal.AllocHGlobal(Marshal.SizeOf(AcqImg));
        Marshal.StructureToPtr(AcqImg, pAcqImg, true);
       // MyCallBackFunc();
        rv = AcquireImage(pAcqImg);
        ErrorCheck(rv, "AcquireImage");

        Marshal.FreeHGlobal(pImgBuf);
        Marshal.FreeHGlobal(pImgSize);
        Marshal.FreeHGlobal(pTimeout);
        Marshal.FreeHGlobal(pAcqImg);

现在,一旦它调用rv = AcquireImage(ref pImgAcq);之前我曾经收到消息vshost32.exe已停止工作。现在没有调用托管回调函数。除此之外,一切似乎都很好。我编辑了文本以更新进度。我所做的也是我在非托管代码周围编写了一个非托管包装器,它只从托管代码中获取参数并将其传递给库。这样做的原因是检查是否正确的参数从托管代码传递到非托管代码,在这种情况下正在传递正确的地址。问题是为什么我的回调函数(委托)没有被执行?

1 个答案:

答案 0 :(得分:-1)

数据指针在C#和C ++之间传递时绝对没有任何意义,它只是一个整数。在C#中,托管指针指向托管堆,托管堆无法通过外部引用的本机代码访问,甚至可以相对于堆的位置(根本不是真正的指针)。在C ++中,指针指向进程空间内存,该内存不是C#托管堆的一部分,因此无法从托管代码访问。

C#中的结构不保证符合任何组织规则,例如内存中的顺序,或者不同大小的元素之间存在多少填充。在C ++中,您可以在编译期间为结构指定填充,并且保证成员按照您在声明时指定的顺序。因此,除了偶然之外,C#中的结构将与C ++中的结构不兼容。

最初,我写道无法从本机代码调用托管代码。在简单的情况下,这在技术上是错误的,因为.NET能够将调用编组为显式的非托管函数调用,如下所述: http://www.codeproject.com/Tips/318140/How-to-make-a-callback-to-Csharp-from-C-Cplusplus

这并没有改变在托管和非托管之间传递数据的整体问题,虽然支持编组,但可能没有COM,但即使是这样,我怀疑它会很困难而且非常脆弱,由于LoadLibrary计时,可能会泄漏本机内存。

为了使其工作正如您所描述的那样,您需要修改C ++ .DLL以实现和支持COM,这样您就可以使用共享COM模型(.NET基于COM)来编组本机和托管之间的数据码。我过去做过一些COM工作,这是我坚持使用本机代码的主要原因。

AcqImage做了什么,有没有办法在.NET中做到这一点?是否存在可以为您完成的库/框架/工具包,并且使用.NET处理COM互操作,因此您不必这样做?或者,您是否可以使用C ++编写应用程序并避免互操作问题?