How to map C connection to const char * with C # struct?

In the callback function from my own library, I need to access the espeak_EVENT array. The problem is the UNION statement in the C source code:

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; 

I have

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

And then

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

But he always fails on

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

However, when I delete

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

From espeak_event, then it works.

How can I do this work without deleting a row in a join? I need to access it in a callback function.

Edit: And by the way, what happens to field offsets if I allow it to run on x64 and the size is "public IntPtr user_data;" varies from 32 to 64 bits?

Hmm, thinking about it, is polefoss 32 true? It seems I mixed the size of the pointer while thinking about x64. Perhaps this will be another mistake.

Hm, combining with int and char *, I think they never compiled it for x64. Since sizof (int) is 32 bits on x64 Linux system.

+6
source share
3 answers

Declare name as IntPtr , not string , and then use Marshal.PtrToStringAnsi to get it in a string variable.

I ignore the fact that the contents of the string are UTF-8. If your text is pure ASCII, that's fine. If not, you need to copy to byte[] into the array and then translate from UTF-8 using Encoding.UTF8.GetString .

+3
source

Now this may sound overly intense, but whenever I get into a situation where I have to access by binding some C structure using C-specific functions (like unions or void* pointers), I usually write the get / set functions from / to native (marshalling) types of the target language in C and provide them through the binding and keep the structure opaque.

0
source

Can you first create a structure with only the espeak_EVENT_TYPE type ? Then you can choose one of two structures depending on what you expect to find in the contents of the union: one has only int number and one has only const char *name .

0
source

Source: https://habr.com/ru/post/891985/


All Articles