3, 2, 1, ... старт!
© 2005 Алексей Морозов
Вот уже несколько лет в нашей организации эксплуатируется один мощный программный пакет, доработкой и исправлением которого довелось заниматься и мне. Количество вносимых изменений иногда доходило до нескольких раз в день, да и количество пользователей не так уж и мало, поэтому еще на этапе внедрения было принято решение о хранении исполняемых файлов на сервере (стоит лишь заметить, что рабочие каталоги для всех пользователей определены на их локальных компьютерах для возможности создания временных файлов). Как вы понимаете, для того чтобы скопировать новую исправленную или дополненную версию программы, необходимо сначала "изгнать" всех пользователей. Кто сталкивался с этой проблемой, тот знает, как это порой тяжело сделать (один заблокировал машину и ушел пообедать, другому нужно "еще пять минут" и т.п.). Так как существует возможность "политических" последствий радикальных способов борьбы с пользователями (как то закрытие их сетевых сессий), приходится упрашивать и порой умолять позволить выполнять вам свои прямые обязанности.
Для решения вышеизложенной проблемы и была написана программа Launcher. Она запускает переданное ей в качестве параметра приложение, предварительно копируя его (при необходимости) на локальный диск. Копирование производится только в случае, если файл на сервере имеет другой размер или дату создания. Но у старого способа прямого запуска программы был один полезный побочный эффект: с помощью мониторинга сетевых ресурсов можно было определить, кто и сколько времени использует какую часть пакета программ. При запуске локальной копии эти сведения восстановить невозможно (хотя, так как сам Launcher находится на сервере, определить список активных пользователей все же возможно, но кто что запустил и когда - это вопрос без ответа). Поэтому Launcher создает (или открывает без блокировки) в том же каталоге, где находится исходное приложение, файл-"тень", а после завершения приложения закрывает его. По сетевым сессиям на файлы-"тени" можно установить, кто, что и когда запустил.
program Launcher;
{$R *.RES}
{$WARN SYMBOL_PLATFORM OFF}
uses
Windows,
SysUtils,
Classes,
IniFiles;
procedure Error(const Msg: string);
begin
MessageBox(0, PChar(Msg), 'Launcher Error', MB_ICONERROR or MB_OK);
Halt(1);
end;
procedure Launch(const AppName, CmdLine: string);
procedure CacheFile(const SrcName, DestName: string);
function IsCacheNeeded(const SrcName, DestName: string): Boolean;
function FileLength(const FileName: string): Int64;
var
FileHandle: Integer;
begin
Result := -1;
FileHandle := FileOpen(FileName, fmOpenRead or fmShareDenyWrite);
if FileHandle = -1 then
Exit;
try
Result := FileSeek(FileHandle, Int64(0), 2);
finally
FileClose(FileHandle);
end;
end;
begin
Result := (not FileExists(DestName)) or (FileLength(SrcName)
<> FileLength(DestName)) or
(FileAge(SrcName) <> FileAge(DestName));
end;
const
BufSize = 32768;
var
SrcFile,
DestFile: Integer;
Buffer: Pointer;
BufLen: Integer;
begin
if not IsCacheNeeded(SrcName, DestName) then
Exit;
SrcFile := FileOpen(SrcName, fmOpenRead or fmShareDenyWrite);
if SrcFile = -1 then
Error('Unable to open source file "' + SrcName + '"!');
try
DestFile := FileCreate(DestName);
if DestFile = -1 then
Error('Unable to create destination file "' + DestName + '"!');
try
GetMem(Buffer, BufSize);
try
repeat
BufLen := FileRead(SrcFile, Buffer^, BufSize);
if FileWrite(DestFile, Buffer^, BufLen) <> BufLen then
Error('Unable to write to destination file "' + DestName + '"!');
until BufLen = 0;
finally
FreeMem(Buffer, BufSize);
end;
FileSetDate(DestFile, FileGetDate(SrcFile));
finally
FileClose(DestFile);
end;
finally
FileClose(SrcFile);
end;
end;
procedure LaunchApp(const AppName, CmdLine, WorkDir: string; WaitForEnd: Boolean = True);
var
si: TStartupInfo;
pi: TProcessInformation;
begin
FillChar(si, SizeOf(si), 0);
si.cb := SizeOf(si);
si.dwFlags := STARTF_USESHOWWINDOW;
si.wShowWindow := SW_SHOWNORMAL;
if not CreateProcess(nil, PChar(Trim(AppName + ' ' + CmdLine)), nil, nil, False,
CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, nil, PChar(WorkDir), si, pi) then
Error('Unable to execute application "' + AppName + '"!');
CloseHandle(pi.hThread);
if WaitForEnd then
begin
SetProcessWorkingSetSize(GetCurrentProcess(), DWORD(-1), DWORD(-1));
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE);
WaitForSingleObject(pi.hProcess, INFINITE);
end;
CloseHandle(pi.hProcess);
end;
var
SrcPath,
DestPath,
WorkDir,
DestName,
ShadowName: string;
PrecopyFiles: TStrings;
UseShadow: Boolean;
I: Integer;
begin
PrecopyFiles := TStringList.Create;
try
with TIniFile.Create(ChangeFileExt(ParamStr(0), '.ini')) do
try
SrcPath := IncludeTrailingBackslash(ReadString('Source', 'Path', ExtractFileDir(AppName)));
DestPath := IncludeTrailingBackslash(ReadString('Destination', 'Path', GetEnvironmentVariable('TEMP')));
WorkDir := ExcludeTrailingBackslash(ReadString('Destination', 'WorkDir', DestPath));
ReadSectionValues('Precopy', PrecopyFiles);
UseShadow := ReadBool('General', 'UseShadow', True);
finally
Free;
end;
if not ForceDirectories(DestPath) then
Error('Unable to create destination directory "' + DestPath + '"!');
for I := 0 to PrecopyFiles.Count - 1 do
CacheFile(ExpandUNCFileName(SrcPath + PrecopyFiles.ValueFromIndex[I]), ExpandUNCFileName(DestPath + PrecopyFiles.ValueFromIndex[I]));
finally
PrecopyFiles.Free;
end;
DestName := ExpandUNCFileName(DestPath + ExtractFileName(AppName));
CacheFile(AppName, DestName);
if UseShadow then
begin
ShadowName := AppName + '.shadow';
if not FileExists(ShadowName) then
begin
I := FileCreate(ShadowName);
if I = -1 then
Error('Unable to create shadow file "' + ShadowName + '"!');
FileClose(I);
FileSetAttr(ShadowName, faHidden);
end;
I := FileOpen(ShadowName, fmOpenRead or fmShareDenyNone);
if I = -1 then
Error('Unable to open shadow file "' + ShadowName + '"!');
end;
try
LaunchApp(DestName, CmdLine, WorkDir, UseShadow);
finally
if UseShadow then
FileClose(I);
end;
end;
var
I: Integer;
Cmd: string;
begin
if ParamCount = 0 then
Error('Missing parameter(s)!'#13'Usage: Launcher.exe application [cmdline]');
Cmd := ParamStr(2);
for I := 3 to ParamCount do
Cmd := Cmd + ' ' + ParamStr(I);
Launch(ExpandUNCFileName(ParamStr(1)), Cmd);
end.
Глобальные параметры Launcher'а хранятся в .ini файле в том же каталоге. Секция [General] может содержать параметр UseShadow, который определяет использование системы "теней" для мониторинга сетевых сессий. Секция [Source] может содержать параметр Path, определяющий исходный каталог запускаемого файла. Секция [Destination] может содержать параметры Path и WorkDir, которые определяют путь для копирования копии приложения и рабочий каталог для его запуска. Секция [Precopy] может содержать имена файлов, которые необходимо также скопировать для корректного запуска локальной копии программы (.ini файлы, динамические библиотеки и т.п.).
Программа создавалась в Delphi 7, поэтому может потребовать некоторых изменений исходного кода для компиляции под более старыми версиями Delphi. При желании, код может быть доработан для обеспечения дополнительной функциональности, но и в данном виде он уже около полугода помогает справляться с задачей поддержки программного пакета в сети без лишних нервов. Возможно, Launcher поможет и вам.
Исходные коды программы Launcher (3.12K).
Об авторе: Морозов Алексей Викторович (fox@europaplus.ru), MCSE, MCSD, Brainbench Delphi Professional, Brainbench C Professional
Специально для Delphi Plus
| 2011 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 2010 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 2009 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 2008 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 2007 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 2006 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 2005 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 2004 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 2003 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 2002 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 2001 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 2000 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 1999 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
- Услуги аутсорсинга в области программирования
- Как продлить срок службы картриджей
- Мошенничество во Всемирной Паутине. Осторожно: фишинг!
- Web-студия
- Как легально поднять уровень индекса цитирования.
- Мы реально сможем помочь вам в управлении предприятием
- Создание сайтов – популяризация вашего замысла
- Свой сайт. Управление ресурсом
- Семантическое ядро сайта или правила подбора ключевых фраз
- Инфо-Предприятие: выгоды явные и не явные
- Программирование в среде Delphi 8 for .NET
- Практикум по Delphi для решения прикладных задач
- Фундаментальные алгоритмы и структуры данных в Delphi
- Delphi 6. Программирование на Object Pascal
- Delphi и технология COM
- Delphi в шутку и всерьез: что умеют хакеры
- Программирование в Delphi глазами хакера
- Delphi 2005. Секреты программирования
- Искусство создания компонентов Delphi
- Приемы программирования в Delphi на основе VCL
- Программирование баз данных в Delphi 7
- Программирование баз данных в Delphi
- Программирование в среде Delphi
- Программирование в Delphi 7
- Язык SQL в Delphi 5