Futurebasic/语言/fsref 现代
在 OS X 中访问文件和文件夹的首选方法。
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
以下是一个函数,用于为 CFM 或 Mach-O 应用程序检索应用程序包的 FSRef
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