質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

ファイルI/O

ファイルI/Oは、コンピューターにおけるファイルの入出力です。これは生成/削除やファイルを読み込んだり、出力をファイルに書き込むようなディレクトリやファイルの運用を含みます。

Q&A

解決済

3回答

16124閲覧

usingでStreamWriterのインスタンスを破棄しているはずなのに、ファイルが別のプロセスで使用されていて開けない

peimish

総合スコア17

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

ファイルI/O

ファイルI/Oは、コンピューターにおけるファイルの入出力です。これは生成/削除やファイルを読み込んだり、出力をファイルに書き込むようなディレクトリやファイルの運用を含みます。

0グッド

0クリップ

投稿2020/02/07 11:38

前提・実現したいこと

現在同一のイベント内で一旦平文のままCSV出力し、出力処理の後にさらにそのCSVファイルを読み込んで内容を暗号化する機能を実装しています。
CSVを出力する処理とファイルの内容を暗号化する処理を別々で動かすと正常に動作するのですが、それぞれの処理をまとめて関数化するところで苦戦しています。
CSVの出力にはCsvHelperを使っています。

発生している問題・エラーメッセージ

"別のプロセスで使用されているため、プロセスはファイル 'C:\test\sample.csv' にアクセスできません。"

FileEncryptの一番最後のusingの箇所で上記のエラーが発生します。

c#

1using (FileStream fs = new FileStream(FilePath, FileMode.Open, FileAccess.Read))

該当のソースコード

c#

1{ 2 string Path = @"C:\test\sample.csv"; 3 4 //CSV出力 5 using (var streamWriter = new StreamWriter(Path , true, Encoding.GetEncoding("shift_jis"))) 6 using (var csvWriter = new CsvWriter(streamWriter, CultureInfo.InvariantCulture)) 7 { 8 csvWriter.Configuration.HasHeaderRecord = true; 9 csvWriter.Configuration.RegisterClassMap<EntityMappper>(); 10 csvWriter.WriteRecords(EntityList); 11 } 12 //暗号化 13 FileEncrypt(Path); 14} 15 16private void FileEncrypt(string FilePath) 17{ 18 byte[] buffer = new byte[4096]; 19 20 //Output file path. 21 string OutFilePath = Path.Combine(Path.GetDirectoryName(FilePath), Path.GetFileNameWithoutExtension(FilePath)) + ".enc"; 22 23 using (FileStream outfs = new FileStream(OutFilePath, FileMode.Create, FileAccess.Write)) 24 { 25 using (AesManaged aes = new AesManaged()) 26 { 27 28 ... 29 30 //Encryption interface. 31 ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV); 32 33 using (CryptoStream cse = new CryptoStream(outfs, encryptor, CryptoStreamMode.Write)) 34 { 35 outfs.Write(salt, 0, 16); // salt をファイル先頭に埋め込む 36 outfs.Write(aes.IV, 0, 16); // 次にIVもファイルに埋め込む 37 using (DeflateStream ds = new DeflateStream(cse, CompressionMode.Compress)) //圧縮 38 { 39 using (FileStream fs = new FileStream(FilePath, FileMode.Open, FileAccess.Read)) 40 { 41 while (len = (fs.Read(buffer, 0, 4096)) > 0) 42 { 43 ds.Write(buffer, 0, len); 44 } 45 } 46 } 47 48 } 49 50 } 51 } 52}

試したこと

エラーの発生したusingにて、

  • FileAccess.ReadではなくFileAccess.ReadWriteを渡す
using (FileStream fs = new FileStream(FilePath, FileMode.Open, FileAccess.ReadWrite))

→ 上記と同様のエラー

  • 引数にFileShare.ReadWriteを追加
using (FileStream fs = new FileStream(FilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))

→ エラーは発生しなくなるが、その後の処理でlenが0

using (FileStream fs = new FileStream(FilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)) { while (len = (fs.Read(buffer, 0, 4096)) > 0) { ds.Write(buffer, 0, len); } }

Csv書き込みをしたusingの外で暗号化の処理をしているので、usingを出た時点でファイルは一度閉じられて再度暗号化のためにファイルを開くという想定なのですが、どうもそうではないらしくどうしたらよいのかかなり悩んでおります。
プロセスやファイルの読み書きについての理解が浅く恐縮ですが、よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/02/08 04:33 編集

問題を再現できる必要最小限のところまでどんどんコードを削っていくなどして、質問者さんの方で問題切り分けできませんか? その過程で問題の原因が見つかって自己解決できることが多いです。 自己解決できなくても、コピペするだけで回答者の方でも問題を再現できる最小のコードをアップしてもらえれば、解決策が得られるかもしれませんし。
peimish

2020/02/08 18:00

親切にアドバイス下さりありがとうございます! 問題の処理とは関係ないと思われる部分は削って問題部分が把握してもらいやすくしたつもりでしたが、 削る作業をするならついでに動く最小のコードに加工するくらいの気遣い必要でした... 次回からは動くコードを掲載するように気をつけます!
退会済みユーザー

退会済みユーザー

2020/02/09 00:13

削るという作業はやってみたのですか? であればその結果はどうだったのでしょう?
dodox86

2020/02/09 10:35

>質問者さん SurferOnWwwさんが回答で示された同等のコードで問題が再現しないということは、環境によるものかもしれないので、質問者さんのWindowsバージョンや.NET Frameworkのバージョンも示された方が良いと思います。(意外と.NET Frameworkの既知、あるいは未知の問題だったり。<考えづらいので、これを疑うのは最後です)
guest

回答3

0

閉じたつもりが実は開いたままになっているとか、アップされたコードでは閉じたが別のところでまた開いたとか、何かの勘違い・思い違いのような気がします。

基本的な部分は同じコードで、自分の環境(Windows 10 Pro 64-bit, Visual Studio 2019, .NET 4.7.2)で試してみましたが、何も問題はなかったです。

イメージ説明

コードは以下の通りです。コンソールアプリのプロジェクトを作って、CsvHelper を NuGet でインストールし、コードをコピペして path を適当なパスに書き換えれば動くはずです。

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; using System.Runtime.Serialization.Formatters.Binary; using System.Threading; using System.Xml.Linq; using CsvHelper; using CsvHelper.Configuration; using System.Globalization; using System.Security.Cryptography; using System.IO.Compression; namespace ConsoleApp1 { class Program { static void Main(string[] args) { var entityList = new List<Product> { new Product { ProductID = 1, ProductName = "Apple", UnitPrice = 100m, Discontinued = false }, new Product { ProductID = 2, ProductName = "Orange", UnitPrice = 200m, Discontinued = true }, new Product { ProductID = 3, ProductName = "Banana", UnitPrice = 300m, Discontinued = false } }; string path = @"C:\Users\surfe\Documents\Visual Studio 2019\CsProject\ConsoleApp1\ConsoleApp1\sample.csv"; using (var streamWriter = new StreamWriter(path, true, Encoding.GetEncoding("shift_jis"))) using (var csvWriter = new CsvWriter(streamWriter, CultureInfo.InvariantCulture)) { csvWriter.Configuration.HasHeaderRecord = true; csvWriter.Configuration.RegisterClassMap<EntitytMapper>(); csvWriter.WriteRecords(entityList); } FileEncrypt(path); } private static void FileEncrypt(string filePath) { string outFilePath = Path.Combine(Path.GetDirectoryName(filePath), Path.GetFileNameWithoutExtension(filePath)) + ".enc"; using (FileStream outfs = new FileStream(outFilePath, FileMode.Create, FileAccess.Write)) { using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) { string original; // 元の文字列 byte[] cipherText; // 暗号化後のバイト列 string roundTrip; // 復号化後の文字列 using (StreamReader sr = new StreamReader(fs)) { original = sr.ReadToEnd(); } using (AesManaged aes = new AesManaged()) { ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV); using (MemoryStream ms = new MemoryStream()) { using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write)) { using (DeflateStream ds = new DeflateStream(cs, CompressionMode.Compress)) { using (StreamWriter sw = new StreamWriter(ds)) { sw.Write(original); } } } using (BinaryWriter bw = new BinaryWriter(outfs)) { cipherText = ms.ToArray(); bw.Write(cipherText); } } ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV); using (MemoryStream ms = new MemoryStream(cipherText)) { using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read)) { using (DeflateStream ds = new DeflateStream(cs, CompressionMode.Decompress)) { using (StreamReader sr = new StreamReader(ds)) { roundTrip = sr.ReadToEnd(); } } } } } Console.WriteLine(original); Console.WriteLine(roundTrip); } } } } public class Product { public int ProductID { get; set; } public string ProductName { set; get; } public decimal UnitPrice { set; get; } public bool Discontinued { set; get; } } public class EntitytMapper : ClassMap<Product> { private EntitytMapper() { Map(c => c.ProductID).Index(0); Map(c => c.ProductName).Index(1); Map(c => c.UnitPrice).Index(2); Map(c => c.Discontinued).Index(3); } } }

投稿2020/02/09 05:21

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

CsvWriterで書き込むところでもFileStreamを使って、FileStreamを閉じる前に Flush(true) を呼んでみてください。
ストレージのキャッシュの問題ならそれで解決するかもしれません。

それでだめなら、文字通りファイルがセキュリティソフトなど別のプロセスで使用されていて開けない可能性もあります。

** [解説] **
上でストレージのキャッシュとぼんやりとした表現を書きましたが、幾通りもあります。
1.SSDやHDDなどに搭載されているキャッシュメモリ
2.ディスクコントローラなどが搭載または占有しているキャッシュメモリ
3.OSに搭載されている遅延書き込み機能(ディスクキャッシュ)
4.Framework が間接的に利用するAPIの書き込みバッファ
5.StreamWriter や CsvWriter が持っている(かもしれない)書き込みバッファ

このうち1と2は無関係です。
5は(4も多分) using を抜けた時点でFlushされます。
消去法で3が怪しくなります。
これをFlushする機能を持っているのが FileStream なのです。

投稿2020/02/08 02:27

編集2020/02/08 10:01
hihijiji

総合スコア4150

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

peimish

2020/02/08 17:49

解説つけていただきありがとうございます! 遅延書き込み機能があるんですね...挙動的にまさにその遅延機能で引っかかっていそうです。 理解を深めるためにこちらの方法も確認させていただきます。 ありがとうございました!
dodox86

2020/02/09 10:22

私は質問者さんのコードは目視でしか確認していませんが、問題は見受けられませんでした。(そのため回答を見送りました) > 文字通りファイルがセキュリティソフトなど別のプロセスで使用されていて開けない可能性もあります。 開けない理由の候補も含め、hihijijiさんのこの指摘を支持します。順当にファイルが閉じられるならタスクマネージャーでカーネルハンドル数が減ることが確認できるでしょうし、sysinternalsのProcessMonitorで他ソフトがファイルを開くことを確認できるかもしれません。
guest

0

ベストアンサー

削除リクエストは通らないらしいので、回答を準備しました。
ファイルを閉じずに、そのまま再利用する方法です。
実際の動作を見ていないのと、質問されている内容に対する回答ではないので、ご了承ください。

問題を解決するかもしれない回答

{ string path = @"C:\test\sample.csv"; //Output file path. string OutFilePath = Path.Combine(Path.GetDirectoryName(path), Path.GetFileNameWithoutExtension(path)) + ".enc"; //CSV出力 using (var fileStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite)) using (var streamWriter = new StreamWriter(fileStream, Encoding.GetEncoding("shift_jis"))) using (var csvWriter = new CsvWriter(streamWriter, CultureInfo.InvariantCulture)) { csvWriter.Configuration.HasHeaderRecord = true; csvWriter.Configuration.RegisterClassMap<EntityMappper>(); csvWriter.WriteRecords(EntityList); // バッファ上のデータを書き出して、ポインタを先頭へ fileStream.Flush(); fileStream.Seek(0, SeekOrigin.Begin); // 暗号化 FileEncrypt(fileStream, OutFilePath); } } private void FileEncrypt(FileStream input, string OutFilePath) { byte[] buffer = new byte[4096]; var salt = ASCIIEncoding.ASCII.GetBytes("salt1234"); using (AesManaged aes = new AesManaged()) { //... //Encryption interface. ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV); // If file is existed file, throw using (var outfs = new FileStream(OutFilePath, FileMode.Create, FileAccess.Write)) using (CryptoStream cse = new CryptoStream(outfs, encryptor, CryptoStreamMode.Write)) using (DeflateStream ds = new DeflateStream(cse, CompressionMode.Compress)) //圧縮 { outfs.Write(salt, 0, 16); // salt をファイル先頭に埋め込む outfs.Write(aes.IV, 0, 16); // 次にIVもファイルに埋め込む int len; while ( (len = input.Read(buffer, 0, 4096)) > 0) { ds.Write(buffer, 0, len); } } } }

誤った回答

Pathを使用中(StreamWriterのusingブロック中)にFileStream(FileEncrypt)でさらに開こうとしています。
usingブロックの外で呼び出す(下記例)か、インターフェイスを変更し、Streamを渡すように(未掲示)してみてください。

//CSV出力 using (var streamWriter = new StreamWriter(Path , true, Encoding.GetEncoding("shift_jis"))) using (var csvWriter = new CsvWriter(streamWriter, CultureInfo.InvariantCulture)) { csvWriter.Configuration.HasHeaderRecord = true; csvWriter.Configuration.RegisterClassMap<EntityMappper>(); csvWriter.WriteRecords(EntityList); } } //暗号化 FileEncrypt(Path);

投稿2020/02/08 08:58

編集2020/02/08 09:38
testset

総合スコア221

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

testset

2020/02/08 09:01

usingブロックの外側でした。誤った回答だったので、削除リクエストしました。
Zuishin

2020/02/08 09:05

https://teratail.com/help/delete-policy > 削除リクエスト機能では、主に以下の理由での削除リクエストを許可しておりません。 > 内容の誤った回答をした > → 編集より修正するか、誤っている旨を追記してください
peimish

2020/02/08 17:44

詳しく回答いただきありがとうございます! 今コードを動かせる環境にないためすぐに確認はできませんが、解消するためのポイントが得られました!
退会済みユーザー

退会済みユーザー

2020/02/08 23:12

質問者さん> まだ未確認なのですよね? もしそうであればベストアンサーを付けるのは、確認してからにしていただけませんか。 Teratail は質問者さん専用の QA スレッドではなくて、技術者同士の情報交換の場ということです。きちんと確認のとれた情報を提供する意味でそうしていただけたらと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問