Problems extracting an OLE Object from a Rich-Text (RTF) File by parsing the file
Posted on 2004-10-13
I am writing a program that parses out RTF files. I want to be able to parse out embedded OLE Objects, such as an Excel Spreadsheet, and convert the object to the same bytes as you would have for a standalone file -- in other words, stripping the spreadheet out of the RTF file and saving it as a disk file that can be opened.
If you open up an RTF file in notepad and look, you will se that an embedded object looks something like this:
and it continues on until the closing braces.
Now the first problem is that you can't just convert this binary encoding to bytes and save the file. It is close to the right format, but the application can't open it.
Accoring to the RTF FAQ, data objects are written to RTF using the OLESaveToStream function. That makes me think I have to use OLELoadFromStream to retrieve the data.
So here would be the process as I see it:
1. Decode the text representation of the binary to true binary and store as something like a blob.
2. Somehow get the blob to an HGLOBAL and use CreateStreamOnHGlobal to convert it to an IStream.
3. Call OleLoadFromStream to convert the stream to an object.
4. Somehow convert the object to a blob that can be written as a disk file.
I have only been able to get step 1 to work.
Here is some code
// assume I have properly populated my blob here
// Converts the given BLOB to a stream.
BlobToStream( blob, &pStream);
// THE FOLLOWING LINE FAILS!
// returns error REGDB_E_CLASSNOTREG which means
// A specified class is not registered in the registration database.
// Also can indicate that the type of server you requested in the
// CLSCTX enumeration is not registered or the values for
// the server types in the registry are corrupt.
HRESULT hr = OleLoadFromStream(pStream, IID_IPersistStream,
void BlobToStream(const BLOB& blob, IStream** ppStream)
HGLOBAL handle = NULL;
// Create a handle from the output BLOB
handle = ::GlobalAlloc( GMEM_MOVEABLE, blob.cbSize );
::AfxErrorDlg(NULL, _T("Call to GlobalAlloc Failed"), GetLastError());
throw new CMemoryException();
// Copy the blob to the new memory.
if ( blob.pBlobData )
::memcpy( ::GlobalLock( handle ), blob.pBlobData, blob.cbSize );
::GlobalUnlock( handle );
// Create an IStream object that stores data in memory.
HRESULT hr = ::CreateStreamOnHGlobal(handle, TRUE, ppStream);
} // BlobToStream
Finally the questions:
1. Are my steps correct?
2. How come my code doesn't work (see the line that fails)? Could it be possible that I am not decoding the bytes correctly?
3. Once I have the object, how can I get a blob that can be written to a disk file and opened in the application?
Thanks in advance