将 MS Access 中的 OLE 图片对象转换为 .NET 使用

7
我正在将一个基于Access的系统重新开发为c#.net,然而,当MS从Office 2003升级到Office 2007时,他们在Access中删除了图片编辑器,这意味着以前存储的图片将不再显示在系统中。公司的人使用了一种黑客方法,通过VBA在后台使用Excel保存图像(如果您需要更多信息,我可以提供),但基本上这意味着可以继续使用Access图像控件(对象绑定框架)。
然而,现在我面临的问题是尝试在.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);
    }

这段代码是从这里改编来的:这里,由于我只处理图像,所以不需要各种文档类型的识别,但是图像类型可以是任何东西——jpg、bmp、gif、png等等。
我也尝试保存输出的字节数组,但我没有成功查看它。但是当我将访问数据库并让它查看时,一切都很好。此外,.NET Crystal Report设计师能够以某种方式获取这些图像——因此它们必须在其中某个地方...
有人有什么想法吗?
马龙
5个回答

3

尝试从.NET检索MS-access OLE图像字段比其价值更大。在此帖子中,有关于此主题的一些好的讨论和信息。

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


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

1

这不是C#代码,但这里有一个Delphi示例可以解决这个问题。

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

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

0

我需要对1600个不同扩展类型的对象执行完全相同的操作。在我的情况下,这是一个传统数据库,已经使用了几十年。多年来,许多不同类型的文件通过OLE对象框架添加进来。有些看起来像“图像”的项目实际上是带有嵌入式图像的Word文档...无法确定还有哪些其他文件类型?我只知道我研究并尝试了不同的提取方法超过一周。即使是Steven Leban的提取工具,如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张图片需要很长时间,但现在我可以把这个项目放心地结束了!


0

谢谢提供信息,但我认为你误解了问题。那篇文章只是解释如何将图像文件存储到数据库中,而不是如何将已经存储的OLE对象转换为图像。显然,OLE就是魔法... - Marlon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接