【发布时间】:2011-04-17 10:52:33
【问题描述】:
首先,很抱歉发布这样的问题,因为这个话题有很多其他人被问到,但我一直在阅读我能找到的所有问题 (+google),但没有一个真正给我任何提示至于我的情况。
我有一个第 3 方 .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* (IntPtr) 的调用不起作用(它会引发 AccessViolationException)。
我将完全相同的第 1、第 3 和第 4 个参数传递给两者,所以我很确定问题出在 IntPtr FileHandle 参数上(第 3 和第 4 个为空,并且根据文档可以为空。提供真实值不会也无济于事)。我也确信实际的文件句柄没问题:我在另一个项目中使用与 _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 可能存在某种问题,因此我将其切换为由另一个 3rd 方编译的版本(大小增加了 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。
提前感谢您的宝贵时间
【问题讨论】:
标签: c# pinvoke access-violation