Приветствую всех!
Столкнулся с проблемой, которую никак не могу решить, прошу помочь знающих.
Задача: по нажатию кнопки показать файл из файлохранилища.
Решение: копируем файл во временную папку, показываем с помощью 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);
}
Помогите заставить работать это чудо правильно.
Здравствуйте, Аноним, Вы писали:
А>Приветствую всех!
А>Столкнулся с проблемой, которую никак не могу решить, прошу помочь знающих.
А>Задача: по нажатию кнопки показать файл из файлохранилища.
А>Решение: копируем файл во временную папку, показываем с помощью ShellExecute, удаляем временный файл.
А>Когда удалять временный файл? Можно, конечно, подождать когда закончится дочерний процесс, запущенный ShellExecute, но как получить его хендл?
ShellExecuteEx с флагом SEE_MASK_NOCLOSEPROCESS
А>К тому же если запускающий процесс закончит работу раньше дочернего, будет нехорошо.
1. Процесс можно не закрывать, а просто скрывать все окна — тогда можно прозрачно для пользователя дождаться, пока файл не закроется
2. Можно написать специальную маленькую утилиту, которая будет вызываться после запуска файла — пусть ждет окончания запустившего процесса и удаляет после этого ненужный файл. Как вариант — пусть именно она и занимается как запуском файла, так и его ожиданием (но этом случае процессу-родителю сложнее проконтроливать успешность операции)
3. Ерундовые способы: файл можно помечать на удаление системой после ребута (MoveFileEx с флагом MOVEFILE_DELAY_UNTIL_REBOOT). Минус в том, что система может долго не ребутиться. Также можно запускать сборщик мусора хоть каждый час (в том числе и спомощью шедулера).
4. Можно копировать файл сразу в корзину и оттуда уже и запускать

Шутка. Слишком много подводных камней
А>Вообще не удалять, запуская раз в месяц сборщик мусора — тоже не вариант, файлы могут занимать сотни мегабайт.
Каждый раз копируются сотни мегабайт? А если места на диске не хватит? А почему сразу не открывать оригинал?
Здравствуйте, fuyant, Вы писали:
А>>Когда удалять временный файл? Можно, конечно, подождать когда закончится дочерний процесс, запущенный ShellExecute, но как получить его хендл?
F>ShellExecuteEx с флагом SEE_MASK_NOCLOSEPROCESS
Способ не годится в следующих сценариях:
1. Если запускаемый ShellExecuteEx процесс просто создает дочерний и завершает свою работу.
2. Если происходит не запуск нового процесса, а DDE обмен.
Т.е практически не годится никогда
P.S. 100% решения задачи не существует.