【问题标题】:How do I Marshal a c/cpp struct containing a function pointer?如何编组包含函数指针的 c/cpp 结构?
【发布时间】:2021-04-29 12:55:51
【问题描述】:

早上好,我正在尝试为 SDL 的音频功能创建一个 C# dot net core wrapper。 特别是, 我正在尝试为https://wiki.libsdl.org/SDL_LoadWAV创建一个包装器

现有代码为:

表示正在编组的非基本类型:

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

导入方法签名:

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

尝试调用该函数产生空异常:

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

【问题讨论】:

  • 你的实际回调函数在哪里,你没有为此传递任何东西?
  • 我没有为此传递任何东西......我正在尝试将这个播放声音的示例转换为 c#。 gist.github.com/armornick/3447121

标签: c# audio sdl-2


【解决方案1】:

您应该更多地依赖 CLR 的编组而不是自己做,这样更容易犯严重错误。

此外:

  • 字符串需要用[MarshalAs(UnmanagedType.LPStr)]编组到ANSI

  • 您不应该像这样将 &amp; 指针转换为 *,结构需要编组。

  • 需要保留给非托管代码的回调,以便 GC 不会将其扫除。

不清楚为什么你有一个LoadFunction 设置而不是仅仅使用标准的 P/Invoke,但我已经使用它了。

所以委托应该是

private static SDL_AudioSpec SDL_LoadWAV_t(
    [MarshalAs(UnmanagedType.LPStr)] string file,
    in SDL_AudioSpec spec,
    ref byte[] audio_buf,
    out UInt32 audio_len);

然后SDL_AudioSpec struct 应该声明回调

public SDL_AudioCallback callback;

回调也是错误的:len 不是out 参数,它是按值;目前还不清楚stream应该是ref还是out还是in,我想out

public delegate void SDL_AudioCallback(IntPtr userdata, out byte[] stream, int len);

根据回调的使用时间,您需要坚持使用它

  • 如果它只是在单个调用中使用,并且未被非托管代码持有,则在调用之后放置 GC.KeepAlive(callback);
  • 如果非托管代码进一步持有它,那么您需要将其存储在某个字段中,您可以保证在最后一次回调之前它不会被 GC 删除

你的调用代码是:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-02
    • 1970-01-01
    • 2017-07-27
    • 2018-04-21
    • 1970-01-01
    相关资源
    最近更新 更多