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

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

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

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

VB.NET

Microsoft Visual Basic .NETのことで、Microsoft Visual Basic(VB6)の後継。 .NET環境向けのプログラムを開発することができます。 現在のVB.NETでは、.NET Frameworkを利用して開発を行うことが可能です。

Q&A

解決済

4回答

4943閲覧

Imageの保存が出来ない

heart_crimson

総合スコア15

C#

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

VB.NET

Microsoft Visual Basic .NETのことで、Microsoft Visual Basic(VB6)の後継。 .NET環境向けのプログラムを開発することができます。 現在のVB.NETでは、.NET Frameworkを利用して開発を行うことが可能です。

0グッド

0クリップ

投稿2019/01/21 05:14

編集2019/01/22 09:51

※当方C#も読めるというのと、C#の方が人口が多いだろうという理由でタグをつけました。
C#での回答も歓迎しております。

前提・実現したいこと

PCに接続したカメラで画像を撮影、保存するプログラムを作成しています。
ですが、たまに例外が出て保存ができない時があります。
保存できない条件が判明していないため、「こういった場合だと保存出来ないことがある」等の
アドバイスがございましたら、ご教授いただければ幸いです。

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

GDI+ で一般的なエラーが発生しました 場所 System.Drawing.Image.Save(String filename, ImageCodecInfo encoder, EncoderParameters encoderParams) 場所 System.Drawing.Image.Save(String filename, ImageFormat format) 場所 ***.SaveShotImage() 場所 ***.vb:行 664

該当のソースコード

コード一部抜粋(保存先は本来コンフィグファイルで設定していますが、今は固定で入れています)

VB

1 Private Sub SaveShotImage() 2 Dim saveDir As String 3 Dim savePath As String 4 For Each data As DataDto In DataDtoList 5 If data.SnapShotImage Is Nothing Then Continue For 6 7 Try 8 saveDir = System.IO.Path.Combine("C:\Users\MYNAME\Desktop", Strings.Format(Now, "yyyyMMdd")) 9 If Not System.IO.Directory.Exists(saveDir) Then System.IO.Directory.CreateDirectory(saveDir) 10 11 Do 12 savePath = System.IO.Path.Combine(saveDir, Strings.Format(Now, "yyMMddHHmmss") & ".jpg") 13 If Not System.IO.File.Exists(savePath) Then Exit Do 14 Loop 15 16 data.SnapShotImage.Save(savePath, Imaging.ImageFormat.Jpeg) 'ここで落ちる 17 Catch ex As Exception 18 Throw New Exception("画像保存に失敗しました。" & ex.Message, ex) 19 End Try 20 21 data.ImageFilePath = savePath 22 Next 23 End Sub

DataDto.vb

VB

1Namespace Test 2 3 Public Class DataDto 4 5#Region "プロパティ" 6 7 ''' <summary>画像ファイルパス</summary> 8 Public Property ImageFilePath As String 9 10 ''' <summary>画像格納用(スナップショット格納用)</summary> 11 Public Property SnapShotImage As System.Drawing.Image = Nothing 12 13#End Region 14 15#Region "メソッド" 16 17 ''' <summary> 18 ''' データをコピーする 19 ''' </summary> 20 ''' <param name="target">コピー先対象</param> 21 ''' <remarks></remarks> 22 Public Sub CopyData(ByRef target As DataDto) 23 target.ImageFilePath = ImageFilePath 24 target.SnapShotImage = SnapShotImage?.Clone 25 End Sub 26 27 ''' <summary> 28 ''' データをクリアにする 29 ''' </summary> 30 ''' <remarks></remarks> 31 Public Sub Clear() 32 ImageFilePath = Nothing 33 SnapShotImage = Nothing 34 End Sub 35 36 ''' <summary> 37 ''' 同一データの作成(別インスタンス) 38 ''' </summary> 39 ''' <remarks></remarks> 40 Public Function Clone() As DataDto 41 Dim dto As New DataDto With { 42 .ImageFilePath = ImageFilePath, 43 .SnapShotImage = SnapShotImage?.Clone 44 } 45 46 Return dto 47 End Function 48 49#End Region 50 51#Region "コンストラクタ" 52 53 Public Sub New() 54 Me.Clear() 55 End Sub 56 57#End Region 58 59 End Class 60 61End Namespace

他にこういった個所のコードを開示してほしい等ありましたら随時開示いたします。

試したこと

書き込み権限が無い→保存できる時とできない時があるので権限が無いわけではなさそうです。
imageインスタンスのデータがおかしい→保存できる時とできない時でのデータに差はなさそうです。

補足情報(FW/ツールのバージョンなど)

VB 2012
.NET Framework 4

どうぞよろしくお願いいたします。

追記1

カメラの実装部分です。

VB

1Imports System.Drawing 2Imports System.Runtime.InteropServices 3 4#Region "Private変数" 5 6 Private mGrp As IGraphBuilder 'グラフ 7 Private mFlt As IBaseFilter 'キャプチャフィルタ 8 Private mPin As IPin 'キャプチャフィルタの出力ピン 9 Private mSmp As ISampleGrabber 'サンプルグラバフィルタ 10 11 Private mSize As Size '映像サイズ 12 Private mBuff As IntPtr 'ビットマップ取得用バッファ 13 14 Private mFmt As PIXELFORMATYPE 'ピクセル形式 15 16 Private mVisible As Boolean 'カメラ映像を表示するか? 17 18#End Region 19 20'----一部省略---- 21 22 Public Function snapShot() As Bitmap 23 If mGrp Is Nothing Or mSmp Is Nothing Then Throw New Exception("キャプチャーが取得できません。") 24 25 'イメージの取得 26 Try 27 '画像データサイズ取得 28 Dim bmpsz As Integer = 0 29 mSmp.GetCurrentBuffer(bmpsz, IntPtr.Zero) 30 31 '画像取得領域確保 32 Dim bmpptr As IntPtr = Marshal.AllocHGlobal(bmpsz) 33 34 'イメージ取得 35 mSmp.GetCurrentBuffer(bmpsz, bmpptr) 36 37 '画像サイズを取得 38 Dim vsz As Size 39 vsz = GetVideoSize(mGrp) 40 '描画 41 Dim bmpdata As Bitmap 42 bmpdata = New Bitmap(vsz.Width, vsz.Height, vsz.Width * 3, Imaging.PixelFormat.Format24bppRgb, bmpptr) 43 bmpdata.RotateFlip(RotateFlipType.RotateNoneFlipY) '上下反転させる 44 45 snapShot = bmpdata 46 Catch ex As Exception 47 Throw New Exception("スナップショットに失敗しました。" & " : " & ex.Message, ex) 48 End Try 49 End Function

追記2

色々調べてIDisposable実装してみました……。
これでうまく動くのが一番理想ですがまだ動きません……。

DataDto.vb

VB

1 Public Class DataDto 2 Implements IDisposable 3 4#Region "プロパティ" 5 6 ''' <summary>画像ファイルパス</summary> 7 Public Property ImageFilePath As String 8 9 ''' <summary>画像格納用(スナップショット格納用)</summary> 10 Public Property SnapShotImage As System.Drawing.Image = Nothing 11 12#End Region 13 14#Region "メソッド" 15 16 ''' <summary> 17 ''' データをコピーする 18 ''' </summary> 19 ''' <param name="target">コピー先対象</param> 20 ''' <remarks></remarks> 21 Public Sub CopyData(ByRef target As DataDto) 22 target.ImageFilePath = ImageFilePath 23 target.SnapShotImage = SnapShotImage?.Clone 24 End Sub 25 26 ''' <summary> 27 ''' データをクリアにする 28 ''' </summary> 29 ''' <remarks></remarks> 30 Public Sub Clear() 31 ImageFilePath = Nothing 32 SnapShotImage = Nothing 33 End Sub 34 35 ''' <summary> 36 ''' 同一データの作成(別インスタンス) 37 ''' </summary> 38 ''' <remarks></remarks> 39 Public Function Clone() As DataDto 40 Dim dto As New DataDto With { 41 .ImageFilePath = ImageFilePath, 42 .SnapShotImage = SnapShotImage?.Clone 43 } 44 45 Return dto 46 End Function 47 48#End Region 49 50#Region "コンストラクタ" 51 52 Public Sub New() 53 Me.Clear() 54 End Sub 55 56#End Region 57 58#Region "IDisposable Support" 59 Private disposedValue As Boolean ' 重複する呼び出しを検出するには 60 61 ' IDisposable 62 Protected Overridable Sub Dispose(disposing As Boolean) 63 If Not disposedValue Then 64 If disposing Then 65 SnapShotImage.Dispose() 66 End If 67 68 ' TODO: アンマネージド リソース (アンマネージド オブジェクト) を解放し、下の Finalize() をオーバーライドします。 69 ' TODO: 大きなフィールドを null に設定します。 70 End If 71 disposedValue = True 72 End Sub 73 74 ' TODO: 上の Dispose(disposing As Boolean) にアンマネージド リソースを解放するコードが含まれる場合にのみ Finalize() をオーバーライドします。 75 Protected Overrides Sub Finalize() 76 ' このコードを変更しないでください。クリーンアップ コードを上の Dispose(disposing As Boolean) に記述します。 77 Dispose(False) 78 MyBase.Finalize() 79 End Sub 80 81 ' このコードは、破棄可能なパターンを正しく実装できるように Visual Basic によって追加されました。 82 Public Sub Dispose() Implements IDisposable.Dispose 83 ' このコードを変更しないでください。クリーンアップ コードを上の Dispose(disposing As Boolean) に記述します。 84 Dispose(True) 85 ' TODO: 上の Finalize() がオーバーライドされている場合は、次の行のコメントを解除してください。 86 GC.SuppressFinalize(Me) 87 End Sub 88#End Region 89 90 End Class

VB

1 Private Sub SaveShotImage() 2 Dim saveDir As String 3 Dim savePath As String 4 For Each data As DataDto In DataDtoList 5 If data.SnapShotImage Is Nothing Then Continue For 6 7 Try 8 saveDir = System.IO.Path.Combine("C:\Users\MYNAME\Desktop", Strings.Format(Now, "yyyyMMdd")) 9 If Not System.IO.Directory.Exists(saveDir) Then System.IO.Directory.CreateDirectory(saveDir) 10 11 Do 12 savePath = System.IO.Path.Combine(saveDir, Strings.Format(Now, "yyMMddHHmmss") & ".jpg") 13 If Not System.IO.File.Exists(savePath) Then Exit Do 14 Loop 15 16 Using image As New Bitmap(data.SnapShotImage) 17 image.Save(savePath, Config.ImageFormat) ' ここで落ちる 18 image.Dispose() 19 data.Dispose() 20 End Using 21 Catch ex As Exception 22 Throw New Exception("画像保存に失敗しました。" & ex.Message, ex) 23 End Try 24 25 data.ImageFilePath = savePath 26 Next 27 End Sub

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

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

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

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

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

izmktr

2019/01/21 05:18

例外が出るのであれば、出ている例外のメッセージも添えてください
heart_crimson

2019/01/21 05:37

メッセージありがとうございます。exceptionのMesseageは「GDI+ で汎用エラーが発生しました。」のみです。スタックトレースについては追記しましたのでご確認のほどよろしくお願いいたします。
guest

回答4

0

自己解決

ファイル名に半角スラッシュが入っているのが原因のようでした。
出力する際、ファイル名として使えない半角スラッシュが入っているため、
正常にファイルを出力出来ず、例外が出ているようでした。
エラー内容が「GDI+で汎用エラーが発生しました。」であるため、
画像ファイルが何か悪さをしているのだとすっかり勘違いしておりました。

皆様のお時間頂戴して申し訳ございませんでした。
この度は様々なご意見をいただき、誠にありがとうございました。

投稿2019/01/23 09:16

編集2019/01/23 09:18
heart_crimson

総合スコア15

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

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

0

Do...Loop内で、現在時刻でファイル名を生成し、それが存在する場合は次の秒まで待つ、といったことをしていますが、その間にdata.SnapShotImageが利用不可(Disposeされている等)となってはいませんか?
そもそも、このDo...Loopの使い方は無駄にループを行っているため、あまり良い方法とは言えません。

例えば、System.IO.Path.GetRandomFileNameのようなものでランダムなファイル名を生成し、即座に保存してみてもエラーが発生しますか?

ところで、data.SnapShotImageには、具体的にどのような形式の画像データが入っているのでしょうか?

投稿2019/01/22 07:38

kenshirou

総合スコア772

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

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

heart_crimson

2019/01/22 08:07

コメントありがとうございます。Do~Loopをコメントアウトし、「savePath = System.IO.Path.Combine(saveDir, System.IO.Path.GetRandomFileName)」とすると、例外が出なくなりました。「Loopしている間にSnapShotImageが利用不可になっている」という点に心当たりが無く判断ができないのですが、本文に書きました通りFor Each外でやると問題無く出力出来ますので、原因は恐らくこれなのでしょうか。 ループは無駄だろうなとは薄々感じていたのですが、思いつかずにこのままで放置しておりました。時間以外の部分でファイル名を一意に出来ないか考えます。 もう少しDispose周辺も見直してみて、扱えなさそうでしたらこちらを採用しようと思います。ベストアンサーはしばらくお待ちください。 カメラ周辺の実装はまだきちんと目を通せてないのですが、追記しますので、恐れ入りますがご確認いただけますでしょうか。
guest

0

Image.Save()でのエラー
原因は色々あるようですが、とりあえず新しいBitmapを作ってみては?

C#

1(new Bitmap(data.SnapShotImage)).Save(savePath, ImageFormat.Jpeg)

投稿2019/01/21 05:45

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

heart_crimson

2019/01/23 04:15 編集

コメントありがとうございます。記載してくださったコードを参考に(質問文追記)直してみたのですが、同様の例外が出てしまいます。他に心当たりはございませんか? 提示していただいたサイトも拝見したのですが、このdata.SnapShotImageはまだファイルにしていないImage型のデータであり、ストリームで開いていないため、「ストリームが閉じている」という原因にピンと来ておりません。申し訳ありません。
guest

0

ぱっと思いつくのは2つぐらいです。
1.data.SnapShotImage の 元のStream が閉じてしまっている。
2.data.SnapShotImage を破棄してないためGDI+のリソース不足。

1の場合はdata.SnapShotImageから新しく、Bitmapを生成して保存してください。
2は、data.SnapShotImage を Dispose してください。

投稿2019/01/21 05:43

hihijiji

総合スコア4150

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

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

heart_crimson

2019/01/21 06:44

コメントありがとうございます。質問なのですが、SnapShotImageをDisposeしてから、どうすればよいのでしょうか? DisposeしてしまったらそのインスタンスでSaveは出来ませんよね?
hihijiji

2019/01/21 06:47

使う前ではなく、使った後に Dispose してください。
heart_crimson

2019/01/21 09:20

data.SnapShotImage.Save(...)してから、FinallyでDisposeする、ということでしょうか?
hihijiji

2019/01/21 09:43

原則 生成から破棄までを Using で括ります。 IDisposeの実装を除いて、別の場所でDisposeするのはお勧めしません。 逆に言うと、Using で括れるように考えてコードを書くことが大切です。
heart_crimson

2019/01/22 05:45

コードを書いてみましたが、やはりsaveで例外が発生します。追記しましたので、恐れ入りますがご覧いただけますでしょうか。
hihijiji

2019/01/22 07:13

DataDtoList の定義と生成を書いてください。
heart_crimson

2019/01/22 07:28

「Private DataDtoList As New SortableBindingList(Of DataDto)」 です。それ以外でDataDtoListをNewしている所はありません。
hihijiji

2019/01/22 08:03 編集

Dispose を実装しているリソースは、極力フィールドに持たずに都度生成-破棄するか、引数で受渡ししてください。 さらにそのコレクションをフィールドにするともっと厄介です。 フィールドに持つ場合は、そのクラスでIDisposeを実装して、フィールドのリソースをDisposeする必要があります。
hihijiji

2019/01/22 08:08

VB 2012 でしたね。 前記の Is Nothing Then云々は間違いです。
heart_crimson

2019/01/22 09:54

色々調べてみたのですが、IDisposeがまだ十分理解出来ておりません。大変申し訳ありません。DataDtoListにIDisposeにImplements IDisposableをして、Visual Studioに自動実装してもらって、Dispose(disposing As Boolean)内にSnapShotImage.Dispose()を書けばいいのでしょうか?
hihijiji

2019/01/22 10:04

DataDto から見直すと良いと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問