I need to be able to pass a pointer to a (managed code) Stream object to a
COM method so it can serialize its data into the stream.
I have a C# application that makes uses of Randolf Duke's MimeSniffer COM
component (http://www.codeproject.com/internet/mimesniffer.asp). My
application (pop3ImageGrabber) uses MimeSniffer to identify each image
attachment in emails that have been collected from a POP3 mailbox.
MimeSniffer gives the option of saving each attachment to a stream. It
provides two methods for this:
interface IMimeBody : IDispatch {
HRESULT ExportAsFile([in] BSTR Path, [out,retval] VARIANT_BOOL*
pbvarResult);
HRESULT Export([in] LPUNKNOWN pStream, [out,retval] VARIANT_BOOL*
pbvarResult);
}
I am currently saving to a file on disk using ExportAsFile(), then loading
the saved image back from disk using:
attachment.ExportAsFile(path + "tmp_" + attachFileName); // save the image
to disk
Bitmap image = new Bitmap(path + "tmp_" + attachFileName); // load it into a
bitmap for processing
However, for performance reasons I'd like to be able to simply do this:
MemoryStream ms = new MemoryStream();
bool err = attachment.Export(ms);
image = new Bitmap(ms);
But this causes the run-time error:
"An unhandled exception of type 'System.ArgumentException' occurred in
system.drawing.dll - Invalid parameter used"
when the call to Bitmap(ms) is made because the MemoryStream object wasn't
written to by attachment.Export() - size remains zero.
I tried various permutations of marshaling including:
MemoryStream ms = new MemoryStream();
IntPtr pStream = Marshal.GetIUnknownForObject(ms);
attachment.Export(pStream);
Marshal.Release(pStream);
image = new Bitmap(ms);
This doesn't work either. I've Googled all over without finding a solution.
You can see from the C++ source for the COM methods Export() and
ExportAsFile() quoted below that the method expects a pointer to an IStream.
STDMETHODIMP CMimeBody::Export(LPUNKNOWN pStream, VARIANT_BOOL *pbvarResult)
{
CYYSType& type = GetField(_T("Content-Transfer-Encoding"));
*pbvarResult = VARIANT_FALSE;
if (type.GetType() != CYYSType::type_Long)
type = (long)CMimeDecoder::mechanism_unknown;
if (type.IsValid())
{
if (type.GetType() == CYYSType::type_Long)
{
CYYSType* bContentIsRaw = NULL;
CYYSType& body = GetBody(bContentIsRaw);
if (body.GetType() == CYYSType::type_Bulk)
{
IStreamPtr pDest;
if (SUCCEEDED(pStream->QueryInterface(&pDest)))
{
long encoding = bContentIsRaw != NULL && bContentIsRaw->m_nVal ?
CMimeDecoder::mechanism_unknown : (long)type;
CMIMECode* pCoder = CMIMECode::GetCoderByTransferType(encoding);
if (pCoder != NULL)
{
SEEK_TO_BEGIN(body.m_streamVal)
pCoder->Decode(body.m_streamVal, pDest);
*pbvarResult = VARIANT_TRUE;
delete pCoder;
}
}
}
}
}
return S_OK;
}
STDMETHODIMP CMimeBody::ExportAsFile(BSTR Path, VARIANT_BOOL* pbvarResult)
{
USES_CONVERSION;
*pbvarResult = VARIANT_FALSE;
HANDLE hFile = ::CreateFile(OLE2CT(Path), GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, 0, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
CFileSystemStream* pStream = new CFileSystemStream(hFile);
Export(pStream, pbvarResult);
pStream->Release();
}
return S_OK;
}
class CFileSystemStream : public IStream
{//...}
This managed code business is driving me nuts - it seems like its casually
put out of bounds thousands of useful COM objects simply to be able to have
a nifty garabage collector!
TJ.