开发者

Converting an OLE Image Object from MS Access for use in .NET

I'm working on redeveloping an Access based system into c#.net, however when MS went from office 2003 to office 2007 they removed the picture editor within access - which meant that previously stored pictures would no longer display in the system. The guys at the company did a hack that basically saved the images with VBA using excel in the background (I can get more information if you need it) but basically it meant the access image controls could still be used (Object bound frames).

Howeve开发者_高级运维r, I now have the problem of trying to display these in .NET applications, and after countless days of trying different ways of manipulating the byte array I'm close to giving up. I have tried at least 8 different suggested solutions and each one ends with a 'Parameter not recognised' exception when doing Image.fromStream(). Below is the code which has got me the closest so far:

    private void imageExtractTest()
    {
        LogOnDataSetTableAdapters.QueriesTableAdapter qa =
            new LogOnDataSetTableAdapters.QueriesTableAdapter();

        object docO = qa.GetLogonImage();
        if (docO == null || !(docO is byte[]))
        {
            return;
        }
        byte[] doc = (byte[])docO;

        MemoryStream ms = new MemoryStream();
        ms.Write(doc, 0, doc.Length);
        int firstByte;
        int secondByte;
        ms.Seek(0, SeekOrigin.Begin);
        firstByte = ms.ReadByte();
        secondByte = ms.ReadByte();

        if (firstByte != 0x15 && secondByte != 0x1C)
        {
            //ErrorResponse("Stored object is not an Access File.");
            return;
        }

        int fileTypeLoc = 20; // begin of the file type
        short offset; // end of the file type

        byte[] buffer = new byte[2];
        ms.Read(buffer, 0, 2);
        offset = BitConverter.ToInt16(buffer, 0);

        long seekTotal = 0;
        seekTotal += offset;

        string docType = String.Empty;
        for (int i = fileTypeLoc; i < offset; i++)
        {
            docType += (char)doc[i];
        }

        //if I query docType now I get 'Picture\0\0'

        // magic eight bytes 01 05 00 00 03 00 00 00
        ms.Seek(seekTotal, SeekOrigin.Begin);
        buffer = new byte[8];
        ms.Read(buffer, 0, 8);
        seekTotal += 8;

        // Second offset to move to 
        buffer = new byte[4];
        ms.Read(buffer, 0, 4);
        seekTotal += 4;
        long offset2 = BitConverter.ToInt32(buffer, 0);
        seekTotal += offset2;
        ms.Seek(seekTotal, SeekOrigin.Begin);

        // eight empty bytes
        buffer = new byte[8];
        ms.Read(buffer, 0, 8);
        seekTotal += 8;

        // next n bytes are the length of the file
        buffer = new byte[4];
        ms.Read(buffer, 0, 4);
        seekTotal += 4;
        long fileByteLength = BitConverter.ToInt32(buffer, 0);

        // next N bytes are the file
        byte[] data = new byte[fileByteLength];

        // store file bytes in data buffer
        ms.Read(data, 0, Convert.ToInt32(fileByteLength));

        MemoryStream imageStream = new MemoryStream(data);
        Image test = Image.FromStream(imageStream);
    }

This code was adapted from here, I didn't need the various doctypes identification as I'm only dealing with images, however the image type could be any number of things - jpg, bmp, gif, png etc.

I've also tried saving the outputted byte array but I've had no luck viewing that either. But when I point access to the database and get it to view it, everything is fine. Also the .NET Crystal Report designer is able to get these images some how - so they must be in there somewhere...

Has anyone got any ideas?

Marlon


Trying to retrieve an MS-access OLE image field from .NET is way more headache than it is worth. There is some good discussion and info about this topic in this post.

Ultimately, your best, and easiest, solution to be successful with this is to use your working viewing method to save these images as separate files, then import those files into the database as BLOB fields, rather than image fields. Then you can easily read them into .NET.


It is not C# code but here is an Delphi example of a way to solve this problem.

It uses the IOLEObject to draw whatever is stored instead of trying to read out the raw data. Steps:

  1. Read the Access Header in front of the OLE Object
  2. Read out the OLE1 stream
  3. Convert the OLE1 stream to an OLE2 IStorage Object
  4. Use OLELoad to "run" the OLE Ojbect.
  5. Call OLEDraw to draw out the image to a canvas of your choice.


In my case the following function worked. The data are stored by a VB6 application.

public static byte[] ConvertOleBytesToRawBytes(byte[] oleBytes)
{
   // The default encoding is in my case - Western European (Windows), Code Page 1252
   return Encoding.Convert(Encoding.Unicode, Encoding.Default, (byte[])oleBytes);
}


Try this KB http://support.microsoft.com/kb/317701 article from microsoft. It contains information on how to access the image blob from access and display that in winforms application.


I needed to do the exact same thing for some 1600 object of different extension types. In my case it was a legacy database that had literally been used for decades. Over the years many different types of files had been added via an OLE Object frame. Some of the items that looked like "Images" turned out to be Word documents with an embedded image... no telling what other file types were in there? All I know is that I researched and tried different extraction methods for more than a week. Even all of Steven Leban's extraction tools like OLEtoDisk, A2KExportOLEtoJPEG, and SaveOLEtoBitmap. Each of which would extract some images... but their was no one size fits all... it was a mess!

In the end I ended up performing an automated screen shot of each image via VBA using the method below. While this may not have been the most ideal it worked for every file type. However the screen shot method captures a screen shot of the full screen. Once I had all of them extracted I then had to use PhotoShop to do another automated process to batch crop all of the photos. Not ideal but it worked!

Private Sub CaptureAllImages()
    On Error Resume Next
    Me.RecordsetClone.MoveFirst
    Do While Not Me.RecordsetClone.EOF
        Me.Bookmark = Me.RecordsetClone.Bookmark
        Call Pause(2)
        Call SaveClip2Bit("C:\Users\agriggs\Desktop\Parts Images\MasterPart_" & Me.MasterPartNumber & ".bmp")
        Me.RecordsetClone.MoveNext
    Loop

End Sub

Public Function Pause(NumberOfSeconds As Variant)
    On Error GoTo Error_GoTo

    Dim PauseTime As Variant
    Dim start As Variant
    Dim Elapsed As Variant

    PauseTime = NumberOfSeconds
    start = Timer
    Elapsed = 0
    Do While Timer < start + PauseTime
        Elapsed = Elapsed + 1
        If Timer = 0 Then
            ' Crossing midnight
            PauseTime = PauseTime - Elapsed
            start = 0
            Elapsed = 0
        End If
        DoEvents
    Loop

Exit_GoTo:
    On Error GoTo 0
    Exit Function
Error_GoTo:
    Debug.Print Err.Number, Err.Description, Erl
    GoTo Exit_GoTo
End Function

I tried a couple different screen shot modules but found that the SaveClip2Bit worked the best. Lastly I added a common Pause function to insure the image got saved to disk before moving on to the next. As you can imagine 1600 images took a long time to extract but I can now but this project to rest!

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜