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

Ste*_*ger 6 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;
Run Code Online (Sandbox Code Playgroud)

我有

[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; 
        }
Run Code Online (Sandbox Code Playgroud)

然后

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;
        }
Run Code Online (Sandbox Code Playgroud)

但它总是失败

cEspeak.espeak_EVENT events = (cEspeak.espeak_EVENT) System.Runtime.InteropServices.Marshal.PtrToStructure(ptr, typeof(cEspeak.espeak_EVENT));
Run Code Online (Sandbox Code Playgroud)

但是,当我删除

[System.Runtime.InteropServices.FieldOffset(32)][System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPStr)]
            public string name; 
Run Code Online (Sandbox Code Playgroud)

从espeak_event,它的工作原理.

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

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

嗯,想一想,fieldoffset 32​​是否正确?似乎我在考虑x64时混淆了指针大小.这可能是另一个错误.

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

Dav*_*nan 4

声明nameIntPtr而不是string然后使用Marshal.PtrToStringAnsi将其放入字符串变量中。

我忽略了字符串内容是 UTF-8 的事实。如果您的文本是纯 ASCII 就可以了。如果没有,那么您需要复制到一个byte[]数组,然后使用 UTF-8 进行转换Encoding.UTF8.GetString

  • @Quandary 完全正确。我只是不想理会 FieldOffset 的东西,因为你也可以从 IntPtr 中获取整数。 (2认同)