从MS Access转换OLE图像对象以在.NET中使用

时间:2010-03-10 11:37:13

标签: c# .net ms-access ole

我正在努力将基于Access的系统重新开发到c#.net,但是当MS从办公室2003迁到办公室2007时,他们删除了访问中的图片编辑器 - 这意味着以前存储的图片将不再显示在系统中。该公司的人做了一个黑客,基本上使用excel在后台保存了VBA图像(如果你需要它我可以获得更多信息)但基本上它意味着仍然可以使用访问图像控件(对象绑定帧)。

但是,我现在遇到了尝试在.NET应用程序中显示这些问题的问题,并且经过无数天尝试操作字节数组的不同方法后,我接近放弃了。我尝试了至少8个不同的建议解决方案,每个解决方案在执行Image.fromStream()时以“参数未识别”异常结束。下面是迄今为止让我最接近的代码:

    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);
    }

此代码改编自here,我不需要各种doctypes识别,因为我只处理图像,但图像类型可以是任意数量的东西 - jpg,bmp,gif,png等。

我也尝试保存输出的字节数组,但我也没有运气查看。但是当我指向访问数据库并让它查看它时,一切都很好。此外,.NET Crystal Report设计器能够获得这些图像的方式 - 所以它们必须在某处......

有没有人有任何想法?

马龙

5 个答案:

答案 0 :(得分:3)

尝试从.NET检索MS访问OLE图像字段比使用它更令人头痛。在this post中有关于此主题的一些很好的讨论和信息。

最终,您最好,最简单的解决方案是使用您的工作查看方法将这些图像保存为单独的文件,然后将这些文件作为BLOB字段而不是图像字段导入数据库。然后,您可以轻松地将它们读入.NET。

答案 1 :(得分:1)

这不是C#代码,但这是解决此问题的Delphi example方法。

它使用IOLEObject绘制存储的内容,而不是尝试读取原始数据。   步骤进行:

  1. 读取OLE对象前面的Access Header
  2. 读出OLE1流
  3. 将OLE1流转换为OLE2 IStorage对象
  4. 使用OLELoad“运行”OLE Ojbect。
  5. 调用OLEDraw将图像绘制到您选择的画布上。

答案 2 :(得分:1)

在我的情况下,以下功能有效。数据由VB6应用程序存储。

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);
}

答案 3 :(得分:0)

尝试Microsoft的这篇KB http://support.microsoft.com/kb/317701文章。它包含有关如何从访问中访问图像blob以及在winforms应用程序中显示的信息。

答案 4 :(得分:0)

我需要对大约1600个不同扩展类型的对象执行完全相同的操作。就我而言,这是一个已经使用了数十年的遗留数据库。多年来,通过OLE对象框架添加了许多不同类型的文件。某些看起来像“图像”的项目竟然是带有嵌入式图像的Word文档……不知道其中还有其他文件类型吗?我所知道的是,我研究和尝试了超过一周的不同提取方法。甚至所有Steven Leban's提取工具,例如OLEtoDisk,A2KExportOLEtoJPEG和SaveOLEtoBitmap。每一个都会提取一些图像...但是它们大小都不适合所有...太乱了!

最后,我最终使用以下方法通过VBA对每个图像进行了自动截屏。尽管这可能不是最理想的选择,但它适用于每种文件类型。但是,截屏方法会捕获全屏的截屏。提取完所有照片后,我必须使用PhotoShop进行另一个自动处理,以批量修剪所有照片。不太理想,但是有效!

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

我尝试了几个不同的屏幕截图模块,但发现SaveClip2Bit效果最好。最后,我添加了一个通用的暂停功能,以确保在继续下一个图像之前将图像保存到磁盘。您可以想象,提取了1600张图像花费了很长时间,但现在我可以使该项目停止!