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
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