我在使用CxImage的时候发现,如果用Load()函数加载图片的时候,参数指定的图片类型和文件实际类型不一样(例如文件后缀是.bmp,但它实际不是图片文件),会导致异常。
    现在,我想在使用Load()函数之前,通过读文件头来判断文件的真正格式,而不是仅仅使用后缀名判断(目前是这么做的,有时导致我的嵌入式终端死机),例如,最简单的bmp文件,开头2个字节肯定是"BM",但是我看了几个jpg格式的文件,文件头并不是固定的,我想请教各位,jpg文件的文件头有没有什么规律?如何判断...
    顺便png,gif,tga,tif,tiff,pcx,ico,wbmp,wmf,jbg这些文件头的规律我也想知道,求教各位,有知道的麻烦告知一下,急!!!

解决方案 »

  1.   

    Public Function IsBmp(ByVal FileName As String) As Boolean
        Dim FileNumber              As Long, BytesRead              As Long
        Dim BmpInfoHeaderSize       As Long
        Dim BmpFileHeader           As BITMAPFILEHEADER
        
        FileNumber = CreateFile(FileName, GENERIC_READ, 0, ByVal 0&, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
        ReadFile FileNumber, BmpFileHeader, Len(BmpFileHeader), BytesRead, ByVal 0&
        If BmpFileHeader.Type = &H4D42 Then                                     'BmpFileHeader.OffBits和BmpFileHeader.Size都有可能是0
            ReadFile FileNumber, BmpInfoHeaderSize, 4, BytesRead, ByVal 0&
            If BmpInfoHeaderSize = 12 Or BmpInfoHeaderSize = 40 Or BmpInfoHeaderSize = 56 Then
                IsBmp = True
            End If
        End If
        CloseHandle FileNumber
        
    End FunctionPrivate Type GifHeader
        Key     As String * 6
    End Type
    Public Function IsGif(ByVal FileName As String) As Boolean
        Dim FileNumber              As Long
        Dim BytesRead               As Long
        Dim Header                  As GifHeader
        FileNumber = CreateFile(FileName, GENERIC_READ, 0, ByVal 0&, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
        If FileNumber <> 0 Then
            ReadFile FileNumber, Header, Len(Header), BytesRead, ByVal 0&
            If (Header.Key = "GIF87a" Or Header.Key = "GIF89a") Then
                IsGif = True
            End If
        End If
        CloseHandle FileNumber
    End FunctionPrivate Type ICONDIRENTRY
        Width As Byte               '   图标图片的显示宽度,以像素为单位,最大值为255
        Height As Byte              '   图标图片的显示高度, 以像素为单位,最大值为255 (times 2)
        ColorCount As Byte          '   图标图片的颜色数 (0 if >=8bpp)
        Reserved As Byte            '   保留域总是 0
        Planes As Integer           '   图标图片的位面数,1
        BitCount As Integer         '   标图片的颜色深度
        BytesInRes As Long         '   图标图片占用的数据量(字节为单位)
        ImageOffset As Long        '   图标图片的开始位置
    End TypePrivate Type ICONDIR
        Reserved As Integer       '   保留域,目前始终为0
        Type As Integer           '   定义为资源类型,图标值为 1、光标是2
        Count As Integer          '   表示的是这个文件里包含了几个图标
    End TypePrivate Const M_Image_ICON               As Long = 1
    Private Const CBM_INIT                   As Long = &H4
    Public Function IsIcon(ByVal FileName As String) As Boolean
        Dim Id                  As ICONDIR, IDE()       As ICONDIRENTRY
        Dim FileNumber          As Long, Entry          As Long
        Dim BytesRead           As Long, Length         As Long
        IsIcon = True
        FileNumber = CreateFile(FileName, GENERIC_READ, 0, ByVal 0&, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
        If FileNumber <> 0 Then
            Length = GetFileSize(FileNumber, ByVal 0&)
            ReadFile FileNumber, Id, Len(Id), BytesRead, ByVal 0&   'BytesRead返回从文件中实际读入的字符数
            If BytesRead = Len(Id) And Id.Reserved = 0 And Id.Type = M_Image_ICON Then
                ReDim IDE(0 To Id.Count - 1) As ICONDIRENTRY
                For Entry = 0 To Id.Count - 1
                    ReadFile FileNumber, IDE(Entry), Len(IDE(Entry)), BytesRead, ByVal 0&
                    If BytesRead <> Len(IDE(Entry)) Then
                        IsIcon = False
                        CloseHandle FileNumber
                        Exit For
                    End If
                Next
            Else
                IsIcon = False
            End If
        Else
            IsIcon = False
        End If
        CloseHandle FileNumber
    End FunctionPublic Function IsJpg(ByVal FileName As String) As Boolean
        Dim FileNumber              As Long
        Dim BytesRead               As Long
        Dim FileSize                As Long
        Dim SOI                     As Integer          'JPG文件开始的标志
        Dim EOI                     As Integer          'JPG文件结束的标志
      
        FileNumber = CreateFile(FileName, GENERIC_READ, 0, ByVal 0&, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
        If FileNumber <> 0 Then
            FileSize = GetFileSize(FileNumber, ByVal 0&)
            ReadFile FileNumber, SOI, 2, BytesRead, ByVal 0&
            SetFilePointer FileNumber, FileSize - 2, ByVal 0&, FILE_BEGIN
            ReadFile FileNumber, EOI, 2, BytesRead, ByVal 0&
            If SOI = &HD8FF And EOI = &HD9FF Then
                IsJpg = True
            End If
        End If
        CloseHandle FileNumber
    End FunctionPrivate Type PCXHeader
        Manufacturer        As Byte         '固定为10=ZSoft
        Version             As Byte
                                            '0 = 2.5
                                            '2 = 2.8 with palette
                                            '3 = 2.8 without palette
                                            '4 = Pc paintbrush for windows
                                            '5 = Pc Paintbrush ver 3.0+24位固定为5
        
        Encoding            As Byte         '1 = .PCX RLE,固定为1
        Bpp                 As Byte         '1, 2, 4, 8,每像素所需位数
        Left                As Integer      '图像相对于屏幕左上角X坐标(像素为单位)
        Top                 As Integer      '图像相对于屏幕左上角Y坐标(像素为单位)
        Right               As Integer      '图像相对于屏幕右下角X坐标(像素为单位)
        Bottom              As Integer      '图像相对于屏幕右下角Y坐标(像素为单位)
        HoriResolution      As Integer      '图像水平分辨率(像素/英寸)
        VertResolution      As Integer      '图像垂直分辨率(像素/英寸)
        Palette(0 To 15)    As PcxPalette   '指明调色板数据,只对16色以下有用
        Reserved            As Byte         '保留,固定为0
        Planes              As Byte         '指定图像色彩平面数,24位真彩色为3个平面
        BytesPerLine        As Integer      '图像宽度(字节),且必须为偶数
        PaletteType         As Integer      '调色板类型,彩色或单色图像为1,灰度图像为2
        HScreenSize         As Integer      '图像的屏幕宽度(像素为单位),0为基准
        VScreenSize         As Integer      '图像的屏幕高度(像素为单位),0为基准
        Filter(0 To 53)     As Byte         '保留域,固定为0
    End Type
    Public Function IsPcx(FileName As String) As Boolean
        Dim FileNumber              As Long, BytesRead              As Long
        Dim Header                  As PCXHeader
        
        FileNumber = CreateFile(FileName, GENERIC_READ, 0, ByVal 0&, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
        If FileNumber <> 0 Then
            ReadFile FileNumber, Header, Len(Header), BytesRead, ByVal 0&
            If Header.Manufacturer = 10 Then
                If Header.Encoding = 1 Then
                    If Header.Version = 0 Or Header.Version = 2 Or Header.Version = 3 Or Header.Version = 4 Or Header.Version = 5 Then
                        IsPcx = True
                    End If
                End If
            End If
        End If
        CloseHandle FileNumber
    End Function
    Private Const II_ORDER              As Integer = &H4949 '   Intel 类型的字节顺序
    Private Const MM_ORDER              As Integer = &H4D4D '   Mortorola 类型的字节顺序Private Type TiffHeader
        ByteOrder       As Integer      '规定为 'II' 或者 'MM' Intel /Mortorola 类型的字节顺序
        Version         As Integer      'TIFF文件的版本,为与以前的兼容,为42
        IFDOffset       As Long         'TIFF文件的第一个IFD在文件中的偏移量,肯定不小于8
    End Type
    Public Function IsTif(ByVal FileName As String) As Boolean
        Dim FileNumber              As Long
        Dim BytesRead               As Long
        Dim Header                  As TiffHeader
        FileNumber = CreateFile(FileName, GENERIC_READ, 0, ByVal 0&, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
        If FileNumber <> 0 Then
            ReadFile FileNumber, Header, Len(Header), BytesRead, ByVal 0&
            If Header.ByteOrder = II_ORDER Or Header.ByteOrder = MM_ORDER Then
                If Header.Version = &H2A And Header.IFDOffset >= 8 Then
                    IsTif = True
                End If
            End If
        End If
        CloseHandle FileNumber
    End Function
      

  2.   


    Private Type WbmpHeader
        Type            As Byte     '指明了WBMP的型态。因为目前只有型态0(黑白,非压缩)的 WBMP 格式被定义,第一个8个位组应该为零
        Null            As Byte     '是固定标头部分,也为0
        Width           As Byte     '动态的字节数
        Height          As Byte
    End Type
    Public Function IsWbm(FileName As String) As Boolean
        Dim FileNumber              As Long, BytesRead              As Long
        Dim Header(5)               As Byte, Index                  As Long
        Dim High                    As Long, Low                    As Long
        Dim Width                   As Long, Height                 As Long
        Dim FileSize                As Long
        FileNumber = CreateFile(FileName, GENERIC_READ, 0, ByVal 0&, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
        If FileNumber <> 0 Then
            FileSize = GetFileSize(FileNumber, ByVal 0&)
            ReadFile FileNumber, Header(0), 6, BytesRead, ByVal 0&
            If Header(0) = 0 And Header(1) = 0 Then
                Index = 2
                If Header(Index) > 127 Then                     '大于127,则用两个字节表示,并且宽度是用两个字节的后七位拼接而成。
                    High = (Header(Index) * 128) And &H3F80     '取高字节的前七位
                    Low = Header(Index + 1) And &H7F            '取底字节的前七位
                    Width = High Or Low                         '拼合
                    Index = Index + 2
                Else
                    Width = Header(Index)
                    Index = Index + 1
                End If
                If Header(Index) > 127 Then
                    High = (Header(Index) * 128) And &H3F80
                    Low = Header(Index + 1) And &H7F
                    Height = High Or Low
                    Index = Index + 2
                Else
                    Height = Header(Index)
                    Index = Index + 1
                End If
                If Width > 0 And Height > 0 Then
                     If FileSize - Index = ((Width + 7) \ 8) * Height Then
                        IsWbm = True
                    End If
                End If
            End If
        End If
      
        CloseHandle FileNumber
    End Function'TGA格式从文件开始到结束的结构为:
    '    TGA文件头  (文件头包含了基本的图像参数)
    '    Image ID   (这一部分的内容不是必须的,其大小由文件头的IDLength指定,不能大于255字节,可以用来保存作者信息等)
    '    Colormap   (调色板的信息,TGA格式对于真彩色也可以有调色板的)
    '    Image Data (TGA的数据可能是RLE压缩后的,也可能是未压缩的,这个由头文件中的ImageType指定)
    '    Developer Area(这是TGA2.0格式独有的,一般无需关注)
    '    Extension Area(TGA2.0版本独有,可用于保存软件作者,开发时间等信息)
    '    Footer        (TGA2.0版本独有,保留了2.0版本的签名和上面两个Area部分的起始地址)
    '   注意TGA格式的压缩不是针对位,也不是针对字节,而是针对一个像素的,比如RGB色,就是把RGB看做一个整体来编码的。Private Type TgaHeader
        IDLength                As Byte             '在头部之后的Image ID段的长度,最大为255字节,一般为空
        ColorMapType            As Byte             '1表示有调色板,0表示没有,其他值保留
        ImageType               As Byte             '图像类型 0=为空, 1= 不压缩+调色板 9=RLE压缩+调色板, 2=未压缩的真彩色,10=压缩的真彩色,3=未压缩的灰度,11=压缩后的灰度, 32&33 哈夫曼编码压缩
        CMapStart               As Integer          '调色板开始位置
        CMapLength              As Integer          '调色板的数量
        CMapDepth               As Byte             '每个调色板项所占用的字节数,可为 8,15,16,24,32
        XOffset                 As Integer          '一般为0
        YOffset                 As Integer          '一般为0
        Width                   As Integer          '图像像素宽度
        Height                  As Integer          '图像像素高度
        PixelDepth              As Byte             '图像一个像素占用字节数,本类可解析 8,15,16,24,32
        ImageDescriptor         As Byte             '图像描述符 (图像的方向,ALPHA的应用等)
    End Type
        Private Type TgaFooter
        ExtensionOffset         As Long             '扩展快的位置
        DeveloperOffset         As Long
        Signature(18)           As Byte             '2.0版本的标识符
    End Type
    Private Pal32LUT(0 To 31)   As Byte             ' 5位,32个调色板实体的查找表, 32 entry, palette lookup tablePublic Function IsTGA(FileName As String) As Boolean
        Dim FileNumber              As Long, BytesRead              As Long
        Dim FileSize                As Long
        Dim Header                  As TgaHeader, Foot              As TgaFooter
        Dim Bpp                     As Long, ExpectedSize           As Long
        
        FileNumber = CreateFile(FileName, GENERIC_READ, 0, ByVal 0&, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
        If FileNumber <> 0 Then
            FileSize = GetFileSize(FileNumber, ByVal 0&)
            If FileSize > 26 Then
                SetFilePointer FileNumber, FileSize - 26, ByVal 0&, FILE_BEGIN      'TGA2.0里有这个标签
                ReadFile FileNumber, Foot, Len(Foot), BytesRead, ByVal 0&
                If InStr(1, StrConv(Foot.Signature, vbUnicode), "TRUEVISION-XFILE.", vbTextCompare) > 0 Then
                    IsTGA = True
                    CloseHandle FileNumber
                    Exit Function
                End If
            End If        If FileSize > 18 Then
                SetFilePointer FileNumber, 0, ByVal 0&, FILE_BEGIN
                ReadFile FileNumber, Header.IDLength, 1, BytesRead, ByVal 0&
                ReadFile FileNumber, Header.ColorMapType, 1, BytesRead, ByVal 0&
                ReadFile FileNumber, Header.ImageType, 1, BytesRead, ByVal 0&
                ReadFile FileNumber, Header.CMapStart, 2, BytesRead, ByVal 0&
                ReadFile FileNumber, Header.CMapLength, 2, BytesRead, ByVal 0&      '不能直接通过结构体来读取数据,主要是存在字节对齐问题
                ReadFile FileNumber, Header.CMapDepth, 1, BytesRead, ByVal 0&
                ReadFile FileNumber, Header.XOffset, 10, BytesRead, ByVal 0&
            Else
                CloseHandle FileNumber
                Exit Function
            End If
        End If
        CloseHandle FileNumber    Select Case Header.ImageType        '第一步测试,看图像类型是否合理
        Case 1, 2, 3, 9, 10, 11             'Truevision保留在0到 127之间的 ImageType为自己所用,128 到 255 可由开发者所用
        Case Else                           '0=空白图像, 32或33是合法的,但找不到相关文档
            Exit Function
        End Select
        
        If Header.Width < 1 Or Header.Height < 1 Then Exit Function     ' 第二步测试
       
        Select Case Header.PixelDepth        '第三步测试,看图像的像素深度是否合理
            Case 8
                Bpp = 1
            Case 15, 16                     '如果没有调色板也可以支持
                If Header.ImageType = 1 Or Header.ImageType = 9 Then Exit Function
                Bpp = 2
            Case 24, 32                     '如果没有调色板也可以支持
                If Header.ImageType = 1 Or Header.ImageType = 9 Then Exit Function
                Bpp = Header.PixelDepth \ 8
            Case Else
                Exit Function               ' 文档里其他的也支持,这里不支持
        End Select
        
        If Header.ImageType = 1 Or Header.ImageType = 9 Then        '第四步测试,看调色板的设置统一不
            If Header.ColorMapType <> 1 Then Exit Function          ' 1代表有调色板,0表示没有,'Truevision保留在0到 127之间的 ImageType为自己所用,128 到 255 可由开发者所用
        End If
        
        If Header.ColorMapType = 1 Then         '第五步,继续验证调色板,TGA在真彩色时也是可以有调色板的
            If Header.CMapLength < 1 Then Exit Function '调色板的数量不能少于1
            If Header.CMapStart < 0 Then Exit Function  '调色板的偏移是否正常
            ExpectedSize = Header.CMapStart + Header.CMapDepth
            
            Select Case Header.CMapDepth                ' ignore unsupported palette bit depths
            Case 8, 24, 32
                ExpectedSize = ExpectedSize * (Header.CMapDepth \ 8)
            Case 15, 16 '
                ExpectedSize = ExpectedSize * 2
            Case Else:
                Exit Function
            End Select
        End If
        
        If Header.ImageType < 8 Then  ' 第六步,最小文件测试,未压缩
            ExpectedSize = ExpectedSize + Bpp * Header.Width * Header.Height
        End If
        If ExpectedSize < FileSize Then IsTGA = True
    End Function
      

  3.   

    如果可以用开源的库,可以使用MediaInfo库来判断
      

  4.   

    把类型设为0,它会自己帮你判断的~~~
    另外我发现CxImage类不支持BITMAPV4HEADER类型的位图,
    并且里面的UNOCODE和MBCS很混乱,似乎还有越界的情况。
      

  5.   

    看看这个帖子吧
    寻找各种图像文件格式资料:GIF、JPG、PNG、PCX、PSD、TGA、TIF http://topic.csdn.net/t/20021106/07/1151880.html
      

  6.   

    用这个
    IMsmMerge2::ExtractFilesEx
      

  7.   


       我把CxImage的库改了,原来的问题是因为图片解不开,在Load函数里面会先用后缀指定的格式解一次,解码不成功的话,会把图片格式设置成0,即未知格式,然后依次按各种支持的格式都去尝试解码,这样就会对文件解码很多次,而且每一次都会有异常抛出,异常处理会消耗CPU很多时间,所以如果加上用户的快速操作就会很容易导致死机,现在,我把那些抛出的地方都改成 return 了,具体就是先保存了错误信息,然后返回false,错误信息可以通过 CxImage::GetLastError()获得。
        这样暂时解决了死机的问题,但是修改了库会不会导致其他问题目前还未知,我这个做法其实不太好,如果有高手想到更好的办法麻烦不啬赐教啊...   我本来是想在解图片的时候先打开文件检查文件头,以此来判断是不是正确的图片文件,但是这样会导致程序效率问题,损失一部分性能,所以没按以前的思路做了。   问题已解决,结贴了。如果有高手赐教麻烦发Email至 :[email protected]
      

  8.   

    楼主,谢谢你的热心回答..
    我本来是想用TRY CATCH 做的,抓异常,给用户弹个消息的,可是发现积累没向上抛,直接倒掉了,没办法,我就判断一下LOAD的返回值,然后弹个消息,呵呵,有点傻.