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 (pop3ImageGrabb er) 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.Expo rtAsFile(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.Expo rt(ms);
image = new Bitmap(ms);
But this causes the run-time error:
"An unhandled exception of type 'System.Argumen tException' 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.Expo rt() - size remains zero.
I tried various permutations of marshaling including:
MemoryStream ms = new MemoryStream();
IntPtr pStream = Marshal.GetIUnk nownForObject(m s);
attachment.Expo rt(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::Expo rt(LPUNKNOWN pStream, VARIANT_BOOL *pbvarResult)
{
CYYSType& type = GetField(_T("Co ntent-Transfer-Encoding"));
*pbvarResult = VARIANT_FALSE;
if (type.GetType() != CYYSType::type_ Long)
type = (long)CMimeDeco der::mechanism_ unknown;
if (type.IsValid() )
{
if (type.GetType() == CYYSType::type_ Long)
{
CYYSType* bContentIsRaw = NULL;
CYYSType& body = GetBody(bConten tIsRaw);
if (body.GetType() == CYYSType::type_ Bulk)
{
IStreamPtr pDest;
if (SUCCEEDED(pStr eam->QueryInterface (&pDest)))
{
long encoding = bContentIsRaw != NULL && bContentIsRaw->m_nVal ?
CMimeDecoder::m echanism_unknow n : (long)type;
CMIMECode* pCoder = CMIMECode::GetC oderByTransferT ype(encoding);
if (pCoder != NULL)
{
SEEK_TO_BEGIN(b ody.m_streamVal )
pCoder->Decode(body.m_ streamVal, pDest);
*pbvarResult = VARIANT_TRUE;
delete pCoder;
}
}
}
}
}
return S_OK;
}
STDMETHODIMP CMimeBody::Expo rtAsFile(BSTR Path, VARIANT_BOOL* pbvarResult)
{
USES_CONVERSION ;
*pbvarResult = VARIANT_FALSE;
HANDLE hFile = ::CreateFile(OL E2CT(Path), GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, 0, NULL);
if (hFile != INVALID_HANDLE_ VALUE)
{
CFileSystemStre am* pStream = new CFileSystemStre am(hFile);
Export(pStream, pbvarResult);
pStream->Release();
}
return S_OK;
}
class CFileSystemStre am : 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.