tagCANDY CGI VBレスキュー(花ちゃん) の Visual Basic 2010 用 掲示板(VB.NET 掲示板) [ツリー表示へ]   [Home]
一括表示(VB.NET VB2005)
タイトルExceptionがcatchできない
記事No9702
投稿日: 2009/12/05(Sat) 09:18
投稿者ダンボ
皆さんこんにちは。
jpeg埋め込みサムネールを活用するのに、System.Drawing.ImageのGetThumbnailImage()
メソッドを使っていましたが、今ひとつ速度が遅くて向上策を探していました。

http://www.vbforums.com/showthread.php?t=342386
試したところ、劇的に速くなりました。MrPoliteさん、どうもありがとうございます。
vb.net(VB2005)に直したコードは下記のGetThumbnailです。

扱っているjpeg画像にはサムネールが含まれないケースが少ないので、その場合だけ例外に
した方が更に速度向上になると思い改修したのが、GetThumbnail_1とGetThumbnail_2ですが
いずれもArgumentExceptionをcatchできません。何故?
http://msdn.microsoft.com/ja-jp/library/system.drawing.image.getpropertyitem%28VS.80%29.aspx
で言うように確かにArgumentExceptionは発生するのですがcatchできません。
(返信は遅れるかもしれませんがよろしくお願いします)

Private Function GetThumbnail(ByVal path As String) As Image
    Const THUMBNAIL_DATA As Integer = &H501B
    Dim p As Imaging.PropertyItem
    Using fs As IO.FileStream = IO.File.OpenRead(path)
        Using img As Image = Image.FromStream(fs, False, False)
            Dim propertyFound As Boolean = False
            For i As Integer = 0 To img.PropertyIdList.Length - 1
                If img.PropertyIdList(i) = THUMBNAIL_DATA Then
                    propertyFound = True
                    Exit For
                End If
            Next
            If Not propertyFound Then
                Return Nothing
            End If
            p = img.GetPropertyItem(THUMBNAIL_DATA)
            fs.Close()
        End Using
    End Using

    Dim imageBytes As Byte() = p.Value
    Using stream As New IO.MemoryStream(imageBytes.Length)
        stream.Write(imageBytes, 0, imageBytes.Length)
        Return Image.FromStream(stream)
    End Using
End Function

(ArgumentExceptionがcatchできない)
Private Function GetThumbnail_1(ByVal path As String) As Image
    Const THUMBNAIL_DATA As Integer = &H501B
    Dim p As Imaging.PropertyItem
    Try
        Using fs As IO.FileStream = IO.File.OpenRead(path)
            Using img As Image = Image.FromStream(fs, False, False)
                p = img.GetPropertyItem(THUMBNAIL_DATA)
            End Using
            fs.Close()
        End Using
        Dim imageBytes As Byte() = p.Value
        Using stream As New IO.MemoryStream(imageBytes.Length)
            stream.Write(imageBytes, 0, imageBytes.Length)
            Return Image.FromStream(stream)
        End Using
    Catch ex As Exception
        Return Nothing
    End Try
End Function

Private Function GetThumbnail_2(ByVal path As String) As Image
    Const THUMBNAIL_DATA As Integer = &H501B
    Dim p As Imaging.PropertyItem
    Using fs As IO.FileStream = IO.File.OpenRead(path)
        Using img As Image = Image.FromStream(fs, False, False)
            Try
                p = img.GetPropertyItem(THUMBNAIL_DATA)
            Catch ex As System.ArgumentException
                Return Nothing
            Finally
                fs.Close()
            End Try
        End Using
    End Using
    Dim imageBytes As Byte() = p.Value
    Using stream As New IO.MemoryStream(imageBytes.Length)
        stream.Write(imageBytes, 0, imageBytes.Length)
        Return Image.FromStream(stream)
    End Using
End Function

[ツリー表示へ]
タイトルRe: Exceptionがcatchできない
記事No9703
投稿日: 2009/12/05(Sat) 10:15
投稿者Hongliang
> 扱っているjpeg画像にはサムネールが含まれないケースが少ないので、その場合だけ例外に
> した方が更に速度向上になると思い
つまり PropertyIdList に &H501B が含まれているかどうかの事前チェックをせずに
直接取りに行くってことですね?

> いずれもArgumentExceptionをcatchできません。何故?
catch できないっていうのはどういう状態になるのでしょう?
呼び出し元に ArgumentException が伝播している?
そもそもスローされていないようにみえる?
これを呼び出したときどういう動作を示しますか?
例外が出るなら、そのスタックトレースはどうなっていますか?
サムネイルを持っている(&H501B の PropertyItem がある)画像ファイルの場合はどうなりますか?
逆に持っていない画像の場合は?

以下、ソースを眺めて気になった部分を指摘しておきます。

> Private Function GetThumbnail_1(ByVal path As String) As Image

> fs.Close()
FileStream に Using を使っているのですからこれは不要です。

> Dim imageBytes As Byte() = p.Value
> Using stream As New IO.MemoryStream(imageBytes.Length)
>     stream.Write(imageBytes, 0, imageBytes.Length)
>     Return Image.FromStream(stream)
> End Using
MemoryStream はコンストラクタの引数に Byte 配列を受け取ることができます。
http://msdn.microsoft.com/ja-jp/library/e55f3s5k.aspx
これを使えば imageBytes や Write メソッドの呼び出しは不要です。渡した Byte() で初期化済みになるので。
また、画像のソースとなる Stream は、画像を使わなくなるまで Close すべきではありません。
特に MemoryStream は Close するメリットは無いと言っていいでしょう。


> Private Function GetThumbnail_2(ByVal path As String) As Image

> Using fs As IO.FileStream = IO.File.OpenRead(path)
>         Finally
>             fs.Close()
>         End Try
> End Using
Using はこの Finally 節を自動的に実装してくれる機能です(Nothing チェックとかも入りますが)。
わざわざ自分で Finally 節を書くなら Using の意味がありません。
この Finally 節は不要です。

[ツリー表示へ]
タイトルRe^2: Exceptionがcatchできない
記事No9704
投稿日: 2009/12/05(Sat) 11:01
投稿者ダンボ
Hongliang さん、早々にありがとうございます。

> つまり PropertyIdList に &H501B が含まれているかどうかの事前チェックをせずに
> 直接取りに行くってことですね?

はい、含まれている場合が多いので。投機的処理ですね。

> catch できないっていうのはどういう状態になるのでしょう?

catch句があるのに、そこに飛ばずに、ArgumentException 例外を伝える例外ダイアログが
出てきます。

> 例外が出るなら、そのスタックトレースはどうなっていますか?

調査方法がわかりません。例外ダイアログから見れるのでしょうか?

> サムネイルを持っている(&H501B の PropertyItem がある)画像ファイルの場合はどうなりますか?
> 逆に持っていない画像の場合は?

100%再現しますが、サムネイル有りなら思った通りの動作、無しなら必ず例外ダイアログです。


> > fs.Close()
> FileStream に Using を使っているのですからこれは不要です。

Disposeを代行してくれるとだけ思っていました(もっともDisposeにはCloseも含まれるか)


> MemoryStream はコンストラクタの引数に Byte 配列を受け取ることができます。
> http://msdn.microsoft.com/ja-jp/library/e55f3s5k.aspx
> これを使えば imageBytes や Write メソッドの呼び出しは不要です。渡した Byte() で初期化済みになるので。
> また、画像のソースとなる Stream は、画像を使わなくなるまで Close すべきではありません。
> 特に MemoryStream は Close するメリットは無いと言っていいでしょう。

ありがとうございます。試してみます。

> > Using fs As IO.FileStream = IO.File.OpenRead(path)
> >         Finally
> >             fs.Close()
> >         End Try
> > End Using
> Using はこの Finally 節を自動的に実装してくれる機能です(Nothing チェックとかも入りますが)。
> わざわざ自分で Finally 節を書くなら Using の意味がありません。
> この Finally 節は不要です。

ありがとうございます。Usingは偉いんですね。なんだか例外をcatchできないのは
UsingとTry_Catch_Finallyとの組み合わせの問題のような気がしてきました。
(というかそれ以外にあり得ないのでは)

[ツリー表示へ]