2016-08-10 8 views
1

メモリストリームを使用してイメージをAccessデータベーステーブルに正常に保存できました。イメージを保存する列には、データ型 "OLEObject"があります。テーブルを開くと、このImage Columnに "Long binary data"の値が表示されます。問題は、このデータベースでレポートを作成しているときに画像が表示されないことです。「ビットマップイメージ」としてデータベースにイメージを保存

MSPaintとCtrl + Aで画像ファイルを開いた後、Ctrl + Cですべてのグラフィックをコピーし、Accessデータベース列に貼り付けたところ、貼り付けは成功しました。 Nowはvalueを「ビットマップイメージ」として表示します。イメージの列の値をダブルクリックすると、MSPaint.Andでイメージが開き、レポートでも機能します。イメージを直接「ビットマップイメージ」タイプとして保存する方法を知りたいですか?私はMS AccessのOLEフィールド構造は、私が見つけることができる任意の文献に記載されていないVBの.NET Framework 4.0、Visual Studioの2012年

Image added thru VB .Net

Manually pasted from MSPaint

+0

あなたはそこでどのレポートを使用していますか?結晶? –

+0

いいえ、MS Accessレポートウィザードを使用してレポートを作成するだけです.. – DK2014

+0

私は本当に長い時間のアクセスを知りません...私は問題があなたのストリーム中にコピーしていない画像のいくつかのヘッダーに関連していると信じています。ストリームを使用する代わりにFILE.READALLBYTES()を使用してデータをそこに配置しようとしましたか?つまり、問題はストリーム自体に関連している可能性があります。あなたはそれを閉じる前にストリームを流していますか? –

答えて

2

を使用しています。

以下に示すクラスAccessOLEBitmapConverterメソッドGetBitmapStreamためSystem.Drawing.ImageConverter Classのソースコード内の参照資料(How To Retrieve Bitmap from Access and Display It in Web Page)に基づいて、フォレンジックハックの結果です。

ハッキングのビットに基づいて、フィールドが2つの構造、1)ヘッダー構造、および2)EmbeddedObject構造を含むことがわかりました。もう少しハッキングした後、私は、EmbeddedObject構造のPresentationフィールドを、1232バイトのシーケンスで置き換えることができることを発見しました。ヘッダー構造のnameフィールドに6バイトのシーケンス({33,0,255 、255,255,255})。詳細については、コードを参照してください。

Public Class AccessOLEBitmapConverter 
    Const nullByte As Byte = 0 
    Const OLEVersion As Int32 = 1281 
    Private Shared arrayInvalid As String = "Source array is invalid" 

    Public Shared Function ToByteArray(sourceBitmap As Bitmap) As Byte() 

     Dim ret As Byte() = Nothing 
     Using ms As New IO.MemoryStream(5000) 
      EmbeddedObjectHeader.Write(ms) 
      EmbeddedObject.Write(ms, sourceBitmap) 
      ret = ms.ToArray() 
     End Using 

     Return ret 
    End Function 

    Public Shared Function ToBitmap(bytes As Byte()) As Bitmap 
     Dim ret As Bitmap = Nothing 

     If bytes Is Nothing Then Throw New ArgumentNullException("Argument ""bytes"" cannot be null") 
     If bytes.Length < 14 Then Throw New ArgumentOutOfRangeException(arrayInvalid) 
     Using ms As New IO.MemoryStream(bytes, False), 
       br As New IO.BinaryReader(ms, System.Text.Encoding.ASCII) 
      Dim signature As Int16 = br.ReadInt16() 
      If signature <> &H1C15 Then Throw New ArgumentOutOfRangeException(arrayInvalid) 

      Dim headersize As Int16 = br.ReadInt16() ' 47 
      If bytes.Length < headersize Then Throw New ArgumentOutOfRangeException(arrayInvalid) 

      ms.Position = headersize 

      ' Start ObjectHeader 
      If ms.Position = bytes.Length Then Throw New ArgumentOutOfRangeException(arrayInvalid) 
      Dim OLEVersion As UInt32 = br.ReadUInt32 
      If ms.Position = bytes.Length Then Throw New ArgumentOutOfRangeException(arrayInvalid) 
      Dim FormatID As UInt32 = br.ReadUInt32 

      If ms.Position = bytes.Length Then Throw New ArgumentOutOfRangeException(arrayInvalid) 
      Dim ClassName As String = LengthPrefixedAnsiString.Read(br) 

      If ms.Position = bytes.Length Then Throw New ArgumentOutOfRangeException(arrayInvalid) 
      Dim TopicName As String = LengthPrefixedAnsiString.Read(br) 

      If ms.Position = bytes.Length Then Throw New ArgumentOutOfRangeException(arrayInvalid) 
      Dim ItemName As String = LengthPrefixedAnsiString.Read(br) 
      ' End ObjectHeader 

      If ms.Position = bytes.Length Then Throw New ArgumentOutOfRangeException(arrayInvalid) 
      Dim NativeDataSize As Int32 = br.ReadInt32 

      If (ms.Position + NativeDataSize) > bytes.Length Then Throw New ArgumentOutOfRangeException(arrayInvalid) 
      Dim NativeData As Byte() = br.ReadBytes(NativeDataSize) 

      Dim msImage As New IO.MemoryStream(NativeData) 

      ret = CType(Image.FromStream(msImage), Bitmap) 
     End Using 


     Return ret 
    End Function 

    Private Class EmbeddedObjectHeader 
     ' ref: How To Retrieve Bitmap from Access and Display It in Web Page 
     ' https://support.microsoft.com/en-us/kb/175261 

     Friend Shared Sub Write(ms As System.IO.MemoryStream) 
      Const signature As Int16 = &H1C15 
      Const headersize As Int16 = 47S 
      Const objectType As Int16 = 2S 
      Const nameLen As Int16 = 0S 
      Const classLen As Int16 = 13S 
      Const nameOffset As Int16 = 14S 
      Const classOffset As Int16 = 20S 
      Const classType As String = "Bitmap Image" 
      Const hdrClassName As String = "Paint.Picture" 

      Using bw As New IO.BinaryWriter(ms, System.Text.Encoding.ASCII, True) 
       With bw 
        .Write(signature) 
        .Write(headersize) 
        .Write(objectType) 
        .Write(nameLen) 
        .Write(classLen) 
        .Write(nameOffset) 
        .Write(classOffset) 

        ' Even though this offset is declared as being for the 'name' field and 
        ' the 'name' field always has a zero length, these six bytes must be present 
        ' to allow the resultant byte array to be identified as a BitMap Image by Access 
        ms.Position = nameOffset 
        .Write(New Byte() {33, 0, 255, 255, 255, 255}) 

        ms.Position = classOffset 
        .Write(classType.ToCharArray()) 
        .Write(nullByte) 
        .Write(hdrClassName.ToCharArray()) 
        .Write(nullByte) 
       End With 
      End Using 
     End Sub 
    End Class ' EmbeddedObjectHeader 

    Private Class EmbeddedObject 
     ' ref: https://msdn.microsoft.com/en-us/library/dd942053.aspx 
     'Header (variable): This MUST be an ObjectHeader (section 2.2.4). 
     ' The FormatID field of the Header MUST be set to 0x00000002. 

     'NativeDataSize (4 bytes): This MUST be set to the size of the NativeData field, in bytes. 

     'NativeData (variable): This must be an array of bytes that contains the native data. 

     'Presentation (variable): This MUST be a MetaFilePresentationObject (section 2.2.2.1), 
     ' a BitmapPresentationObject (section 2.2.2.2), a DIBPresentationObject (section 2.2.2.3), 
     ' a StandardClipboardFormatPresentationObject (section 2.2.3.2), or 
     ' a RegisteredClipboardFormatPresentationObject (section 2.2.3.3). 

     Friend Shared Sub Write(ms As System.IO.Stream, sourceBitmap As Bitmap) 

      Using bw As New IO.BinaryWriter(ms, System.Text.Encoding.ASCII, True) 
       With bw 
        ObjectHeader.Write(ms) 

        ' Determine and write the NativeDataSize and NativeData fields 
        Using imgStream As New IO.MemoryStream(20000) 
         sourceBitmap.Save(imgStream, System.Drawing.Imaging.ImageFormat.Bmp) 
         Dim NativeDataSize As Int32 = CInt(imgStream.Length) 
         .Write(NativeDataSize)  
         .Write(imgStream.ToArray) 
        End Using 

        ' At this point the 'Presentation' variable should be written. 
        ' However, Bitmap files copied from Windows Explorer and pasted into 
        ' the MS Access OLE field have only 12 bytes written and this allows for 
        ' a much smaller size to be stored as the 'Presentation' variable appears to 
        ' duplicate the NativeData field. Adding the Bitmap via the 'Insert Object' 
        ' dialog creates a storage nearly twice that of this method. 

        ' The first 4 bytes correspond to the integer value for OLEVersion. 
        .Write(OLEVersion) 
        ' The next 4 bytes are always zero. 
        .Write(0I) 
        ' The next byte (position 8) appears variable and its value does not appear 
        ' to impact using the 'BitMap Image' in Access. So write a zero. 
        .Write(nullByte) 
        ' The final three bytes appear to be constant {173, 5, 254} 
        .Write(New Byte() {173, 5, 254}) 
        .Flush() 
       End With 
      End Using 
     End Sub 

    End Class 'EmbeddedObject 

    Private Class ObjectHeader 
     Friend Shared Sub Write(ms As System.IO.Stream) 
      Const FormatID As Int32 = 2 
      Const ClassName As String = "PBrush" 
      Const TopicName As String = "" 
      Const ItemName As String = "" 

      Using bw As New IO.BinaryWriter(ms, System.Text.Encoding.ASCII, True) 
       With bw 
        .Write(OLEVersion) 
        .Write(FormatID) 
        LengthPrefixedAnsiString.Write(bw, ClassName) 
        LengthPrefixedAnsiString.Write(bw, TopicName) 
        LengthPrefixedAnsiString.Write(bw, ItemName) 
       End With 
      End Using 
     End Sub 

    End Class ' ObjectHeader 

    Private Class LengthPrefixedAnsiString 
     ' ref : https://msdn.microsoft.com/en-us/library/dd942554.aspx 
     ' This structure specifies a null-terminated American National Standards Institute (ANSI) character set string. 
     ' Length (4 bytes): This MUST be set to the number of ANSI characters in the String field, 
     ' including the terminating null character. Length MUST be set to 0x00000000 to indicate an empty string. 
     ' String (variable): This MUST be a null-terminated ANSI string. 

     Const nullChar As Byte = 0 

     Friend Shared Function Read(br As IO.BinaryReader) As String 
      Dim ret As String = String.Empty 
      Dim length As Int32 = br.ReadInt32 
      If length > 0 Then 
       Dim chars As Char() = br.ReadChars(length) 
       ret = New String(chars) 
      End If 
      Return ret 
     End Function 

     Friend Shared Sub Write(bw As IO.BinaryWriter, val As String) 
      If val.Length = 0 Then 
       bw.Write(0I) 
      Else 
       bw.Write(val.Length + 1) 
       bw.Write(val.ToCharArray) 
       bw.Write(nullChar) 
      End If 
     End Sub 
    End Class 'LengthPrefixedAnsiString 
End Class 

使用例:

' To obtain a Byte() to store a Bitmap to MS Access 
Dim bm As Bitmap = CType(Image.FromFile("SomeBitmapFile.bmp"), Bitmap) 
Dim bytesToStoreInAccess As Byte() = AccessOLEBitmapConverter.ToByteArray(bm) 

' To retrieve a Bitmap from an Access Bitmap Image field. 
Dim bytesFromBitmapImageField As Byte() ' set this equal to the field data 
Dim bmRetrieved As Bitmap = AccessOLEBitmapConverter.ToBitmap(bytesFromBitmapImageField) 

このコードを正常にすることができるのMS Access Bitmap Imageを得るために、アクセスOLEフィールドにビットマップを保存するには、Windows 10マシン上のMS Access 2007を使用してテストされていますアクセスレポートに表示されます。

+0

この非常に詳細なコードとあなたのハードワークのために多くのおかげでTnTinMn。私のアプリケーションにクラスを追加しましたが、この行にエラーが発生しました '新しいIO.BinaryWriter(ms、System.Text.Encoding.ASCII、True)として使用しています'エラー:エラーアクセス不可能なため、 'はこの数の引数を受け入れます。 \tこれについて助言していただけますか?ここに何かを輸入する必要はありますか? – DK2014

+0

@ DK2014は新しい文を削除するだけです:bwをIo.Binaryとして使用します.... –

+0

@David BS、私はすでにそれを試みましたが、それはさらにコードを壊します。 bwを新しいIO.BinaryWriter(MS_Write、System.Text.Encoding.ASCII、True)として使用してbwを新しいIOとして使用するように変更したとき。BinaryWriter(MS_Write、System.Text.Encoding.ASCII)は、少なくともエラーを取り除きますが、変換中に使用すると、異なる段階でメモリストリームに関連するエラーが発生します。私はさまざまなアプローチを試してきましたが、まだまだ幸運なことではありません。 – DK2014

関連する問題