ошибка при одновременном доступе к файлу
От: Аноним  
Дата: 25.05.11 10:45
Оценка:
Приветствую всех!

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

Задача: по нажатию кнопки показать файл из файлохранилища.
Решение: копируем файл во временную папку, показываем с помощью 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);
}

Помогите заставить работать это чудо правильно.
Re: ошибка при одновременном доступе к файлу
От: genok Россия  
Дата: 25.05.11 11:00
Оценка:
Здравствуйте, Аноним, Вы писали:



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


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


А>Вот мой код:



А>hOld=CreateFile(from, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);


Вы же тут указываете режим "только на чтение", следующие дескрипторы файла уже будут ограничены этим режимом
Re: ошибка при одновременном доступе к файлу
От: fuyant  
Дата: 25.05.11 11:35
Оценка:
Здравствуйте, Аноним, Вы писали:



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


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


А>Задача: по нажатию кнопки показать файл из файлохранилища.

А>Решение: копируем файл во временную папку, показываем с помощью ShellExecute, удаляем временный файл.
А>Когда удалять временный файл? Можно, конечно, подождать когда закончится дочерний процесс, запущенный ShellExecute, но как получить его хендл?

ShellExecuteEx с флагом SEE_MASK_NOCLOSEPROCESS

А>К тому же если запускающий процесс закончит работу раньше дочернего, будет нехорошо.


1. Процесс можно не закрывать, а просто скрывать все окна — тогда можно прозрачно для пользователя дождаться, пока файл не закроется
2. Можно написать специальную маленькую утилиту, которая будет вызываться после запуска файла — пусть ждет окончания запустившего процесса и удаляет после этого ненужный файл. Как вариант — пусть именно она и занимается как запуском файла, так и его ожиданием (но этом случае процессу-родителю сложнее проконтроливать успешность операции)
3. Ерундовые способы: файл можно помечать на удаление системой после ребута (MoveFileEx с флагом MOVEFILE_DELAY_UNTIL_REBOOT). Минус в том, что система может долго не ребутиться. Также можно запускать сборщик мусора хоть каждый час (в том числе и спомощью шедулера).
4. Можно копировать файл сразу в корзину и оттуда уже и запускать Шутка. Слишком много подводных камней

А>Вообще не удалять, запуская раз в месяц сборщик мусора — тоже не вариант, файлы могут занимать сотни мегабайт.


Каждый раз копируются сотни мегабайт? А если места на диске не хватит? А почему сразу не открывать оригинал?
Re[2]: ошибка при одновременном доступе к файлу
От: De-Bugger  
Дата: 25.05.11 11:46
Оценка:
Здравствуйте, fuyant, Вы писали:

А>>Когда удалять временный файл? Можно, конечно, подождать когда закончится дочерний процесс, запущенный ShellExecute, но как получить его хендл?


F>ShellExecuteEx с флагом SEE_MASK_NOCLOSEPROCESS


Способ не годится в следующих сценариях:
1. Если запускаемый ShellExecuteEx процесс просто создает дочерний и завершает свою работу.
2. Если происходит не запуск нового процесса, а DDE обмен.
Т.е практически не годится никогда

P.S. 100% решения задачи не существует.
Re[2]: ошибка при одновременном доступе к файлу
От: imerlin  
Дата: 25.05.11 11:53
Оценка:
Здравствуйте, genok, Вы писали:

G>Вы же тут указываете режим "только на чтение", следующие дескрипторы файла уже будут ограничены этим режимом


Это файл-источник. Из него копируестя в hNew и он больше не нужин и закрывается.
Re[2]: ошибка при одновременном доступе к файлу
От: imerlin  
Дата: 25.05.11 11:58
Оценка:
Здравствуйте, fuyant, Вы писали:

F>Каждый раз копируются сотни мегабайт? А если места на диске не хватит? А почему сразу не открывать оригинал?

Файл должен быть гарантировано сохранен, а практически все программы работы с файлами имеют функцию save.
Кроме того, возможно, в будущем, потребуется шифровать файлы и расшифровывать при показе.
Ну и имена в файлохранилице нечеловеческие — id записи в БД, пользователю будет малокомфортно

По поводу ShellExecuteEx — спасибо, сейчас полезу изучать msdn.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.