跳转到内容

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

华夏公益教科书