在本机库的回调函数中,我需要访问一个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位。
答案 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
。