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