DOBON.NET DOBON.NETプログラミング掲示板過去ログ

VB.NETで空き容量がない場合にFileCloseできない。

環境/言語:[使用言語(VB.NET)]
分類:[.NET]

お世話になります。
VB.NETにてファイル出力のアプリを開発しています。
その際、空き容量の少ないFDへ「FileOpen−FilePut−FileClose」を
行なった場合、エラーが発生します。

ここまでは良いのですが、エラー発生時にFileCloseできないと言う
問題が発生しています。
⇒'System.IO.IOException' のハンドルされていない例外が mscorlib.dll で
 発生しました。
 追加情報 : ディスクに十分な空き領域がありません。

また、エラーを無視して終了(End)しても、上記エラーが発生します。
VBからのコンバートで同様の処理を行なっているPGが多数あり、未だにFDの
運用がある為、困っています。

FileCloseを正しく行なう方法か、上記エラーを表示させない方法を
ご存知の方がいらっしゃいましたら、ご教授の程お願いします。

宜しくお願い致します。
■No13815に返信(さすらいのスナフキンさんの記事)
> お世話になります。
> VB.NETにてファイル出力のアプリを開発しています。
> その際、空き容量の少ないFDへ「FileOpen−FilePut−FileClose」を
> 行なった場合、エラーが発生します。
> ここまでは良いのですが、エラー発生時にFileCloseできないと言う
> 問題が発生しています。

なさそうですね。
http://www.gdncom.jp/general/bbs/ShowPost.aspx?PostID=37815
■No13818に返信(じゃんぬねっとさんの記事)

さっそくのお返事ありがとうございます。

> なさそうですね。

やはりですかぁ。
これってMicroSoftのバグだと思うのですが、サイト上にはバグ情報として
なさそうでした。。(探し方が足りなかったのでしょうか)

とは言え、できないものは仕方ないので諦めます。
じゃんぬねっとさん、ありがとうございました。
解決済み!
この問題はCloseメソッドでFlushすることが原因のようです。Win32 APIのCloseHandle関数を使えば閉じることができるのではないでしょうか?
■No13841に返信(管理人さんの記事)
> この問題はCloseメソッドでFlushすることが原因のようです。Win32 APIのCloseHandle関数を使えば閉じることができるのではないでしょうか?

管理人さんありがとうございます。
CloseHandle関数に関しては考えたのですが、FileOpenで開く際はFileIOを
使用し、CloseHandle関数はハンドルを使用しているようでOpenから変えないと
できないと思い断念しました。

FileIO(FreeFileで取得)からハンドルの取得は可能でしょうか?

また最悪、PGを強制終了しようとしているのですが、DLLのエラーメッセージが
表示される為、困っています。
DLLのエラーメッセージをクリアーする方法があれば教えて頂けないでしょうか?
> FileIO(FreeFileで取得)からハンドルの取得は可能でしょうか?

「どんなことをしてもよい」と言うことなら、Reflection を使えば取得できます。(FileStream の private フィールドを列挙してみましょう)

以前、C# MVP の菊池さんが「邪悪な回答」を用意してくれていたんですが、blog を引っ越した時に記事が消えてしまっているようです。

> また最悪、PGを強制終了しようとしているのですが、DLLのエラーメッセージが
> 表示される為、困っています。
> DLLのエラーメッセージをクリアーする方法があれば教えて頂けないでしょうか?

FileStream の内部状態を整えてあげないことには、↑もままなりません。
■No13846に返信(渋木宏明(ひどり)さんの記事)

皆さんありがとうございました。
 正当な対応は無理ということが良く分かりました。 
 色々とお手数をお掛けしましたが、無理やりな対応をして別の問題を
 発生させることになりそうなので、やはり諦めて、MicroSoftの対応
 を待ちたいと思います。

 色々とご協力ありがとうございました。
解決済み!
>正当な対応は無理ということが良く分かりました。 

前にも書きましたが、PSS に連絡して、正規のパスで hotfix の提供を受けることはできるはずです。

「ユーザコードによる対処」も、トレードオフするべき点がいくつかありますが、不可能ではありません。

障害を障害のまま残せないような状況であれば、↑のどちらかを選択することになります。

>  色々とお手数をお掛けしましたが、無理やりな対応をして別の問題を
>  発生させることになりそうなので、やはり諦めて、MicroSoftの対応
>  を待ちたいと思います。

Microsoft の対応についてですが、あまり優先度の高い問題として扱っていないようです。

.NET Framrwork 1.1 SP1 リリース前に KB にエントリされているはずですが、.NET Framework 1.1 SP1 ではこの問題は修正されていないそうです。

今後、仮に .NET Framework 1.1 SP2 がリリースされるとしても、それにこの問題の対策が含められるかどうかは現時点では不明です。

優先度を上げたい場合は、例によって

MSDN Product Feedback Center
http://lab.msdn.microsoft.com/productfeedback/

に投稿して Vote を募るのが最善と思います。
■No13850に返信(渋木宏明(ひどり)さんの記事)
返信が遅れ申し訳ございません。

> .NET Framrwork 1.1 SP1 リリース前に KB にエントリされているはずですが、
KBの情報が検索できていません。色々と検索キーを変えて探しているのですが
HITしません。お手数ですが、KBのNoは分からないでしょうか?
⇒不明であればMicroSoftサポート契約がありますので問い合わせをするか
ご提案頂いたPSS、Bug報告をしたいと思います。

最終的にユーザーに納得してもらえればよいと考えていますのでMicroSoftの正式回答
(バグ情報)を添付し説明しようと思っています。(PGを強制終了(ファイルはClose
される)する際にメッセージが表示されることを除けば動作に問題ないと思っています)

宜しくお願い致します。
■No13868に返信(さすらいのスナフキンさんの記事)

本日、正式にMicroSoftに問い合わせを行ないました。
結果はしばらく後になりそうですが、回答が出次第、報告します。

これで一旦、解決としたいと思います。
回答を頂いた皆様、色々とありがとうございました。
解決済み!
> KBの情報が検索できていません。色々と検索キーを変えて探しているのですが
> HITしません。お手数ですが、KBのNoは分からないでしょうか?

すみません、再度確認中ですが、結局 KB には登録されなかったようです。

ただし、Micrsooft の開発の方まで情報は伝わっているので、お急ぎ or 正式回答が必要であれば Microsoft のサポートに連絡してみてください。

私から確約は出来ませんが、明らかにライブラリのバグなので、使用したインシデントは返ってくるはずです。(たまに杜撰な対応をされることがあるので、話しが通じない風なら少し食い下がってみてください)

って、既に連絡されているようですね。。。
余分な日数を使わせてしまって申し訳ないです m(_ _)m

お詫びに。。。というわけでもありませんが、たまに見かける問題なので、Microsoft 社内でこの問題がもっとちゃんと扱われるように、私の方でも努力してみます。(せめて QFE を出すか、できれば次の SP に含まれるといいんですが。。。)
■No13918に返信(渋木宏明(ひどり)さんの記事)
>
> すみません、再度確認中ですが、結局 KB には登録されなかったようです。
>

本日、MicroSoftより正式に回答を頂きました。
回答は。。。。なんと仕様!!と言う事でした。(KBにないはずだ・・・)

以下はMicroSoftの回答(そのまま載せると問題なので要約)です。
------------------------------------------------------------------
[回答]
 ■NET Framework の動作仕様により発生している現象
 ■FileStream クラスや StreamWriter クラスは、Close を行う際に未書き込みのデータを Flush する処理を行っています。
  その際、ディスクに空き容量が無い場合には、Flush によるデータの書き込みを保証できないため、例外が発生いたします。

- 回避策
 ■テンポラリに書いてコピーする
 ■ファイルを消して空き容量を確保する
 ■FileStream クラスの SetLength メソッドを使用し、必要なファイルサイズのファイルをディスク上に前もって確保する
  1. 対象ファイルを作成後、FileStream.SetLength で十分大きなサイズを確保。
  2. ディスク容量がない場合には、例外が発生するので、サイズ 0(またはすでに成功しているサイズ)で再度SetLength
    を実行し、FileStream をクローズ。
  3. SetLength で指定したサイズまでは、ディスク容量が不足することがない為、 FileStream.Write, Flush で書き込みを行う。
  4. ファイルの書き込みが完了した後は、データのサイズ(バイト)を指定して、再度 SetLength を実行し、最後に Closeを実行。
------------------------------------------------------------------
回答の内容(例外が起こる)はそうでしょうが、それが仕様?と思ってしまいます。

VBからコンバートして多少の修正は分かりますが、ここまでの修正をしないといけないと言うことが納得できません。
VBで出来ていたことが.NETで出来なくなることが仕様と言うのでしょうか?と愚痴を言っても始まらない為、
「書く前に空き容量のチェックをする」ことで対応しようと思います。

皆様に協力頂いたのですが、残念な結果となりました。
皆様(特に渋木宏明(ひどり)さん)、色々とありがとうございました。
解決済み!
逆アセンブラで調べたところ、下に示すようなコードでファイルのハンドルが取得できるようです(詳しくは調べていませんが)。内部でVB6Fileクラスというものを使っており、そのm_fileプロパティが開いているファイルのFileStreamであるようです。ただし、このm_fileを閉じれば万事OKかは疑問です。このような危険な方法よりもはじめからFileStreamを使うべきでしょうし、事前に空き容量を調べる方が賢いと思います。

Dim fileNum As Integer = FreeFile()
FileOpen(fileNum, "C:\test.txt", OpenMode.Output)

Dim t1 As Type = GetType(Microsoft.VisualBasic.FileSystem)

'VB6Fileオブジェクトの取得
Dim vb6File As Object = t1.InvokeMember("GetChannelObj", _
Reflection.BindingFlags.InvokeMethod Or _
Reflection.BindingFlags.Static Or _
Reflection.BindingFlags.NonPublic, _
Nothing, _
Nothing, _
New Object() {Reflection.Assembly.GetEntryAssembly(), fileNum})

Dim t2 As Type = vb6File.GetType()

'FileStreamの取得
Dim fs As Object = t2.InvokeMember("m_file", _
Reflection.BindingFlags.GetField Or _
Reflection.BindingFlags.Instance Or _
Reflection.BindingFlags.NonPublic, _
Nothing, _
vb6File, _
Nothing)
Dim fileStream As System.IO.FileStream = _
CType(fs, System.IO.FileStream)

'結果表示
MessageBox.Show(fileStream.Handle.ToString)

FileClose(fileNum)
解決済み!
> 本日、MicroSoftより正式に回答を頂きました。
> 回答は。。。。なんと仕様!!と言う事でした。(KBにないはずだ・・・)

それは問題です。

「仕様だ」とするなら、その挙動を説明した文書がヘルプやKBに存在しないのはおかしいです。

ワークアラウンドまで存在しているなら、なおのこと文書化されていないことに疑問を感じます。

# ということで、手持ちのインシデントで問い合わせを開始しました。
CloseするとFlushが呼び出されるというのが仕様であり、この問題はそれに伴う害ということではないでしょうか。つまり問題なのは、FlushせずにCloseする方法が用意されていないということではないでしょうか(FileStreamの_handleProtectorフィールドのCloseメソッドで閉じる事だけできそうですが、これも当てにならないでしょう)。
> CloseするとFlushが呼び出されるというのが仕様であり、この問題はそれに伴う害ということではないでしょうか。

いや、それがダメなんですよ。

最終的に Close() しない(できない)まま FileStream を放置すると、FileStream のインスタンスがGCによって回収される時にファイナライザから IDisposable.Dispose() が呼び出されます。

FileStream の IDisposable.Dispose() は FileSteream.Close() と同じ実装を呼び出すため、GCスレッドで例外が発生してしまい、アプリケーションがクラッシュします。

なので、「事後処理」の目処はまったくなく、現象を回避するためにはワークアラウンドで示されていたように「ディスクフルを引き起こさない」ようにコードを改変しなくてはなりません。
>>.NET Framrwork 1.1 SP1 リリース前に KB にエントリされているはずですが、
> KBの情報が検索できていません。色々と検索キーを変えて探しているのですが
> HITしません。お手数ですが、KBのNoは分からないでしょうか?

さるスジから情報提供を受けました。

http://support.microsoft.com/?kbid=892544

「StreamWriter のバグ」として記載されていますが、根は件の FileStream のバグと思われます。(異なる可能性も数%くらいはありますが。。。)

この件で問い合わせを行えば、HOTFIX が入手できるのではないかと思います。

# FileStream における現象の問い合わせは継続します。
■No13918に返信(渋木宏明(ひどり)さんの記事)
> すみません、再度確認中ですが、結局 KB には登録されなかったようです。

これは?
http://support.microsoft.com/kb/892544/ja
■No14021に返信(渋木宏明(ひどり)さんの記事)
>
> さるスジから情報提供を受けました。
>
> http://support.microsoft.com/?kbid=892544
>
> 「StreamWriter のバグ」として記載されていますが、根は件の FileStream のバグと思われます。(異なる可能性も数%くらいはありますが。。。)
>
> この件で問い合わせを行えば、HOTFIX が入手できるのではないかと思います。

情報ありがとうございます。
早速、この情報を元に再度MicroSoftサポートに問い合わせを再開します。
私も問い合わせしてます。
もしもなら大同団結で(^^;;
2005/12/03(Sat) 04:40:23 編集(管理者)

>>CloseするとFlushが呼び出されるというのが仕様であり、この問題はそれに伴う害ということではないでしょうか。
>
> いや、それがダメなんですよ。
>
> 最終的に Close() しない(できない)まま FileStream を放置すると、FileStream のインスタンスがGCによって回収される時にファイナライザから IDisposable.Dispose() が呼び出されます。
>
> FileStream の IDisposable.Dispose() は FileSteream.Close() と同じ実装を呼び出すため、GCスレッドで例外が発生してしまい、アプリケーションがクラッシュします。
>
> なので、「事後処理」の目処はまったくなく、現象を回避するためにはワークアラウンドで示されていたように「ディスクフルを引き起こさない」ようにコードを改変しなくてはなりません。

よく読んでいただければ分かっていただけると思いますが(書き方が悪かったかもしれません)、「そうできる」ということではなく、「そうできたらいいのに」ということです。Closeを呼び出せば当然Disposeが呼び出されるはずですので、CloseでFlushが呼び出されないということは、Disposeでも呼び出されないということです。つまらないことを書いて、混乱させてしまったようで、失礼しました。

ちなみに、_handleProtector.IsClosedプロパティをtrueにすればDisposeが呼び出されてもFlushされず、前回紹介した_handleProtector.CloseでIsClosedはtrueとなるようです(実際に確認はしていません)。
> >>CloseするとFlushが呼び出されるというのが仕様であり、この問題はそれに伴う害ということではないでしょうか。
>>
>>いや、それがダメなんですよ。
(略)
> よく読んでいただければ分かっていただけると思いますが(書き方が悪かったか

「伴う害が問題だ」ってことですね、すみません (^^;
害自体についての言及が無かったもんで。。。

> ちなみに、_handleProtector.IsClosedプロパティをtrueにすればDisposeが呼び

Reflection を使って。。。っていうのは以前なら「あり」の解だったんですが、.NET Framework 2.0 の正式リリースが間近な今となっては、Framework のバージョンを確認してから対応コードを走らせるようにしないとNGですね。
> 私も問い合わせしてます。
> もしもなら大同団結で(^^;;

よろしくお願いします m(_ _)m

「仕様」なら、StreamWriter だけでなく関連するすべてのクラスについてドキュメントして欲しいし、バグなら HOTSFIX を入手しやすくして欲しいですね。
>>この件で問い合わせを行えば、HOTFIX が入手できるのではないかと思います。
>
> 情報ありがとうございます。
> 早速、この情報を元に再度MicroSoftサポートに問い合わせを再開します。

すみません、サポートの手続きに勘違いがありました。

http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=26450&forum=7&start=8&12

で似たような議論が行われているので、参照してみてください。

現状、HOTFIX の入手自体は無料ですが、PSS の「サポートを受けるため」に費用が発生してしまうそうです。(このプロセスは改良される予定があるそうです)
2005/12/07(Wed) 12:32:14 編集(投稿者)
2005/12/07(Wed) 12:28:30 編集(投稿者)

■No14037に返信(さすらいのスナフキンさんの記事)
>>
>>この件で問い合わせを行えば、HOTFIX が入手できるのではないかと思います。
>
> 情報ありがとうございます。
> 早速、この情報を元に再度MicroSoftサポートに問い合わせを再開します。

本日、MicroSoftより回答を頂きました。以下回答です。
《ディスクが一杯の場合》
(次のサイトより引用:http://support.microsoft.com/kb/892544/ja)
 @Microsoft .NET Framework 1.1 を実行しているコンピュータで、ディスクのファイルにアクセスするために、
StreamWriter.Flush()メソッドまたは StreamWriter.Close()メソッドを使用すると、十分な領域がそれにある、次の例外エラー メッセージを表示します。
  System.IO.IOException:十分な領域はディスクにありません。
 Aディスクの領域を解放し、そして再度ファイルにアクセスしようと
次にした後次の例外エラー メッセージを表示します。
  System.IO.IOException:別のプロセスで使用されているため、「C:\outputTest.txt」ファイルにプロセスはアクセスできません。
//
 要約すると@は【言語仕様】、Aは【不具合:修正プログラムを入手対象】と言うことらしいです。
簡単に言うとCloseでエラーとなるのは仕様、Closeできないのは不具合と言うことになります。
一応納得できる回答だと思いますのでMicroSoftへの問い合わせは終了したいと思います。

実際はこのやり取りの間にディスクの空き容量のチェックを行なう関数を追加し対応したので現在は問題がない状態にはなっていますが、
今後の事を考え、[修正プログラムを入手]するようにしました。
皆様の協力の元、ここまでたどりつきました。ありがとうございます。

ただ、1回目に以下のような質問をしたら、Closeでエラーが発生するのは仕様なので費用が発生すると言われました。
最終的な問題(Closeできない)は不具合なので無償と返していますが、質問の仕方次第で対応が変わるので皆様もご注意を。。

 「 Visual Basic .Net (Framework 1.1) にて FileClose を行う際に、
  空き容量が無い場合にファイルを Coseできない。(エラーが発生する。)
’System.IO.IOException'のハンドルされていない例外が発生しました。
追加情報:ディスクに十分な空き領域がありません。」
解決済み!
私の方からも報告
たしかにCloseでエラーが発生するのは仕様でいいと思いますが、Disposeでエラーが発生するために現状はいかんともしがたいです。

KB892544のQFEでは事象は解決できなかったので、このままインシデント消費覚悟で突っ込みます。

1.1, 2.0ともに解消していないので対応してもらいたいですね。
■No14107に返信(中博俊さんの記事)
> 1.1, 2.0ともに解消していないので対応してもらいたいですね。

追伸ですが、MicroSoftの回答では
 - サポート技術情報 892544 について
1. 「System.IO.IOException:十分な領域はディスクにありません。」
2. 「System.IO.IOException:別のプロセスで使用されているため、「C:\outputTest.txt」ファイルにプロセスはアクセスできません。」

上記 1 のメッセージは、仕様上の動作により発生しているエラー。
上記 2 のメッセージは、障害により発生しているエラーであり、サポート技術情報で公開。

 - .NET Framework 2.0 での対応について
   サポート技術情報 892544 の障害は.NET Framework 2.0 においては発生いたしません。

とのことです。
私の確認コードがおかしかったので再度調査しました m(_ _)m

結果的には適切なコードを作成してやればアプリケーションクラッシュ(ファイルを閉じれない)はQFEで回避可能でした。

http://naka.wankuma.com/site/column/dotnet/00028.htm

もし良かったら見てやってください。

#Disposeではエラーをはいてほしくはないんだけどねぇ・・・・

DOBON.NET | プログラミング道 | プログラミング掲示板