Практика
Создание ярлыка с относительным путем

:: Меню ::
:: На главную ::
:: FAQ ::
:: Заметки ::
:: Практика ::
:: Win API ::
:: Проекты ::
:: Скачать ::
:: Секреты ::
:: Ссылки ::

:: Сервис ::
:: Написать ::

:: MVP ::

:: RSS ::

Яндекс.Метрика


Для начала вспомним, в чем разница между абсолютным и относительным путями?

Абсолютный путь (например, C:\Windows) неизменен и интерпретируется одинаково независимо от текущей рабочей папки, в то время как относительный путь не включает одно или несколько имен родительских папок (начиная с корня диска), заменяя их либо обозначением .. (две точки, например, ..\autoexec.bat), либо ссылаясь на файлы внутри текущей папки (например, System32\calc.exe). Ярлыки с относительными путями могут быть актуальны для флэшек, которые на разных компьютерах могут иметь разную букву, но при этом всегда будут запускать файл, на который они ссылаются.

Ярлыки с абсолютным путем создаются в Windows очень легко, чего не скажешь о ярлыках с относительным путем. А если мы хотим добиться работоспособности таких ярлыков как в Windows 7, так и, скажем, в Windows XP - все, приехали, тушите свет!

Однако не все так плохо, выход есть. Посмотрим на следующую файловую структуру (попутно обратив внимание на пробелы в именах каталогов и файлов, это важно, и нужно про это не забыть).


В папке _files есть 2 папки - files и output. Папка files содержит файлы, для которых нужно создать ярлыки с относительными путями в папке output. В конечном итоге должно получиться следующее:


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

Разберем, как будет выглядеть относительный путь для ярлыка на файл _files\files\1 2.txt. Так как ярлык будет лежать в папке _files\output\, то нужно будет подняться на один уровень вверх, и в итоге относительный путь будет выглядеть так: ..\files\1 2.txt.

Но ярлык с таким путем создать не получится. Корректная команда для запуска файла по относительному пути должна иметь следующий вид:

%comspec% /C "start /B /D ..\files\ ..\files\1" "2.txt"

%comspec% - то же самое, что и %windir%\system32\cmd.exe

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

Посмотрим на код примера, решающего описанную выше задачу:

function CreateShortcut(const CmdLine, Args, IconFile, LinkFile: string): IPersistFile;
begin
   // Код функции создания ярлыка смотрите в прилагаемом примере
end;

procedure MakeLnkFile(fName, OutputDir: string);

  function Claer(Dir: string; const Pattern: string): string;
  var
    i, Сount: Integer;
  begin
     Сount := 0;
     for i := 1 to Length(Pattern) do
     begin
        if i > Length(Dir) then
           Break;

        if Dir[i] = Pattern[i] then
           Inc(Сount)
        else
           Break;
     end;

     if Сount > 0 then
        Result := Copy(Dir, Сount+1, Length(Dir)-Сount);
  end;

var
  &Path, &File, &Dots: string;
begin
   // Вычисляем путь к файлу, обрамляя пробелы двойными кавычками
   &Path := ExtractFilePath(fName);
   &Path := Claer(&Path, OutputDir);
   &Path := IncludeTrailingPathDelimiter(TRegEx.Replace(&Path, '(\s+)', '"$1"'));

   // Вычисляем, на сколько уровней вверх нужно подняться
   &Dots := Claer(IncludeTrailingPathDelimiter(OutputDir), fName);
   &Dots := TRegEx.Replace(&Dots, '[^\\]+', '..');

   // Вычисляем имя файла, обрамляя пробелы двойными кавычками
   &File := ExtractFileName(fName);
   &File := TRegEx.Replace(&File, '(\s+)', '"$1"');

   CreateShortcut('%comspec%',
      '/C "start /B /D ' + &Dots+&Path + ' ' + &Dots+&Path+&File + '"',
      &Dots + Claer(fName, OutputDir),
      IncludeTrailingPathDelimiter(OutputDir) +
      ChangeFileExt(TRegEx.Replace(Claer(fName, OutputDir), '\\+', '_'), '.lnk'));
end;

function SearchFiles(Dir: string; const OutputDir: string): Boolean;
var
  isFound: Boolean;
  sRec: TSearchRec;
begin
   isFound := FindFirst(IncludeTrailingPathDelimiter(Dir) + '*.*', faAnyFile, sRec) = 0;
   while isFound do
   begin
      if (sRec.Name <> '.') and (sRec.Name <> '..') then
         if (sRec.Attr and faDirectory) = faDirectory then
         begin
            if not SearchFiles(IncludeTrailingPathDelimiter(Dir) + sRec.Name, OutputDir) then
               Exit;
         end
         else
            // Для найденного файла создаем ярлык
            MakeLnkFile(IncludeTrailingPathDelimiter(Dir) + sRec.Name, OutputDir);
      isFound := FindNext(sRec) = 0;
   end;
   FindClose(sRec);
   Result := IOResult = 0;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
   SearchFiles(
      ExtractFilePath(Application.ExeName) + '..\..\_files\files\',
      ExtractFilePath(Application.ExeName) + '..\..\_files\output\');
   ShellExecute(Handle, 'open',
      PChar(ExtractFilePath(Application.ExeName) + '..\..\_files\output\'),
      '', '', SW_SHOWNORMAL);
end;

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

.: Пример к данной статье :.


При использовании материала - ссылка на сайт обязательна