Home All Groups Group Topic Archive Search About

Extract ole object using vb from access db

Author
20 Oct 2005 5:33 PM
Bala
Hi

In my access table one of the field data type OLE Object. and its contain
word document file and omni file. I need to save this file into local
folder. any idea how to do this thru vb?

Thanks
Bala

Author
20 Oct 2005 9:26 PM
Jeff Johnson [MVP: VB]
"Bala" <B***@discussions.microsoft.com> wrote in message
news:CCC8EC0F-AD36-42A2-86A7-1794B70C8684@microsoft.com...

> In my access table one of the field data type OLE Object. and its contain
> word document file and omni file. I need to save this file into local
> folder. any idea how to do this thru vb?

How much code do you want? I'm going to assume you can already read this
data into a byte array (and if you can't search back a few threads for one
with"BLOB" in the title) and just go for the writing part:

Dim nFile as Integer

nFile = FreeFile()
Open <PathToFile> For Binary Access Write As #nFile
Put #nFile, , MyByteArray()
Close #nFile
Author
21 Oct 2005 7:40 PM
Paul Clement
On Thu, 20 Oct 2005 10:33:12 -0700, "Bala" <B***@discussions.microsoft.com> wrote:

¤ Hi
¤
¤ In my access table one of the field data type OLE Object. and its contain
¤ word document file and omni file. I need to save this file into local
¤ folder. any idea how to do this thru vb?

If it's stored as an OLE file then the OLE header needs to be stripped from the file. See if the
following works for you:

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
        (lpvDest As Any, lpvSource As Any, ByVal cbCopy As Long)

Type PT
    Width As Integer
    Height As Integer
End Type

Type OBJECTHEADER
    Signature As Integer
    HeaderSize As Integer
    ObjectType As Long
    NameLen As Integer
    ClassLen As Integer
    NameOffset As Integer
    ObjectSize As PT
    OleInfo As String * 256
End Type

Function GetOLEField()

Dim cnn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim strSQL As String
Dim strFilePath As String

cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
                     "Data Source=e:\My Documents\db1 XP.mdb;" & _
                     "Jet OLEDB:Engine Type=4;"

strSQL = "Select OLEField from Table1 WHERE [record ID]=3"

rs.Open strSQL, cnn

strFilePath = GetFile(rs.Fields("OLEField"), "c:\temp\")

rs.Close
cnn.Close

End Function

Private Function GetFile(ByVal ADOField As ADODB.Field, _
                        tmpPath As String) As String

Dim Arr() As Byte
Dim ObjHeader As OBJECTHEADER
Dim Buffer As String
Dim ObjectOffset As Long
Dim FileOffset As Long
Dim FileHeaderOffset As Integer
Dim FileStream() As Byte
Dim i As Long
Dim tmpDoc

    'Resize the array, then fill it with
    'the entire contents of the field
    ReDim Arr(ADOField.ActualSize)
    Arr() = ADOField.GetChunk(ADOField.ActualSize)

    'Copy the first 19 bytes into a variable of the
    ' defined type OBJECTHEADER
    CopyMemory ObjHeader, Arr(0), 19

    'Determine where the header ends
    ObjectOffset = ObjHeader.HeaderSize + 1

    'Grab enough bytes after the OLE header to get file header
    Buffer = ""

    For i = ObjectOffset To ObjectOffset + 512
        Buffer = Buffer & Chr(Arr(i))
    Next i

    'Make sure the class of the object is Word Document
    If Mid(Buffer, 12, 13) = "Word.Document" Then
        FileHeaderOffset = InStr(Buffer, "ÐÏ")
        If FileHeaderOffset > 0 Then
            'Calculate the beginning of the document
            FileOffset = ObjectOffset + FileHeaderOffset - 1

            'Move document into its own array
            ReDim FileStream(UBound(Arr) - FileOffset)
            CopyMemory FileStream(0), Arr(FileOffset), UBound(Arr) - FileOffset + 1

            'Document file path
            tmpImg = tmpPath & "WordFile.doc"

            Open tmpDoc For Binary As #1

            For i = 0 To UBound(FileStream)
                Put #1, , FileStream(i)
            Next i

            Close #1

            GetFile = tmpDoc

        End If

    End If

End Function


Paul
~~~~
Microsoft MVP (Visual Basic)
Author
22 Oct 2005 4:57 AM
Bala
thanks. got it.


bala

Show quoteHide quote
"Paul Clement" wrote:

> On Thu, 20 Oct 2005 10:33:12 -0700, "Bala" <B***@discussions.microsoft.com> wrote:
>
> ¤ Hi
> ¤
> ¤ In my access table one of the field data type OLE Object. and its contain
> ¤ word document file and omni file. I need to save this file into local
> ¤ folder. any idea how to do this thru vb?
>
> If it's stored as an OLE file then the OLE header needs to be stripped from the file. See if the
> following works for you:
>
> Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
>         (lpvDest As Any, lpvSource As Any, ByVal cbCopy As Long)
>        
> Type PT
>     Width As Integer
>     Height As Integer
> End Type
>
> Type OBJECTHEADER
>     Signature As Integer
>     HeaderSize As Integer
>     ObjectType As Long
>     NameLen As Integer
>     ClassLen As Integer
>     NameOffset As Integer
>     ObjectSize As PT
>     OleInfo As String * 256
> End Type
>
> Function GetOLEField()
>
> Dim cnn As New ADODB.Connection
> Dim rs As New ADODB.Recordset
> Dim strSQL As String
> Dim strFilePath As String
>
> cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
>                      "Data Source=e:\My Documents\db1 XP.mdb;" & _
>                      "Jet OLEDB:Engine Type=4;"
>
> strSQL = "Select OLEField from Table1 WHERE [record ID]=3"
>
> rs.Open strSQL, cnn
>
> strFilePath = GetFile(rs.Fields("OLEField"), "c:\temp\")
>
> rs.Close
> cnn.Close
>
> End Function
>
> Private Function GetFile(ByVal ADOField As ADODB.Field, _
>                         tmpPath As String) As String
>
> Dim Arr() As Byte
> Dim ObjHeader As OBJECTHEADER
> Dim Buffer As String
> Dim ObjectOffset As Long
> Dim FileOffset As Long
> Dim FileHeaderOffset As Integer
> Dim FileStream() As Byte
> Dim i As Long
> Dim tmpDoc
>
>     'Resize the array, then fill it with
>     'the entire contents of the field
>     ReDim Arr(ADOField.ActualSize)
>     Arr() = ADOField.GetChunk(ADOField.ActualSize)
>    
>     'Copy the first 19 bytes into a variable of the
>     ' defined type OBJECTHEADER
>     CopyMemory ObjHeader, Arr(0), 19
>    
>     'Determine where the header ends
>     ObjectOffset = ObjHeader.HeaderSize + 1
>    
>     'Grab enough bytes after the OLE header to get file header
>     Buffer = ""
>    
>     For i = ObjectOffset To ObjectOffset + 512
>         Buffer = Buffer & Chr(Arr(i))
>     Next i
>    
>     'Make sure the class of the object is Word Document
>     If Mid(Buffer, 12, 13) = "Word.Document" Then
>         FileHeaderOffset = InStr(Buffer, "ÐÏ")
>         If FileHeaderOffset > 0 Then
>             'Calculate the beginning of the document
>             FileOffset = ObjectOffset + FileHeaderOffset - 1
>            
>             'Move document into its own array
>             ReDim FileStream(UBound(Arr) - FileOffset)
>             CopyMemory FileStream(0), Arr(FileOffset), UBound(Arr) - FileOffset + 1
>            
>             'Document file path
>             tmpImg = tmpPath & "WordFile.doc"
>
>             Open tmpDoc For Binary As #1
>        
>             For i = 0 To UBound(FileStream)
>                 Put #1, , FileStream(i)
>             Next i
>            
>             Close #1
>            
>             GetFile = tmpDoc
>        
>         End If
>            
>     End If
>            
> End Function
>
>
> Paul
> ~~~~
> Microsoft MVP (Visual Basic)
>
Author
8 Nov 2005 1:07 AM
Tony
Hi Paul,

Your post was very educational.  The approach worked for me for Word and
PowerPoint documents but I ran into problems with Excel and PDF documents
(both apps of which appear to be OLE registered) and OLE Packaged documents.

Is there something you can think of that is a generic way to
access/dissect/process ole wrapper and data information?  Perhaps a
class/structure similar to OBJECTHEADER that works for anything in an OLE
field?

What we want to do is programmatically loop through records in an Access
table (each of which has an OLE field with an embedded document) and write
that document as its original file back on to the hard disk.

There has to be a standard/generic way of doing this because Microsoft seems
to be able to, i.e. a user can always get ole/package information or open the
document by double-clicking.

Just a tidbit for other users...Your example below is for VB 6.  I am using
VB .Net.  I am posting the equivalent code below:

  Private Function GetHeader(Field) As OBJECTHEADER
        Dim ObjHeader As OBJECTHEADER
        Dim sar(20) As Byte
        Dim MyGC As GCHandle
        Dim Arr() As Byte

        ReDim Arr(Field.ActualSize)
        Arr = Field.GetChunk(Field.ActualSize)
        Array.Copy(Arr, 0, sar, 0, 20)

        MyGC = GCHandle.Alloc(sar, GCHandleType.Pinned)

       'Marshals data from an unmanaged block of memory to a newly
       'allocated managed object of the specified type.

        ObjHeader = Marshal.PtrToStructure(MyGC.AddrOfPinnedObject,

ObjHeader.GetType)

        Return ObjHeader
    End Function

Thanks
Tony

P.S. In the OBJECTHEADER definition my compiler didn't like:
OleInfo As String * 256

What does the '* 256' mean and what is OleInfo supposed to be?

Show quoteHide quote
"Paul Clement" wrote:

> On Thu, 20 Oct 2005 10:33:12 -0700, "Bala" <B***@discussions.microsoft.com> wrote:
>
> ¤ Hi
> ¤
> ¤ In my access table one of the field data type OLE Object. and its contain
> ¤ word document file and omni file. I need to save this file into local
> ¤ folder. any idea how to do this thru vb?
>
> If it's stored as an OLE file then the OLE header needs to be stripped from the file. See if the
> following works for you:
>
> Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
>         (lpvDest As Any, lpvSource As Any, ByVal cbCopy As Long)
>        
> Type PT
>     Width As Integer
>     Height As Integer
> End Type
>
> Type OBJECTHEADER
>     Signature As Integer
>     HeaderSize As Integer
>     ObjectType As Long
>     NameLen As Integer
>     ClassLen As Integer
>     NameOffset As Integer
>     ObjectSize As PT
>     OleInfo As String * 256
> End Type
>
> Function GetOLEField()
>
> Dim cnn As New ADODB.Connection
> Dim rs As New ADODB.Recordset
> Dim strSQL As String
> Dim strFilePath As String
>
> cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
>                      "Data Source=e:\My Documents\db1 XP.mdb;" & _
>                      "Jet OLEDB:Engine Type=4;"
>
> strSQL = "Select OLEField from Table1 WHERE [record ID]=3"
>
> rs.Open strSQL, cnn
>
> strFilePath = GetFile(rs.Fields("OLEField"), "c:\temp\")
>
> rs.Close
> cnn.Close
>
> End Function
>
> Private Function GetFile(ByVal ADOField As ADODB.Field, _
>                         tmpPath As String) As String
>
> Dim Arr() As Byte
> Dim ObjHeader As OBJECTHEADER
> Dim Buffer As String
> Dim ObjectOffset As Long
> Dim FileOffset As Long
> Dim FileHeaderOffset As Integer
> Dim FileStream() As Byte
> Dim i As Long
> Dim tmpDoc
>
>     'Resize the array, then fill it with
>     'the entire contents of the field
>     ReDim Arr(ADOField.ActualSize)
>     Arr() = ADOField.GetChunk(ADOField.ActualSize)
>    
>     'Copy the first 19 bytes into a variable of the
>     ' defined type OBJECTHEADER
>     CopyMemory ObjHeader, Arr(0), 19
>    
>     'Determine where the header ends
>     ObjectOffset = ObjHeader.HeaderSize + 1
>    
>     'Grab enough bytes after the OLE header to get file header
>     Buffer = ""
>    
>     For i = ObjectOffset To ObjectOffset + 512
>         Buffer = Buffer & Chr(Arr(i))
>     Next i
>    
>     'Make sure the class of the object is Word Document
>     If Mid(Buffer, 12, 13) = "Word.Document" Then
>         FileHeaderOffset = InStr(Buffer, "ÐÏ")
>         If FileHeaderOffset > 0 Then
>             'Calculate the beginning of the document
>             FileOffset = ObjectOffset + FileHeaderOffset - 1
>            
>             'Move document into its own array
>             ReDim FileStream(UBound(Arr) - FileOffset)
>             CopyMemory FileStream(0), Arr(FileOffset), UBound(Arr) - FileOffset + 1
>            
>             'Document file path
>             tmpImg = tmpPath & "WordFile.doc"
>
>             Open tmpDoc For Binary As #1
>        
>             For i = 0 To UBound(FileStream)
>                 Put #1, , FileStream(i)
>             Next i
>            
>             Close #1
>            
>             GetFile = tmpDoc
>        
>         End If
>            
>     End If
>            
> End Function
>
>
> Paul
> ~~~~
> Microsoft MVP (Visual Basic)
>
Author
16 Nov 2005 8:23 PM
Paul Clement
On Mon, 7 Nov 2005 17:07:02 -0800, "Tony" <T***@discussions.microsoft.com> wrote:

¤ Hi Paul,
¤
¤ Your post was very educational.  The approach worked for me for Word and
¤ PowerPoint documents but I ran into problems with Excel and PDF documents
¤ (both apps of which appear to be OLE registered) and OLE Packaged documents.
¤

For Excel, you need to look for Excel.Sheet. I haven't tried a PDF file.

¤ Is there something you can think of that is a generic way to
¤ access/dissect/process ole wrapper and data information?  Perhaps a
¤ class/structure similar to OBJECTHEADER that works for anything in an OLE
¤ field?
¤

I believe that the class name of the document is stored in the OLE header information although I've
never looked at the specifications for embedded documents so I can't really help with the OLE info.
From the class name the host application can then determine the native application for the document
through its registry entries and then launch the app with the document.

¤ What we want to do is programmatically loop through records in an Access
¤ table (each of which has an OLE field with an embedded document) and write
¤ that document as its original file back on to the hard disk.
¤
¤ There has to be a standard/generic way of doing this because Microsoft seems
¤ to be able to, i.e. a user can always get ole/package information or open the
¤ document by double-clicking.

You wouldn't be the first one to try this. However, I've looked at some of these embedded documents
and can't seem to find a consistency as to how the OLE wrapper is created - at least not without use
of the OLE API. Trying to scrape off the OLE wrapper through VB code seems to necessitate exact
knowledge of the wrapper contents for each document type.

¤ P.S. In the OBJECTHEADER definition my compiler didn't like:
¤ OleInfo As String * 256
¤
¤ What does the '* 256' mean and what is OleInfo supposed to be?
¤

It's just fixed space to pad out the buffer to the proper length.


Paul
~~~~
Microsoft MVP (Visual Basic)