Преобразование объекта изображения OLE из MS Access для использования в .NET - PullRequest
7 голосов
/ 10 марта 2010

Я работаю над перестройкой системы на основе Access в c # .net, однако, когда MS перешел из офиса 2003 в офис 2007, они удалили редактор изображений в рамках доступа - это означало, что ранее сохраненные изображения больше не будут отображаться в системе. Ребята из компании сделали хак, который в основном сохранил изображения с VBA, используя Excel в фоновом режиме (я могу получить больше информации, если вам это нужно), но в основном это означало, что все еще можно использовать элементы управления доступом к изображениям (рамки, связанные с объектом).

Однако теперь у меня возникла проблема с попыткой отобразить их в приложениях .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 может каким-то образом получить эти изображения, поэтому они должны быть где-то там ...

У кого-нибудь есть идеи?

Marlon

Ответы [ 5 ]

3 голосов
/ 10 марта 2010

Попытка извлечь поле изображения OLE MS-доступа из .NET - это гораздо больше головной боли, чем оно того стоит. В этом сообщении есть хорошая дискуссия и информация по этой теме .

В конечном счете, лучшим и легким решением для достижения этой цели является использование рабочего метода просмотра, чтобы сохранить эти изображения в виде отдельных файлов, а затем импортировать эти файлы в базу данных как поля BLOB, а не поля изображений. Тогда вы можете легко прочитать их в .NET.

1 голос
/ 10 февраля 2012

В моем случае сработала следующая функция. Данные хранятся в приложении 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 голос
/ 17 ноября 2010

Это не код C #, но вот пример Delphi способа решения этой проблемы.

Он использует IOLEObject для рисования всего, что хранится, вместо попытки считывания необработанных данных. Шаги:

  1. Чтение заголовка доступа перед объектом OLE
  2. Считать поток OLE1
  3. Преобразование потока OLE1 в OLE2 IStorage Объект
  4. Используйте OLELoad для «запуска» OLE-объекта OLE.
  5. Позвоните OLEDraw , чтобы нарисовать изображение на холсте по вашему выбору.
0 голосов
/ 16 мая 2019

Мне нужно было сделать то же самое для примерно 1600 объектов различных типов расширений. В моем случае это была устаревшая база данных, которая буквально использовалась десятилетиями. За эти годы много разных типов файлов были добавлены через OLE Object frame. Некоторые из элементов, которые выглядели как «Изображения», оказались документами Word со встроенным изображением ... не говоря, какие еще типы файлов были там? Все, что я знаю, это то, что я исследовал и пробовал разные методы извлечения более недели. Даже все инструменты извлечения Стивена Лебана, такие как 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 голосов
/ 07 июля 2010

Попробуйте эту статью KB http://support.microsoft.com/kb/317701 от Microsoft. Он содержит информацию о том, как получить доступ к блоку изображений из доступа и отобразить его в приложении winforms.

...