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

26.03.2019 автор: deface

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

И так, определившись, что распространять буду через сервер FTP, начал стряпать мини-проект.

form-idFTP

1. Форма с одной кнопкой, дабы не вызывать о пользователя лишних вопросов.
Как видно, на форме кроме кнопки имеется 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;

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

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