首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >C#调用返回具有固定大小字符数组的结构的C函数

C#调用返回具有固定大小字符数组的结构的C函数
EN

Stack Overflow用户
提问于 2012-04-26 01:10:02
回答 3查看 8.5K关注 0票数 18

所以,这个问题有很多变体,在看了几个之后,我仍然无法理解。

这是C代码:

typedef struct
{
unsigned long Identifier;
char Name[128];
} Frame;

Frame GetFrame(int index);

这是C#代码:

struct Frame
{
    public ulong Identifier;
    [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I1, SizeConst = 128)]
    public char[] Name;
}

[DllImport("XNETDB.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern Frame GetFrame(int index);

这是我在C#中尝试的最后一次尝试,看起来非常合理,但是我得到错误消息"Method's signature is not PInvoke compatible“。所以,我有点不知道下一步该怎么做。任何帮助都是非常感谢的。

谢谢,凯文

更新了 Kevin在我的回答中添加了这个编辑

我应该改为更改我的C代码:

void GetFrame(int index, Frame * f);

并将其用于C#:

struct Frame
{
    public uint Identifier;
    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 128)]
    public string Name;
}

[DllImport("XNETDB.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern void GetFrame(int index, ref Frame f);
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-04-26 02:21:46

问题是本机函数返回一个非blittable类型作为返回值。

http://msdn.microsoft.com/en-us/library/ef4c3t39.aspx

P/Invoke不能将非blittable类型作为返回值。

您不能p/调用该方法。[EDIT这实际上是可能的,请参见]

通过值返回132个字节不是一个好主意。如果这个本机代码是你的,我会修复它。你可以通过分配132个字节并返回一个指针来修复它。然后添加一个FreeFrame方法来释放该内存。现在可以p/调用它了。

或者,您可以将其更改为接受指向它将填充的帧内存的指针。

票数 11
EN

Stack Overflow用户

发布于 2012-04-26 01:25:19

您选择的PInvoke签名有两个问题。

第一个问题很容易解决。您对unsigned long的翻译有误。在C中,unsigned long通常只有4个字节。您选择了8字节的C#类型long。将C#代码更改为使用uint可以解决此问题。

第二个更难一些。正如Tergiver指出的那样,CLR Marshaller只支持返回位置的结构(如果它是blittable的)。Blittable是一种奇妙的说法,它在本机代码和托管代码中具有完全相同的内存表示形式。您选择的结构定义不是blittable,因为它有一个嵌套数组。

但是,如果您还记得PInvoke是一个非常简单的过程,那么这个问题是可以解决的。CLR Marshaller实际上只需要您用您的类型和pinvoke方法的签名回答两个问题

  • 我要复制多少字节?
  • 它们需要朝哪个方向移动?

在本例中,字节数为sizeof(unsigned long) + 128 == 132。所以我们需要做的就是建立一个托管类型,它是blittable的,大小为132字节。最简单的方法是定义一个blob来处理数组部分

[StructLayout(LayoutKind.Sequential, Size = 128)]
struct Blob
{
   // Intentionally left empty. It's just a blob
}

这是一个没有成员的结构,对编组程序来说,它的大小是128字节(另外,它是blittable!)。现在,我们可以轻松地将Frame结构定义为uint和此类型的组合

struct Frame
{
    public int Identifier;
    public Blob NameBlob;
    ...
}

现在我们有了一个大小为132字节的blittable类型。这意味着它可以很好地使用您定义的GetFrame签名

剩下的唯一部分就是让您访问该名称的实际char[]。这有点棘手,但可以通过一些法警魔法来解决。

public string GetName()
{
    IntPtr ptr = IntPtr.Zero;
    try
    {
        ptr = Marshal.AllocHGlobal(128);
        Marshal.StructureToPtr(NameBlob, ptr, false);
        return Marshal.PtrToStringAnsi(ptr, 128);
    }
    finally
    {
        if (ptr != IntPtr.Zero) 
        {
            Marshal.FreeHGlobal(ptr);
        }
    }
}

注意:我不能评论调用约定部分,因为我不熟悉GetFrame应用程序接口,但我肯定会检查这一点。

票数 23
EN

Stack Overflow用户

发布于 2012-04-26 06:22:01

JaredPar的另一个选择是利用C#固定大小的缓冲区特性。然而,这确实需要您打开该设置以允许不安全代码,但它避免了具有2个结构。

class Program
{
    private const int SIZE = 128;

    unsafe public struct Frame
    {
        public uint Identifier;
        public fixed byte Name[SIZE];
    }

    [DllImport("PinvokeTest2.DLL", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    private static extern Frame GetFrame(int index);

    static unsafe string GetNameFromFrame(Frame frame)
    {
        //Option 1: Use if the string in the buffer is always null terminated
        //return Marshal.PtrToStringAnsi(new IntPtr(frame.Name));

        //Option 2: Use if the string might not be null terminated for any reason,
        //like if were 128 non-null characters, or the buffer has corrupt data.

        return Marshal.PtrToStringAnsi(new IntPtr(frame.Name), SIZE).Split('\0')[0];
    }

    static void Main()
    {
        Frame a = GetFrame(0);
        Console.WriteLine(GetNameFromFrame(a));
    }
}
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10320502

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档