Управление наследованием дескрипторов файлов, созданных C std::fstream в Windows

#c #winapi #fstream #child-process #filehandle

#c #winapi #fstream #дочерний процесс #Дескриптор файла

Вопрос:

В Windows при создании процесса с помощью CreateProcess можно передать его true в качестве bInheritHandles аргумента.

 CreateProcess( , , , , bInheritHandles, , , , )
  

Это означает, что все дескрипторы файлов, помеченные как наследуемые, действительно будут унаследованы дочерним процессом.

Как мы можем контролировать, является ли базовый дескриптор файла, созданный std::fstream классом C , наследуемым или нет?

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

1. Вы не можете. Если вы планируете наследовать, используйте CreateFile().

2. Однако, по крайней мере, при использовании MSVC есть способ сначала получить ФАЙЛ * из ДЕСКРИПТОРА, а затем привязать std::fstream к этому ФАЙЛУ *.

3. Зачем беспокоиться обо всем этом. Поскольку вам необходимо использовать API, используйте только API, а не оба API и std.

4. @MichaelChourdakis, fstream объекты намного проще в использовании. Используя operator << и >> , вы получаете автоматический синтаксический анализ и упорядочивание переменных.

5. @Mercalli все объекты std ограничены в своей функциональности по сравнению с API и полезны только для базового уровня обработки. В полноразмерном проекте требуется больше возможностей, а это означает API. Кроме того, вы все еще можете использовать потоковые функции на уровне буфера, а затем записать буфер в файл с помощью API.

Ответ №1:

Среда выполнения C создает наследуемые дескрипторы по умолчанию.

 ofstream outFile("filename.txt") ;
CreateProcess("program.exe", ..., true, ...) ; //program.exe will inherit the above file handle
  

Итак, если вы хотите, чтобы дескриптор был унаследован, вам не нужно ничего делать.

Если вы НЕ хотите, чтобы дескриптор наследовался, вам нужно самостоятельно установить HANDLE_FLAG_INHERIT флаг дескриптора с помощью функции WinAPI SetHandleInformation , вот так:

 FILE* filePtr = fopen("filename.txt", "w") ;
SetHandleInformation( (HANDLE)_get_osfhandle(_fileno(filePtr)), HANDLE_FLAG_INHERIT, 0) ;
ofstream outFile(filePtr) ;
  

В третьей строке выше конструктор ofstream(FILE*) является расширением стандарта, существующего в Visual Studio (я не знаю о других компиляторах).

После этого конструктор filePtr теперь принадлежит outFile , поэтому вызов также outFile.close() закрывается filePtr . Вы можете полностью забыть о filePtr переменной.

Документация: fopen, _fileno, _get_osfhandle, SetHandleInformation

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

1. В качестве альтернативы, вместо «черного списка» всех дескрипторов, которые не должны наследоваться (и, возможно, отсутствующих дескрипторов ОС или библиотеки, которые находятся вне вашего контроля), вы можете внести в белый список конкретно те дескрипторы, которые вы хотите унаследовать, передав PROC_THREAD_ATTRIBUTE_LIST в CreateProcess .

Ответ №2:

Если вы используете fopen для открытия файла, вы можете указать "N" режим, специфичный для Windows, в fopen параметрах, чтобы дескрипторы не наследовались.

Пример:

 #include <stdio.h>
#include <stdlib.h>

int main(void) {
  FILE *fp = fopen("SomeFile.txt", "rwN");
  if (!fp) {
    return -1;
  }

  system("SomeProcess.exe");

  fclose(fp);
  return 0;
}
  

Источники:

https://wiki.sei.cmu.edu/confluence/display/c/WIN03-C. Understand HANDLE inheritance

https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fopen-wfopen?view=vs-2019