如何将C union与const char *映射到C#struct?

时间:2011-07-04 20:29:41

标签: c# c++ c interop callback

在本机库的回调函数中,我需要访问一个espeak_EVENT数组。 问题是原始C代码中的UNION语句:

typedef struct {
    espeak_EVENT_TYPE type;
    unsigned int unique_identifier; // message identifier (or 0 for key or character)
    int text_position;    // the number of characters from the start of the text
    int length;           // word length, in characters (for espeakEVENT_WORD)
    int audio_position;   // the time in mS within the generated speech output data
    int sample;           // sample id (internal use)
    void* user_data;      // pointer supplied by the calling program
    union {
        int number;        // used for WORD and SENTENCE events. For PHONEME events this is the phoneme mnemonic.
        const char *name;  // used for MARK and PLAY events.  UTF8 string
    } id;
} espeak_EVENT;

我有

[StructLayout(LayoutKind.Explicit)]
        public struct espeak_EVENT
        {
            [System.Runtime.InteropServices.FieldOffset(0)]
            public espeak_EVENT_TYPE type;

            [System.Runtime.InteropServices.FieldOffset(4)]
            public uint unique_identifier;  // message identifier (or 0 for key or character)

            [System.Runtime.InteropServices.FieldOffset(8)]
            public int text_position;    // the number of characters from the start of the text

            [System.Runtime.InteropServices.FieldOffset(12)]
            public int length;           // word length, in characters (for espeakEVENT_WORD)

            [System.Runtime.InteropServices.FieldOffset(16)]
            public int audio_position;   // the time in mS within the generated speech output data

            [System.Runtime.InteropServices.FieldOffset(20)]
            public int sample;           // sample id (internal use)

            [System.Runtime.InteropServices.FieldOffset(24)]
            public IntPtr user_data;      // pointer supplied by the calling program

            [System.Runtime.InteropServices.FieldOffset(32)]
            public int number;

            [System.Runtime.InteropServices.FieldOffset(32)]
            [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPStr)]
            public string name; 
        }

然后

public static Int32 SynthCallback(IntPtr wav, Int32 numsamples, IntPtr eventsParameter)
        {
            if (wav == IntPtr.Zero) 
                return 0;

            int j=0;
            while(true)
            {
                System.IntPtr ptr = new IntPtr( 
                                                (
                                                    eventsParameter.ToInt64() 
                                                    + (j *
                                                        System.Runtime.InteropServices.Marshal.SizeOf(typeof(cEspeak.espeak_EVENT))
                                                      ) 
                                                )
                                              );
                if(ptr == IntPtr.Zero)
                    Console.WriteLine("NULL");

                cEspeak.espeak_EVENT events = (cEspeak.espeak_EVENT) System.Runtime.InteropServices.Marshal.PtrToStructure(ptr, typeof(cEspeak.espeak_EVENT));

                if(events.type == cEspeak.espeak_EVENT_TYPE.espeakEVENT_SAMPLERATE)
                {
                    Console.WriteLine("Heureka");
                }
                break;


                //Console.WriteLine("\t\t header {0}: address={1}: offset={2}\n", j, info.dlpi_phdr, hdr.p_offset);
                ++j;
            }


            if(numsamples > 0)
            {
                byte[] wavbytes = new Byte[numsamples * 2];
                System.Runtime.InteropServices.Marshal.Copy(wav, wavbytes, 0, numsamples*2);
                bw.Write(wavbytes, 0, numsamples*2);
            }
            return 0;
        }

但总是失败

cEspeak.espeak_EVENT events = (cEspeak.espeak_EVENT) System.Runtime.InteropServices.Marshal.PtrToStructure(ptr, typeof(cEspeak.espeak_EVENT));

然而,当我删除

[System.Runtime.InteropServices.FieldOffset(32)][System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPStr)]
            public string name; 

从espeak_event开始,它就可以了。

如何在不删除联合中的字符串的情况下完成此工作? 我需要在回调函数中访问它。

修改 顺便说一句,如果我让它在x64上运行,并且“public IntPtr user_data;”的大小,该字段会发生什么影响。从32位变为64位?

嗯,考虑到这一点,是否正确偏移? 似乎我在考虑x64时混淆了指针大小。 这可能是另一个错误。

嗯,与int和char *联合,我的猜测是他们从来没有为x64编译它。 因为sizof(int)在x64 Linux系统上是32位。

3 个答案:

答案 0 :(得分:3)

name声明为IntPtr而不是string,然后使用Marshal.PtrToStringAnsi将其转换为字符串变量。

我忽略了字符串内容为UTF-8的事实。如果您的文本是纯ASCII,那很好。如果没有,则需要复制到byte[]数组,然后使用Encoding.UTF8.GetString从UTF-8翻译。

答案 1 :(得分:0)

现在这可能听起来过于繁重,但每当我必须通过绑定访问一些利用C特定功能(如联合或void*指针)的C结构时,我通常会写一组从/向C中的目标语言本机(编组)类型获取/设置函数,并通过绑定提供这些函数并使结构本身保持不透明。

答案 2 :(得分:0)

你能首先编组只有espeak_EVENT_TYPE type的结构吗?然后,您可以选择两个结构中的一个,具体取决于您希望在联合内容中找到的内容:一个只有int number而另一个只有const char *name