P / Invoke AccessViolationException毫无意义

时间:2010-09-26 16:16:17

标签: c# pinvoke access-violation

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

我有第三方.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参数(第三个和第四个是空的,根据文档可以为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 个答案:

答案 0 :(得分:2)

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

或者您可以获得64位libFLAC DLL,然后您必须同时包含32位和64位版本,并根据IntPtr.Size的值调用相应的版本(至确定您是以32位还是64位模式运行。

答案 1 :(得分:1)

    FILE.Open(out FileHandle, "test.flac", "w+b");

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

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