在C#

时间:2016-02-22 22:00:20

标签: c# .net marshalling

我在C#中使用C lib。 这是我想要使用的函数的原型:

heatmap_render_default_to(const heatmap_t* h, unsigned char* colorbuf)

此函数为colorbuf分配内存,如下所示:

colorbuf = (unsigned char*)malloc(h->w*h->h * 4);

从c#调用我的函数我首先尝试创建一个非托管内存,如下所示:

 string image = "";
 //allocate from COM heap           
 Marshal.StringToCoTaskMemAnsi(image);
 GCHandle gch = GCHandle.Alloc(image, GCHandleType.Pinned);
 HeatMap.HeatMapWrapper.NativeMethods.Render_default_to(hmPtr, image);

但是我得到了这个例外: 在Test.exe中的0x0F17263A(EasyDLL.dll)抛出异常:0xC0000005:访问冲突写入位置0x01050000。 如果存在此异常的处理程序,则可以安全地继续该程序。

这是我第一次尝试在c#中集成非托管库。

有人可以帮我这个吗?

的PInvoke:

[DllImport(DLL, EntryPoint = "heatmap_render_default_to", CallingConvention = CallingConvention.Cdecl)]
public static extern string Render_default_to(IntPtr h, byte[] colorbuf);  
[DllImport(DLL, EntryPoint = "heatmap_render_to", CallingConvention = CallingConvention.Cdecl)]
public static extern string Render_to(IntPtr h, IntPtr colorscheme, byte[] colorbuf);
[DllImport(DLL, EntryPoint = " heatmap_render_saturated_to", CallingConvention = CallingConvention.Cdecl)]
public static extern string Render_saturated_to(IntPtr h, IntPtr colorscheme, float saturation, byte[] colorbuf);

这是C代码:

__declspec(dllexport) unsigned char* __cdecl heatmap_render_default_to(const heatmap_t* h, unsigned char* colorbuf)
{
return heatmap_render_to(h, heatmap_cs_default, colorbuf);
}
__declspec(dllexport) unsigned char* heatmap_render_to(const heatmap_t* h, const heatmap_colorscheme_t* colorscheme, unsigned char* colorbuf)
{
return heatmap_render_saturated_to(h, colorscheme, h->max > 0.0f ? h->max : 1.0f, colorbuf);
}
__declspec(dllexport) unsigned char* __cdecl heatmap_render_saturated_to(const heatmap_t* h, const heatmap_colorscheme_t* colorscheme, float saturation, unsigned char* colorbuf)
{
unsigned y;
assert(saturation > 0.0f);
/* For convenience, if no buffer is given, malloc a new one. */
if (!colorbuf) {
    colorbuf = (unsigned char*)malloc(h->w*h->h * 4);
    if (!colorbuf) {
        return 0;
    }
}

/* TODO: could actually even flatten this loop before parallelizing it. */
/* I.e., to go i = 0 ; i < h*w since I don't have any padding! (yet?) */
for (y = 0; y < h->h; ++y) {
    float* bufline = h->buf + y*h->w;
    unsigned char* colorline = colorbuf + 4 * y*h->w;

    unsigned x;
    for (x = 0; x < h->w; ++x, ++bufline) {
        /* Saturate the heat value to the given saturation, and then
        * normalize by that.
        */
        const float val = (*bufline > saturation ? saturation : *bufline) / saturation;

        /* We add 0.5 in order to do real rounding, not just dropping the
        * decimal part. That way we are certain the highest value in the
        * colorscheme is actually used.
        */
        const size_t idx = (size_t)((float)(colorscheme->ncolors - 1)*val + 0.5f);

        /* This is probably caused by a negative entry in the stamp! */
        assert(val >= 0.0f);

        /* This should never happen. It is likely a bug in this library. */
        assert(idx < colorscheme->ncolors);

        /* Just copy over the color from the colorscheme. */
        memcpy(colorline, colorscheme->colors + idx * 4, 4);
        colorline += 4;
    }
}

return colorbuf;

}

1 个答案:

答案 0 :(得分:1)

看起来heatmap_render_default_to需要一个字节块来写入其输出。这些字节必须在本机堆上分配(例如,通过Marshal.AllocHGlobal),或者由GC固定,以便在调用C函数时它们不会移动。

一个例子:

var colourbuf = new byte[width * height * 4];
fixed (byte* colourbufPtr = colourbuf)
    heatmap_render_default_to(hmPtr, colourbufPtr);
// Now you can play with the bytes in colourbuf

或者如果P / Invoke声明函数接受byte[],则在编组调用期间应该为您完成GC固定。简单地说:

var colourbuf = new byte[width * height * 4];
heatmap_render_default_to(hmPtr, colourbuf);

您遇到访问冲突的原因是您将对应于长度为0的字符串的原生ANSI字节(因此,包含单个空字节的缓冲区)传递给需要大字节缓冲区的函数(并因此写了它的结尾)。

另外,请确保在P / Invoke导入中正确设置了调用约定和封送参数(尤其是字符串)。

一般情况下,如果你不得不使用C API进行大量的调用,我发现下载到C ++ / CLI并编写一个manged-native包装程序集更容易,然后从C#中使用它作为管理大会。