Delphi. Установка программ через FTP.

24.01.2020

Предистория: Не так давно мне необходимо было распространить на кучу компьютеров свеже-написанную программу, — очередную хотелку местного руковоства, но поскольку у нас все ПК находятся в домене, то соответсвенно и действуют доменные политики. Так вот, распространить программу на пару тысяч компьютеров через доменную политику не удалось, поскольку софтина не включена в некий реестр программ имеющих цетрализованную техподдержку, и соответсвенно в таком способе распространения мне было отказано. Но поскольку мы имеем местное начальство, считающее написанную программу остро необходимой всем работникам, то вынужден был в обход доменных политик распространять ее через FTP-сервер. Естественно, выполнять соединение с парой тысяч ПК через UltraVNC, — задача проблемная. К тому же пользователи не имеют прав админа и установка софта стандартным способом подсыпает немало проблем. Поэтому подготовил загрузчик, который попросту выкачивает с FTP-сервера готовый софт, заливая его на целевой компьютер. Далее опишу как я это сделал, заодно обойдя пару «подводных камней».
И так, определившись, что распространять буду через сервер FTP, начал стряпать мини-проект.

1. Форма с одной кнопкой, дабы не вызывать о пользователя лишних вопросов.

download form

Как видно, на форме кроме кнопки имеется ProgressBar — дабы показать пользователю что процесс идет, а не «висит программа».

2. Для соединения с FTP-сервером был использован компонент Indy, соответсвенно секция uses содержит

IdBaseComponent, IdComponent, IdTCPConnection,  IdTCPClient, IdFTP

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

const
SFolderKey = '\Software\Microsoft\Windows\CurrentVersion\' +
'Explorer\Shell Folders'; // ветка реестра, отвечающая за ярлыки.
AppName = 'c:\problvop\prv.exe';  //приложение на которое будет ссылаться ярлык.

3. Функция, позволяющая создать ярлык, как на рабочем столе, так и добавлять его в в меню «Избранное», в автозагрузку, или же в ветку «Программы» меню «Пуск».
Не буду подробно описывать каждую строку функции, но для понимания ее работы, советую Вам изучить ее код. Возможно Вы ее оптимизируете:

function CreateShortcut(const ShortcutDir, CmdLine, Param, WorkDir,DirName: string;  LinkFile:String): Boolean;
var
MyObject  : IUnknown;
MySLink   : IShellLink;
MyPFile   : IPersistFile;
WideFile  : WideString;
Directory, TempStr: String;
Reg:TRegistry;
begin
Result:=False;Directory:='';
If (ShortcutDir='Desktop') or (ShortcutDir='Favorites') or
(ShortcutDir='Programs') or (ShortcutDir='SendTo') or
(ShortcutDir='Start Menu') or (ShortcutDir='Startup') then
begin
Reg:=TRegistry.Create;
Reg.OpenKey('Software\MicroSoft\Windows\CurrentVersion\Explorer\Shell Folders',False);
Directory := Reg.ReadString(ShortcutDir);
Reg.Free;
If not(DirName='') then
Directory:=Directory+'\'+DirName;
end;
If linkFile='' then Exit;
TempStr:=linkFile;
If Length(linkFile)>4 then
Delete(TempStr,1,Length(TempStr)-3);
If not(AnsiUpperCase(TempStr)='LNK') then
linkFile:=linkFile+'.lnk';
MyObject := CreateComObject(CLSID_ShellLink);
MySLink := MyObject as IShellLink;
MyPFile := MyObject as IPersistFile;
with MySLink do
begin
SetPath(PChar(CmdLine));
SetArguments(PChar(Param));
SetWorkingDirectory(PChar(WorkDir));
end;
if Directory[Length(Directory)]='\' then
WideFile := Directory+LinkFile
else
WideFile := Directory+'\'+LinkFile;
if MyPFile.Save(PWChar(WideFile), False)= S_OK then
Result:=True;
end;

4. Ну и собственно сам процесс загрузки файлов и создания ярлыка. Поскольку у нас ограниченные права пользователя, то создание каталога программы без прав админа, запуском setup.exe невозможно, а вручную пользователь создать папки может, то имитируем пользовательскую операцию.
Будем создавать каталог «problvop» и заливать в него необходимые файлы, а после и создавать ярлык. Вот и дошли до обработчика нажатия единственной кнопки:

procedure TForm1.Button1Click(Sender: TObject);
var
si: TSTARTUPINFO;
pif: PROCESS_INFORMATION;
begin
button1.Enabled:=false;
si.cb := SizeOf(tstartupinfo);
si.dwFlags  := STARTF_USESHOWWINDOW;
si.wShowWindow := SW_SHOWDEFAULT;
si.lpReserved := nil;
si.lpDesktop := nil;
si.lpTitle := nil;
if DirectoryExists('c:\problvop')=false then
CreateDir('c:\problvop');
idFtp1.Host:='10.110.29.58';             (*передача адреса*)
idFtp1.Username:='anonymous';         (*передача логина*)
idFtp1.Password:='';         (*передача пароля*)
idFtp1.Port:=21;
idFtp1.Passive:=True;
idFtp1.Connect;
if idFtp1.Connected then begin
Label1.Caption:='Установлено соединение с сервером';
pb.Position:=10; // Это позиция ProgressBar
//закачка с фтп всех необходимых файлов
idFtp1.ChangeDir('/upload/DISTR/problvop/');                  pb.Position:= 20;
idFtp1.Get('upd.ini','c:\problvop\upd.ini',True);                   pb.Position:= 30;
idFtp1.Get('Acryl.asz','c:\problvop\Acryl.asz',True);            pb.Position:= 40;
idFtp1.Get('iphist.dat','c:\problvop\iphist.dat',True);            pb.Position:= 50;
idFtp1.Get('istr_prv.pdf','c:\problvop\istr_prv.pdf',True);     pb.Position:= 60;
idFtp1.Get('libmysql.dll','c:\problvop\libmysql.dll',True);    pb.Position:= 70;
idFtp1.Get('prv.exe','c:\problvop\prv.exe',True);                   pb.Position:= 80;
idFtp1.Get('upd.exe','c:\problvop\upd.exe',True);                 pb.Position:= 99;
//конец закачки с фтп
// создание ярлыка на рабочий стол
pb.Position:= 100;
CreateShortcut( 'Desktop', 'c:\problvop\prv.exe', '', 'c:\problvop\', '', 'Проблемные вопросы');
//конец создания ярлыка
end
else
begin
Label1.Caption:='Невозможно установить соединение с сервером';
end;
// И не забываем разорвать соединение с сервером, и завершить работу загрузчика.
idFtp1.Disconnect;     application.Terminate;
end;

Вот собственно и всё. Как видите нет ничего сложного.

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