0 / 0 / 0
Регистрация: 09.02.2017
Сообщений: 69
1

Как дождаться закрытия файла другим процессом/потоком

16.08.2017, 04:47. Показов 3266. Ответов 14
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Всем привет.

Допустим известно имя файла. Необходимо:
* найти его.
* если его сейчас использует другой процесс/поток, то дождаться его закрытия.
* открыть из своего процесса/потока для записи.

Я так понимаю:
* искать через FindFirstFile(), FindNextFile(), FindClose().
* ждать через WaitForSingleObject().
* писать через WriteFile().

Но где взять нормальный хэндл под функции WaitForSingleObject() и WriteFile()?
Ведь FindFirstFile() даёт поисковый (узкоспециализированный) хендл? Или не так?

Заранее благодарю.
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
16.08.2017, 04:47
Ответы с готовыми решениями:

Как дождаться закрытия файла?
Допустим открываем файл, а потом закрываем file.close(); Или открываем для чтения ресурсов и...

Отслеживание закрытия файла процессом, запущенным через CreateProcess
Доброго времени суток! Возникла нетривиальная(на мой взгляд) задачка: Открываем какой нибудь файл...

Дождаться освобождения файла другим процессом
Доброго дня, форумчане. Подскажите, пожалуйста, есть ли в Visual Foxpro команда с помощью которой...

Ошибка открытия файла, так как он занят другим процессом
Возникла проблема: при нажатии на кнопку прописал код, чтобы открывался файл из определенной...

14
93 / 92 / 21
Регистрация: 16.08.2017
Сообщений: 188
Записей в блоге: 1
16.08.2017, 18:04 2
Посмотрите документацию на функцию CreateFile. Она позволяет получить хендл файла. Смотрите вот здесь HANDLE файла зная путь к нему
0
Эксперт С++
3573 / 2827 / 451
Регистрация: 03.05.2011
Сообщений: 5,193
Записей в блоге: 22
17.08.2017, 17:29 3
Цитата Сообщение от codesurfer Посмотреть сообщение
где взять нормальный хэндл под функции WaitForSingleObject()
для WaitForSingleObject нужен хендл процесса
C++
1
2
3
4
5
6
7
8
9
10
STARTUPINFO StartInfo = { 0 };
PROCESS_INFORMATION ProcInfo = { 0 };
ZeroMemory(&StartInfo, sizeof(StartInfo));
ZeroMemory(&ProcInfo, sizeof(ProcInfo));
StartInfo.cb = sizeof(StartInfo);
if (CreateProcess("path", NULL, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &StartInfo, &ProcInfo)) {
    WaitForSingleObject(ProcInfo.hProcess, INFINITE);
    CloseHandle(ProcInfo.hThread);
    CloseHandle(ProcInfo.hProcess);
}
а вот
Цитата Сообщение от codesurfer Посмотреть сообщение
WriteFile()
нужен хендл файла, который можно получить например из структуры LOADED_IMAGE
1
0 / 0 / 0
Регистрация: 09.02.2017
Сообщений: 69
18.08.2017, 01:29  [ТС] 4
_lunar_, не уверен что правильно Вас понимаю.

Предположим:
* есть файл: "C:\\test.csv".
* в этом же каталоге есть приложение: "C:\\test.exe".
* в файл test.csv могут синхронно писать и читать данные потоки из разных процессов.
* test.exe хочет вызвать WriteFile(), но прежде ему нужен хэндл файла test.csv.
* для этого он должен открыть test.csv через CreateFile().
* но на сколько мне известно, если другой поток монопольно владеет доступом к test.csv, то CreateFile() из текущего потока вернёт INVALID_HANDLE_VALUE, и следовательно вызвать WriteFile() не получится пока другой поток не освободит файл.

Т.е. нужно ждать. Я видел такие способы:
1. Идеально если была функция типа WaitFile(имя_файла, сколько_ждать)...
2. Либо как-то так WaitForSingleObject(нечто типа GetFileHandle(имя_файла), сколько_ждать)
3. Плохой вариант так:
C++
1
2
while(CreateFile()==INVALID_HANDLE_VALUE) Sleep(1); // ждём освобождения файла
WriteFile(); // пишем
Не знал что для этого нужно создавать целый процесс.
Вопрос - это точно подходящее решение?
0
Эксперт С++
3573 / 2827 / 451
Регистрация: 03.05.2011
Сообщений: 5,193
Записей в блоге: 22
18.08.2017, 02:24 5
Цитата Сообщение от codesurfer Посмотреть сообщение
1. Идеально если была функция типа WaitFile(имя_файла, сколько_ждать)
не получится, пока файл не будет удален или перемещен или переписан, событие не сработает.

Я отвечал на вопрос как получить разного рода хендлы. Естественно если файл занят, то вы в него ничего не запишите, но открыть файл для чтения (чтобы получить хендл файла) ничто не мешает.
Можно попробовать найти хендл окна (если оно есть), а из него получить pID и хендл процесса
C++
1
2
3
4
5
6
HWND hWnd = FindWindow(NULL, "test");
DWORD pID = 0;
GetWindowThreadProcessId(hWnd, &pID);
HANDLE hProcess = OpenProcess(PROCESS_VM_READ, FALSE, pID);
 
WaitForSingleObject(hProcess, INFINITE);
1
0 / 0 / 0
Регистрация: 09.02.2017
Сообщений: 69
18.08.2017, 02:33  [ТС] 6
Цитата Сообщение от _lunar_ Посмотреть сообщение
Можно попробовать найти хендл окна (если оно есть), а из него получить pID и хендл процесса
нет наверное. Просто лежит себе файл, и с ним работают разные потоки (созданные программами, не людьми). Соответственно окон никто не создаёт.

Цитата Сообщение от _lunar_ Посмотреть сообщение
Я отвечал на вопрос как получить разного рода хендлы. Естественно если файл занят, то вы в него ничего не запишите, но открыть файл для чтения (чтобы получить хендл файла) ничто не мешает.
Это уже ближе...

Скажите, а CreateFile() открывающий файл для чтения не даст нам хэндл?
0
232 / 135 / 19
Регистрация: 10.11.2015
Сообщений: 305
18.08.2017, 06:52 7
Цитата Сообщение от codesurfer Посмотреть сообщение
Скажите, а CreateFile() открывающий файл для чтения не даст нам хэндл?
Даст, если при его открытии в чужом процессе, был установлен режим FILE_SHARE_READ. Иначе получишь ERROR_SHARING_VIOLATION.

Цитата Сообщение от codesurfer Посмотреть сообщение
если его сейчас использует другой процесс/поток, то дождаться его закрытия
Самый простой вариант, это крутится в цикле. Пытаемся открыть, если получили код ошибки ERROR_SHARING_VIOLATION тогда делаем небольшую задержку и пытаемся снова.

Из непростых, это перечислить хэндлы посредством NtQuerySystemInformation (с инфоклассом SystemHandleInformation) и продублировать нужный хэндл к себе в процесс, но много подводных камней в таком способе.

Добавлено через 8 минут
Enumerate handles
Кликните здесь для просмотра всего текста
"How do I enumerate handles/opened files?" is a common beginner question here. I'll try to expand on the first handle enumeration topic and provide a bit more information on how to get handle names, etc. Complete sample code in C is attached.

Step 1: Enumerating handles
Call NtQuerySystemInformation with SystemHandleInformation (16). This will give you a list of handles opened by every single process. Here are the definitions:

#define SystemHandleInformation 16

typedef NTSTATUS (NTAPI *_NtQuerySystemInformation)(
ULONG SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength
);

/* The following structure is actually called SYSTEM_HANDLE_TABLE_ENTRY_INFO, but SYSTEM_HANDLE is shorter. */
typedef struct _SYSTEM_HANDLE
{
ULONG ProcessId;
BYTE ObjectTypeNumber;
BYTE Flags;
USHORT Handle;
PVOID Object;
ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE, *PSYSTEM_HANDLE;

typedef struct _SYSTEM_HANDLE_INFORMATION
{
ULONG HandleCount; /* Or NumberOfHandles if you prefer. */
SYSTEM_HANDLE Handles[1];
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;

An unusual aspect of calling NtQuerySystemInformation with SystemHandleInformation is that if you supply a buffer which is too small, it returns STATUS_INFO_LENGTH_MISMATCH (0xc0000004) instead of giving you the correct buffer size in ReturnLength. This means you will have to guess the buffer size. A common technique is to call NtQuerySystemInformation in a loop until it succeeds with STATUS_SUCCESS (0), reallocating and doubling the buffer size each time it fails with STATUS_INFO_LENGTH_MISMATCH.

Step 2: Getting handle types and names
After you have the list of handles, you will probably want to get the types and names of the handles. There is no way to do this without duplicating the handle into your own process, so we can do that using DuplicateHandle (NOTE: in the source code I use NtDuplicateObject, but it's the same idea).

Handle types
Call NtQueryObject with ObjectTypeInformation (2). You will get this structure back:

typedef struct _OBJECT_TYPE_INFORMATION
{
UNICODE_STRING TypeName;
ULONG TotalNumberOfObjects;
ULONG TotalNumberOfHandles;
ULONG TotalPagedPoolUsage;
ULONG TotalNonPagedPoolUsage;
ULONG TotalNamePoolUsage;
ULONG TotalHandleTableUsage;
ULONG HighWaterNumberOfObjects;
ULONG HighWaterNumberOfHandles;
ULONG HighWaterPagedPoolUsage;
ULONG HighWaterNonPagedPoolUsage;
ULONG HighWaterNamePoolUsage;
ULONG HighWaterHandleTableUsage;
ULONG InvalidAttributes;
GENERIC_MAPPING GenericMapping;
ULONG ValidAccessMask;
BOOLEAN SecurityRequired;
BOOLEAN MaintainHandleCount;
ULONG PoolType;
ULONG DefaultPagedPoolCharge;
ULONG DefaultNonPagedPoolCharge;
} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;

Processes
To get the "name" of a process, you can duplicate the handle into your own process with PROCESS_QUERY_INFORMATION access and call GetProcessId (or use NtQueryInformationProcess with ProcessBasicInformation if you prefer, since GetProcessId is only supported on XP SP1 and higher).

Threads
Same idea as with processes (except you use THREAD_QUERY_INFORMATION access), and you can call GetThreadId and GetProcessIdOfThread (again, you can also call NtQueryInformationThread with ThreadBasicInformation and use the ClientId).

Tokens
Again, you can duplicate the handle and use GetTokenInformation to get the username.

Files, Events, Mutants, other objects
To get the names of other objects, you must duplicate the handle and use NtQueryObject with ObjectNameInformation (1) to get the name of the object. You will get a UNICODE_STRING back. IMPORTANT: NtQueryObject may hang on file handles pointing to named pipes. To fix this, do not query any file handles opened with an access (GrantedAccess) of 0x0012019f. This problem only appears for ObjectNameInformation, not ObjectTypeInformation. See the sample code for more information.

You will get filenames in native filename format (i.e. \Device\HarddiskVolume1\...). You can use QueryDosDevice to get mappings between DOS drive letters and device prefixes.

(Step 3: Closing remote handles)
To close handles opened by other processes, you simply call DuplicateHandle with DUPLICATE_CLOSE_SOURCE (1) specified in the options parameter (it's documented on the MSDN page for DuplicateHandle, so go read it). You can specify NULL for the target process handle and target handle parameters. For example:

DuplicateHandle(handleToTheRemoteProcess, theRemoteHandle, NULL, NULL, 0, FALSE, 0x1);
0
0 / 0 / 0
Регистрация: 09.02.2017
Сообщений: 69
20.08.2017, 01:20  [ТС] 8
Цитата Сообщение от jupman Посмотреть сообщение
Самый простой вариант, это крутится в цикле.
Предлагаю рассматривать его как последний вариант.

Цитата Сообщение от jupman Посмотреть сообщение
Даст, если при его открытии в чужом процессе, был установлен режим FILE_SHARE_READ. Иначе получишь ERROR_SHARING_VIOLATION.
А вот это интересный вариант. Т.е. выходит, если я управляю всеми потоками которые работают с этим файлом, то я просто для них всех при открытии файла ставлю одним из флагов FILE_SHARE_READ, и тогда все остальные потоки смогут спокойно ждать освобождения хэндла файла через WaitForSingleObject(). Правильно..?
0
232 / 135 / 19
Регистрация: 10.11.2015
Сообщений: 305
20.08.2017, 06:26 9
Цитата Сообщение от codesurfer Посмотреть сообщение
... я просто для них всех при открытии файла ставлю одним из флагов FILE_SHARE_READ, и тогда все остальные потоки смогут спокойно ждать освобождения хэндла файла через WaitForSingleObject(). Правильно..?
Неправельно. WaitForSingleObject вообще не применим для хэндлов файла, он используется для объектов которые могу переходить в сигнальное состояние (процессы, потоки, мютексы, семафоры, etc). Что касается FILE_SHARE_READ. Это режим позволяющий одновременно читать из файла другим процессам, включая тот, который осуществил открытие файла для совместного доступа на чтению.

WaitForSingleObject
The WaitForSingleObject function checks the current state of the specified object. If the object's state is nonsignaled, the calling thread enters the wait state until the object is signaled or the time-out interval elapses.

The function modifies the state of some types of synchronization objects. Modification occurs only for the object whose signaled state caused the function to return. For example, the count of a semaphore object is decreased by one.

The WaitForSingleObject function can wait for the following objects:

Change notification
Console input
Event
Memory resource notification
Mutex
Process
Semaphore
Thread
Waitable timer
CreateFile
FILE_SHARE_READ 0x00000001

Enables subsequent open operations on a file or device to request read access.

Otherwise, other processes cannot open the file or device if they request read access.

If this flag is not specified, but the file or device has been opened for read access, the function fails

Цитата Сообщение от codesurfer Посмотреть сообщение
Т.е. выходит, если я управляю всеми потоками которые работают с этим файлом ...
Стоп. Получается что потоки которые открывают файл ваши? Тогда я не вижу проблемы. Можно любые средства синхронизации применить. Можно воспользоваться LockFileEx к примеру.
0
0 / 0 / 0
Регистрация: 09.02.2017
Сообщений: 69
21.08.2017, 10:11  [ТС] 10
Цитата Сообщение от jupman Посмотреть сообщение
Можно воспользоваться LockFileEx к примеру.
Я думаю это подойдёт, нужно потестировать. Спасибо.

Добавлено через 41 минуту
Хочу ещё уточнить по LockFileEx.
Как правильно понимать эти два параметра:
nNumberOfBytesToLockLow [in]
The low-order 32 bits of the length of the byte range to lock.
nNumberOfBytesToLockHigh [in]
The high-order 32 bits of the length of the byte range to lock.
По идее это должен быть диапазон блокировки, nNumberOfBytesToLockLow - _С_ какого байта и nNumberOfBytesToLockHigh - _ПО_ какой байт.
Но в примере MSDN параметр nNumberOfBytesToLockHig равен 0. Это значит что _С_ у них был конечный, а _ПО_ начальный байт (т.е. наоборот)? Или как?
C++
1
2
3
4
5
6
7
    fSuccess = LockFileEx(hFile,         // exclusive access, 
                          LOCKFILE_EXCLUSIVE_LOCK | 
                          LOCKFILE_FAIL_IMMEDIATELY,
                          0,             // reserved, must be zero
                          TESTSTRLEN,    // number of bytes to lock
                          0,
                          &sOverlapped); // contains the file offset
0
232 / 135 / 19
Регистрация: 10.11.2015
Сообщений: 305
21.08.2017, 15:49 11
Цитата Сообщение от codesurfer Посмотреть сообщение
По идее это должен быть диапазон блокировки, nNumberOfBytesToLockLow - _С_ какого байта и nNumberOfBytesToLockHigh - _ПО_ какой байт.
Это не диапазон, это количество байт для блокировки (64-битное значение), а начало блокируемого участка в OVERLAPPED указывается.
0
0 / 0 / 0
Регистрация: 09.02.2017
Сообщений: 69
21.08.2017, 16:10  [ТС] 12
Цитата Сообщение от jupman Посмотреть сообщение
Это не диапазон, это количество байт для блокировки (64-битное значение), а начало блокируемого участка в OVERLAPPED указывается.
Всё равно не понятно. В OVERLAPPED указывается начало, хорошо, тогда как указывается конец, ведь там два параметра по 32-бита каждый?
0
232 / 135 / 19
Регистрация: 10.11.2015
Сообщений: 305
21.08.2017, 16:27 13
Цитата Сообщение от codesurfer Посмотреть сообщение
тогда как указывается конец, ведь там два параметра по 32-бита каждый
количество байт для блокировки - это 64-битное значение. Младшие 32 бита указываются в nNumberOfBytesToLockLow, старшие 32 бита в nNumberOfBytesToLockHigh. В примере nNumberOfBytesToLockHigh равен 0. Т.к. кол-во блокируемых байтов не превышает 4 Гб.
0
0 / 0 / 0
Регистрация: 09.02.2017
Сообщений: 69
21.08.2017, 16:52  [ТС] 14
Грубо говоря понятно
Спасибо большое!
0
1483 / 1403 / 240
Регистрация: 19.02.2010
Сообщений: 3,908
27.08.2017, 22:39 15
Цитата Сообщение от codesurfer Посмотреть сообщение
Допустим известно имя файла. Необходимо:
* найти его.
* если его сейчас использует другой процесс/поток, то дождаться его закрытия.
* открыть из своего процесса/потока для записи.
Я так понимаю:
* искать через FindFirstFile(), FindNextFile(), FindClose().
* ждать через WaitForSingleObject().
Искать не нужно - можно просто попытаться открыть (путь-то к файлу известен) через старую функцию OpenFile() с флагом OF_SHARE_EXCLUSIVE. Она откроет файл, если тот никем не занят.
А если файл занят - крутить цикл попыток открытия, делая между неудачными попытками Sleep() на некоторое время.

Добавлено через 14 секунд
Цитата Сообщение от codesurfer Посмотреть сообщение
Допустим известно имя файла. Необходимо:
* найти его.
* если его сейчас использует другой процесс/поток, то дождаться его закрытия.
* открыть из своего процесса/потока для записи.
Я так понимаю:
* искать через FindFirstFile(), FindNextFile(), FindClose().
* ждать через WaitForSingleObject().
Искать не нужно - можно просто попытаться открыть (путь-то к файлу известен) через старую функцию OpenFile() с флагом OF_SHARE_EXCLUSIVE. Она откроет файл, если тот никем не занят.
А если файл занят - крутить цикл попыток открытия, делая между неудачными попытками Sleep() на некоторое время.
0
27.08.2017, 22:39
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
27.08.2017, 22:39
Помогаю со студенческими работами здесь

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

Чтение файла занятого другим процессом
Нашел много по этому вопросу, да и сам когда то делал но сейчас не работает. Файл занят другим...

Чтение файла занятого другим процессом
Приветствую. Сразу к сути. Есть бинарный файл в который постоянно пишется информация. Как мне...

Проверка занятости файла другим процессом
Здравствуйте. Существует ли метод, сообщающий, записывается ли файл другим процессом? Хочется...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
15
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru