マルチプラットフォームなJavaでプラットフォームに依存するバグにぶちあたりちょっと困った事になった。
プログラム自体はなんてことないFileInputStreamを使ったファイルの読み取り処理。
開発機であるWindowsではもちろん正常に動作していたんですが
環境が変わったときに問題が発生してしまいました。
[環境]
// コードはサンプルです FileInputStream fis = null; try { fis = new FileInputStream(path); /*** 中略 ***/ Closeable closeable = fis; Channel channel = ((FileInputStream) closeable).getChannel(); if (channel.isOpen()) { closeable.close(); } } catch (Exception e) { throw new Exception(e); }
これを実行するとFileInputStreamのclose処理で落ちてしまいます。
Caused by: java.io.IOException: Bad file number
at sun.nio.ch.FileDispatcher.preClose0(Native Method)
at sun.nio.ch.FileDispatcher.preClose(Unknown Source)
at sun.nio.ch.FileChannelImpl.implCloseChannel(Unknown Source)
at java.nio.channels.spi.AbstractInterruptibleChannel.close(Unknown Source)
at java.io.FileInputStream.close(Unknown Source)
RHELで動かしてみても以下のエラーが発生。
[環境]
- RHEL5.5(2.6.18-194.el5)
- jre1.6.0_20
Caused by: java.io.IOException: Bad file descriptor
at sun.nio.ch.FileDispatcher.preClose0(Native Method)
at sun.nio.ch.FileDispatcher.preClose(Unknown Source)
at sun.nio.ch.FileChannelImpl.implCloseChannel(Unknown Source)
at java.nio.channels.spi.AbstractInterruptibleChannel.close(Unknown Source)
at java.io.FileInputStream.close(Unknown Source)
sun bug databaseからそれらしきものを拾ってくると、此処らへんになるのかなぁ…
Bug ID: JDK-6322678 FileInputStream(FileDescriptor) throws IOException when reading a file if FD is invalid
Bug ID: JDK-6359397 FileInputStream.read() and FileOutputStream.write() throw IOException: No such file or directory
FileInputStream(FileDescriptor) throws IOException when reading a file if FD is invalid辺りはそれっぽいですが、これはこれでまた別の事象なような。
ちなみに、この問題はjre1.6.0_25で一応解決してるとのことでしたので、早速jreを入れ替えて試してみると
………動いた。
はて、これでいいのかな… と思いつつも今回はこれ以上調査する時間もないのでまた時間があるときに調べてみよう。
【追記】
http://256.com/gray/docs/misc/java_bad_file_descriptor_close_bug.shtml
上記サイトにはRandomAccessFile.seek()、FileInputStream.readBytes()で落ちると例で書かれていたので試してみましたが正常に動きますねぇ…まあ、バージョン違うんですけど。
今のところFileInputStream.close()以外では問題は発生していないみたい。
ExcelVBAでExcelのページ数を取得する(2)
前回の方法よりもっと確実な方法を見つけた
どうやらPageSetupから直接ページ数が取得出来るみたい。
Sub getPageCount() Dim strFile As String Dim intPageCount As Integer 'ファイル指定 strFile = "C:\test\test.xlsx" '関数からExcel WorkBookのページ数を取得 intPageCount = ExcelPrintPageCount(strFile) MsgBox intPageCount & "ページだよ!" End Sub 'Excelブックのページ数をカウントする Function ExcelPrintPageCount(ByVal strFile As String) As Integer Dim pageCount As Integer Dim xlApp As Excel.Application Dim objBooks As Excel.workbooks Dim objBook As Excel.Workbook Dim sht As Excel.Worksheet Set xlApp = New Excel.Application 'xlApp.Visible = True 'デバッグ時に使用する 'エラー処理 On Error Resume Next Set objBooks = xlApp.workbooks If Dir(CStr(strFile)) <> "" Then 'Excelファイルを読み取り専用で開く Set objBook = objBooks.Open( _ Filename:=strFile, _ UpdateLinks:=False, _ ReadOnly:=True, _ IgnoreReadOnlyRecommended:=True) If Err.Number = 0 Then For Each sht In objBook.Worksheets 'シートをアクティブに変更 sht.Activate 'ウィンドウを改ページプレビューで表示する xlApp.windows(Dir(strFile)).View = xlPageBreakPreview '印刷プレビューのページカウントを取得する pageCount = pageCount + CInt(sht.PageSetup.Pages.Count) Next sht Else 'ファイルが読み取れない場合は-1をセット pageCount = -1 End If Else 'ファイルが存在しない場合は-1をセット pageCount = -1 End If If Err.Number <> 0 Then 'その他例外が発生した場合は-1をセット pageCount = -1 End If If Not objBook Is Nothing Then 'Workbookを閉じる objBook.Saved = True objBook.Close End If If Not objBooks Is Nothing Then 'Workbooksを閉じる objBooks.Close End If If Not xlApp Is Nothing Then 'Excelを閉じる xlApp.Quit End If '戻り値をセット ExcelPrintPageCount = pageCount 'メモリ解放 Set sht = Nothing Set objBook = Nothing Set objBooks = Nothing Set xlApp = Nothing End Function
しかし、この方法他のExcelVBAサイトにまだ載ってないのね・・・
ExcelVBAでExcelのページ数を取得する(1)
こまめに修正が入るドキュメント類のページ数を、わざわざ手動で数えるのが面倒なので手っ取り早くExcelのVBAマクロで取得する方法がないか調べてみました。
ちなみに、当方の環境は以下の通りです。
Officeは一応2007と2010で動作確認してます。
まずGoogle先生に質問して出てくるのはここら辺ですね。
H_Break = Sheet1.HPageBreaks.Count '横の改ページ数取得 V_Break = Sheet1.VPageBreaks.Count '縦の改ページ数取得
VPageBreaks.Count、HPageBreaks.Countで縦横の改ページ数を取得出来るみたいですが、このサンプルでは
こんな感じ
な場合に上手く取得出来なかったりします。
他にも何個かのファイルで試してみましたが『VPageBreaks.Count』の数値自体、改ページプレビューになってないファイルのページ数を上手く取得出来なかったりして、これでマクロ組むのはちょいと面倒そうなのでパス。
Sub PrintPage() Dim PPage As Integer Sheets("sheet1").Select PPage = Application.ExecuteExcel4Macro("get.document(50)") MsgBox PPage End Sub
なるほど、Excel4Macroを使ってページ数を取得するんですね。
という訳でちょっと組んでみました。
'Excelファイルのページ数を取得する。(メイン) Sub getPageCount() Dim xlApp As Excel.Application Dim objFileNameList As Variant Dim strFile As Variant Dim intPageCount As Integer Dim strOut As String '制御用Excelアプリケーションインスタンス生成 Set xlApp = ActiveWindow.Application 'ファイルを選択 objFileNameList = selectFileProp(xlApp) For Each strFile In objFileNameList 'Excel WorkBookのページ数を取得 intPageCount = ExcelPrintPageCount(CStr(strFile)) strOut = strOut & CStr(intPageCount) & "ページだよ!" & vbCrLf Next Set xlApp = Nothing MsgBox strOut End Sub 'ファイル選択用のウィンドウを立ち上げる Function selectFileProp(ByRef xlApp As Excel.Application) As Variant Dim objFileNameList As Variant objFileNameList = xlApp.GetOpenFilename( _ FileFilter:="Microsoft Excelブック,*.xls*", _ MultiSelect:=True, _ Title:="ページ数をカウントするファイルを選択(複数選択可)") 'ファイル名読込キャンセル判定 If Not IsArray(objFileNameList) Then Set xlApp = Nothing End 'キャンセルした場合は処理終了 End If '戻り値設定 selectFileProp = objFileNameList End Function 'Excelブックのページ数をカウントする Function ExcelPrintPageCount(ByVal strFile As String) As Integer Dim pageCount As Integer Dim xlApp As Excel.Application: Set xlApp = New Excel.Application Dim objBooks As Excel.Workbooks: Set objBooks = xlApp.Workbooks Dim objBook As Excel.Workbook Dim sht As Excel.Worksheet 'xlApp.Visible = True 'デバッグ用 On Error Resume Next 'Excelファイルを読み取り専用で開く Set objBook = objBooks.Open( _ Filename:=strFile, _ UpdateLinks:=False, _ ReadOnly:=True, _ IgnoreReadOnlyRecommended:=True) For Each sht In objBook.Worksheets 'シート選択 sht.Select 'ページ数を取得にゃー pageCount = pageCount + xlApp.ExecuteExcel4Macro("get.document(50)") Next sht 'ファイルが読み取れない場合はページ数に-1を指定 If Err.Number <> 0 Then pageCount = -1 '戻り値設定 ExcelPrintPageCount = pageCount 'Workbookを閉じる If Not objBook Is Nothing Then objBook.Saved = True If Not objBook Is Nothing Then objBook.Close If Not objBooks Is Nothing Then objBooks.Close 'Excelを閉じる If Not xlApp Is Nothing Then xlApp.Quit Set sht = Nothing Set objBook = Nothing Set objBooks = Nothing Set xlApp = Nothing End Function
ただ、get.document(50)は改ページプレビューしていないファイルや、ページレイアウトで「縦1ページ×横1ページ」を指定している場合上手く所得できないみたい?
もう少し改良が必要ですね。
初めてみた
技術系ネタやら写真ネタを少しずつ投稿していく予定。
まとまった時間の取れる日まで保留かな。