Embedding OLE Objects Into an Access Database
Posted
by Vigen Dolbakyan
on July 29th, 1999

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;
LPOLE1STREAM lpOle1Stream = NULL;
LPOLECLIENTSITE lpClientSite = NULL;
HGLOBAL hgMemObject = NULL;
HGLOBAL hgAccessHeader = NULL;
HGLOBAL hgNewLongBinary = NULL;
LPBYTE lpbMemObject = NULL;
LPBYTE lpbAccessHeader = NULL;
LPBYTE lpbNewLongBinary= NULL;
int nIDLast = 1;
ULARGE_INTEGER ulStreamSize;
// 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() )
m_pSet->Open();
m_pSet->Edit();
// 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 );
m_pSet->Update();
/////////////////////////////////////////////////////////////////////////////////////
COleDocument Doc;
HGLOBAL hgAccessHeader = NULL;
//To get the class header and implementation code download the source
CLongBinaryItem oci(&Doc);
CString strPath;
HGLOBAL hgAccessHeader = NULL;
::GetTempPath(MAX_PATH,strPath.GetBuffer(MAX_PATH));
strPath.ReleaseBuffer();
strPath += _T("some.bmp");
//Create from file
if(!oci.CreateFromFile(strPath))
{//Processing fault}
// allocate enough memory to write access header to
hgAccessHeader = GlobalAlloc(GMEM_MOVEABLE, sizeof( OLEOBJHDR ) + 100);
int iHeaderSize = WriteAccessHeader(hgAccessHeader,oci.m_lpObject);
Downloads
Download demo project - 81 KbDownload source - 10 Kb

Comments
Nike Air Max 1 FB release, cause a eager color character, the unheard of shoes
Posted by Geozyoceada on 04/24/2013 03:20pmIn the summer in a pane inside the cool sprite seems to be a godlike fitting, but if the sprite "feet"? Resolution also give you a trip, accompany a sustenance! This summer, Nike and Sprite [url=http://markwarren.org.uk/goodbuy.cfm]nike free[/url] and his sneakers to a commingling of classic snow spread of non-professional, white and indecent color scheme in the definitive Nike Arrogance Max 1 shoes let it be known a refreshing chill scent.[url=http://markwarren.org.uk/property-waet.cfm]nike air max 90[/url] Summer is the metre to pick a cleanly shoe, shoes should be a acceptable choice. Qualifying series Nike Air Max HomeTurf metropolis recently definitely comes up, this series in the immortal Breath Max shoes to London, Paris and Milan the three paid tribute to the iconic metropolis of Europe, combined with the characteristics of the three cities, Air Max 1 HYP,Air Max 90 HYP,Superciliousness Max 1 and shoes such as Quality Max 95, combined [url=http://turbo-vac.co.uk/components_13.cfm]nike free uk[/url] with the Hyperfuse, as kind-heartedly as a collection of materials, such as suede, Whether you crave going or retro-everything.
ReplyHow to Extract Data
Posted by Legacy on 12/01/2003 12:00amOriginally posted by: Amit
Is there a way to extract the OLE data and save it as a file w/ the correct extension?
Thanks,
ReplyAmit
Excellent
Posted by Legacy on 05/08/2002 12:00amOriginally posted by: P.S.Kumaran
ReplyYou are the man
Posted by Legacy on 07/13/2001 12:00amOriginally posted by: Marius Mihalca
Thanks u very much! I liked your articole verry much. Keep up the good work!
ReplyWord documents in Acces database
Posted by Legacy on 04/17/2001 12:00amOriginally 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.
ReplyCan't extract it from MS Access
Posted by Legacy on 03/19/2001 12:00amOriginally 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?
ReplyWhile I am inserting an object using the same dialog from MS Access, everything is fine
OleConvertOleStreamToIStorage help!
Posted by Legacy on 04/28/2000 12:00amOriginally posted by: Fethi Migaou
Hi;
ReplyDoes 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.
Thanks.
Fethi
Where is "Using Storage Objects in OLE"
Posted by Legacy on 01/18/2000 12:00amOriginally 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...
ReplyMihai