Заметки, идеи и мысли автора, обзор кода, алгоритмов, инструментов.

вторник, 12 января 2010 г.

Открыть файл и дождаться закрытия.

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

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

 Файлы ведь могут быть разных форматов, и ворд и эксель, и текстовый и может даже фотография формата GIMP. Из этого следует что CreateProcess нам не подходит, так как в коде определять какой программой открывать файл - лишние проблемы и подводные грабли. Не стоит так извращаться.
В свою очередь ShellExecute имеет другой недостаток, он хоть и способен автоматически открыть файл в ассоциированной ему программе, но он не дает хэнд процесса, что не позволяет нам дождаться завершения процесса. А раз мы не можем дождаться завершения процесса, стало быть как мы узнаем через какое время пользователю надумается сохранить изменения в файле и закрыть его, для того что бы затянуть измененный файл назад в базу. Это основная проблема на которую я наткнулся при решении задачи.

Но Google никогда не подводил, буквально час ушел на поиск нужного решения. Есть такой промежуточный метод между CreateProcess и ShellExecute. И имя его ShellExecuteEx.

Его использование несколько заморочено, посему не стал много времени тратить на копание вглубь, но нашел отличную такую процедурку, которая выполняет это действо внутри себя, а нам нужно лишь вызвать её передав в неё путь к файлу, если надо параметр запуска и параметр запуска процесса.

Код процедуры:
procedure RunAndWaitShell(Executable, Parameter: STRING; ShowParameter: INTEGER);
  var
     Info: TShellExecuteInfo;
     pInfo: pShellExecuteInfo;
     exitCode: DWord;
  begin
     pInfo := @Info;
     Info.cbSize := SizeOf(Info);
     Info.fMask  := SEE_MASK_NOCLOSEPROCESS;

     Info.wnd    := application.Handle;
     Info.lpVerb := NIL;
     Info.lpFile := PChar(Executable);
     Info.lpParameters := PChar(Parameter + #0);
     Info.lpDirectory  := NIL;
     Info.nShow        := ShowParameter;
     Info.hInstApp     := 0;
     ShellExecuteEx(pInfo);
     repeat
        exitCode := WaitForSingleObject(Info.hProcess, INFINITE); //было 500
        Application.ProcessMessages;
     until (exitCode <> WAIT_TIMEOUT);
  end;
{Если форма приложения имеет свойство AlwaysOnTop заменить SEE_MASK_NOCLOSEPROCESS на SEE_MASK_FLAG_DDEWAIT }

Теперь пример использования:

//...
  RunAndWaitShell('D:\test.txt','',SW_SHOWNORMAL); //Открываем файлик, где бы он ни находился.

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

//Тут в общем-то берем этот же файл и пишем его  в базу, заменяя старый вариант, по желанию
//можно устроить проверку на изменения в файле.
//  ShowMessage('The End or the programm.');
//...

Такое в общем простое решение на такую в общем-то нужную в жизни задачу, если у кого-то есть более оптимальные решения - пишите :) Если что-то не понятно, тоже пишите =)

Комментариев нет:

Отправить комментарий

Постоянные читатели