Перекрытый последовательный порт и синий экран смерти - PullRequest
2 голосов
/ 12 июля 2009

Я создал класс, который обрабатывает последовательный порт асинхронно. Я использую его для связи с модемом. Я понятия не имею, почему, но иногда, когда я закрываю свое приложение, я получаю синий экран, и мой компьютер перезагружается. Я регистрировал свой код шаг за шагом, но когда появился BSOD и мой компьютер перезагрузился, файл, в который я записывал данные, содержал только пробелы. Поэтому я понятия не имею, в чем может быть причина BSOD.

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

Когда я переосмыслил идею асинхронных операций, мне пришло в голову несколько вещей. Пожалуйста, убедитесь, что это правильно:

1) WaitCommEvent () получает указатель на перекрывающуюся структуру. Поэтому, если я вызываю WaitCommEvent () внутри функции и затем покидаю функцию, перекрывающаяся структура не может быть локальной переменной, верно? Переменная маски события и дескриптор события тоже, верно?

2) ReadFile () и WriteFile () также принимают ссылки или указатели на переменные. Поэтому все эти переменные должны быть доступны до тех пор, пока не завершатся перекрывающиеся операции чтения или записи, верно?

3) Я вызываю WaitCommEvent () только один раз и проверяю его результат в цикле, а пока делаю другие вещи. Поскольку я понятия не имею, как завершить асинхронные операции (возможно ли это?), Когда я уничтожаю свой класс, который хранит дескриптор последовательного порта, я сначала закрываю дескриптор, а затем жду события в использованной перекрытой структуре при вызове функции WaitCommEvent (). Я делаю это, чтобы быть уверенным, что поток, который асинхронно ожидает события comm, не имеет доступа к полям моего класса, который уничтожен. Это хорошая идея или глупая?

try
  CloseHandle(FSerialPortHandle);
  if Assigned(FWaitCommEvent) then
    FWaitCommEvent.WaitFor(INFINITE);      
finally
  FSerialPortHandle := INVALID_HANDLE_VALUE;
  FreeAndNil(FWaitCommEvent);
end;

Прежде, чем я заметил все это, большинство переменных, упомянутых в первом и втором пунктах, были локальными переменными функций, которые вызвали три метода выше. Может ли это быть причиной BSOD или я должен искать другие ошибки в моем коде?

Когда я исправил код, BSOD прекратил свое существование, но это может быть совпадением. Как вы думаете?

Будут оценены любые идеи. Заранее спасибо.


Я прочитал документацию по функции CancelIo (), и в ней говорится, что этот метод отменяет все операции ввода-вывода, выполненные вызывающим потоком. Можно ли ждать FWaitCommEvent после вызова CancelIo (), если я знаю, что WaitCommEvent () был выпущен не тем потоком, который вызывает CancelIo ()?

  if Assigned(FWaitCommEvent) and CancelIo(FSerialPortHandle) then
  begin
    FWaitCommEvent.WaitFor(INFINITE);
    FreeAndNil(FWaitCommEvent);
  end;

Я проверил, что происходит в таком случае, и поток, вызывающий этот фрагмент кода, не заблокировался, даже если он не вызывал WaitCommEvent (). Я проверил на Windows 7 (если это имеет значение). Могу ли я оставить код как есть или это опасно? Может быть, я неправильно понял документацию, и это причина моего вопроса. Я прошу прощения за то, что задал так много вопросов, но я действительно должен быть уверен в этом.

Спасибо.

Ответы [ 2 ]

5 голосов
/ 13 июля 2009

Приложение, работающее от имени обычного пользователя, никогда не должно вызывать проверку ошибок (например, BSOD). (И приложение, работающее от имени администратора, должно делать все возможное, чтобы сделать это.) Либо вы столкнулись с ошибкой драйвера, либо у вас плохое оборудование.

По умолчанию Windows настроена на сохранение минидампа в %SystemRoot%\minidump всякий раз, когда происходит проверка на наличие ошибок. Вы можете определить дополнительную информацию о сбое, загрузив файл мини-дамп в WinDbg , сконфигурировав WinDbg для использования общего хранилища символов Microsoft и выполнив команду !analyze -v в WinDbg. По крайней мере, это должно определить, какой драйвер, вероятно, виноват (хотя я предполагаю, что это ваш драйвер модема).

4 голосов
/ 13 июля 2009
  1. Да, вам нужно сохранить структуру TOverlapped доступной на время операции с перекрытием. В какой-то момент вы будете вызывать GetOverlappedResult, и GetOverlappedResult говорит, что должен получить указатель на структуру, которая использовалась при запуске операции перекрытия. Маска события и дескриптор могут быть сохранены в локальных переменных, если хотите; у вас все равно будет их копия в структуре TOverlapped.

  2. Да, буферы, используемые ReadFile и WriteFile, должны оставаться действительными. Они не делают свои собственные локальные копии для внутреннего использования. Документация для ReadFile даже гласит:

    Этот буфер должен оставаться действительным в течение всей операции чтения. Вызывающая сторона не должна использовать этот буфер до завершения операции чтения.

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

  3. Чтобы отменить операцию перекрытия ввода / вывода, используйте CancelIo. Важно, чтобы вы не освобождали память вашей записи TOverlapped до тех пор, пока не убедитесь, что связанная операция завершена. Аналогично для буфера, который вы читаете или пишете. CancelIo не отменяет операцию немедленно, поэтому ваши буферы могут все еще использоваться, даже после того, как вы ее вызовите.

...