画像ファイルのフォーマットを識別するには?

解決


やたこ  2008-03-13 11:43:34  No: 139265  IP: 192.*.*.*

先日は丁寧に教えていただきありがとうございました。

また分からないことができましたので、教えていただけないでしょうか。
指定されたファイルの、フォーマットを調べる方法を知りたいです。
基本的に画像ファイルのみが対象で、jpeg, png, gif, bmp, 他、くらいの
識別ができればいいです。
読み込んだビットマップのGuidをGDI+を使って調べることで
可能だということは下記から分かりました。
http://www.atmarkit.co.jp/fdotnet/dotnettips/022getformat/getformat.html

が、実際に VBA で実行するにはどうすればよいのか分かりません。
ビットマップから、上記サイトでいうところの bitmap.RawFormat.Guid を
得る方法が全然分かりません。
照合するための配列 decoders = ImageCodecInfo.GetImageDecoders()
については、以前教えていただいた SDK に記載があったのですが、
http://msdn2.microsoft.com/ja-jp/library/system.drawing.imaging.imagecodecinfo.getimagedecoders(VS.80).aspx
これを VBA で使うための宣言や方法が分からないのです。

毎度申し訳ありませんが、お力をいただけたらと思います。

編集 削除
魔界の仮面弁士  2008-03-13 12:45:44  No: 139266  IP: 192.*.*.*

> jpeg, png, gif, bmp, 他、くらいの

簡易判定で良ければ、ファイルの内容で判定するとか。


ビットマップ:
  先頭 2 バイトが 42,4D。(文字列 "BM")
  http://kuwalab.net/technics/bitmap/index.html

JPEG:
  先頭 3 バイトが FF,D8,FF。
  http://siisise.net/jpeg.html

PNG:
  先頭 8 バイトが 89,50,4E,47,0D,0A,1A,0A。
  http://www.setsuki.com/hsp/ext/png.htm

GIF:
  先頭 6 バイトが 47,49,46,38,39,61。(文字列 "GIF89a")
  または
  先頭 6 バイトが 47,49,46,38,37,61。(文字列 "GIF87a")
  http://uketama.nekopps.com/article/gif_format/#header

編集 削除
魔界の仮面弁士  2008-03-13 13:38:59  No: 139267  IP: 192.*.*.*

> これを VBA で使うための宣言や方法が分からないのです。

こちらは、こんな感じかな。

Option Explicit

Private Declare Function GdiplusStartup Lib "GDIPlus" _
   (ByRef hToken As Long, _
    ByRef inputBuf As Long, _
    ByVal outputBuf As Long) As Long

Private Declare Sub GdiplusShutdown Lib "GDIPlus" _
   (ByVal token As Long)

Private Declare Function GdipLoadImageFromFile Lib "GDIPlus" _
   (ByVal FileName As Long, _
    ByRef hImage As Long) As Long

Private Declare Function GdipDisposeImage Lib "GDIPlus" _
   (ByVal hImage As Long) As Long

Private Declare Function GdipGetImageRawFormat Lib "GDIPlus" _
   (ByVal hImage As Long, _
    ByRef fmt As Currency) As Long

Public Function GetFormat(ByVal FileName As String) As String
    Dim ret As Long
    Dim hToken As Long
    Dim startInfo(3) As Long
    startInfo(0) = 1
    ret = GdiplusStartup(hToken, startInfo(0), 0&)
    If ret <> 0 Then
        GetFormat = "Error:" & CStr(ret)
        Exit Function
    End If

    Dim hImage As Long
    ret = GdipLoadImageFromFile(StrPtr(FileName), hImage)
    If ret <> 0 Then
        GetFormat = "Error:" & CStr(ret)
        GdiplusShutdown hToken
        Exit Function
    End If
    
    Dim fmt(1) As Currency
    ret = GdipGetImageRawFormat(hImage, fmt(0))
    If ret = 0 Then
        GetFormat = "Unknown"
        If fmt(1) = 338308179558612.8797@ Then
            Select Case fmt(0)
             Case 128437819022162.2443@
                GetFormat = "BMP"
             Case 128437819022162.2444@
                GetFormat = "EMF"
             Case 128437819022162.2445@
                GetFormat = "WMF"
             Case 128437819022162.2446@
                GetFormat = "JPEG"
             Case 128437819022162.2447@
                GetFormat = "PNG"
             Case 128437819022162.2448@
                GetFormat = "GIF"
             Case 128437819022162.2449@
                GetFormat = "TIFF"
             Case 128437819022162.2453@
                GetFormat = "ICO"
            End Select
        End If
    Else
        GetFormat = "Error:" & CStr(ret)
    End If
    GdipDisposeImage hImage
    GdiplusShutdown hToken
End Function

編集 削除
やたこ  2008-03-13 16:39:54  No: 139268  IP: 192.*.*.*

魔界の仮面弁士様

いただいたソースでちゃんと識別することができました!
いつもありがとうございます!!
最初の8バイトさえ読めば分かるんですね。
そのために Currency 型を使うところなどは、感心してしまいました。
fmt の辺りがよく分からないので、よろしければ教えていただけないでしょうか。

GdipGetImageRawFormat(hImage, fmt(0))
で、fmt(0) にファイルの最初の8バイトを格納し、分類しているのだと思うのですが、
If fmt(1) = 338308179558612.8797@ Then
は何をしているのでしょうか??
fmt(1)には他には特に何も処理していないようなので、空なのではないでしょうか?
また、PNG については最初の8バイトから識別できるようですが、
BMP, JPEG, GIF については最初の2〜6バイトですよね。
8バイト読み込んだfmt(0)のうち、最初の2〜6バイトだけを使って
分類しないといけないような気がするのですが、
どうしてうまくいくのでしょうか??
多分、バイトを Currency 型で表現した時の変換ルールが
分からないせいで、理解できないのだと思うのですが。

分からない事だらけですいませんが、教えていただけたら嬉しいです。

編集 削除
魔界の仮面弁士  2008-03-13 17:30:41  No: 139269  IP: 192.*.*.*

> fmt(0) にファイルの最初の8バイトを格納し、分類しているのだと
いえ、違います。

最初に書いた『ファイルの先頭バイトを読む方法』と、
2 番目の『GDIPlus.DLL の API を使う方法』は、まったくの別物です。


> 多分、バイトを Currency 型で表現した時の変換ルールが
> 分からないせいで、理解できないのだと思うのですが。
fmt に入るのは、画像情報を表す GUID 値です。
やたこさんが書かれた「bitmap.RawFormat.Guid」に相当する情報です。

GUID とは、128bit(16バイト)のデータです。
そして VB の 型(Currency) は、64 ビット (8 バイト) の変数です。

ですから、
>>  Dim fmt(1) As Currency
とすると、16 バイト分のメモリ領域を確保できるので、ここに、
GUID 構造体の値を受け取っているというわけです。


> If fmt(1) = 338308179558612.8797@ Then
> は何をしているのでしょうか??
GDI+ で扱える各画像の GUID 値は、このようになります。

 b96b3cab-0728-11d3-9d7b-0000f81ef32e   … Bitmap
 b96b3cae-0728-11d3-9d7b-0000f81ef32e   … JPEG
 b96b3cb0-0728-11d3-9d7b-0000f81ef32e   … GIF
 b96b3cac-0728-11d3-9d7b-0000f81ef32e   … EMF
 b96b3cad-0728-11d3-9d7b-0000f81ef32e   … WMF
 b96b3cb1-0728-11d3-9d7b-0000f81ef32e   … TIFF
 b96b3caf-0728-11d3-9d7b-0000f81ef32e   … PNG
 b96b3cb5-0728-11d3-9d7b-0000f81ef32e   … ICON

後続 8 バイトの 9d7b-0000f81ef32e が、すべて一致していますよね。
それが
>> If fmt(1) = 338308179558612.8797@ Then
のチェックにあたります。

9d7b-0000f81ef32e というデータを、リトルエンディアンとして並び替えると
2EF31EF800007B9D という 16 進数表現になり、10進数に変換すると、
3383081795586128797 という数値になります。

Currency 型は、これを 1/10000 した値として表現されるので、
338308179558612.8797@ となるわけです。(@ は、Currency の型宣言文字です)

編集 削除
やたこ  2008-03-17 10:56:33  No: 139270  IP: 192.*.*.*

魔界の仮面弁士様

丁寧な解説をいただきましてありがとうございます。
よく分かりました。
自分だけでは解決まで全く見当もつきませんでした。
お力をいただきとても感謝しています。
どうもありがとうございました。

編集 削除