Embedding OLE Objects Into an Access Database


Desktop-as-a-Service Designed for Any Cloud ? Nutanix Frame

This article illustrates how to programmatically embed OLE objects into Microsoft Access database.

  • 1. Write the header
  • 2. Write the name GetUserType( USERCLASSTYPE_SHORT )
  • 3. Write the class ProgIDFromCLSID
  • 4. Write the storage OleConvertIStorageToOLESTREAM

One remaining item is that before Access actually writes out the storage, it adds a stream to it that contains information that is lost in the process of OleConvertIStorageToOLESTREAM/OleConvertOLESTREAMToIStorage. However, if this stream isn't there, Access just uses the defaults so it isn't fatal.

This means that to read an object back in the following steps have to be taken:

  • 1. Read the header
  • 2. Read the name (ignore)
  • 3. Read the class CLSIDFromProgID
  • 4. Read the storage OleConvertOLESTREAMToIStorage

The sample attached uses the Insert OLE Object dialog.

DWORD dwObjectSize = 0;
DWORD dwCheckSum = 0;
int iHeaderSize = 0;
CLongBinaryDoc* pDoc = NULL;
CLongBinaryItem* pClone = NULL;
LPSTREAM lpObjStream = NULL;
HGLOBAL hgMemObject = NULL;
HGLOBAL hgAccessHeader = NULL;
HGLOBAL hgNewLongBinary = NULL;
LPBYTE lpbMemObject = NULL;
LPBYTE lpbAccessHeader = NULL;
LPBYTE lpbNewLongBinary= NULL;
int nIDLast = 1;

// allocate enough memory to write access header to
if( NULL == ( hgAccessHeader = GlobalAlloc(GMEM_MOVEABLE, sizeof( OLEOBJHDR ) + 100 )))
{//Processing fault}

// write the access-header
if( 0 == ( iHeaderSize = WriteAccessHeader(hgAccessHeader, m_pSelection->m_lpObject )))
{//Processing fault}

// adjust size of memory-block according to WriteAccessHeader()
if ( NULL == ( hgAccessHeader = GlobalReAlloc(hgAccessHeader, iHeaderSize, GMEM_MOVEABLE )))
{//Processing fault}
// Alloc the memory to store the long binary
if ( NULL == ( hgMemObject = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, 0 )))
{//Processing fault}
// create an IStream on top of the ILockBytes 
// (indicate automatic deletion of HGLOBAL on Release)
// IMPORTANT: This IStream* (and therewith the memory
// will be released during DeleteOleStream()
if ( FAILED( ::CreateStreamOnHGlobal( hgMemObject, TRUE, &lpObjStream )))
{//Processing fault}
// convert the IStream to an IStorage ( ACCESS stores OLE Objects the OLE1 way)
// in order to do so, we first have to wrap the OLE2 IStream into an OLE1 OLESTREAM
// see MSDN Article: "More on Using Storage Objects in OLE"
// NOTE: the Get() and Put() functions wrapped in the OLESTREAMVTBL will
// access the newly opened stream using an IStream* variable
if ( NULL == ( lpOle1Stream = CreateOle1Stream( lpObjStream, 0 )))
{//Processing fault}
// if the OLE object was not loaded from the DB, but rather
// created using CreateNewItem(), or if the item was modified
// after loading it from the DB, the m_lpStorage of the item
// will not contain the updated item information, since it wasn't
// yet saved to that IStorage.
// in order to assure that m_lpStorage points to the updated
// information, we force the item to save it's data.
// NOTE: GetClientSiteFromHere() will internally call GetClientSite()
// and GetInterface(). The MFC's implementation of GetInterface()
// however, doesn't AddRef() "lpClientSite", so we don't have to
// release it.
lpClientSite = m_pSelection->GetClientSiteFromHere();
if ( FAILED ( lpClientSite->SaveObject()))
{//Processing fault}
// convert the client-item's IStorage* to an OLE1 OLESTREAM
// NOTE: as soon as the stream gets converted, the data will be written
// to the IStream* and thereby to the HGLOBAL (see Put() function)
if ( FAILED ( ::OleConvertIStorageToOLESTREAM(
m_pSelection->m_lpStorage, ( LPOLESTREAM ) lpOle1Stream )))
{//Processing fault}
// adjust stream's size to reflect exactly the amount of bytes written to it
ulStreamSize.LowPart = lpOle1Stream->dwSize;
ulStreamSize.HighPart = 0;
if ( FAILED ( lpOle1Stream->lpStream->SetSize( ulStreamSize )))
{//Processing fault}
// allocate a new memory handle which is large enough to hold the
// Access header, the OLE1 OLESTREAM containing the object and the checksum
if ( NULL == ( hgNewLongBinary = GlobalAlloc( 
GHND, iHeaderSize + lpOle1Stream->dwSize + sizeof( DWORD ))))
{//Processing fault}
// and copy the complete information into it
if ( NULL == ( lpbAccessHeader = ( LPBYTE ) GlobalLock(hgAccessHeader )))
{//Processing fault}
if ( NULL == ( lpbMemObject = ( LPBYTE ) GlobalLock( hgMemObject)))
{//Processing fault}
if ( NULL == ( lpbNewLongBinary = ( LPBYTE ) GlobalLock( hgNewLongBinary )))
{//Processing fault}

memcpy( lpbNewLongBinary, lpbAccessHeader, iHeaderSize );
lpbNewLongBinary += iHeaderSize;
memcpy( lpbNewLongBinary, lpbMemObject, lpOle1Stream->dwSize );
lpbNewLongBinary += lpOle1Stream->dwSize;
memcpy( lpbNewLongBinary, &dwCheckSum, sizeof( DWORD ));
lpbNewLongBinary += sizeof( DWORD );
GlobalUnlock( hgMemObject );
GlobalUnlock( hgAccessHeader );
GlobalUnlock( hgNewLongBinary );

// make sure recordset is open and in edit mode
if( !m_pSet->IsOpen() )

// throw away old memory and point the longbinary to the new memory
GlobalFree( m_pSet->m_OLE_Object.m_hData );
m_pSet->m_OLE_Object.m_hData = hgNewLongBinary;

// set the CLongBinary length
m_pSet->m_OLE_Object.m_dwDataLength = 
iHeaderSize + lpOle1Stream->dwSize + sizeof( DWORD );

// Update the datasource
m_pSet->SetFieldDirty( &m_pSet->m_OLE_Object, TRUE );
m_pSet->SetFieldNull( &m_pSet->m_OLE_Object, FALSE );
If given only file path you can use following lines of code


COleDocument Doc;

HGLOBAL hgAccessHeader = NULL;

//To get the class header and implementation code download the source
CLongBinaryItem oci(&Doc);
CString strPath;

HGLOBAL hgAccessHeader = NULL; 


strPath += _T("some.bmp");
//Create from file
{//Processing fault}
// allocate enough memory to write access header to
hgAccessHeader = GlobalAlloc(GMEM_MOVEABLE, sizeof( OLEOBJHDR ) + 100);
int iHeaderSize = WriteAccessHeader(hgAccessHeader,oci.m_lpObject);


Download demo project - 81 Kb
Download source - 10 Kb


  • How to Extract Data

    Posted by Legacy on 12/01/2003 08:00am

    Originally posted by: Amit

    Is there a way to extract the OLE data and save it as a file w/ the correct extension?


  • Excellent

    Posted by Legacy on 05/08/2002 07:00am

    Originally posted by: P.S.Kumaran

    Very usefull code, Excellent work 

  • You are the man

    Posted by Legacy on 07/13/2001 07:00am

    Originally posted by: Marius Mihalca

    Thanks u very much! I liked your articole verry much. Keep up the good work!

  • Word documents in Acces database

    Posted by Legacy on 04/17/2001 07:00am

    Originally posted by: Udi

    Does anyone know how to store Word documents on Access 2000, possibly using OLE embedding, without flickering, with speed etc.

    I then want to merge documents according to queries, back into Word documents.

  • Can't extract it from MS Access

    Posted by Legacy on 03/19/2001 08:00am

    Originally posted by: Nikola

    O.K. I ran the program and yes, it inserts an object. But once I've double clicked onto it the following message appears: "A problem occured while Microsoft Access was communicating with OLE Server" - "Close the OLE Server and restart it outside of Microsoft Access". Yet the object was stored as a Long binary data. How could I get rid of it?
    While I am inserting an object using the same dialog from MS Access, everything is fine

  • OleConvertOleStreamToIStorage help!

    Posted by Legacy on 04/28/2000 07:00am

    Originally posted by: Fethi Migaou

    Does anyone know where I can find some (complete)
    examples on how to use OleConvertOleStreamToIStorage or
    OleConvertOleStreamToIStorageEx functions. I am trying
    to convert an OLE1 storage file format to OLE2 storage
    file. Your help is appreciated.

  • Where is "Using Storage Objects in OLE"

    Posted by Legacy on 01/18/2000 08:00am

    Originally posted by: Mihai Filimon

    Hi, and sorry if I bother you, but I am searching for the MSDN article: "Using Storage Objects in OLE". Where I can find it. Thanks in advance...

  • You must have javascript enabled in order to post comments.

Leave a Comment
  • Your email address will not be published. All fields are required.

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date