Futurebasic/語言/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。
針對這些挑戰都有一些解決方法。
這是一個用於 FB 的 Files$ 函式的現代導航服務替換函式。它與 FB 和 FBtoC 相容。它為從預設 導航對話方塊 中選擇的檔案建立 FSRef:
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
與 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 的本機功能
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 獲取檔名比其前身要困難一些,但此函式應該可以完成這項工作
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 的函式,適用於 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
建立新資料夾(也稱為目錄)的能力是許多 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