P / Invoke AccessViolationException, который не имеет смысла - PullRequest
0 голосов
/ 26 сентября 2010

Прежде всего, извините, что опубликовал такой вопрос, когда так много других было задано по этой теме, но я читал все вопросы, которые смог найти (+ google), и ни один из них не дал мне никаких намеков относительно того, что происходит в моем случае.

У меня есть сторонний .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) в некоторых случаях не очень хорошо. Вот почему разработчик предоставляет вторую функцию, которой можно передать открытый FILE * в режиме "w + b" (который я открываю с помощью вызова 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).

Я передаю одни и те же 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 может иметь какую-то проблему, поэтому я переключил ее на версию, скомпилированную другой третьей стороной (размером на 150 КБ), но у меня точно такая же проблема.

Код, который я использую для вызова:

[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: я не знаю, имеет ли это значение, но я использую VS2010 под Win7 x64.

Спасибо, что уделили время заранее

Ответы [ 2 ]

2 голосов
/ 26 сентября 2010

«Я не знаю, имеет ли это значение, но я использую VS2010 под Win7 x64». - это имеет большое значение! Вы используете 32-битную библиотеку libFLAC DLL? Если это так, вам нужно установить «Цель платформы» на вкладке сборки свойств проекта на «x86», чтобы заставить управляемую сборку работать в 32-битном режиме, чтобы она соответствовала 32-битной DLL, которую вы используете. р-вызов в.

Или вы можете получить 64-битную библиотеку libFLAC DLL, а затем вам нужно будет включить как 32-битную, так и 64-битную версии и вызвать соответствующую, в зависимости от значения IntPtr.Size вы работаете в 32- или 64-битном режиме).

1 голос
/ 26 сентября 2010
    FILE.Open(out FileHandle, "test.flac", "w+b");

Что это на самом деле делает?Кажется, что для кода flac требуется ФАЙЛ *.Это указатель, а не ручка.ЭЛТ абсолютно не зависит от дескрипторов Windows.Если FileHandle на самом деле является дескриптором Windows, тогда вы гарантированно получите большой Kaboom, дескрипторы представляют собой запутанные значения указателя.

Вы не можете получить FILE * из CRT, не вызвав функцию, которую вы пишете, которая возвращает требуемуюуказатель.Эта функция должна вызывать fopen (или _wfopen_s) и должна использовать ту же версию CRT, что и код flac.В конце концов, вы, конечно, не впереди, с тем же успехом можете вызвать работающую функцию.

...