P / Invoke AccessViolationException

时间:2013-08-13 13:04:43

标签: c# pinvoke libvlc

当我尝试将路径传递给文件名时,我有一个非托管函数调用抛出此异常。

我已经读过这可能是由DLL本身引起的,但我不认为是这种情况,因为DLL在另一个应用程序中使用,所以问题很可能在我调用该函数的方法中。

规范:

libvlc_media_new_path (libvlc_instance_t *p_instance, const char *path)

说明

p_instance  the instance
path    local filesystem path

我的方法:

[DllImport("libvlc", EntryPoint = "libvlc_media_new_path")]
public static extern IntPtr NewMedia(IntPtr instance,
                            [MarshalAs(UnmanagedType.LPStr)] string path);

我想我错过了常规电话,但这可能是什么?或者它会导致这种异常吗?

编辑:基于一些评论,我做了一些探讨,发现......好吧,没什么。该实例的结构是不透明的,这意味着我不知道Laymans术语。我的猜测是,这意味着您不需要在使用它的应用程序中重建它?

在基于this的盲目猜测中,我将我一直使用的返回值替换为负责将* p_instance值设置为long而不是IntPtr的函数。因为当它是IntPtr时它返回0,并且很长时间我看到了一个值。再一次,IntPtr我真的不知道。我很高兴在实例变量中看到不是0的东西,但是当我将它运行过去时,它再次出错了。

编辑:我已将问题扩展为here

2 个答案:

答案 0 :(得分:1)

根据您所看到的例外以及您为本机功能提供的声明,

libvlc_media_new_path (libvlc_instance_t *p_instance, const char *path)

你的p / invoke声明不正确。你的调用约定不匹配。 .NET p / invoke系统的默认设置是stdcall(以匹配Windows API函数),但C和C ++代码的默认值是cdecl。您必须明确告诉.NET您的函数使用cdecl调用约定。

所以改成它看起来像这样:

[DllImport("libvlc", EntryPoint = "libvlc_media_new_path", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr NewMedia(IntPtr instance,
                                     [MarshalAs(UnmanagedType.LPStr)] string path);

当然,我猜你的回归值是指针是正确的。您显示的本机函数声明缺少返回类型。

关于instance参数的问题,以及您是否正确使用IntPtr类型:参数是指向libvlc_instance_t的指针,因此您有两种基本方法:使用p / invoke进行工作。首先是将参数声明为IntPtr,使其像原始指针值一样进行编组。这对于指针需要不透明(即从一个本机函数检索,存储,然后传递给另一个本机函数)的情况不是特别有用。其次是声明一个镜像本机结构的托管结构,然后编写p / invoke声明来使用这个结构,以便编组器自动处理事物。如果您确实需要与存储在指针所指向的结构中的值进行交互,那么这将非常有用。

在这种情况下,在Google搜索之后,您似乎正在使用其中一个VLC API。具体来说是this onealso tells us libvlc_instance_t是什么:它是一个代表libvlc实例的不透明结构。因此,声明托管结构不是一种选择,因为即使本机代码也将结构视为不透明。你真正需要的只是指针,来回传递;我上面谈到的第一种方法的完美案例。所以上面显示的声明就是你的赢家。

现在唯一的战斗是获取一个指向libvlc实例的有效指针,只要你调用它就可以传递给该函数。有可能会事先调用像libvlc_new这样的函数,它被记录为创建和初始化新的libvlc实例。它的返回值正是您所需要的。因此,除非您已经创建了一个libvlc实例(在这种情况下,使用该指针),您还需要调用此函数并存储其结果。

如果the documentation对于libvlc_new函数的参数所需的值是正确的,您可以非常简单地声明:

[DllImport("libvlc", EntryPoint = "libvlc_new", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr NewCore(int argc, IntPtr argv);

然后这样称呼它:

IntPtr pLibVlc = NewCore(0, IntPtr.Zero);
// now pLibVlc should be non-zero

当然,我对VLC API一无所知,但是我对API设计的一般知识告诉我,一旦你完成,你可能需要用一个实例的同一个指针来调用libvlc_release函数用它。

答案 1 :(得分:0)

尝试没有[MarshalAs(UnmanagedType.LPStr)],通常适合我。