调用采用指向指针参数的指针的非托管函数
Calling unmanaged function which takes a pointer to pointer parameter
我正在尝试从我的 .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
方法时,我都会得到相同的异常,所以当我说 "It didn't work" 时,我的意思是调用抛出了那个异常。
所以我想也许我必须把它变成一个 ref 参数:
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int mpv_render_context_create(ref IntPtr renderContext, IntPtr mpvHandle,
MpvRenderParam[] parameters);
调用时出现同样的错误
然后我阅读了另一个 Whosebug 问题,我应该像下面这样传递裸 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;
}
可悲的是它仍然抛出同样的错误,我不确定我做错了什么,这真的很令人沮丧。
我已经将我的代码放在这个 repo 中:(对于 .net core 2.1 和 sdl2 以及 X64 的 mpv dll)
https://github.com/shodo/MPVCore.git
更新 2:太棒了!
首先,这是我的(真的很丑)代码。我不知道我所做的每件事是否都需要:
函数定义为:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int MpvRenderContextCreate(ref IntPtr context, IntPtr mpvHandler, IntPtr parameters);
private MpvRenderContextCreate _mpvRenderContextCreate;
和代码(它是 c++ sdl 示例的硬编码科学怪人的一部分,加上 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 函数所需的回调。
在 C++ 中检查这个使用 SDL 的示例,以初始化 window 并获取 opengl 上下文。
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
我正在尝试从我的 .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
方法时,我都会得到相同的异常,所以当我说 "It didn't work" 时,我的意思是调用抛出了那个异常。
所以我想也许我必须把它变成一个 ref 参数:
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int mpv_render_context_create(ref IntPtr renderContext, IntPtr mpvHandle,
MpvRenderParam[] parameters);
调用时出现同样的错误
然后我阅读了另一个 Whosebug 问题,我应该像下面这样传递裸 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;
}
可悲的是它仍然抛出同样的错误,我不确定我做错了什么,这真的很令人沮丧。
我已经将我的代码放在这个 repo 中:(对于 .net core 2.1 和 sdl2 以及 X64 的 mpv dll) https://github.com/shodo/MPVCore.git
更新 2:太棒了! 首先,这是我的(真的很丑)代码。我不知道我所做的每件事是否都需要:
函数定义为:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int MpvRenderContextCreate(ref IntPtr context, IntPtr mpvHandler, IntPtr parameters);
private MpvRenderContextCreate _mpvRenderContextCreate;
和代码(它是 c++ sdl 示例的硬编码科学怪人的一部分,加上 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 函数所需的回调。 在 C++ 中检查这个使用 SDL 的示例,以初始化 window 并获取 opengl 上下文。
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