Как проверить, является ли ДЕСКРИПТОР допустимым или нет?

#c #c #winapi #port #handle

#c #c #winapi #порт #дескриптор

Вопрос:

В C я открыл последовательный порт, который имеет HANDLE . Поскольку порт может быть закрыт внешним приложением, как я могу убедиться, что HANDLE дескриптор все еще действителен перед чтением данных?

Я думаю, это можно сделать, сверив HANDLE с подходящей функцией API, но какой? Спасибо.

Комментарии:

1. Какой HANDLE у вас есть? Какая функция его создала?

2. какой это компилятор и операционная система?

3. Согласно комментариям под удаленным ответом, это Win32.

4. извините, это Windows 32, и CreateFile() является создателем ДЕСКРИПТОРА. (посмотрите на ответ, пожалуйста)

5. К стыду некоторых авторов API, функция с именем Get ….Handle не обязательно возвращает ДЕСКРИПТОР системному файлу или объекту. Например, GdiGetSpoolFileHandle , что удивительно, возвращает свой внутренний идентификатор, замаскированный под int HANDLE .

Ответ №1:

Проверка того, является ли дескриптор «допустимым», является ошибкой. Вам нужен лучший способ справиться с этим.

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

Например, рассмотрим эту последовательность:

  1. Дескриптор открыт, фактическое значение равно 0x1234
  2. Используется дескриптор, и значение передается по кругу
  3. Дескриптор закрыт.
  4. Какая-то другая часть программы открывает файл, получает значение дескриптора 0x1234
  5. Исходное значение дескриптора «проверяется на достоверность» и передается.
  6. Используется дескриптор, работающий с неправильным файлом.

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

Комментарии:

1. Я так не думаю. Если это другой поток той же программы, да, это возможно. Но, во-первых, у вас есть контроль, поскольку это ваша программа. Современная реализация ОС использует только 1 для обработки значений, что делает невозможным столкновение за короткое время. (Если ваша программа написана тщательно, внутри одной и той же программы, эту проблему можно обнаружить.) Если вы говорите о другом процессе… Я полагаю, что процесс, не владеющий дескриптором (с тем же значением дескриптора), будет рассматривать его как недопустимый дескриптор, в противном случае это нарушение безопасности.

2. @RobinHsu Unix-подобные операционные системы выделяют дескриптор с наименьшим доступным номером. Это упрощает close(0); dup(h); подход и select() работу. В Unix-подобных системах, да, вы не можете закрыть дескриптор в другом процессе. Windows не дает никаких гарантий 1. Это дает гарантию 4, поэтому младшие биты могут использоваться для прикладных целей. Мы не знаем время жизни значения в коде — для долговременного процесса это может быть очень длительный период времени. Корректность не должна зависеть от «короткого времени». В DuplicateHandle() — при соответствующих разрешениях другой процесс может вызвать неожиданности.

3. Спасибо. Думаю, я понял, что вы имеете в виду. Тем не менее, благодаря тщательному программированию проверка дескриптора все еще должна быть возможной. (Ну, программе нужно убедиться, что она установила контрольную точку, где, возможно, создается дескриптор. Хотя это может быть очень утомительно, и я согласен с вашими комментариями по этому утомительному.).

4. @RobinHsu Суть в том, что «проверить дескриптор» может быть правильным, только если вы гарантируете, что процесс не откроет никаких дескрипторов после закрытия проверяемого дескриптора. Как только вы создадите такую надежную гарантию, вам не понадобится функция «проверить дескриптор».

5. Не совсем верно. Когда дескриптор является каналом, он может быть закрыт другой стороной. (и признан недействительным другой стороной). Вызывая PeekNamedPipe() , вы получите возвращаемое значение ошибки, и ошибка является недопустимым дескриптором при вызове getLastError() .

Ответ №2:

Некоторые функции WinAPI возвращают бессмысленную ошибку ERROR_INVALID_PARAMETER, даже если им переданы допустимые дескрипторы, поэтому существует реальный вариант использования для проверки дескрипторов на достоверность.

Функция GetHandleInformation выполняет эту работу: http://msdn.microsoft.com/en-us/library/ms724329(v=vs.85).aspx

Ответ №3:

поскольку порт может быть закрыт внешним приложением

Это невозможно, внешнее приложение не может получить правильное значение дескриптора для передачи CloseHandle(). Как только вы откроете порт, любой другой процесс, пытающийся получить дескриптор порта, получит отказ в доступе.

Тем не менее, существует вредоносная программа, которая обходит это ограничение, обладая секретными знаниями о недокументированных структурах ядра, в которых хранятся дескрипторы процесса. Вы бессильны против них, не совершайте ошибку, вступая в эту битву, делая то же самое. Вы проиграете. Если клиент жалуется на это, передайте ему совет моего врача: «если это больно, то не делайте этого».

Комментарии:

1. Это не так. Приложение с соответствующими разрешениями может использовать DuplicateHandle() для закрытия дескриптора в другом процессе. Документировано; смотрите страницу MSDN.

2. @janm — это предполагает, что второй процесс может получить значение дескриптора. Это крайне нетривиально, когда процесс-владелец не сотрудничает. Требуется взлом недокументированной таблицы ядра или другое секретное знание процесса для считывания его из памяти.

3. @hans — Тривиально, если дескриптор был передан из другого приложения в первую очередь, и это приложение запоминает значение дескриптора, которое оно передало.

4. Рассмотрено в последнем предложении.

5. Зависит от того, кому принадлежит другой процесс. Также рассматривается в последних нескольких предложениях моего ответа! (разница: «ведите бухгалтерию» или «не делайте этого»). Но да, я согласен, что это функция, которую, вероятно, никогда не следует использовать.

Ответ №4:

Если вам выдан дескриптор открытого файла HANDLE и вы просто хотите выяснить, действительно ли это дескриптор открытого файла, для этого есть функция Windows API GetFileInformationByHandle.

В зависимости от разрешений, которые ваш дескриптор предоставляет вам для файла, вы также можете попытаться переместить указатель файла с помощью SetFilePointer, прочитать некоторые данные из него с помощью ReadFile или выполнить операцию записи с нулевым значением, используя WriteFile с nNumberOfBytesToWrite значением 0.

Ответ №5:

Вероятно, вы находитесь под Windows и используете ReadFile для чтения данных. Единственный способ проверить это — попытаться прочитать. Если дескриптор HANDLE недействителен, он вернет код ошибки (используйте GetLastEror(), чтобы увидеть, какой это), который, вероятно, будет ERROR_HANDLE_INVALID .

Комментарии:

1. Вы также можете проверить возвращаемое значение GetCommState , чтобы увидеть, HANDLE по-прежнему ли оно допустимо.

Ответ №6:

Я знаю, что уже немного поздно, но у меня возник аналогичный вопрос к вам, как проверить, все еще открыт ли канал (канал, который я создал с помощью CreateFile) (возможно, другой конец отключил соединение) и может ли он читать, и, если это не так, открыть его снова. Я сделал то, что предложил @Felix Dombek, и использовал файл записи для проверки соединения. Если он вернул 1, это означает, что канал открыт, иначе я снова открыл его с помощью CreateFile. Это означает, что ваш канал является дуплексным. Вот файл CreateFile:
hPipe2 = CreateFile(lpszPipename2, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_WRITE_THROUGH, NULL);
и вот как я проверил наличие соединения:

 while(1)
{   
    bool MessageSent = WriteFile(hPipe2, "Test", 0, amp;cbWritten, NULL);
    if (!(MessageSent))
    {
        LogsOut("Read pipe has been disconnected");
        //Call method to start the pipe again
        break;
    }
    Sleep(200); // I need this because it is a thread
}
  

У меня это работает просто отлично 🙂

Ответ №7:

Вы можете использовать DuplicateHandle для проверки правильности дескриптора.

Первый метод: вы можете попробовать дублировать дескриптор, который хотите проверить на достоверность. В принципе, недопустимые дескрипторы нельзя дублировать.

Второй метод: DuplicateHandle Функция выполняет поиск в таблице дескрипторов дескрипторов Win32 с самого начала в поисках пустой записи, чтобы повторно использовать ее и, таким образом, назначить ей дублированный дескриптор. Вы можете просто проверить дублированное значение адреса дескриптора на значении, большем, чем ваш адрес дескриптора, и если оно больше, то дескриптор не считается недействительным и поэтому не используется повторно. Но этот метод очень специфичен и ограничен, и он работает только тогда, когда над адресом значения дескриптора, который вы хотите протестировать, больше нет пустых или недопустимых записей дескриптора.

Но все, что только что было сказано выше, действительно, только если вы отслеживаете создание и дублирование всех дескрипторов на вашей стороне.

Примеры для Windows 7:

Способ # 1

 // check stdin on validity

HANDLE stdin_handle_dup = INVALID_HANDLE_VALUE;
const bool is_stdin_handle_dup = !!DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_INPUT_HANDLE), GetCurrentProcess(), amp;stdin_handle_dup, 0, FALSE, DUPLICATE_SAME_ACCESS);
if (is_stdin_handle_dup amp;amp; stdin_handle_dup != INVALID_HANDLE_VALUE) {
    CloseHandle(stdin_handle_dup);
    stdin_handle_dup = INVALID_HANDLE_VALUE;
}
  

Способ # 2

 // Assume `0x03` address has a valid stdin handle, then the `0x07` address can be tested on validity (in Windows 7 basically stdin=0x03, stdout=0x07, stderr=0x0b).
// So you can duplicate `0x03` to test `0x07`.

bool is_stdout_handle_default_address_valid = false;
HANDLE stdin_handle_dup = INVALID_HANDLE_VALUE;
const bool is_stdin_handle_dup = !!DuplicateHandle(GetCurrentProcess(), (HANDLE)0x03, GetCurrentProcess(), amp;stdin_handle_dup, 0, FALSE, DUPLICATE_SAME_ACCESS);
if (is_stdin_handle_dup amp;amp; stdin_handle_dup != INVALID_HANDLE_VALUE) {
    if (stdin_handle_dup > (HANDLE)0x07) {
        is_stdout_handle_default_address_valid = true; // duplicated into address higher than 0x07, so 0x07 contains a valid handle
    }
    CloseHandle(stdin_handle_dup);
    stdin_handle_dup = INVALID_HANDLE_VALUE;
}
  

Комментарии:

1. DuplicateHandle — довольно неуклюжая функция. Почему бы вам не опубликовать какой-нибудь пример кода?

2. @Elmue Дублирующий дескриптор не имеет особого смысла без другого кода. Это зависит от того, что вы хотите сделать.

Ответ №8:

Чтобы проверить дескриптор, сначала нам нужно знать, для чего предназначен наш ДЕСКРИПТОР (для файла / порта / окна, …), Затем найти соответствующую функцию для его проверки (спасибо @janm за помощь). Обратите внимание, что назначение функции может быть специально для этого назначения или нет. В моем случае, когда iv’e открыл последовательный порт с помощью CreateFile() , я могу проверить статус COM с помощью API-функции GetCommState(), которая заполняет нашу информационную структуру COM. Если порт больше не открыт или недоступен, функция возвращает 0, и если вы немедленно вызовете GetLastError(), вы получите значение ERROR_INVALID_HANDLE. Спасибо всем за помощь.

Комментарии:

1. Осторожно; вызова чего-то вроде GetCommState недостаточно из-за состояния гонки, когда может быть открыто что-то еще, и операционные системы повторно используют значение дескриптора.