在托管和非托管代码之间共享结构 [英] Sharing a structure between managed and unmanaged code

查看:98
本文介绍了在托管和非托管代码之间共享结构的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有C#应用程序,需要与本机(非托管)DLL进行接口.本机DLL通过C ++类公开其库,因此我决定将类包装为C ++/CLI DLL,以便能够在C#应用程序中使用它.问题集中在C ++/CLI中包装方法的实现以及如何从托管C#应用程序中调用包装方法.即使有此方法返回,也将使用2个缓冲区来调用该方法以供本机代码写入.本机库将使用回调函数将新数据通知托管应用程序.所以我不能使用pin_ptr<>如果它具有全球范围,那将是完美的. C ++/CLI DLL和C#应用程序可以很好地编译并且可以正常运行.我可以从本机端的托管端看到数据.我可以在本机端写入数据而不会出现问题.但是,我看不到在托管方面本地编写的内容.我认为可以通过GCHandle.Alloc()调用在某处创建副本.

本机方法具有称为sHSLPCalibProfileData的结构,定义为

I have C# application that needs to interface with a native (unmanaged) DLL. The native DLL exposes its library through a C++ Class, so I decided to wrap the class with a C++/CLI DLL to be able to use it in the C# app. The problem centers on the implementation of a wrapper method within C++/CLI and how it is called from the managed C# app. The method is called with 2 buffers for the native code to write to even after this method returns. The native library will use a callback function to notify the managed application of new data. So I can''t use pin_ptr<> which would be perfect if it had global scope. The C++/CLI DLL and the C# application compiles fine and runs without exceptions. I can see the data from the managed side on the native side. I can write to the data on the native side without issue. However, I cannot see what was natively written on the managed side. I think a copy is made somewhere possibly through the GCHandle.Alloc() call.

The native method has a structure called sHSLPCalibProfileData defined as

<pre lang="cs">typedef struct _sHSLPDataPt<br />
{<br />
    float fX;<br />
    float fZ;<br />
    int   iI;<br />
    float fJ;<br />
}sHSLPDataPt;<br />
<br />
typedef struct _sHSLPCalibProfileData<br />
{<br />
    unsigned int uiHeader;<br />
    unsigned int uiIO;<br />
    unsigned int uiProfileID;<br />
    unsigned int uiNbValidPts;<br />
    sHSLPDataPt  sData[1280];<br />
}sHSLPCalibProfileData;</pre><br />



我创建了类似的托管结构,如下所示:



I created analogous managed structures as follows:

<br />
<pre lang="cs">[StructLayoutAttribute(LayoutKind::Explicit, Size = 16)]<br />
public value struct S_HSLPDataPt<br />
{<br />
    [FieldOffsetAttribute(0)] float     fX;<br />
    [FieldOffsetAttribute(4)] float     fZ;<br />
    [FieldOffsetAttribute(8)] int       iI;<br />
    [FieldOffsetAttribute(12)] float    fJ;<br />
};<br />
<br />
[StructLayoutAttribute(LayoutKind::Explicit, Size = 16 + 16 * 1280)]<br />
public value struct S_HSLPCalibProfileData<br />
{<br />
    [FieldOffsetAttribute(0)] unsigned int      uiHeader;<br />
    [FieldOffsetAttribute(4)] unsigned int      uiIO;<br />
    [FieldOffsetAttribute(8)] unsigned int      uiProfileID;<br />
    [FieldOffsetAttribute(12)] unsigned int     uiNbValidPts;<br />
    [FieldOffsetAttribute(16)] array<S_HSLPDataPt> ^sData;<br />
};</pre><br />


上面的目的是使本机和托管结构的占用空间相同,以便我可以毫无问题地使用本机指针.

这是wrapper方法的实现:


The intent with the above was to make the footprint of the native and managed structures the same so that I could use a native pointer without issue.

Here is the implementation of the wrapper method:

<pre lang="vb">int SetDumpingBuffer(unsigned int uiSensorIndex, System::IntPtr ipBuf1, System::IntPtr ipBuf2)<br />
{<br />
    int ret = 0;<br />
<br />
    // get native pointer to managed structure<br />
    sHSLPCalibProfileData** p1 = static_cast<sHSLPCalibProfileData**>(ipBuf1.ToPointer());<br />
    sHSLPCalibProfileData** p2 = static_cast<sHSLPCalibProfileData**>(ipBuf2.ToPointer());<br />
<br />
    // native call<br />
    ret = pHSLP->SetDumpingBuffer(uiSensorIndex, *p1, *p2, sizeof(sHSLPCalibProfileData));<br />
<br />
    return ret;<br />
}</pre><br />



这是调用包装器方法的托管应用程序的实现的一段摘要:



Here is a contrived snippet of the implementation of the managed app calling the wrapper method:

<pre>        private void cbDumping_Click(object sender, EventArgs e)<br />
        {<br />
            int ret;<br />
            // allocate gc memory for managed structure<br />
            HSLPLibWrapper.S_HSLPCalibProfileData Buf1 = new HSLPLibWrapper.S_HSLPCalibProfileData();<br />
            Buf1.sData = new S_HSLPDataPt[1280];<br />
            HSLPLibWrapper.S_HSLPCalibProfileData Buf2 = new HSLPLibWrapper.S_HSLPCalibProfileData();<br />
            Buf2.sData = new S_HSLPDataPt[1280];<br />
            // tell gc to not collect Buf1 and Buf2 because native code has reference<br />
            GCHandle gch1 = GCHandle.Alloc(Buf1, GCHandleType.Weak);<br />
            GCHandle gch2 = GCHandle.Alloc(Buf2, GCHandleType.Weak);<br />
            // get pointer to handle<br />
            IntPtr ip1 = GCHandle.ToIntPtr(gch1);<br />
            IntPtr ip2 = GCHandle.ToIntPtr(gch2);<br />
            <br />
            // make call to wrapper (hslp is managed wrapper)<br />
            ret = hslp.SetDumpingBuffer(0, ip1, ip2);<br />
            // free handle so gc can collect Buf1 and Buf2<br />
            gch1.Free();<br />
            gch2.Free();<br />
        }</pre>


我对C ++/CLI还是相当陌生,所以如果有更好的方法可以做到这一点,我将不为所动.


I am fairly new to C++/CLI so if there is a better way to accomplish this I am all ears.

推荐答案

看起来Buf1和Buf2可以进行垃圾回收了cbDumping_Click完成后立即收集.如果回调在GC之后运行,则可能会出现问题.

由于您知道结构的大小和布局,因此可以潜在地使用Marshal.AllocHGlobal/FreeHGlobal来准备缓冲区,然后使用带有指针的不安全代码来读取数据的C样式:
It looks like Buf1 and Buf2 become eligible for garbage collection as soon as cbDumping_Click completes. This may be problematic if the callback runs after GC.

Since you know the sizes and the layouts of your structures, you could potentially use Marshal.AllocHGlobal/FreeHGlobal to prepare your buffers, and then use unsafe code with pointers to read your data C-style:
unsafe {
    void* p = Marshal.AllocHGlobal(N); // Allocate N bytes
    ...
    float v = *((float*)(((byte*)p)+4)); // Read a float value at the offset of 4
    ...
    Marshal.FreeHGlobal(p); // Free the buffer
    ...
}

当然,在您的情况下,分配,访问和释放将以不同的方法发生.仅当您确定非托管代码将不再执行回调时,才应进行解除分配.

Of course, in your case, the allocation, the access, and the deallocation would happen in different methods. Deallocation should be done only when you are certain that the unmanaged code is not going to perform callbacks any longer.


这篇关于在托管和非托管代码之间共享结构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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