Копия викизеркало

Это зеркало вики с pascal.chat. Правка, история и вход — там.

Locating macOS significant directories/ru

English · Русский

macOSlogo.png

Эта статья относится только к macOS.

См. также: Multiplatform Programming Guide

│ English (en) │ русский (ru) │

Обзор

Чтобы найти различные важные каталоги в macOS, вы можете использовать встроенную функцию macOS Foundation

pascal
NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory directory, NSSearchPathDomainMask domainMask, BOOL expandTilde);

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

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

Определение места для хранения файлов

Прежде чем мы продолжим, давайте вспомним, где, согласно рекомендациям Apple, ваше приложение должно хранить свои файлы:

Функция получения важных каталогов

pascal
{$mode objfpc}{H+}
    {$modeswitch objectivec1} 
    
    interface
    
    Uses
      ...
      CocoaUtils,   // for NStringToString
      CocoaAll;     // for NSArray      
    ...
    
    function GetSignificantDir(DirLocation: qword; DomainMask: qword; count: byte): string;
    var
      paths : NSArray;
    begin
      paths := NSSearchPathForDirectoriesInDomains(DirLocation, DomainMask, True);
      if(count < paths.count) then
        Result := NSString(paths.objectAtIndex(0)).UTF8String
      else
        Result := '';  
    end;

Таблица расположения каталогов

Directory Locations Имя каталога Описание каталога
NSApplicationDirectory Поддерживаемые приложения (/Applications)
NSDemoApplicationDirectory Неподдерживаемые приложения и демонстрационные версии
NSDeveloperApplicationDirectory Приложения для разработчиков (/Developer/Applications)
NSAdminApplicationDirectory Приложения для системного и сетевого администрирования
NSLibraryDirectory Различная доступная пользователю документация, поддержка и конфигурационные файлы (/Library)
NSDeveloperDirectory Ресурсы разработчика (/Developer)
NSUserDirectory Домашние каталоги пользователей (/Users)
NSDocumentationDirectory Документация
NSDocumentDirectory Каталог документов
NSCoreServiceDirectory Службы ядра (System/Library/CoreServices)
NSAutosavedInformationDirectory Автоматически сохраненные документы пользователя (Library/Autosave Information)
NSDesktopDirectory Каталог рабочего стола пользователя
NSCachesDirectory Удаляемые файлы кэша (Library/Caches)
NSApplicationSupportDirectory Файлы поддержки приложений (Library/Application Support)
NSDownloadsDirectory Каталог загрузок пользователя
NSInputMethodsDirectory Методы ввода (Library/Input Methods)
NSMoviesDirectory Каталог фильмов пользователя (~/Movies)
NSMusicDirectory Каталог музыки пользователя (~/Music)
NSPicturesDirectory Каталог изображений пользователя (~/Pictures)
NSPrinterDescriptionDirectory Системный каталог PostScript Printer Description (Library/Printers/PPDs)
NSSharedPublicDirectory Общедоступный каталог общего доступа пользователя (~/Public)
NSPreferencePanesDirectory Каталог панели настроек (preference panes) для системных настроек (Library/PreferencePanes)
NSApplicationScriptsDirectory Папка пользовательских сценариев для вызывающего приложения (~/Library/Application Scripts/<code-signing-id>
NSItemReplacementDirectory Константа, используемая для создания временного каталога
NSAllApplicationsDirectory Все каталоги, в которых могут храниться приложения
NSAllLibrariesDirectory Все каталоги, в которых могут храниться ресурсы
NSTrashDirectory Каталог корзины пользователя

Таблица доменов путей

Домены Путей Доменное имя Описание домена
NSUserDomainMask Домашний каталог пользователя — место для установки личных настроек пользователя (~).
NSLocalDomainMask Место для установки элементов, доступное каждому на этом компьютере
NSNetworkDomainMask Место для установки элементов, доступных в сети (/Network)
NSSystemDomainMask Каталог для системных файлов, предоставленный компанией Apple (/System)
NSAllDomainsMask Все домены

Примеры кода

Теперь вы можете находить различные важные каталоги и отображать их, как показано в примерах кода ниже.

Каталог пользовательских кэшей

pascal
procedure TForm1.MenuItem5Click(Sender: TObject);
    begin
      ShowMessage('User caches dir: ' + GetSignificantDir(NSCachesDirectory,NSUserDomainMask,0));
    end;

Пользовательский каталог корзины

pascal
procedure TForm1.MenuItem16Click(Sender: TObject);
    begin
      ShowMessage('User trash dir: '
              + GetSignificantDir(NSTrashDirectory,NSUserDomainMask,0));
    end;

Каталог пользователей

Возможно, это не то, что вы ожидаете... это каталог "/Users" :-)

pascal
procedure TForm1.MenuItem10Click(Sender: TObject);
    begin
      ShowMessage('Users dir: ' +  GetSignificantDir(NSUserDirectory,NSLocalDomainMask,0));
    end;

Чтобы получить домашний каталог текущего пользователя, вам нужно вместо этого использовать функцию NSHomeDirectory() :

pascal
procedure TForm1.MenuItem11Click(Sender: TObject);
    begin
      ShowMessage('User''s dir: ' + NSStringToString(NSHomeDirectory));
    end;

НО обратите внимание, что если приложение находится в изолированной среде, это вернет каталог приложения sandbox, а не домашний каталог текущего пользователя.

Альтернативный способ получить домашний каталог текущего пользователя:

pascal
function NSUserName: CFStringRef external name '_NSUserName';
    ...
    
    procedure TForm1.MenuItem22Click(Sender: TObject);
    var
      usernameStr: ShortString;
      status: Boolean = false;
    begin
       status := CFStringGetPascalString(CFStringRef(NSusername),@usernameStr,255,CFStringGetSystemEncoding);
      if(status = true) then
         ShowMessage('Current user''s dir: ' + NSStringToString(NShomeDirectoryForUser(NSStr(usernameStr))))
      else
         ShowMessage('Error retrieving username'); 
    end;

Все каталоги приложений

pascal
procedure TForm1.MenuItem7Click(Sender: TObject);
    var
      count: byte;
    begin
      for count := 0 to 12 do
       if GetSignificantDir(NSAllApplicationsDirectory, NSAllDomainsMask, count) <> '' then
          ShowMessage('Application dir ' + IntToStr(count) + ': '
               + GetSignificantDir(NSAllApplicationsDirectory,NSAllDomainsMask,count))
       else
          exit; 
    end;

Все каталоги пользовательских приложений

pascal
procedure TForm1.MenuItem8Click(Sender: TObject);
    var
      count: byte;
    begin
      for count := 0 to 12 do
       if GetSignificantDir(NSAllApplicationsDirectory, NSUserDomainMask, count) <> '' then
          ShowMessage('User application dir ' + IntToStr(count) + ': '
               + GetSignificantDir(NSAllApplicationsDirectory,NSUserDomainMask,count))
       else
          exit; 
    end;

Все системные каталоги приложений

pascal
procedure TForm1.MenuItem9Click(Sender: TObject);
    var
      count: byte;
    begin
      for count := 0 to 12 do
       if GetSignificantDir(NSAllApplicationsDirectory, NSSystemDomainMask, count) <> '' then
          ShowMessage('System application dir ' + IntToStr(count) + ': '
               + GetSignificantDir(NSAllApplicationsDirectory,NSSystemDomainMask,count))
       else
          exit; 
    end;

Замена GetAppConfigDir и GetAppConfigFile в FPC

Существующие функции FPC GetAppConfigDir и GetAppConfigFile не подходят для Mac OS, поскольку они используют соглашение UNIX и не соответствуют соглашению Apple macOS. Например, выходные данные этих функций:

pascal
writeLn(GetAppConfigDir(true));
    writeLn(GetAppConfigDir(false));
    
    writeLn(GetAppConfigFile(true));
    writeLn(GetAppConfigFile(false));
    
    writeLn(GetAppConfigFile(true,true));
    writeLn(GetAppConfigFile(true,false));
    
    writeLn(GetAppConfigFile(false,false));
    writeLn(GetAppConfigFile(false,true));

это:

pascal
/etc/
    /Users/<username>/.config/
    
    /etc/project1.cfg
    /Users/<username>/.config/project1.cfg
    
    /etc/project1/project1.cfg
    /etc/project1.cfg
    
    /Users/<username>/.config/project1.cfg
    /Users/<username>/.config/project1/project1.cfg

принимая во внимание, что правильный вывод для приложения командной строки macOS (т.е. без Пакета приложений) выглядит следующим образом:

pascal
/Library/Application Support/project1/
    /Users/<username>/Library/Application Support/project1/
    
    /Library/Application Support/project1/project1.plist
    /Users/<username>/Library/Application Support/project1/project1.plist
    
    /Library/Application Support/project1/project1.plist
    /Library/Application Support/project1/project1.plist
    
    /Users/<username>/Library/Application Support/project1/project1.plist
    /Users/<username>/Library/Application Support/project1/project1.plist

и правильный вывод для приложения с графическим интерфейсом Mac OS (например, с пакетом приложений):

pascal
/Library/Application Support/com.company.project1/
    /Users/<username>/Library/Application Support/com.company.project1/
    
    /Library/Application Support/com.company.project1/project1.plist
    /Users/<username>/Library/Application Support/com.company.project1/project1.plist
    
    /Library/Application Support/com.company.project1/project1.plist
    /Library/Application Support/com.company.project1/project1.plist
    
    /Users/<username>/Library/Application Support/com.company.project1/project1.plist
    /Users/<username>/Library/Application Support/com.company.project1/project1.plist

Вот заменяющие функции, названные с добавлением 2 к существующим именам функций, и с ${IFDEF}s, чтобы их можно было использовать в мультиплатформенном коде:

pascal
{$mode objfpc}{H+}
    {$modeswitch objectivec1}
    
    ...
    
    Uses
      ...
      SysUtils,
      CocoaAll,
      Cocoautils;
    ...
    
    //
    // Замена GetAppConfigDir
    //
    
    function GetAppConfigDir2(Global: Boolean): String;
    
    {$IFDEF DARWIN}
    var
      bundleIdStr: String;
      pathsArr : NSArray;
    {$ENDIF}
    begin
      {$IFDEF DARWIN}
      bundleIdStr := NSStringToString(NSBundle.mainBundle.bundleIdentifier);
      if(bundleIdStr = '') then
         bundleIdStr := ExtractFileName(paramstr(0)); // Работа с приложениями, не входящими в пакет приложения, например, с командной строкой
    
      if(Global) then
        pathsArr := NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSLocalDomainMask, True)
      else
        pathsArr := NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, True);
    
      Result := NSString(pathsArr.objectAtIndex(0)).Utf8String + '/' + bundleIdStr + '/';
      {$ELSE}
        GetAppConfigDir(global);
      {$ENDIF}
    end;  
    
    //    
    // Замена GetAppConfigFile
    //
    
    Function GetAppConfigFile2(Global : Boolean; SubDir : Boolean) : String;
    
    {$IFDEF DARWIN}
    var
      bundleIdStr: ShortString;
      appNameStr: ShortString;
      pathBufferStr: String;
      isAppBundle : Boolean = True;
    {$ENDIF}
    begin
      {$IFDEF DARWIN}
      bundleIdStr := NSStringToString(NSBundle.mainBundle.bundleIdentifier);
      if(bundleIdStr = '') then
        begin
         bundleIdStr := ExtractFileName(paramstr(0)); // Работа с приложениями, не входящими в пакет приложения, например, с командной строкой
         isAppBundle := False;
        end;
    
      pathBufferStr := GetAppConfigDir2(Global);
    
      if(isAppBundle) then
        appNameStr := StringReplace(ExtractFileExt(bundleIdStr), '.', '', [rfReplaceAll])
      else
        appNameStr := extractFileName(ExcludeTrailingPathDelimiter(pathBufferStr));
    
      if(SubDir) then
        // Игнорируем, потому что Apple уже задает subdir как часть GetAppConfigDir()
        ;
    
      Result := pathBufferStr + appNameStr + '.plist';
    
      {$ELSE}
      Result := GetAppConfigFile(Global, SubDir);
      {$ENDIF}
    end;

см.также


From the Free Pascal wiki, page "Locating macOS significant directories/ru", as the Internet Archive kept it on 2025-04-26, last changed on 2025-04-20. Copyright stays with the author who wrote it, according to the old wiki's Licensing page. If you have concerns about this material, message the admin.

Версия 1, последнее изменение: WikiArchive, .