Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: ошибка при одновременном доступе к файлу  (Прочитано 20342 раз)
0 Пользователей и 3 Гостей смотрят эту тему.
imerlin
Интересующийся

ru
Offline Offline

« : 25-05-2011 08:58 » 

Приветствую всех!

Столкнулся с проблемой, которую никак не могу решить, прошу помочь знающих.

Задача: по нажатию кнопки показать файл из файлохранилища.
Решение: копируем файл во временную папку, показываем с помощью ShellExecute, удаляем временный файл.
Когда удалять временный файл? Можно, конечно, подождать когда закончится дочерний процесс, запущенный ShellExecute, но как получить его хендл?
К тому же если запускающий процесс закончит работу раньше дочернего, будет нехорошо.
Вообще не удалять, запуская раз в месяц сборщик мусора - тоже не вариант, файлы могут занимать сотни мегабайт.
Пытаюсь решить задачу так: создаю временный, расшаренный на чтение, запись и удаление файл функцией CreateFile с ключом FILE_FLAG_DELETE_ON_CLOSE,
копирую данные из файла в хранилище, запускаю ShellExecute, закрываю хэндл файла.
Дочерний процесс пишет, что файл занят другим приложением. Подставив свою прожку определяю код ошибки (GetLastError) после попытки открыть этот файл - 32 (ERROR_SHARING_VIOLATION)
Открываю новый хендл на тот же файл, закрываю старый. Открытие файла дочерним процессом дает ошибку 5 (Access is denied).
Пробовал даже создать дескриптор защиты на полный доступ для всех - результат тот же.

Вот мой код:

Код:
void WINAPI ViewFile(char* from, char* to)
{
HANDLE hNew,hOld,hNewRead;
DWORD dwBytesRead, dwBytesWrite, dwFilePos;
char buff[32768];

int z;

hOld=CreateFile(from, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hOld==INVALID_HANDLE_VALUE){MessageBox(0,"Can't open file from file storage","File error", MB_OK+MB_ICONERROR);goto cleanup;}
z=GetLastError();

hNew=CreateFile(to, FILE_APPEND_DATA+GENERIC_READ, FILE_SHARE_READ+FILE_SHARE_WRITE+FILE_SHARE_DELETE, NULL,CREATE_NEW, FILE_ATTRIBUTE_NORMAL+FILE_FLAG_DELETE_ON_CLOSE, hOld);
if (hOld==INVALID_HANDLE_VALUE){MessageBox(0,"Can't create file to temporary folder","File error", MB_OK+MB_ICONERROR);goto cleanup;}
z=GetLastError();

while (ReadFile(hOld,buff,sizeof(buff),&dwBytesRead,NULL)&&dwBytesRead>0)
{
dwFilePos=SetFilePointer(hNew,0,0,FILE_END);
LockFile(hNew,dwFilePos,0,dwBytesRead,0);
WriteFile(hNew,buff,dwBytesRead,&dwBytesWrite,NULL);
UnlockFile(hNew,dwFilePos,0,dwBytesRead,0);
}

CloseHandle(hOld);


hNewRead=CreateFile(to, GENERIC_READ, FILE_SHARE_READ+FILE_SHARE_WRITE+FILE_SHARE_DELETE, NULL,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, hOld);
//для предотвращения удаления файла оставшегося без ссылок

FlushFileBuffers(hNew);
CloseHandle(hNew);
Sleep(500); //если не подождать - файл удалится раньше запуска дочернего процесса


//ShellExecute(0,"open",to,0,0,3);
system("D:\\Документы_Несмачный\\MyProjects\\FileAccessTest\\Debug\\FileAccessTest.exe C:\\temp\\tmp\\zzz.zts");


z=GetLastError();
Sleep(500);
z=GetLastError();
CloseHandle(hNewRead);
}
Помогите заставить работать это чудо правильно.
Записан
RXL
Технический
Администратор

Offline Offline
Пол: Мужской

WWW
« Ответ #1 : 25-05-2011 09:47 » 

Если приложение-просмоторщик открывает с эксклюзивным доступом, то шарить не поможет.

Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
imerlin
Интересующийся

ru
Offline Offline

« Ответ #2 : 25-05-2011 10:20 » 

С эксклюзивным доступом, мне кажется, редко открывают.
Мое тестовое приложение открывает функцией
Код:
f=fopen(ptr,"rb");
Это эксклюзивное использование? Никогда раньше не задавался этим вопросом.

Добавлено через 16 минут и 53 секунды:
Сейчас погуглил - говорят, fopen шарит на чтение и запись.
« Последнее редактирование: 25-05-2011 10:37 от imerlin » Записан
darkelf
Молодой специалист

nl
Offline Offline

« Ответ #3 : 25-05-2011 13:24 » 

Попробуйте запускать через spawn*() - у него есть _P_WAIT который ожидает завершения процесса. Можно организовать и своё ожидание - запустив с _P_NOWAIT и ожидая на полученном дескрипторе процесса.
ShellExecuteEx() так-же умеет отдавать дескриптор процесса для последующего контроля завершения дочернего процесса.
« Последнее редактирование: 25-05-2011 13:45 от darkelf » Записан
Джон
просто
Администратор

de
Offline Offline
Пол: Мужской

« Ответ #4 : 25-05-2011 13:48 » 

Предполагается, что все файлы должны быть закрыты (просмотрены) к моменту завершения основной программы? Ну той, в которой будут нажимать кнопку. Что должно происходить, если просмотр ещё не завершён?

зы  Я бы всё-таки сделал "уборщик" (службу), который бы просто проверял, заняты файлы во временной папке каким-нить процессом, или нет. Если нет - удалять. Частота опроса определяется эмпирически.
Записан

Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома.
"Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash
"Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman
"All science is either physics or stamp collecting." Ernest Rutherford
"Wer will, findet Wege, wer nicht will, findet Gründe."
imerlin
Интересующийся

ru
Offline Offline

« Ответ #5 : 26-05-2011 06:11 » 

Предполагается, что все файлы должны быть закрыты (просмотрены) к моменту завершения основной программы? Ну той, в которой будут нажимать кнопку. Что должно происходить, если просмотр ещё не завершён?

зы  Я бы всё-таки сделал "уборщик" (службу), который бы просто проверял, заняты файлы во временной папке каким-нить процессом, или нет. Если нет - удалять. Частота опроса определяется эмпирически.

Лучше бы не вводить дополнительных ограничений. Удобнее было бы иметь возможность запустить просмотровщик и забыть: пусть ОС за этим следит. Тем более флажок FILE_FLAG_DELETE_ON_CLOSE у CreateFile как я понял для этого и предназначен. Вот только с одновременным доступом никак разобраться с ошибкой не могу. А "уборщик" никак не годится: могут один за другим десятки сто-мегабайтных файлов просматривать в поисках нужного: системный диск переполнится в момент.
Записан
RXL
Технический
Администратор

Offline Offline
Пол: Мужской

WWW
« Ответ #6 : 26-05-2011 06:39 » 

Может сделать слежение за файлом? Когда файл освободится - удалить его.

Вот ведь как в Unix просто: процесс открыл файл, ты его удалил и можно не париться - после освобождения он ликвидируется сам.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Джон
просто
Администратор

de
Offline Offline
Пол: Мужской

« Ответ #7 : 26-05-2011 06:46 » 

Чёт у тебя какая-то проблема с логикой. С одной стороны, ты напрочь отвергаешь "уборщика". С другой, хочешь чтобы всё было гибко.
Рассмотри сначала возможные сценарии. Допустим, я запускаю прогу и выбираю файл для просмотра. Файл коприруется во временную папку и запускается. Запустить возможно двумя способами: 1. с ожиданием возврата из процесса, или 2. без ожидания - запустил-забыл.
В первом случае тебе придётся создавать кучу тредов - по одному на каждый просмотр файла. Это раз и, ИМХО, не есть гуд. Два - основная прога должна дожидаться завершения просмотра ВСЕХ файлов, те я не могу её закрыть сразу.
Поэтому, опять же ИМХО, 2. предпочтительней - запустил-забыл. Но что ты понимаешь под "ОС за этим следит"? Как ты себе это представляешь? Всё что ты написал после "Пытаюсь решить задачу так", указывет на наличие твоей программы, а не самостоятельной работы ОСи.

С другой стороны, чем тебя конкретно не устраивает "мусорщик"? Временная папка известна, он просто должен следить за ней и удалять незанятые процессами файлы. Это реализировать - как два пальца об асфальт. А опрашивать, как я уже говорил, можно хоть каждую секунду (если кто-то успеет за секунду просмотреть стомегабайтные файлы), хоть каждую минуту. Так что реально файлы будут удаляться практически сразу после просмотра. Да и ещё одно примечание. Речь идёт о просмотре содержимого. Тут возможны два варианта: прога просмотра блокирует файл, или не блокирует. С первым всё ясно - файл "занят", пока работает прога просмотра. Во втором случае проблем тоже не возникает. Например, Проводник считывает данные из файла и тут же его освобождает. Те инфу можно просматривать, даже в том случае, если файл-источник уже удалён.

А "уборщик" никак не годится: могут один за другим десятки сто-мегабайтных файлов просматривать в поисках нужного: системный диск переполнится в момент.

Хе? Так это произойдёт в любом случае, если десятки сто-мегабайтных фалов открыть для просмотра. Как ты представляешь себе решение этой проблемы?

Всё на правах ИМХО конечно.

Добавлено через 42 секунды:
Ром, о чём я ему и говорю.
« Последнее редактирование: 26-05-2011 06:46 от Джон » Записан

Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома.
"Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash
"Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman
"All science is either physics or stamp collecting." Ernest Rutherford
"Wer will, findet Wege, wer nicht will, findet Gründe."
baldr
Команда клуба

cy
Offline Offline
Пол: Мужской
Дорогие россияне


WWW
« Ответ #8 : 26-05-2011 06:48 » 

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

Но Джон предлагает более простой (в разы) способ - блокировать файл просмотрщиком и пытаться удалить мусорщиком. Если не получилось - попробовать через 5-10 минут еще раз...
Записан

Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
zubr
Гость
« Ответ #9 : 26-05-2011 06:59 » 

Зачем так сложно?
Я бы сделал так:
1. Родительский процесс копирует файл во временную папку, используя функцию CopyFileEx
2. Дожидается окончания процесса копирования, смотри параметр lpProgressRoutine
3. Родительский процесс запускает дочерний процесс, используя ShellExecuteEx, с установленной маской SEE_MASK_NOCLOSEPROCESS
4. Родительский процесс дожидается завершения дочернего процесса (WaitForSingleObject ) и удаляет файл.
Записан
Джон
просто
Администратор

de
Offline Offline
Пол: Мужской

« Ответ #10 : 26-05-2011 07:20 » new

zubr, так вот 4. как раз и неприемлемо. Хоцца "запустил-забыл". Хотя конечно "забыл" это очень условно. Если подходить философски, то какая разница, кто следит? Но в любом случае, если ты захочешь запустить просмотр ещё одного файла из хранилища, то 1, 2, 3 надо запускать в новом треде. Те при сотнях открытых файлов, ещё напрягать систему сотнями тредов.
Записан

Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома.
"Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash
"Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman
"All science is either physics or stamp collecting." Ernest Rutherford
"Wer will, findet Wege, wer nicht will, findet Gründe."
zubr
Гость
« Ответ #11 : 26-05-2011 07:31 » 

zubr, так вот 4. как раз и неприемлемо. Хоцца "запустил-забыл". Хотя конечно "забыл" это очень условно. Если подходить философски, то какая разница, кто следит? Но в любом случае, если ты захочешь запустить просмотр ещё одного файла из хранилища, то 1, 2, 3 надо запускать в новом треде. Те при сотнях открытых файлов, ещё напрягать систему сотнями тредов.
Тут зависит от задачи. К примеру, большинство офисных програм так и работает - для каждого открытого документа создает временный файл.
Записан
darkelf
Молодой специалист

nl
Offline Offline

« Ответ #12 : 26-05-2011 09:24 » 

Те при сотнях открытых файлов, ещё напрягать систему сотнями тредов.
Не обязательно, если использовать функции типа WaitForMultipyObjects(), то количество потоков можно значительно сократить. Задача при этом станет немного сложнее.
Записан
Джон
просто
Администратор

de
Offline Offline
Пол: Мужской

« Ответ #13 : 26-05-2011 09:52 » 

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

Ну да, временный файл будет создаваться. Это уже решено. Но как следить за ним? Вот в чём вопрос.

darkelf, WaitForMultipyObjects, ИМХО, ничего не даст. Те сначала, перед её использованием, надо получить все хэндлы запущенных приложений. А как это обеспечить? Сначала я открываю один файл, 5 мин. спустя второй, через час третий...  Как быть?
Записан

Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома.
"Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash
"Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman
"All science is either physics or stamp collecting." Ernest Rutherford
"Wer will, findet Wege, wer nicht will, findet Gründe."
darkelf
Молодой специалист

nl
Offline Offline

« Ответ #14 : 26-05-2011 13:29 » 

Они хранятся у потока, ответственного за эти 63 запущенных программы, один хендл можно использовать для сигнализации о приостановке ожидания на текущем наборе хендлов в связи с их пополнением. При запуске очередного файла, мы ищем поток, у которого есть свободные ячейки для хендлов, будим по своему сигнальному хендлу, дописываем в массив новый хендл, и запускаем поток снова (или создаём заново, а текущий при получении запроса по сигнальному хендлу автоматически завершается).

Нечто подобное используется в unix-ах, например, когда надо будить поток с select()-а, событием, которое не ассоциируется с файловым дескриптором, например сигналом, или семафором, в этом случае заводится сигнальный файловый дескриптор, например через socketpair(), один конец которого стоит у потока ожидающего на select(), а второй - у будящего по необходимому событию потока, который в случае необходимости просто кидает туда байтик.
« Последнее редактирование: 26-05-2011 13:34 от darkelf » Записан
Джон
просто
Администратор

de
Offline Offline
Пол: Мужской

« Ответ #15 : 26-05-2011 14:02 » 

darkelf, ок, согласен. Ключевая фраза: "приостановка ожидания на текущем наборе хендлов в связи с их пополнением". Но всё-равно, "немного" сложнее, это слегка "приуменьшено". Ага А потом, как отнесётся винда к подобным штучкам?
Записан

Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома.
"Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash
"Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman
"All science is either physics or stamp collecting." Ernest Rutherford
"Wer will, findet Wege, wer nicht will, findet Gründe."
darkelf
Молодой специалист

nl
Offline Offline

« Ответ #16 : 26-05-2011 14:06 » 

Но всё-равно, "немного" сложнее, это слегка "приуменьшено". Ага
Не думаю, что намного сложнее, но, конечно, не так легко, как с простым созданием потоков.
А потом, как отнесётся винда к подобным штучкам?
Должна отнестись вполне нормально - это штатный режим работы.
« Последнее редактирование: 26-05-2011 14:07 от darkelf » Записан
Джон
просто
Администратор

de
Offline Offline
Пол: Мужской

« Ответ #17 : 27-05-2011 05:49 » 

Не думаю, что намного сложнее, но, конечно, не так легко, как с простым созданием потоков.

Я, собственно, и против потоков тоже. Одна единственная служба, котороая следит за файлами в определённой папке. Фйлы в этой папке могут быть заняты ТОЛЬКО процессами, запущенными из нашей проги. Если они не заняты, они удаляются. Тут кода-то вполовину меньше, чем вся эта дускуссия. ИМХО разумеется.
Записан

Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома.
"Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash
"Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman
"All science is either physics or stamp collecting." Ernest Rutherford
"Wer will, findet Wege, wer nicht will, findet Gründe."
imerlin
Интересующийся

ru
Offline Offline

« Ответ #18 : 30-05-2011 12:35 » 

Чёт у тебя какая-то проблема с логикой. С одной стороны, ты напрочь отвергаешь "уборщика". С другой, хочешь чтобы всё было гибко.

А почему ты считаешь, что гибкость может быть достигнута только с помощью "уборщика"?

Рассмотри сначала возможные сценарии. Допустим, я запускаю прогу и выбираю файл для просмотра. Файл коприруется во временную папку и запускается. Запустить возможно двумя способами: 1. с ожиданием возврата из процесса, или 2. без ожидания - запустил-забыл.
В первом случае тебе придётся создавать кучу тредов - по одному на каждый просмотр файла. Это раз и, ИМХО, не есть гуд. Два - основная прога должна дожидаться завершения просмотра ВСЕХ файлов, те я не могу её закрыть сразу.
Поэтому, опять же ИМХО, 2. предпочтительней - запустил-забыл. Но что ты понимаешь под "ОС за этим следит"? Как ты себе это представляешь? Всё что ты написал после "Пытаюсь решить задачу так", указывет на наличие твоей программы, а не самостоятельной работы ОСи.

Я всего навсего: создаю временный файл (такой, что система сама удаляет его, когда исчезает последняя ссылка на него - это я и понимаю под "ОС за этим следит"), копирую в него содержимое интересующего меня файла, запускаю просмотровщик и закрываю хендл. Я надеюсь, на то, что просмотровщик освободит его хендл когда закроется (либо сам, либо при закрытии процесса его ресурсы подчистит ось - по крайней мере так это расписано в литературе - у того же Рихтера). Когда закроется хендл файла файл будет удален осью. По моему - самое простое решение.

С другой стороны, чем тебя конкретно не устраивает "мусорщик"? Временная папка известна, он просто должен следить за ней и удалять незанятые процессами файлы. Это реализировать - как два пальца об асфальт. А опрашивать, как я уже говорил, можно хоть каждую секунду (если кто-то успеет за секунду просмотреть стомегабайтные файлы), хоть каждую минуту. Так что реально файлы будут удаляться практически сразу после просмотра.

Уборщик это дополнительный код, но это не главное. Уборщик чем плох: это дополнительный процесс, висящий в системе, непонятно кем удаляемый, кушающий ресурсы на постоянные попытки удалить файл (каждую секунду или хотя бы раз в 10 секунд). Это мне напоминает мои попытки синхронизовать процессы до изучения событий и WaitForSingleObjects и иже с ними: 100% загрузка ядра на циклы считывания флажка и никакого толку. От таких решений я инстинктивно шарахаюсь, может и необоснованно.

Тут возможны два варианта: прога просмотра блокирует файл, или не блокирует. С первым всё ясно - файл "занят", пока работает прога просмотра. Во втором случае проблем тоже не возникает. Например, Проводник считывает данные из файла и тут же его освобождает. Те инфу можно просматривать, даже в том случае, если файл-источник уже удалён.

В первом случае проще (ткни, пожалуйста, как мне дождаться освобождения: его можно синхронизировать с помощью WaitForSingleObject или надо запросить программно состояние и какой функцией?), а во втором: что будет, если юзер ткнет save при убитом файле?

А "уборщик" никак не годится: могут один за другим десятки сто-мегабайтных файлов просматривать в поисках нужного: системный диск переполнится в момент.

Хе? Так это произойдёт в любом случае, если десятки сто-мегабайтных фалов открыть для просмотра. Как ты представляешь себе решение этой проблемы?

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

Добавлено через 2 минуты:
Большое всем спасибо за проявленное внимание. Возможно, придется все же делать "уборщика", но все таки, по вопросу об ошибке доступа никто не может чего нибудь сказать?

Добавлено через 2 дня, 21 час, 24 минуты и 26 секунд:
В общем, разобрался, для открытия файла с флагом FILE_FLAG_DELETE_ON_CLOSE требуется расшарить право на удаление, что, конечно, у сторонних программ обеспечить не удастся. Написал процедурку, раз в пять секунд удаляющую все, что есть в указанной папке, без проверки получилось или нет. Блокированные файлы не удаляются. Отдельный экзешник для управления этим процессом: старт, стоп, пауза, продолжение.
Синхронизация с помощью событий.
ShellExecuteEx использовать нельзя, так как если новый процесс не порождается, а файл открывается в уже открытом окне, возвращается нулевой хендл, кроме того, я, не получая сообщение о закрытии процесса, не могу удалить просматриваемый файл.


Всем большое спасибо.
« Последнее редактирование: 02-06-2011 10:02 от imerlin » Записан
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines