Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

ошибка при одновременном доступе к файлу

38K
25 мая 2011 года
imerlin
6 / / 25.05.2009
Приветствую всех!

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

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


Помогите заставить работать это чудо правильно.
7
25 мая 2011 года
@pixo $oft
3.4K / / 20.09.2006
Разумеется.Если ты выставил атрибуты на чтение/запись/удаление,то и открывающий процесс с такими же атрибутами должен открывать,не меньше.Попробуй в своей дочерней программе использовать этот подход,и удивишься результату.Вот только другие ты так работать не заставишь…

Кстати,чтобы не было
Цитата:
если запускающий процесс закончит работу раньше дочернего, будет нехорошо

,можно поступить следующим образом(вариантов масса,на самом деле)–просто не разрешать закрывать менеджер хранилища,пока все документы не будут освобождены.Вдобавок можно при запуске проверять временную папку и чистить её,если что.А файлы открывай через CreateProcess,например
Ещё можно было бы внимательнее прочитать про ShellExecute и увидеть,что[QUOTE=MSDN]To obtain information about the application that is launched as a result of calling ShellExecute, use ShellExecuteEx[/QUOTE]

33K
26 мая 2011 года
hivewarrior
205 / / 16.11.2010
Уважаемый, imerlin, так удалить файл, как пытаетесь вы все равно не получится. Сам недавно бился над этой проблемой. Когда открывается файл с флагом FILE_FLAG_DELETE_ON_CLOSE, к нему можно обращаться только на удаление. Так же можно его еще открыть (получить только хэндл, а не прочитать данные). Что-то еще с ним сделать невозможно.

Можно удалять с помощью *.bat файла, который пытается удалить этот файл и при успешном удалении самоликвидируется. Проблема может возникнуть в тогда, если приложение, которое работает с файлом разрешает его удалять. Есть еще один способ: удалять с помощью MoveFileEx с флагом DELAY_UNTIL_REBOOT, но соответственно получаем проблему - файл нельзя удалить пока не перезагрузишься.

Есть, конечно и более извращенские методы, типа внедрение в чужой поток (explorer, например) и мониторинг родительского приложения. Если оно закрыто, то удаляем файл. Ну и так далее. Удалить можно все, но только через одно место.
38K
30 мая 2011 года
imerlin
6 / / 25.05.2009
Цитата: @pixo $oft
Разумеется.Если ты выставил атрибуты на чтение/запись/удаление,то и открывающий процесс с такими же атрибутами должен открывать,не меньше.Попробуй в своей дочерней программе использовать этот подход,и удивишься результату.Вот только другие ты так работать не заставишь…



Попробовал открывать в дочерней программе точно с теми же правами, как и в запускающей - скопировал строку:

 
Код:
fff=CreateFile(ptr, GENERIC_READ, FILE_SHARE_READ+FILE_SHARE_WRITE+FILE_SHARE_DELETE, NULL,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);


Таже самая ошибка: 5(Access is denied)

Цитата:
Ещё можно было бы внимательнее прочитать про ShellExecute и увидеть,что



Она не всегда возвращает хендл. Как я понял, если программа уже загружена, и она просто заменяет документ на новый, без запуска нового процесса, вместо хендля вернется NULL. И что с таким файлом тогда делать?

38K
30 мая 2011 года
imerlin
6 / / 25.05.2009
Цитата: hivewarrior
Уважаемый, imerlin, так удалить файл, как пытаетесь вы все равно не получится. Сам недавно бился над этой проблемой. Когда открывается файл с флагом FILE_FLAG_DELETE_ON_CLOSE, к нему можно обращаться только на удаление. Так же можно его еще открыть (получить только хэндл, а не прочитать данные). Что-то еще с ним сделать невозможно.



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

33K
31 мая 2011 года
hivewarrior
205 / / 16.11.2010
Цитата: imerlin
Э-э-э... Мне кажется, Вы не совсем правы... Какой смысл в такой реализации этого флага? Суть этого флага в том, что когда все хендлы на файл убиты - файл удаляется. И это работает: стоит закрыть все хэндлы - и файла нет. Моя программа, создающая этот файл, прекрасно обращается к нему на запись (на чтение не проверял, но, думаю, проблем быть не должно), ошибка выплывает именно в обращении из другого процесса. У меня где то ошибка с правами на расшаривание.



Вот именно! Все права только для твоего приложения. Никто не "видит" этот файл кроме тебя, и никогда больше не увидит. Отличное подспорье для временных файлов с конфеденциальной информацией, не так ли?

Алсо, я и не говорил, что удаление не работает. Оно работает и еще как.

АлсоАлсо, попробуй читать файл с привелегиями System, если код ошибки 5 (Access is denied). Но и то, сомневаюсь я, что взлетит. Когда я ковырялся со своей проблемой, привелегии system были недопустимы для меня и пробовать не стал тогда.

278
31 мая 2011 года
Alexander92
1.1K / / 04.08.2008
Что-то вы намудрили, ребята. Зачем такое городить? :)

Код:
DWORD WINAPI ViewFileThread(LPVOID);

void WINAPI ViewFile(LPCTSTR from, LPCTSTR szTempFileName) {
  DWORD TId = 0;
  HANDLE hThread = INVALID_HANDLE_VALUE;

  CopyFile(from, szTempFileName, FALSE);      // создали временные файл
  hThread = CreateThread(NULL, 0, ViewFileThread, (LPVOID)szTempFileName, 0, &TId);
  WaitForSingleObject(hThread, INFINITE);
  DeleteFile(szTempFileName);
}

DWORD WINAPI ViewFileThread(LPVOID lpParam) {
  LPCTSTR szTempFileName = (LPCTSTR)lpParam;
  TCHAR szCmdLine[MAX_PATH] = {0};
  StringCbCopy(szCmdLine, MAX_PATH, _T("cmd.exe /c start /wait "));
  StringCbCat(szCmdLine, MAX_PATH, szTempFileName);
  PROCESS_INFORMATION processInformation;
  STARTUPINFO startupInfo;

  ZeroMemory(&processInformation, sizeof(PROCESS_INFORMATION));
  ZeroMemory(&startupInfo, sizeof(STARTUPINFO));
  startupInfo.cb = sizeof(startupInfo);
 
  CreateProcess(NULL, szCmdLine, NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE, NULL, NULL, &startupInfo, &processInformation);

  WaitForSingleObject(processInformation.hProcess, INFINITE);
  CloseHandle(processInformation.hProcess);
  CloseHandle(processInformation.hThread);

  return 0;
}
33K
31 мая 2011 года
hivewarrior
205 / / 16.11.2010
Цитата: Alexander92
Что-то вы намудрили, ребята. Зачем такое городить? :)

 
Код:
...
//бла бла бла
...



Извините, конечно, может я не понимаю замысла ТС, но наверно CreateThread не подойдет. В первом же сообщение упоминались файлы в несколько мегабайт. Сомневаюсь, что генерируются экзешники такого размера. Да и ShellExecute как бы намекает, что файл будет открываться ассоциированной с ним програмой.

Алсо, если не нравятся наши костыли, то как вы будете изварачиваться из проблемы, когда основное приложение закрыто? А такая проблема поднималась в первом же сообщением. Как быть тогда?

278
31 мая 2011 года
Alexander92
1.1K / / 04.08.2008
Цитата: hivewarrior
Извините, конечно, может я не понимаю замысла ТС, но наверно CreateThread не подойдет. В первом же сообщение упоминались файлы в несколько мегабайт. Сомневаюсь, что генерируются экзешники такого размера.



А какая связь между CreateThread() и размером открываемого файла?

Цитата: hivewarrior

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



Согласен, но ShellExecute() возвращается сразу после запуска, не позволяя дождаться завершения, в отличие от start /wait ....

Цитата: hivewarrior

Алсо, если не нравятся наши костыли, то как вы будете изварачиваться из проблемы, когда основное приложение закрыто? А такая проблема поднималась в первом же сообщением. Как быть тогда?



В первом сообщении также говорилось, что
[QUOTE=imerlin]
если запускающий процесс закончит работу раньше дочернего, будет нехорошо.
[/QUOTE]
А следовательно, перед завершением основного процесса я закрою все дополнительные нити и процессы - скорее всего, с помощью соответствующих объектов синхронизации.

33K
31 мая 2011 года
hivewarrior
205 / / 16.11.2010
2Alexander92
Понял, как вы стали решать этот вопрос.

Проверил ваш метод. И получил еще одни грабли.
Код:
Эксперимент 1:
Открывал *.txt файл. Открыл. Работал отлично.

Эксперимент 2:
Открывал *.html файл при закрытом браузере (пробывал в двух IE и FF). Открылся и работал метод отлично.

Эксперимент 3:
Открыл *.html файл при открытом браузере (снова оба). И тут крах: открылась отдельная вкладка, консоль закрылась.

Эксперимент 4:
Открыл *.doc файл с открытым редактором. И тут снова крах.


Считаю ваш метод тоже тем еще костылем. Практика подтверждает.
33K
31 мая 2011 года
hivewarrior
205 / / 16.11.2010
Эксперименты проводил в XP SP3. За другие системы на базе висты не уверен, но сомневаюсь, что cmd.exe был переписан.
278
31 мая 2011 года
Alexander92
1.1K / / 04.08.2008
Естественно, все верно. Совершенно очевидно даже, почему так происходит: когда вы пытаетесь открывать *.html-файл в новой вкладке, новый процесс не создается, и start /wait тут же возвращается. То же самое происходит, когда открываете *.doc-файл - в памяти существует только одна копия процесса winword.exe.

Прежде всего, у меня вопрос к ТСу: файлы каких типов будут открываться? Если каких-то фиксированных, то всегда можно дописать свой менеджер, который подменит стандартный start /wait и решит все проблемы. Если никакого ограничения на тип открываемого файла нет, боюсь, что проблема в поставленном виде не разрешима в принципе.

2 hivewarrior
Грабли лежат в том, что не факт, что когда файл открыт в какой-нибудь программе (к примеру, в блокноте), существует его открытый дескриптор, на который можно ориентироваться. Тот же блокнот, например, сразу закрывает дескриптор, специально проверил утилитами из SysInternals. Таким образом, после запуска имеем (в общем случае) некий сторонний процесс, в котором открыт наш файл, все дескрипторы к которому могут быть как закрыты, так и открыты. На что здесь можно повеситься? Разве что попытаться в лоб сделать:
 
Код:
void WINAPI ViewFile(LPCTSTR from, LPCTSTR szTempFileName) {
  CopyFile(from, szTempFileName, FALSE);      // создали временный файл
  ShellExecute(NULL, "open", szTempFileName, NULL, NULL, SW_SHOW);
  Sleep(1000);
  DeleteFile(szTempFileName);
}

В некоторых случаях поможет мой первый вариант, в некоторых - этот. Если их скомбинировать, вероятность правильной работы будет достаточно высока.

P.S. В качестве доказательства, что поставленная проблема отнюдь не тривиальна, могу привести следующий пример. Попробуйте открыть, например, в Total Commander'е какой-нибудь *.html-файл из архива. Увидите окно следующего содержания:
[ATTACH=CONFIG]5170[/ATTACH]
33K
31 мая 2011 года
hivewarrior
205 / / 16.11.2010
Полностью справедливо. Думаешь, не зная о такой особенности, я бы стал делать такой тест сразу, как увидел сообщение?

Разные приложения работают по-разному. Некоторые вообще могут мапить файлы в память. И тут снова придется плясать с бубном.

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

Вопрос очень интересный на самом деле. Там подводных камней куча. Скорее всего придется решать его частными способами, а потом их как-то обрабатывать. Да и не помешало бы вообще иметь сервис, который бы мониторил временную дирректорию...
278
31 мая 2011 года
Alexander92
1.1K / / 04.08.2008
Цитата: hivewarrior

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



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

Цитата: hivewarrior

этот влоб ничем не отличается от вашего первого предложения по большому счету.


Да, готов согласиться. Здесь погорячился.

Цитата: hivewarrior

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



Да, вопрос действительно интересный, не спорю.
А что даст такой сервис? Что он должен делать, по-вашему? Просто смотрите, я еще раз повторюсь: в общем случае, зная только имя файла, мы не имеем никакой информации, использует ли кто-то этот файл в данный момент. И второй вопрос: вы можете привести принципиальный способ (о конкретной реализации потом поговорим), как можно, например, отследить закрытие нужной вкладки в IE или ФФ? И как идентифицировать нужную вкладку при открытии?

33K
31 мая 2011 года
hivewarrior
205 / / 16.11.2010
Цитата: Alexander92
Вопрос действительно интересный, не спорю.
А что даст такой сервис? Что он должен делать, по-вашему? Просто смотрите, я еще раз повторюсь: в общем случае, зная только имя файла, мы не имеем никакой информации, использует ли кто-то этот файл в данный момент.



Ну я бы не стал так уверенно утверждать, что зная имя, мы ничего не имеем. Есть еще куча информации, которая собирается системой и на часть мы можем глянуть. Можно мониторить время последнего обращения к файлу и по истечению определенного времени удалять его, например. Делать это все для каждого файла в отдельном треде и так далее... Мониторить только временную дирректорию и так далее. Тут уже фантазией только ограничивается. Сервис работает всегда (если руками ничего не трогать). А страховка от перезагрузки: MoveFileEx в null с флагом DELAY_UNTIL_REBOOT

278
31 мая 2011 года
Alexander92
1.1K / / 04.08.2008
Цитата: hivewarrior
Ну я бы не стал так уверенно утверждать, что зная имя, мы ничего не имеем. Есть еще куча информации, которая собирается системой и на часть мы можем глянуть. Можно мониторить время последнего обращения к файлу и по истечению определенного времени удалять его, например. Делать это все для каждого файла в отдельном треде и так далее...



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

33K
31 мая 2011 года
hivewarrior
205 / / 16.11.2010
Цитата: Alexander92
Ну в первом сообщении ТС писал, что удалять по времени - не вариант. Я все-таки упорно жду от него ответа, какие именно файлы предстоит открывать. Возможно, весь бубен, который мы сейчас обсуждаем, и не понадобится.



Ну он не хотел "запускать раз в месяц" что-то. Очевидно из просьбы, это надо делать самому и вообще. А сервис шустро мониторит файлы и при отсутствии обращений к нему за час удаляет его. Час и месяц - вполне разнича. Но ждать ответа ТС действительно стоит. Может, и в правду зря бисер здесь мечем.

278
31 мая 2011 года
Alexander92
1.1K / / 04.08.2008
Цитата: hivewarrior
А сервис шустро мониторит файлы и при отсутствии обращений к нему за час удаляет его.



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

33K
31 мая 2011 года
hivewarrior
205 / / 16.11.2010
Цитата: Alexander92
Вопрос в том, что возможна ситуация, когда пользователь открыл какой-то файл и ушел на два часа. :) С точки зрения системы новых обращений нет, а на экране он висит. Поэтому я и говорю, что здесь нужно либо в принципе отходить от "потаймерной" обработки, либо подбирать для каждого конкретного пользователя разумный интервал очистки, что не вполне красиво, согласитесь.



Если на сервере это все крутится, то можно в 0-00 очищать папочку, например. Если не на сервере и компьютер выключают, то, как я уже говорил, привентивный MoveFileEx в null с флагом DELAY_UNTIL_REBOOT

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

278
31 мая 2011 года
Alexander92
1.1K / / 04.08.2008
Да, разумеется, согласен. Значит, пока ждем ответа ТС-а...
38K
03 июня 2011 года
imerlin
6 / / 25.05.2009
Сорри за тормоза - отвлекли на срочную работу.


Всем огромное спасибо за проявленный интерес.


Цитата: Alexander92
Прежде всего, у меня вопрос к ТСу: файлы каких типов будут открываться? Если каких-то фиксированных, то всегда можно дописать свой менеджер, который подменит стандартный start /wait и решит все проблемы. Если никакого ограничения на тип открываемого файла нет, боюсь, что проблема в поставленном виде не разрешима в принципе.




Файлы, увы, любых типов, хотя в 99% случаев ожидаются большие графические файлы - тифы и джипеги.


Цитата: Alexander92
Что-то вы намудрили, ребята. Зачем такое городить? :)

 
Код:
CreateProcess(NULL, szCmdLine, NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE, NULL, NULL, &startupInfo, &processInformation);



Не пойдет: надо чтобы система сама открыла файл ассоциированной с таким типом файлов программой.



Цитата: Alexander92
Согласен, но ShellExecute() возвращается сразу после запуска, не позволяя дождаться завершения, в отличие от start /wait ....



В принципе ShellExecuteEx отдает хендл процесса, но не во всех случаях, только когда порождает новый процесс, а если просто открывает файл в новом окне/вкладке/заменяя предыдущий файл - увы, возвращает NULL.
Кроме того так все равно не получить сообщение об освобождении ранее запущенного файла.

Цитата: hivewarrior
То есть открывается какой-то отчет (я так понимаю, раз большие файлы, то это отчеты к чему-либо), то его смотреть иногда хочется подольше, чем секунду.



Не совсем... База данных хранит (наряду с другой информацией) асооциированные с этой информацией файлы. Для надежности они лежат в недоступном пользователям хранилище с техническими именами и, в будущем, возможно будут
кодироваться (мания такая у начальства ;-)). Но по запросу авторизованного пользователя файл ему надо показать. Для этого файл должен быть скопирован во временную папку с восстановленным именем и применена функция
ShellExecute. Потом файл надо удалить. Файлы могут быть самыми разными, но в основном большие растровые или векторные карты. Если пользователь знает, что к некоторому объекту прикреплена какая то карта, он может захотеть
быстро перебрать все прикрепленные графические файлы в поисках нужного, а на анализ изображения у человека уходят доли секунды, поэтому перебирать может быстро, до тех пор, пока не наткнется на нужный файл.



По поводу моего первоначального вопроса я уже разобрался: нельзя открывать файл, созданный с ключом DELETE_ON_CLOSE без расшаренных прав на удаление, что от стороннего приложения требовать нельзя, поэтому такой путь отпадает.
Пока решил вопрос так: написал два экзешника. Один крутится и раз в 5 сек удаляет все файлы в папке, назначенной для этого безобразия (по GetTempPath, плюс создать подпапку для хулиганства). Если файл не удалился - значит занят и
пусть живет пока. Второй экзешник управляет первым с помощью именованных событий, он умеет стартовать/выгружать/ставить на паузу/снимать с паузы первый. Когда второй экзешник создает событие для остановки первого, первый
закончит работу после удаления последнего файла в папке.
Не айс, конечно, лишний процесс крутится, процессорное время жрет, а главное - диск постоянно тормозит, но пока ничего лучше не придумал...

Как я уже писал, отслеживание выполняющегося потока - не вариант: нет гарантии, что поток завершится вместе с просмотром файла и запустится с просмотром нового.

Еще раз всем огромное спасибо, может лучше идеи появятся? ;-)

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог