Нарушение доступа в C # - winmm.dll ntdll.dll - PullRequest
0 голосов
/ 14 апреля 2011

У меня есть приложение, которое в основном делает три вещи:

  1. Показать изображение пользователю
  2. Воспроизведение 1-2 секундного звука (wav) для пользователя
  3. Запись микрофонного входа в течение 4 секунд (во время воспроизведения звука)

Это происходит 280 раз на пользователя, и все записи сохраняются в каталоге для каждого пользователя. Тем не менее, 2 из 18 последних запусков программы вылетели из-за необработанного исключения с кодом c0000005 (который описывается как нарушение прав доступа) в модуле ntdll.dll. Единственный неуправляемый вызов API, который я использую, это mciSendString из winmm.dll, чтобы получить длительность wav-файлов и сделать запись. Воспроизведение выполняется с использованием экземпляра WindowsMediaPlayer.

Сбои кажутся случайными, и оба произошли на одной машине (используется 3). Это мои вопросы: действительно ли ntdll.dll является источником исключения? Правильно ли я понимаю, что нарушение доступа является недействительным доступом к памяти? И как это могло произойти с программой на C #, работающей на виртуальной машине .NET?

По запросу здесь есть один класс, из которого я вызываю mciSendString

public class JE_SR
{
    [DllImport("winmm.dll", EntryPoint = "mciSendStringA", 
        CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
    private static extern uint mciSendString(string lpstrCommand, 
        string lpstrReturnString, int uReturnLength, int hwndCallback);

    [DllImport("winmm.dll", CharSet = CharSet.Auto)]
    private static extern int mciGetErrorString(uint errorCode, 
        StringBuilder errorText, int errorTextSize);


    private static bool recording = false;
    public static uint lastResult;

    public static void startRecording()
    {
        if (recording)
        {
            return;
        }

        tryMCISendString("open new Type waveaudio Alias recsound", "", 0, 0);
        tryMCISendString("record recsound", "", 0, 0);

        recording = true;
    }

    public static void stopRecording(string file)
    {
        if (!recording)
        {
            return;
        }

        if (!file.Equals(""))
        {
            tryMCISendString("save recsound " + file, "", 0, 0);
            tryMCISendString("close recsound ", "", 0, 0);
        }
        else
        {
            tryMCISendString("close all", "", 0, 0);
        }

        recording = false;
    }

    public static void tryMCISendString(string lpstrCommand,
        string lpstrReturnString, int uReturnLength, int hwndCallback)
    {
        lastResult = mciSendString(lpstrCommand, lpstrReturnString, uReturnLength, hwndCallback);

        StringBuilder error = new StringBuilder(256);
        if(lastResult != 0)
        {
            mciGetErrorString(lastResult, error, error.Length);
            JE_Log.logMessage("MCIERROR(JE_SR): " + error.ToString());
        }
    }
}

Дайте мне знать, если есть другие важные сведения, которые я должен включить ...

1 Ответ

2 голосов
/ 14 апреля 2011

Одна проблема заключается в следующем:

private static extern uint mciSendString(string lpstrCommand, 
        string lpstrReturnString, int uReturnLength, int hwndCallback);

Это последнее значение должно быть IntPtr. В противном случае он не будет работать в 64-битной среде выполнения, и возможно, что что-то может появиться в стеке. Измените его на IntPtr и передайте IntPtr.Zero.

Кроме того, параметр lpstrReturnString позволяет передавать указатель на буфер, который будет принимать возвращаемые данные. Передавать пустую строку здесь - плохая идея, потому что mciReturnString может попытаться сохранить данные в этой строке. Это может привести к нарушению доступа или, что еще хуже, переписать что-то критическое. Если вам не требуется возвращать информацию об ошибке, либо измените ее на IntPtr и передайте IntPtr.Zero, либо используйте StringBuilder. См. http://www.pinvoke.net/default.aspx/winmm.mcisendstring для правильного определения.

И, да, имеет смысл, чтобы ntdll.dll был источником исключения, поскольку вполне вероятно, что функции в winmm.dll вызывают функции, которые находятся в ntdll.dll. Как уже говорили другие, вам понадобится встроенная трассировка стека, чтобы точно увидеть, что происходит.

...