【问题标题】:Calling unmanaged function which takes a pointer to pointer parameter调用带有指向指针参数的指针的非托管函数
【发布时间】:2020-04-14 01:56:34
【问题描述】:

我正在尝试从我的 .Net Core 应用程序中调用 C 中的函数。 为了深入了解,C 函数来自 libmpv render.h,函数的头部如下所示:

int mpv_render_context_create(mpv_render_context **res, mpv_handle *mpv, mpv_render_param *params);

问题是我不知道如何从 C# 调用函数。所以我尝试了以下方法:

[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int mpv_render_context_create([Out] out IntPtr renderContext, IntPtr mpvHandle,
        MpvRenderParam[] parameters);

由于**res 参数应该由函数更新,我认为这是有道理的。我试图用以下方式调用:

var ptr = IntPtr.Zero;
var apiTypePtr = Marshal.StringToHGlobalAuto("opengl");
var i = mpv_render_context_create(out ptr, _mpvHandle, new []
{
    new MpvRenderParam(MpvRenderParamType.ApiType, apiTypePtr),
    new MpvRenderParam(MpvRenderParamType.Invalid, IntPtr.Zero
});

这给我抛出了一个 AccessViolationException,事实上,每次我调用 mpv_render_context_create 方法时,我都会得到相同的异常,所以当我说“它不起作用”时,我的意思是调用抛出了那个异常。

然后我想也许我必须将其设为 ref 参数:

[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int mpv_render_context_create(ref IntPtr renderContext, IntPtr mpvHandle,
        MpvRenderParam[] parameters);

调用时出现同样的错误。

然后我读到另一个 stackoverflow 问题,我应该像下面这样传递裸 IntPtr:

[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int mpv_render_context_create(IntPtr renderContext, IntPtr mpvHandle,
        MpvRenderParam[] parameters);

但首先,它不再起作用,其次我怎么能从中得到结果?

那么我应该如何创建和调用函数以便创建渲染上下文?

MpvRenderParam声明如下:

[StructLayout(LayoutKind.Sequential)]
public struct MpvRenderParam
{
    public MpvRenderParamType Type { get; }
    public IntPtr Data { get; }

    public MpvRenderParam(MpvRenderParamType type, IntPtr data)
    {
        Type = type;
        Data = data;
    }
}

还有MpvRenderParamType

public enum MpvRenderParamType
{
    Invalid = 0,
    ApiType = 1,
    InitParams = 2,
    Fbo = 3,
    FlipY = 4,
    Depth = 5,
    IccProfile = 6,
    AmbientLight = 7,
    X11Display = 8,
    WlDisplay = 9,
    AdvancedControl = 10,
    NextFrameInfo = 11,
    BlockForTargetTime = 12,
    SkipRendering = 13,
    DrmDisplay = 14,
    DrmDrawSurfaceSize = 15,
    DrmDisplayV2 = 15
}

更新

我已经考虑了 Mattias Santoro 提供的所有资源,我只是不完全确定如何将它们翻译成 C#,因为它是一种托管语言。我现在调用mpv函数的方法是这样的:

OpenGlControl.OpenGL.MakeCurrent();
IntPtr ptr;
var apiTypePtr = Marshal.StringToHGlobalAuto("opengl");
var opengl = new MpvOpenGlInitParams {get_proc_address = getProcAdress};
var size = Marshal.SizeOf(opengl);
var arr = new byte[size];

fixed (byte* arrPtr = arr)
{
    Marshal.StructureToPtr(opengl, (IntPtr)arrPtr, true);
    var parameters = new MpvRenderParam[]
    {
        new MpvRenderParam {Type = MpvRenderParamType.ApiType, Data = &apiTypePtr},
        new MpvRenderParam {Type = MpvRenderParamType.OpenGlInitParams, Data = &arrPtr},
        new MpvRenderParam {Type = MpvRenderParamType.Invalid, Data = null}
    };
    var i = mpv_render_context_create(&ptr, _mpvHandle, parameters);
}

MpvOpenGlInitParams

[StructLayout(LayoutKind.Sequential)]
public struct MpvOpenGlInitParams
{
    public delegate IntPtr GetProcAdressDelegate(IntPtr ctx, string name);

    public GetProcAdressDelegate get_proc_address;
    public IntPtr get_proc_address_ctx;
    public string extra_exts;
}

遗憾的是,它仍然抛出同样的错误,我不确定我做错了什么,这真的很令人沮丧。

【问题讨论】:

  • 第一个看起来不错,但是MpvRenderParam怎么声明呢?
  • MpvRenderParam 的声明一直都是一样的,像这样new [] { new MpvRenderParam(MpvRenderParamType.ApiType, apiTypePtr), new MpvRenderParam(MpvRenderParamType.Invalid, IntPtr. },声明是MpvRenderParam[] parameters
  • 我的意思是如何声明 MpvRenderParam 类型
  • 对不起,我已经添加了MpvRenderParamMpvRenderParamType的声明。 @jdweng 我试过了,似乎没有什么区别,因为编译器告诉我我应该把它改成 new[]
  • 不要在 p/invoke 结构中使用属性,而是使用字段(删除 {get;})

标签: c# pinvoke dllimport unmanaged mpv


【解决方案1】:

我已将我的代码放入此 repo 中:(对于 .net core 2.1 和 sdl2 以及用于 X64 的 mpv dll) https://github.com/shodo/MPVCore.git

更新 2:DOOOONEEEE! 首先,这是我的(真的很丑)代码。我不知道我所做的每一件事是否都需要:

函数定义为:

 [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
 private delegate int MpvRenderContextCreate(ref IntPtr context, IntPtr mpvHandler, IntPtr parameters);
 private MpvRenderContextCreate _mpvRenderContextCreate;

还有代码(它是 c++ sdl 示例的硬编码 frankestein 以及 winform 和其他内容的一部分)

if (_mpvHandle != IntPtr.Zero)
            _mpvTerminateDestroy(_mpvHandle);

        LoadMpvDynamic();
        if (_libMpvDll == IntPtr.Zero)
            return;

        _mpvHandle = _mpvCreate.Invoke();
        if (_mpvHandle == IntPtr.Zero)
            return;

        _mpvInitialize.Invoke(_mpvHandle);



        MpvOpenGlInitParams oglInitParams = new MpvOpenGlInitParams();
        oglInitParams.get_proc_address = (ctx, name) => SDL.SDL_GL_GetProcAddress(name);
        oglInitParams.get_proc_address_ctx = IntPtr.Zero;
        oglInitParams.extra_exts = IntPtr.Zero;

        var size = Marshal.SizeOf<MpvOpenGlInitParams>();
        var oglInitParamsBuf = new byte[size];

        fixed (byte* arrPtr = oglInitParamsBuf)
        {
            IntPtr oglInitParamsPtr = new IntPtr(arrPtr);
            Marshal.StructureToPtr(oglInitParams, oglInitParamsPtr, true);

            MpvRenderParam* parameters = stackalloc MpvRenderParam[3];

            parameters[0].type = MpvRenderParamType.ApiType;
            parameters[0].data = Marshal.StringToHGlobalAnsi("opengl");

            parameters[1].type = MpvRenderParamType.InitParams;
            parameters[1].data = oglInitParamsPtr;

            parameters[2].type = MpvRenderParamType.Invalid;
            parameters[2].data = IntPtr.Zero;

            var renderParamSize = Marshal.SizeOf<MpvRenderParam>();

            var paramBuf = new byte[renderParamSize * 3];
            fixed (byte* paramBufPtr = paramBuf)
            {
                IntPtr param1Ptr = new IntPtr(paramBufPtr);
                Marshal.StructureToPtr(parameters[0], param1Ptr, true);

                IntPtr param2Ptr = new IntPtr(paramBufPtr + renderParamSize);
                Marshal.StructureToPtr(parameters[1], param2Ptr, true);

                IntPtr param3Ptr = new IntPtr(paramBufPtr + renderParamSize + renderParamSize);
                Marshal.StructureToPtr(parameters[2], param3Ptr, true);


                IntPtr context = new IntPtr(0);
                _mpvRenderContextCreate(ref context, _mpvHandle, param1Ptr);
            }
        }

这个函数起作用的原因是:

parameters[0].data = Marshal.StringToHGlobalAnsi("opengl");

而不是使用

parameters[0].data = Marshal.StringToHGlobalAuto("opengl");

我认为这不仅仅是识别使用 opengl 渲染上下文初始化 mpv 播放器所需的“opengl”参数

更新: 为了调用该函数,您需要一个初始化的 opengl 上下文,或者,您应该传递一个 MPV_RENDER_PARAM_OPENGL_INIT_PARAMS 类型的参数以及访问 open gl 函数所需的回调。 查看这个使用 SDL 初始化窗口并获取 opengl 上下文的 C++ 示例。

https://github.com/mpv-player/mpv-examples/blob/master/libmpv/sdl/main.c

如果没有正确初始化的 OpenGL 上下文,您总是会收到有关正确 c# 编组的 ViolationException 错误: github.com/mpv-player/mpv/issues/6249
https://github.com/mpv-player/mpv/issues/6507

-- 旧的: 看到这个: https://stackoverflow.com/questions/20419415/c-sharp-call-c-dll-passing-pointer-to-pointer-argument

【讨论】:

  • 感谢您的链接,我回家后会尝试,但由于 mpv_render_context 是一个不透明的指针,我应该使用 IntPtr** 而不是 DataStruct** 吗?还是我应该使用其他东西?
  • 所以我尝试了其他答案的建议并将 mpv_render_context_create 更改为不安全并且第一个参数为 IntPtr* 类型并没有帮助,我仍然得到相同的确切异常。
  • 还要检查这个(它来自 XCode 上的示例):github.com/mpv-player/mpv/issues/6249 访问冲突可能不是由错误的 C# 编组引起的,而是导致在调用 mpv_render_context_create() 之前未初始化 OpenGL 上下文跨度>
  • 嘿,我非常感谢您为帮助我所做的努力,如果仍然无法正常工作,我将在最后奖励赏金,但如果我们能让这个工作正常,我将非常感谢您的帮助跨度>
  • 谢谢!只是一个问题..您真的想使用 opengl 渲染上下文,或者您只是想在您的应用程序中嵌入 MPV?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多