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

本文关键字:函数 回调 问题 结构 C++ | 更新日期: 2023-09-27 18:35:14

这是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就停止工作。现在,不会调用托管回调函数。除此之外,一切似乎都很好。我已经编辑了文本以更新进度。此外,我所做的是,我围绕非托管代码编写了一个非托管包装器,该包装器只是从托管代码中获取参数并将其传递给库。这样做的原因是检查是否将正确的参数从托管代码传递到非托管代码,在本例中,是否传递了正确的地址。问题是为什么我的回调函数(委托)没有被执行?

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

在 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++编写应用程序并避免互操作问题?