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

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

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

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

Q&A

解決済

1回答

1465閲覧

アンマネージドDLLのリソース解放のタイミングを知りたい

lain

総合スコア161

C#

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

0グッド

1クリップ

投稿2021/09/16 02:32

前提・実現したいこと

キャプチャボードで画像をキャプチャし、それをPictureBoxで表示しようとしています。

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

下記コードのCreateImage内のFileStreamのインスタンス作成のところでエラーが発生します。

System.IO.IOException HResult=0x80070020 Message=別のプロセスで使用されているため、プロセスはファイル 'D:\temp.bmp' にアクセスできません。 Source=mscorlib

該当のソースコード

C#

1 2private void Still() 3{ 4 StillSave(_tempBmpFileName, ImageFileFormat.Bitmap, 100); 5 6 /// ビットマップが完成するのを待つ 7 while (!System.IO.File.Exists(_tempBmpFileName)) 8 { 9 System.Threading.Thread.Sleep(100); 10 } 11 _pictureBox.Image = CreateImage(_tempBmpFileName); 12 } 13} 14 15private System.Drawing.Image CreateImage(string fileName) 16{ 17 System.Drawing.Image img = null; 18 using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read)) //ここで例外発生 19 { 20 img = System.Drawing.Image.FromStream(fs); 21 } 22 return img; 23} 24 25 26private void StillSave(string fullFileName, ImageFileFormat imageFormat, int jpegQuality) 27{ 28 /* 29 このメソッド内でキャプチャボードのAPI(アンマネージドDLL)の 30 画像キャプチャをしてBMPで保存する関数を呼び出します。 31 その関数の戻り値が成功の場合はそのまま処理が続き、 32 エラーの場合は例外を出すようにしています。 33 */ 34} 35 36

試したこと

BMPファイルが作成されるのを待ち、Sleepで暫く待つようにしていて、特に問題は起こっていなかったのですが、
最近導入したPCで上記エラーが起こるようになりました。
再現頻度がPCの電源投入後 初回に上記コードのところを通るときに起きやすいです(20回に1回くらい)
PCの電源を落とさずにソフトの再起動ではほぼ大丈夫なようです。

Sleepの時間を短くすれは頻繁に起こるので、長くすればとりあえずはしのげるとは思うのですが、根本的な解決ではないように思っています。

マネージドDLLでDisposeが実装されているのならば、usingで囲めばいいかと思うのですが、
今回のようなアンマネージドDLLの場合はどうやってリソースが解放されるのを知るのかがわかりません。

Sleep以外だと、例外を捕捉して数回リトライをする。くらいしか思いつきません。

他に方法があるようならご教示お願いいたします。

補足情報

Windows 10 64bit
Visual Studio 2019 Professional
C#

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

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

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

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

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

退会済みユーザー

退会済みユーザー

2021/09/16 02:52 編集

DLLと呼び出し側で、同期オブジェクト(MutexやSemaphore)なり使ってファイルIOの同期を行うか、質問文に書いているようにリトライをするしかないと思いますが。
dodox86

2021/09/16 02:55

たぶん、前提として誤解していて、 > /// ビットマップが完成するのを待つ > while (!System.IO.File.Exists(_tempBmpFileName)) 別のプロセスがファイルを開き、キャプチャー画像を書き込んでいるときには既にファイルは存在しています。開いて、必要な書き込みを随時行っています。 完成を待たずして質問者さんのプログラムはBMPファイルを読みにいっていることになります。
fana

2021/09/16 03:05

> 画像キャプチャをしてBMPで保存する関数 は,保存し終えてからreturnするんじゃなくて, 保存し終えるよりも前にreturnするってことですか? (それとも,前者なのに何故かファイルをロックし続ける期間がある?)
退会済みユーザー

退会済みユーザー

2021/09/16 03:09 編集

以前、Dispose したけどどこか別のところで再び使っていたというオチのスレッドがあったのですが、そういうことはないですか?
lain

2021/09/16 04:07

radian様 リトライするのが良さそうなのですね。 コードを書いてみて試そうと思います。 Semaphoreは使用したことがないので調べてみます。
lain

2021/09/16 04:17

dodox86様 書き込みがされている時にアクセスしてるのだと思い、 ファイルの存在を確認した後に、書き込みが終わるであろう時間をSleepするように書いていました。 開発機・テスト機では動いていたので、この方法でおこなっていましいた。 今回新しいPCで引っかかってしまい、もっとちゃんとした方法でこの問題を回避しようと思い質問させていただいた次第です。
lain

2021/09/16 05:25

fana様 保存をし終わる前にreturnされています。 リファレンスには書いてなくて確証はないのですが、非同期で画像ファイルを作成しているようです。
lain

2021/09/16 05:32

SurferOnWww様 今回は他の場所で使っていることは無いです。 Sleep時間を長くすれば、いまのところ例外は出なくなります。 関数が放してくれてないがために起こっているように思われます。
dodox86

2021/09/16 09:50

> PCの電源を落とさずにソフトの再起動ではほぼ大丈夫なようです。 外部の(キャプチャーの?)プログラムが開いているからエラーになる感でコメントしましたが、実は自プログラムが開いたままになっているだけだったりして。Process Explorerで誰が(どのプロセスが)本当にファイルを開いたままであるのか、確認した方が良いかもしれません。 https://docs.microsoft.com/en-us/sysinternals/downloads/process-explorer タイミングの問題もありそうなので、モニターも難しいかもしれませんが。
退会済みユーザー

退会済みユーザー

2021/09/16 11:25 編集

キャプチャーだけが目的であるならば、もう少しましなライブラリを探すという選択肢はないですかね?(OpenCVSharp辺りで出来なかったかな) キャプチャー以外の処理もやってるのであれば、ダメかもしれませんけど。
guest

回答1

0

ベストアンサー

(「Sleepで対処するのが良いかどうか」という話は別として…)

仮に,「ファイルが作られた時点から,ある程度待って(:Sleepを入れて)やれば,CreateImageの処理を問題無く実行できる」のだとしたら,

/// ビットマップが完成するのを待つ while (!System.IO.File.Exists(_tempBmpFileName)) { System.Threading.Thread.Sleep(100); } _pictureBox.Image = CreateImage(_tempBmpFileName);

このコード記述では問題が起きるか否かは「その時次第」みたいな形になっています.

(とりあえず while のループが1回以上は回るのだとして)
ファイルが生成されたタイミングが,直前の 100[ms]のSleep期間のうちのどの時点だったのか次第によって,
「ファイルが作られてから,CreateImageの処理を始めるまで」の期間が変わってしまうからです.

20回に1回くらい

という話の原因かもしれません.

/// ビットマップが完成するのを待つ while (!System.IO.File.Exists(_tempBmpFileName)) { System.Threading.Thread.Sleep(100); //これは単なる「状態ポーリング間隔」である } System.Threading.Thread.Sleep(100); //←ウェイト用のSleepはここに追加する _pictureBox.Image = CreateImage(_tempBmpFileName);

とかしても良いならば,とりあえず試してみては…?

投稿2021/09/16 04:42

編集2021/09/16 04:46
fana

総合スコア11708

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

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

退会済みユーザー

退会済みユーザー

2021/09/16 05:08 編集

質問文のキャプチャ処理がどうなっているかは判りませんが、非同期で動作していて、いつファイルが書き終わるか判らないような類のものであるならば、排他ロックされている可能性があるので、いずれにせよ例外対応は入れるべきだと思います。
fana

2021/09/16 05:37 編集

その点はまことにおっしゃる通り. (しかし非同期処理を開始して返るようなAPIであれば待つための手段も提供されていそうなものですが…) 質問文の末尾付近の記述より,【例外補足して対応すればよいことは質問者にとっては既知の事柄であり,それ以外の話を求めている】ものと読みましたので, この回答は > (「Sleepで対処するのが良いかどうか」という話は別として…) と前置きして,とりあえず Sleep の話(1/20くらいで失敗する原因の話)だけに焦点を当てたものとなっています. (Sleepで対処する,という方針をとるにしても,実装間違ってないか?っていう)
lain

2021/09/16 08:28

ご回答ありがとうございます。 画像ファイルの作成は非同期でおこなわれているようです。 リファレンスを見ているのですが、非同期処理の終わりを教えてくれるものはなさそうです。 Sleep時間を長くすることで、問題機でも現象は今のところ起こらなくなっていますが、 他の要因(他のソフトで重い処理をしている・大量のファイルのコピーをしている等)で、 この長くした時間でもエラーが起こりそうなので、Sleepだけでは駄目な気がしています。 そこで例外を捕捉してリトライという方法を思いついたのですが、自分が知らないような方法があるかも? と思い質問させていただいた次第です。 例外捕捉の方法が悪くないよ。ということであれば、その方向で実装していこうかと思っています。
lain

2021/09/21 03:07

返答が遅くなってしまいました。 例外を捕捉し、複数回リトライをおこなう。という方法で書き直しました。 現在、繰り返しテストをおこなっていて、うまく動いてくれています。 ご回答いただいた皆様がたありがとうございました。
fana

2021/09/21 03:11

その場合,この回答はその話を含んでいないので, 自己解決な形をとると良いのでは.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.46%

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

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

質問する

関連した質問