首页 文章

P / Invoke AccessViolationException毫无意义

提问于
浏览
0

首先,很抱歉发布这样的问题,当有很多其他人被问到这个话题时,但我一直在阅读我能找到的所有问题(谷歌),没有人真正给我任何关于什么是什么的提示发生在我的情况下 .

我有一个第三方.dll(libFLAC),带有两个名字相似的导出函数:

FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_file (FLAC__StreamEncoder *encoder, const char *filename, FLAC__StreamEncoderProgressCallback progress_callback, void *client_data)

FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_FILE (FLAC__StreamEncoder *encoder, FILE *file, FLAC__StreamEncoderProgressCallback progress_callback, void *client_data)

第一个目的是用文件名初始化编码器 . 它调用fopen()在某些情况下在Windows上(使用unicode文件名)不是一件好事 . 这就是为什么开发人员提供第二个函数,可以在“w b”模式下传递一个打开的FILE *(我打开它调用MSVCRT.DLL _wfopen_s)

我已经定义了我的P / Invoke声明如下:

[DllImport(Globals.LIBFLAC_DLL, EntryPoint = "FLAC__stream_encoder_init_file", CharSet = CharSet.Ansi)]
public static extern InitStatus InitFilenameAnsi(IntPtr Encoder, string Filename, ProgressDelegate ProgressCallback, ref Object ClientData);

[DllImport(Globals.LIBFLAC_DLL, EntryPoint = "FLAC__stream_encoder_init_FILE")]
public static extern InitStatus InitFileHandle(IntPtr Encoder, IntPtr FileHandle, ProgressDelegate ProgressCallback, ref Object ClientData);

(奇怪的?)问题是对“file”函数(小写)的调用很有效,而采用FILE *(IntPtr)的函数不起作用(它抛出了AccessViolationException) .

我将两个完全相同的第一,第三和第四个参数传递给两者,所以我很确定问题是IntPtr FileHandle参数(第3和第4个是空的,根据文档可以为null . 提供实际值无效其一) . 我也确定实际的文件句柄是可以的:我在另一个项目中使用与_wfopen_s()完全相同的代码,并且它工作正常 . 该文件也是在崩溃之前创建的,因此它也不是问题 .

编辑:返回值只是一个公共枚举InitStatus:int .

有一会儿,我认为这些函数几乎同名的问题可能存在问题,所以我尝试按顺序调用它们,但它也不起作用:

[DllImport(Globals.LIBFLAC_DLL, EntryPoint = "#259", CharSet = CharSet.Ansi)] //"FLAC__stream_encoder_init_file" OK

[DllImport(Globals.LIBFLAC_DLL, EntryPoint = "#258")] //"FLAC__stream_encoder_init_FILE" FAILS

我也认为可能我使用的DLL可能有某种问题,所以我把它换成了另一个第三方(150K大小)编译的版本,但我得到了完全相同的问题 .

我用来调用的代码:

[TestMethod]
public void ThisFailsMiserably()
{
    Object ClientData = null;
    IntPtr FileHandle = IntPtr.Zero;

    try
    {
        FILE.Open(out FileHandle, "test.flac", "w+b");
        Assert.AreNotEqual(IntPtr.Zero, FileHandle); //Works great! the file is created, and the debugger shows the file handle value.
        StreamEncoder.InitFileHandle(FlacStreamEncoder, FileHandle, null, ref ClientData); //AccessViolationException
        Assert.IsTrue(StreamEncoder.Finish(FlacStreamEncoder));
    }
    finally
    {
        FILE.Close(FileHandle);
    }
}

[TestMethod]
public void ThisWorksGreat()
{
    Object ClientData = null;
    Assert.AreEqual(StreamEncoder.InitStatus.OK, StreamEncoder.InitFilenameAnsi(FlacStreamEncoder, "test.flac", null, ref ClientData));
    Assert.IsTrue(StreamEncoder.Finish(FlacStreamEncoder));
}

PS:我不知道它是否重要,但我在Win7 x64下使用VS2010 .

感谢您提前的时间

2 回答

  • 2

    “我不知道它是否重要,但我在Win7 x64下使用VS2010 . ” - 这很重要!您使用的是32位libFLAC DLL吗?如果是这样,您需要将项目属性的构建选项卡下的“平台目标”设置为“x86”,以强制托管程序集以32位模式运行,因此它与32位DLL匹配p调用 .

    或者您可以获得64位libFLAC DLL,然后您必须包括32位和64位版本,并根据 IntPtr.Size 的值调用适当的版本(以确定您是否正在运行32位或64位模式) .

  • 1

    FILE.Open(输出FileHandle,“test.flac”,“w b”);

    这实际上是做什么的? flac代码似乎需要FILE * . 这是一个指针,而不是一个句柄 . CRT与Windows句柄完全无关 . 如果FileHandle实际上是一个Windows句柄,那么你可以保证得到大的Kaboom,句柄是混淆的指针值 .

    如果不对你编写的返回所需指针的函数进行调整,就无法从CRT获取FILE * . 该函数应该调用fopen(或_wfopen_s),并且应该使用与flac代码完全相同的CRT版本 . 最后你当然没有提前,也可以称之为有效的功能 .

相关问题