Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
508 views
in Technique[技术] by (71.8m points)

pinvoke - C# calling C function that returns struct with fixed size char array

So, there have been many variants of this question, and after looking at several I still can't figure it out.

This is the C code:

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

Frame GetFrame(int index);

This is the C# code:

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);

This is the last attempt I tried in C#, and it seems pretty logical, but I get the error "Method's signature is not PInvoke compatible." So, I'm kind of lost on what to try next. Any help is appreciated.

Thanks, Kevin

Updated Kevin added this as an edit to my answer

I should instead change my C code:

void GetFrame(int index, Frame * f);

and use instead for 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);
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

There are two problems with the PInvoke signature that you've chosen.

The first is easy to fix. You have a mistranslation of unsigned long. In C an unsigned long is typically only 4 bytes. You chose the C# type long which is 8 bytes. Changing the C# code to use uint will fix this.

The second is a bit harder. As Tergiver pointed out the CLR Marshaller only supports a struct in the return position if it's blittable. Blittable is a fancy way of saying that it has the exact same memory representation in native and managed code. The struct definition you've chosen isn't blittable because it has a nested array.

This can be worked around though if you remember that PInvoke is a very simple process. The CLR Marshaller really just needs you to answer 2 questions with the signature of your types and pinvoke methods

  • How many bytes am I copying?
  • In which direction do they need to go?

In this case the number of bytes is sizeof(unsigned long) + 128 == 132. So all we need to do is build up a managed type that is blittable and has a size of 132 bytes. The easiest way to do this is to define a blob to handle the array portion

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

This is a struct with no members that will appear to the marshaller as having a size of 128 bytes (and as a bonus it's blittable!). Now we can easily define the Frame structure as a combination of an uint and this type

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

Now we have a blittable type with a size the marshaller will see as 132 bytes. This means it will work just fine with the GetFrame signature you've defined

The only part left is giving you access to the actual char[] for the name. This is a bit tricky but can be solved with a bit of marshal magic.

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);
        }
    }
}

Note: I can't comment on the calling convention portion because I'm unfamiliar with the GetFrame API but that's something i would definitely check on.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...