跳轉到內容

Futurebasic/語言/fsref 現代

來自華夏公益教科書,開放的書本,開放的世界

FSRef 結構 - 現代方法

[編輯 | 編輯原始碼]

在 OS X 中訪問檔案和資料夾的首選方法。

The OS X 檔案管理器 提供了一個抽象層,隱藏了底層實現細節,例如不同的檔案系統和卷格式。該抽象層的一個關鍵組成部分是 FSRef。

FSRef 是一個不透明的引用,儲存在檔案管理器分配的記錄中,用於描述檔案或資料夾。它的元素隱藏在一個 81 元素的 UInt8 陣列中,其組成部分未由 Apple 文件化。不透明結構透過 Carbon 工具箱函式訪問,而不是像早期 API 中那樣直接訪問記錄的各個欄位。

FSRef API 提供了長 Unicode 名稱支援、大檔案訪問,其效能針對 OS X 進行了最佳化。

FSRef 的內容本質上是動態的。例如,如果您的程式碼使用 FSRef 引用檔案或資料夾,當執行您的程式碼的 Macintosh 重啟時,該 FSRef 結構將被清除。重啟後,當您的程式碼建立指向以前引用的同一檔案或資料夾的 FSRef 時,檔案管理器將建立新的唯一 FSRef 來標識該檔案或資料夾,其結構將與前者不同。

以下是 Carbon Files.h 標頭檔案中描述的 FSRef 的技術描述

FSRef structure:
struct FSRef {
 UInt8     hidden[80];  /* private to File Manager*/
};

Apple 於 2003 年 5 月釋出的 技術說明 TN2078 中詳細介紹了為什麼 FSRef 是訪問檔案和資料夾的首選方法,以及實現相同目的的方法。

FSRef 的細微之處對您的程式碼影響最大,即 FSRef 無法表示不存在的專案,並且 FSRef 不包含它引用的專案的名稱。

FB 使用者面臨的另一個挑戰是 FB 的 Files$ 函式用於從 Finder 對話方塊中選擇檔案和資料夾,它返回的是 FSSpec 而不是 FSRef。

針對這些挑戰都有一些解決方法。

使用導航服務建立檔案 FSRef

[編輯 | 編輯原始碼]

這是一個用於 FB 的 Files$ 函式的現代導航服務替換函式。它與 FB 和 FBtoC 相容。它為從預設 導航對話方塊 中選擇的檔案建立 FSRef:

(程式碼基於此 Apple 示例...)

include "Tlbx Navigation.incl"

_typeFSRef = _"fsrf"

local fn SelectFileFSRef( @fileRef as ^FSRef ) as OSErr
 dim as NavDialogCreationOptions dialogOptions
 dim as NavTypeListHandle     fileTypes
 dim as NavDialogRef      @ navRef
 dim as NavReplyRecord     @ navReply
 dim as long         @ count, @ myDummyClientData
 dim as FSRef           tempRef
 dim as OSStatus         err

 err = fn NavGetDefaultDialogCreationOptions( dialogOptions )
 long if ( err == _noErr )
  err = fn NavCreateChooseFileDialog( @dialogOptions, fileTypes, 0, 0, 0, @myDummyClientData, navRef )
  long if ( err == _noErr )
  err = fn NavDialogRun( navRef )
  long if ( err == _noErr )
   err = fn NavDialogGetReply( navRef, navReply )
    long if ( err == _noErr )
     long if ( navReply.validRecord != _false )
     err = fn AECountItems( navReply.selection, count )
      long if ( count == 1 )
      err = fn AEGetNthPtr( navReply.selection, count, _typeFSRef, #0, #0, @tempRef, SizeOf( FSRef ), #0 )
       long if ( err = _noErr )
        BlockMove @tempRef, fileRef, sizeof( FSRef )
       end if
      end if
     xelse
     // User canceled dialog
     end if
    end if
   end if
  end if                                                                                                                           end if

 err = fn NavDisposeReply (navReply)
 call NavDialogDispose ( navRef )

end fn = err

NavDialogCreationOptions 記錄包含幾個有用的欄位,使用者可以使用這些欄位來自定義導航對話方塊。除其他事項外,視窗標題、對話方塊按鈕名稱、視窗模態性、在對話方塊頂部新增特殊使用者訊息,都可以自定義。這些選項在導航對話方塊中大體上是通用的,無論它用於開啟單個檔案、多個檔案還是資料夾。

以下是呼叫一些導航對話方塊函式的示例。這些可以輕鬆地新增為函式輸入引數,但這裡只是簡單地新增到了函式中

include "Tlbx Navigation.incl"

_typeFSRef = _"fsrf"

local fn SelectFileFSRef( fileRef as ^FSRef ) as OSErr
 dim as NavDialogCreationOptions dialogOptions
 dim as NavTypeListHandle     fileTypes
 dim as NavDialogRef      @ navRef
 dim as NavReplyRecord     @ navReply
 dim as long          @ count
 dim as FSRef           tempRef
 dim as Str255          s
 dim as OSStatus         err

 err = fn NavGetDefaultDialogCreationOptions( dialogOptions )
 long if ( err == _noErr )

 dialogOptions.modality = _kWindowModalityAppModal

 s = "SelectFileFSRef Window"
 dialogOptions.windowTitle = fn CFStringCreateWithCString( 0, s, _kCFStringEncodingASCII )

 s = "Please choose a file..."
 dialogOptions.actionButtonLabel = fn CFStringCreateWithCString( 0, s, _kCFStringEncodingASCII )

 s = "Here is a custom message."
 dialogOptions.message = fn CFStringCreateWithCString( 0, s, _kCFStringEncodingASCII )

  err = fn NavCreateGetFileDialog( @dialogOptions, fileTypes, 0, 0, 0, #0, navRef )
  long if ( err == _noErr )
   err = fn NavDialogRun( navRef )
   long if ( err == _noErr )
    err = fn NavDialogGetReply( navRef, navReply )
    long if ( err == _noErr )
     long if ( navReply.validRecord != _false )
      err = fn AECountItems( navReply.selection, count )
      long if ( count == 1 )
       err = fn AEGetNthPtr( navReply.selection, count, _typeFSRef, #0, #0, @tempRef, SizeOf( FSRef ), #0 )
       long if ( err = _noErr )
        BlockMove @tempRef, fileRef, sizeof( FSRef )
       end if
      end if
     xelse
      // User canceled dialog
     end if
    end if
   end if
  end if
 end if

 err = fn NavDisposeReply (navReply)
 call NavDialogDispose ( navRef )

end fn = err

使用導航服務建立資料夾 FSRef

[編輯 | 編輯原始碼]

與 FB 和 FBtoC 相容的程式碼用於返回從導航對話方塊中選擇的資料夾的 FSRef,類似於檔案。同樣,對話方塊選項也可以輕鬆自定義

include "Tlbx Navigation.incl"

_typeFSRef = _"fsrf"

local fn SelectFolderFSRef( folderRef as ^FSRef ) as OSErr
 dim as NavDialogCreationOptions navOptions
 dim as NavDialogRef      @ navRef
 dim as NavReplyRecord     @ navReply
 dim as long          @ count
 dim as FSRef           tempRef
 dim as OSStatus         err

 err = fn NavGetDefaultDialogCreationOptions( navOptions )
 long if ( err == _noErr )
  err = fn NavCreateChooseFolderDialog( navOptions, 0, 0, #0, navRef )
  long if ( err == _noErr )
   err = fn NavDialogRun( navRef )
   long if ( err == _noErr )
     err = fn NavDialogGetReply( navRef, navReply )
     long if ( err == _noErr )
      long if ( navReply.validRecord != _false )
      err = fn AECountItems( navReply.selection, count )
        long if ( count == 1 )
         err = fn AEGetNthPtr( navReply.selection, count, _typeFSRef, #0, #0, @tempRef, SizeOf( FSRef ), #0 )
         long if ( err = _noErr )
          BlockMove @tempRef, folderRef, sizeof( FSRef )
         end if
       end if
      xelse
      // User canceled dialog
     end if
    end if
   end if
  end if
 end if

 err = fn NavDisposeReply (navReply)
 call NavDialogDispose ( navRef )

end fn = err

使用 FBtoC 的 Files$ 函式建立 FSRef

[編輯 | 編輯原始碼]

FBtoC 在其自定義 Files$ 函式中提供了建立 FSRef 的本機功能

dim as FSRef  fref
dim as str255 fStr

fStr = Files$( _FSRefOpen, "TEXT", "Open text file...", fsRef )
long if ( fStr[0] )
 // Do something with your text file FSRef
 xelse
 // User canceled
end if

從 FSRef 獲取檔名

[編輯 | 編輯原始碼]

從 FSRef 獲取檔名比其前身要困難一些,但此函式應該可以完成這項工作

local fn GetLongFileNameFromFSRef$( fsRef as ^FSRef )
 dim as str255    @ name
 dim as HFSUniStr255  hsfName
 dim as CFStringRef   cfStr
 dim as OSErr      err
 dim as boolean     result

 err = fn FSGetCatalogInfo( #fsRef, _kFSCatInfoNone, #0, hsfName, #0, #0)
 long if ( err == _noErr )
  cfStr = fn CFStringCreateWithCharacters( 0, hsfName.unicode[0], hsfName.length )
  long if ( cfStr )
   result = fn CFStringGetPascalString( cfStr, @name, SizeOf( name ), _kCFStringEncodingMacRoman )
   CFRelease( cfStr )
  end if
 end if

end fn = name

獲取應用程式包的 FSRef

[編輯 | 編輯原始碼]

這是一個用於檢索您的應用程式包的 FSRef 的函式,適用於 CFM 或 Mach-O 應用程式

include "Tlbx Processes.Incl"

toolbox fn GetProcessBundleLocation( ProcessSerialNumber *psn, FSRef *location ) = OSStatus

local fn GetMyBundleFSRef( bundleRef as ^FSRef ) as OSErr
 dim as ProcessSerialNumber @ currentProcess
 dim as FSRef        @ tempRef, @ bundleRef
 dim as OSStatus        err

 currentProcess.highLongOfPSN = 0
 currentProcess.lowLongOfPSN = _kCurrentProcess

 err = fn GetProcessBundleLocation( currentProcess, tempRef )
  long if ( err == _noErr )
  BlockMove @tempRef, bundleRef, SizeOf( FSRef )
 end if

end fn = err

使用 FSRef 建立新資料夾

[編輯 | 編輯原始碼]

建立新資料夾(也稱為目錄)的能力是許多 FB 程式的關鍵要素。以下是一個在 FB 和 FBtoC 中都編譯的完整程式。它包含錯誤檢查,以防止覆蓋現有資料夾。

注意:由於 FB 和 FBtoC 的標頭檔案都不完整,因此此程式碼包含了處理其任務所需的四個 Carbon 工具箱函式的定義。

include "Tlbx Navigation.incl"
include "Tlbx MoreFilesX.incl"

_typeFSRef = _"fsrf"

// Files.h
toolbox fn FSCreateDirectoryUnicode( const FSRef * parentRef,¬
                    UniCharCount nameLength,¬
                     const UniChar * name,¬
                 FSCatalogInfoBitmap whichInfo,¬
               const FSCatalogInfo * catalogInfo,¬
                        FSRef * newRef,¬
                       FSSpec * newSpec,¬
                       UInt32 * newDirID ) = OSErr

toolbox fn FSMakeFSRefUnicode( const FSRef *parentRef,¬
                UniCharCount nameLength,¬
                  const UniChar *name,¬
             TextEncoding textEncodingHint,¬
                     FSRef *newRef ) = OSErr

// UnicodeConverter.h
toolbox fn CreateTextToUnicodeInfoByEncoding( TextEncoding iEncoding,¬
                 TextToUnicodeInfo *oTextToUnicodeInfo ) = OSStatus

toolbox fn ConvertFromPStringToUnicode( TextToUnicodeInfo iTextToUnicodeInfo,¬
                             Str255 *iPascalStr,¬
                           ByteCount iOutputBufLen,¬
                            ByteCount *oUnicodeLen,¬
                             UniChar *oUnicodeStr ) = OSStatus

local fn CreateNewFolder( parentFolderRef as ^FSRef, newFolderName as Str255, newFolderRef as ^FSRef ) as OSErr
 dim as HFSUniStr255  uniName
 dim as OSStatus    err
 dim as ByteCount   @ uniLength
 dim as FSRef     @ tempRef

 begin globals
 dim as TextToUnicodeInfo sTextToUnicodeInfo
 end globals

 err = _noErr
 long if ( sTextToUnicodeInfo == 0 )
  err = fn CreateTextToUnicodeInfoByEncoding( _kTextEncodingMacRoman, @sTextToUnicodeInfo )
   long if ( err = _noErr )
    err = fn ConvertFromPStringToUnicode( sTextToUnicodeInfo, @newFolderName, 510, @uniLength, @uniName.unicode[0] )
     long if ( err == _noErr )
       uniName.length = uniLength / sizeof( UniChar )
       // Check to see if the folder already exists to avoid overwriting it...
       err = fn FSMakeFSRefUnicode( #parentFolderRef,¬
          uniName.length, @uniName.unicode[0], _kTextEncodingUnicodeDefault, @tempRef ) 
        long if ( err != _noErr )
         err = fn FSCreateDirectoryUnicode( #parentFolderRef, uniName.length, @uniName.unicode[0], _kFSCatInfoNone, #0, #0, @tempRef, #0 )
         BlockMoveData( @tempRef, newFolderRef, sizeof( FSRef) )
        xelse
        stop "Could not create new folder. Folder already exists."
        exit fn
        end if
     xelse
      exit fn
     end if
    xelse
    exit fn
   end if
 end if

end fn = err

local fn SelectFolderFSRef( folderRef as ^FSRef ) as OSErr
 dim as NavDialogCreationOptions  navOptions
 dim as NavDialogRef       @ navRef
 dim as NavReplyRecord      @ navReply
 dim as long         @ count
 dim as FSRef           tempRef
 dim as OSStatus          err

 err = fn NavGetDefaultDialogCreationOptions( navOptions )
  long if ( err == _noErr )
   err = fn NavCreateChooseFolderDialog( navOptions, 0, 0, #0, navRef )
    long if ( err == _noErr )
    err = fn NavDialogRun( navRef )
     long if ( err == _noErr )
     err = fn NavDialogGetReply( navRef, navReply )
      long if ( err == _noErr )
       long if ( navReply.validRecord != _false )
       err = fn AECountItems( navReply.selection, count )
        long if ( count == 1 )
        err = fn AEGetNthPtr( navReply.selection, count, _typeFSRef, #0, #0, @tempRef, SizeOf( FSRef ), #0 )
         long if ( err = _noErr )
          BlockMove @tempRef, folderRef, sizeof( FSRef )
         end if
        end if
        xelse
        // User canceled dialog
        end if
      end if
     end if
    end if
  end if

 err = fn NavDisposeReply (navReply)
 call NavDialogDispose ( navRef )

end fn = err

dim as OSErr err
dim as FSRef oldFolder, newFolder

err = fn SelectFolderFSRef( oldFolder )
long if ( err == _noErr )
 err = fn CreateNewFolder( oldFolder, "Test Folder", newFolder )
end if

do
HandleEvents
until gFBQuit

(更多討論待續)

4d69 646e 6967 6874

0100 0011 0110 1111 0110 0100 0110 0101 0111 0010 0010 0000 0010 0000 0010 0000

華夏公益教科書