#c# #access-violation #winmm
#c# #нарушение доступа #winmm
Вопрос:
У меня есть приложение, которое в основном выполняет три вещи:
- Показывать изображение пользователю
- Воспроизведение 1-2-секундного звука (wav) для пользователя
- Запись микрофонного ввода в течение 4 секунд (во время воспроизведения звука)
Это происходит 280 раз для каждого пользователя, и все записи сохраняются в каталоге для каждого пользователя. Однако в 2 из последних 18 запусков программы произошел сбой из-за необработанного исключения с кодом c0000005 (которое описывается как нарушение доступа) в модуле ntdll.dll . Единственный неуправляемый вызов API, который я использую, — это mciSendString из winmm.dll чтобы получить длительность файлов wav и выполнить запись. Воспроизведение выполняется с использованием экземпляра WindowsMediaPlayer.
Сбои кажутся случайными, и оба произошли на одном компьютере (используются 3). Это мои вопросы: является ли ntdll.dll действительно является источником исключения? Правильно ли я понимаю, что нарушение доступа — это недопустимый доступ к памяти? И как это могло произойти с программой на C #, запущенной в .Сетевая виртуальная машина?
По запросу вот один класс, из которого я вызываю 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 lastResu<
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. Исходный код того, как вы определили сигнатуру P / Invoke, и способ, которым вы ее вызываете, может быть полезен.
2. Если код выполняет P / Invoke, вы не можете действительно сказать, что программа запущена в . СЕТЕВАЯ виртуальная машина (только). Это все равно что сказать: «Как заключенный может это сделать, когда он в тюрьме (с открытой дверью)»
3. Нам нужен собственный стек вызовов, прежде чем мы сможем выяснить, что происходит. Чтобы получить это, загрузите средства отладки для Windows, запустите WinDbg, «Присоединить к процессу», затем введите ‘.symfix; kn100’ в окне командной строки
4. Несмотря на точность, печальной частью было сравнение .NET-программы с заключенным…
5. @Lasse Я знаю, что это выполняется не ТОЛЬКО на виртуальной машине. Меня сбил с толку тот факт, что в системном журнале указано, что причина исключения возникла из ntdll.dll , что восходит к моему первому вопросу: разумно ли мне вообще думать, что мое использование winmm.dll в чем причина?
Ответ №1:
Одна из проблем заключается в следующем:
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 . Как уже говорили другие, вам понадобится встроенная трассировка стека, чтобы точно видеть, что происходит.
Комментарии:
1. Итак, я знаю, что это произошло много лет спустя, но я просто хочу сказать вам, что именно эта проблема, как выяснилось, была у меня в библиотеке, которой я пользовался, и теперь я наконец могу использовать ее с .Net Framework 4 . Раньше я по какой-то причине был ограничен 3.5. Большое вам спасибо! ^^