[英]How do I Marshal a c/cpp struct containing a function pointer?
Good Morning, I am attempting to create a C# dot net core wrapper for the Audio functionality of SDL.早上好,我正在尝试为 SDL 的音频功能创建一个 C# 点网核心包装器。 In particular, I am attempting to create a wrapper for https://wiki.libsdl.org/SDL_LoadWAV
特别是,我正在尝试为https://wiki.libsdl.org/SDL_LoadWAV创建一个包装器
The existing code is:现有代码是:
To represent the non basic types being marshalled:表示正在编组的非基本类型:
public delegate void SDL_AudioCallback(IntPtr userdata, byte[] stream,out int len);
[StructLayout(LayoutKind.Sequential)]
public struct SDL_AudioSpec
{
public int freq;
public ushort format;
public byte channels;
public byte silence;
public ushort samples;
public UInt32 size;
public IntPtr callback;
IntPtr userdata;
}
public class SDL_AudioSpecPtr
{
public readonly IntPtr NativePointer;
public SDL_AudioSpecPtr(IntPtr pointer)
{
NativePointer = pointer;
}
public static implicit operator IntPtr(SDL_AudioSpecPtr Sdl2Window) => Sdl2Window.NativePointer;
public static implicit operator SDL_AudioSpecPtr(IntPtr pointer) => new SDL_AudioSpecPtr(pointer);
}
Importing the Method signature:导入方法签名:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate SDL_AudioSpec SDL_LoadWAV_t(string file, IntPtr spec, byte[] audio_buf, out UInt32 audio_len);
private static SDL_LoadWAV_t s_sdl_LoadWAV = LoadFunction<SDL_LoadWAV_t>("SDL_LoadWAV");
public static void SDL_LoadWAV(string file, IntPtr spec, byte[] audio_buf, out UInt32 audio_len) => s_sdl_LoadWAV(file,spec,audio_buf,out audio_len);
Attempting to call the function yields null exception:尝试调用 function 会产生 null 异常:
Sdl2Native.SDL_Init(SDLInitFlags.Audio);
SDL_AudioSpec spec = new SDL_AudioSpec();
byte[] bytes=new byte[20000];
uint len;
SDL_AudioSpec* specptr = &spec;
Sdl2Native.SDL_LoadWAV(@"G:\Assets\sfx_movement_jump2.wav", (IntPtr)specptr, bytes, out len);
You should rely more heavily on the CLR's marshalling rather than doing your own, it's a lot easier to make serious errors.您应该更多地依赖 CLR 的编组而不是自己做,这样更容易犯严重错误。
Furthermore:此外:
Strings need marshalling to ANSI with [MarshalAs(UnmanagedType.LPStr)]
字符串需要使用
[MarshalAs(UnmanagedType.LPStr)]
编组为 ANSI
You shouldn't just convert &
pointer to *
like that, the struct needs marshalling.您不应该像那样将
&
指针转换为*
,该结构需要编组。
A callback given to unmanaged code needs to be held onto, so that the GC doesn't sweep it away.需要保留给非托管代码的回调,以便 GC 不会将其扫除。
It's unclear why you have a
LoadFunction
setup rather than just using standard P/Invoke, but I've run with that.目前尚不清楚为什么您有一个
LoadFunction
设置,而不仅仅是使用标准 P/Invoke,但我已经使用它了。
So the delegate should be所以代表应该是
private static SDL_AudioSpec SDL_LoadWAV_t(
[MarshalAs(UnmanagedType.LPStr)] string file,
in SDL_AudioSpec spec,
ref byte[] audio_buf,
out UInt32 audio_len);
and then SDL_AudioSpec
struct should declare the callback然后
SDL_AudioSpec
结构应该声明回调
public SDL_AudioCallback callback;
The callback is wrong also: len
is not a out
parameter, it is by-value;回调也是错误的:
len
不是out
参数,它是按值; and it's unclear if stream
should be ref
or out
or in
, I think out
.我认为
stream
应该是ref
还是out
还是in
还不清楚out
public delegate void SDL_AudioCallback(IntPtr userdata, out byte[] stream, int len);
Depending on how long the callback is being used for, you need to hold onto it根据回调的使用时间,您需要坚持使用它
GC.KeepAlive(callback);
GC.KeepAlive(callback);
after the callYour calling code is then:您的调用代码是:
var callbackDlgt = new SDL_AudioCallback(myCallbackFunction);
SDL_AudioSpec spec = new SDL_AudioSpec{
callback = callbackDlgt,
// set other values here
};
byte[] bytes = new byte[20000];
Sdl2Native.SDL_LoadWAV(@"G:\Assets\sfx_movement_jump2.wav", in specptr, ref bytes, out uint len);
GC.KeepAlive(callbackDlgt);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.