473,382 Members | 1,238 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,382 software developers and data experts.

How to? Pass C# Stream object to unmanaged COM method that expects a pointer?

TJ
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.
Nov 17 '05 #1
8 15521
TJ wrote:

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


That's the important information here! Now what you need to do is create
a wrapper class for the .NET stream that implements the COM IStream
interface.

I have once written a blog article that contains part of such a wrapper
implementation. Only part, because the task at hand required me only to
implement the Read method, not the Write method. Still, you should be
able to easily use this code and implement the Write method
additionally. You can find the article here (the code for the wrapper
class is about half way down):

http://www.sturmnet.org/blog/archive...arp-extractor/

Oliver Sturm
--
omnibus ex nihilo ducendis sufficit unum
Spaces inserted to prevent google email destruction:
MSN oliver @ sturmnet.org Jabber sturm @ amessage.de
ICQ 27142619 http://www.sturmnet.org/blog
Nov 17 '05 #2
TJ
Many thanks for that, Oliver. I was obstinately resisting having to create
wrapper classes! When I first tried your solution I was confused that
VisualStudio 2003 didn't know about
"System.Runtime.InteropServices.ComTypes.IStre am" but after a quick MSDN
check I realised its only in VisualStudio 2005 beta - so I switched the
project into that environment and made excellent progress.

Because of the way my code uses the MemoryStream I had to inherit from two
base classes: MemoryStream *and* IStream.

// transfer the attachment image to a bitmap
IMemoryStream ms = new IMemoryStream();
attachment.Export(ms); // call IMimeBody.Export(IStream)
image = new Bitmap((Stream)ms);

For others that need to do something similar, here's my code. Notice that I
have commented out the Marshal.WriteInt64() calls because of IllegalAccess
exceptions that I think are caused by insecure code:

using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Drawing;
using System.Drawing.Imaging;
using net.tjworld.utils;

namespace net.tjworld {
/// <summary>
/// Can't do multiple inheritence so need to wrap MemoryStream
// </summary>
class MyMemoryStream : MemoryStream {
public MyMemoryStream() : base() { } // call superclass constructor - in
C# should happen by default anyway
}

/// <summary>
/// COM IStream wrapper for a MemoryStream. Inherit from MyMemoryStream,
implement IStream
/// </summary>
class IMemoryStream : MyMemoryStream, IStream {
public IMemoryStream() : base() { }
public void Clone(out System.Runtime.InteropServices.ComTypes.IStream
ppstm) { ppstm = null; }

public void Read(byte[] pv, int cb, System.IntPtr pcbRead) {
int bytesRead = base.Read(pv, 0, cb);
// Marshal.WriteInt64(pcbRead, (Int64)bytesRead);
}
public void Write(byte[] pv, int cb, System.IntPtr pcbWritten) {
base.Write(pv, 0, cb);
// Marshal.WriteInt64(pcbWritten, cb);
}
public void Seek(long dlibMove, int dwOrigin, System.IntPtr
plibNewPosition) {
int pos = base.Seek(dlibMove, (SeekOrigin)dwOrigin);
// Marshal.WriteInt64(plibNewPosition, (Int64)pos);
}
public void SetSize(long libNewSize) { }
public void CopyTo(System.Runtime.InteropServices.ComTypes.ISt ream pstm,
long cb, System.IntPtr pcbRead, System.IntPtr pcbWritten) { }
public void Commit(int grfCommitFlags) { }
public void LockRegion(long libOffset, long cb, int dwLockType) { }
public void Revert() { }
public void UnlockRegion(long libOffset, long cb, int dwLockType) { }
public void Stat(out System.Runtime.InteropServices.ComTypes.STATSTG
pstatstg, int grfStatFlag) {
pstatstg = new System.Runtime.InteropServices.ComTypes.STATSTG();
}
}
// other members and classes ...
}
Nov 17 '05 #3
TJ wrote:
Many thanks for that, Oliver. I was obstinately resisting having to create
wrapper classes! When I first tried your solution I was confused that
VisualStudio 2003 didn't know about
"System.Runtime.InteropServices.ComTypes.IStre am" but after a quick MSDN
check I realised its only in VisualStudio 2005 beta - so I switched the
project into that environment and made excellent progress.


Oops :-) Sorry about that. You'd have to define the interface yourself
in 2003 if it's not available by default.

It should look something like this:

[ComImport, Guid("0000000c-0000-0000-C000-000000000046"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown )]
public interface IStream
{
void Read([Out, MarshalAs(UnmanagedType.LPArray,
SizeParamIndex=1)] byte[] pv, int cb, IntPtr pcbRead);
void Write([MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)]
byte[] pv, int cb, IntPtr pcbWritten);
void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition);
void SetSize(long libNewSize);
void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr
pcbWritten);
void Commit(int grfCommitFlags);
void Revert();
void LockRegion(long libOffset, long cb, int dwLockType);
void UnlockRegion(long libOffset, long cb, int dwLockType);
void Stat(out STATSTG pstatstg, int grfStatFlag);
void Clone(out IStream ppstm);
}


Oliver Sturm
--
omnibus ex nihilo ducendis sufficit unum
Spaces inserted to prevent google email destruction:
MSN oliver @ sturmnet.org Jabber sturm @ amessage.de
ICQ 27142619 http://www.sturmnet.org/blog
Nov 17 '05 #4
TJ
One last 'little' helping hint...

As I said before, I'm using MimeSniffer by Randolf Duke.At one point
MimeSniffer calls my IMemoryStream.Write(byte[] pv,int cb,System.IntPtr
pcbWritten) using the following C++ code construct:
pDest->Write(pBuf, dwRead, NULL);

from within its MimeBody.Export() method.

Notice that the parameter pcbWritten is NULL. This is what causes the
protected memory exception I mentioned in an earlier article.

I tried testing pcbWritten for equality to null:

if(pcbWritten != null) Marshal.WriteInt64(pcbWritten, (Int64)cb);

but of course the System.IntPtr isn't null.
So how can we test for a null value pointed to by the System.IntPtr?


Nov 17 '05 #5
if( ..... != IntPtr.Zero)

Willy.
"TJ" <mi***************@tjworld.org> wrote in message
news:uE**************@TK2MSFTNGP12.phx.gbl...
One last 'little' helping hint...

As I said before, I'm using MimeSniffer by Randolf Duke.At one point
MimeSniffer calls my IMemoryStream.Write(byte[] pv,int cb,System.IntPtr
pcbWritten) using the following C++ code construct:
pDest->Write(pBuf, dwRead, NULL);

from within its MimeBody.Export() method.

Notice that the parameter pcbWritten is NULL. This is what causes the
protected memory exception I mentioned in an earlier article.

I tried testing pcbWritten for equality to null:

if(pcbWritten != null) Marshal.WriteInt64(pcbWritten, (Int64)cb);

but of course the System.IntPtr isn't null.
So how can we test for a null value pointed to by the System.IntPtr?

Nov 17 '05 #6
TJ
Thanks for that solution Willy! I had been staring at IntrPtr.Zero in the
debugger but hadn't realised it's significance, and didn't find anything in
MSDN that made me realise.

For those in the future wanting to pass pointers to unmanaged code, and to
make calls from unmanaged COM objects into managed code, especially for
Streams, here's my coding. Like the base code provided by Oliver Sturm I've
only implemented the methods I need for the current project, so you might
need to add functionality. If you do please post it back here for the
community to learn from.

C# source code - you may well want to change the namespace to your own.

---- IMemoryStream.cs ----

using System;
using System.IO;
using System.Runtime.InteropServices;

namespace net.tjworld.utils
{
// For framework 1.1 define the COM IStream interface (framework 2.x has it
built in)
// Thanks to Oliver Sturm for this
[ComImport, Guid("0000000c-0000-0000-C000-000000000046"), InterfaceType
(ComInterfaceType.InterfaceIsIUnknown)]
public interface IStream {
void Read([Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] byte[]
pv, int cb, IntPtr pcbRead);
void Write([MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] byte[] pv,
int cb, IntPtr pcbWritten);
void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition);
void SetSize(long libNewSize);
void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten);
void Commit(int grfCommitFlags);
void Revert();
void LockRegion(long libOffset, long cb, int dwLockType);
void UnlockRegion(long libOffset, long cb, int dwLockType);
void Stat(out STATSTG pstatstg, int grfStatFlag);
void Clone(out IStream ppstm);
}
// wrap the Stream prior to extending it
public class MyMemoryStream : MemoryStream {
public MyMemoryStream() : base() { }
}
/// <summary>
/// COM IStream wrapper for a MemoryStream.
/// Thanks to Willy Denoyette for the if(System.IntPr != IntPtr.Zero) test
for a NULL parameter via COM
/// CLR will make the class implement the IDispatch COM interface
/// so COM objects can make calls to IMemoryStream methods
/// </summary>
[ClassInterface(ClassInterfaceType.AutoDispatch)]
public class IMemoryStream : MyMemoryStream, IStream {
public IMemoryStream() : base() { }
// convenience method for writing Strings to the stream
public void Write(string s) {
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
byte[] pv = encoding.GetBytes(s);
Write(pv, 0, pv.GetLength(0));
}
// Implementation of the IStream interface
public void Clone(out IStream ppstm) {
ppstm = null;
}
public void Read(byte[] pv, int cb, System.IntPtr pcbRead) {
long bytesRead = Read(pv, 0, cb);
if(pcbRead != IntPtr.Zero) Marshal.WriteInt64(pcbRead, bytesRead); //
only set actual bytes read if pointer isn't NULL
}
public void Write(byte[] pv, int cb, System.IntPtr pcbWritten) {
Write(pv, 0, cb);
if(pcbWritten != IntPtr.Zero) Marshal.WriteInt64(pcbWritten, (Int64)cb);
// only set actual bytes written if pointer isn't NULL
}
public void Seek(long dlibMove, int dwOrigin, System.IntPtr
plibNewPosition) {
long pos = base.Seek(dlibMove, (SeekOrigin)dwOrigin);
if(plibNewPosition != IntPtr.Zero) Marshal.WriteInt64(plibNewPosition,
pos); // only set actual bytes written if pointer isn't NULL
}
public void SetSize(long libNewSize) { }
public void CopyTo(IStream pstm, long cb, System.IntPtr pcbRead,
System.IntPtr pcbWritten) { }
public void Commit(int grfCommitFlags) { }
public void LockRegion(long libOffset, long cb, int dwLockType) { }
public void Revert() { }
public void UnlockRegion(long libOffset, long cb, int dwLockType) { }
public void Stat(out STATSTG pstatstg, int grfStatFlag) {
pstatstg = new STATSTG();
}
}
}
Nov 17 '05 #7
TJ
Oops! would help to remove redundant code that hung around from my testing
phase!

Remove this completely:

// wrap the Stream prior to extending it
public class MyMemoryStream : MemoryStream {
public MyMemoryStream() : base() { }
}

Alter this:

public class IMemoryStream : MyMemoryStream, IStream {

to read:

public class IMemoryStream : MemoryStream, IStream {

I've written this up at http://tjworld.net/software/IMemoryStream/ and I
will post additions and improvements to the code there.
Nov 17 '05 #8
TJ,

thanks for giving this back to the rest of us.

Oliver Sturm
--
omnibus ex nihilo ducendis sufficit unum
Spaces inserted to prevent google email destruction:
MSN oliver @ sturmnet.org Jabber sturm @ amessage.de
ICQ 27142619 http://www.sturmnet.org/blog
Nov 17 '05 #9

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

16
by: Ekim | last post by:
hello, I'm allocating a byte-Array in C# with byte byteArray = new byte; Now I want to pass this byte-Array to a managed C++-function by reference, so that I'm able to change the content of the...
1
by: Alexander Arlievsky | last post by:
Hi, I have mixed mode dll, which contains files with managed objects written in MC++, and files with regular "C" functions. One of those functions receives function pointer as parameter. I want to...
5
by: apm | last post by:
Any and all: Is there an efficient way to pass a large array from .NET to COM? Can references (or pointer) be passed from COM to NET and NET to COM without the object it refers to being copied?...
7
by: Klaus Bonadt | last post by:
I have an existing VC6 application using the MFC. I am able to pass CString and other parameters from such a VC6 dll to an unmanaged MFC dll (compiled in Visual Studio .NET). Now I want to use...
5
by: Maxwell | last post by:
Hello, Newbie question here. I have a VS.NET 2003 MC++ (not C++/cli) project where I have a managed class reference in a unmanaged class...simple enough. To keep things short I am for the most...
10
by: John Bailo | last post by:
I want to pass a SqlCommand object as a input parameter to a method. I want to pass the SqlCommand "by value" so that any updates to the original object are *not* reflected in the object within...
1
by: Alexander Korsunsky | last post by:
Hi! Is it possible to extract the mode flags of a stream (FILE* stream or fstream - object), without looking at how the stream was opened? What I mean by mode flags is wether the stream is opened...
20
by: Justin Rich | last post by:
so im trying to be good and not leave anything hanging open but i guess ive seen a variety of ways to kill objects.. is there like a recommended way? basically im looking for best practices for...
9
by: =?Utf-8?B?RWR3YXJkUw==?= | last post by:
I would greatly appreciate some help on passing managed object into unmanaged code. I need to pass a reference (address of) of a managed class into unmanaged code (written by a thrid party). The...
0
by: ryjfgjl | last post by:
In our work, we often need to import Excel data into databases (such as MySQL, SQL Server, Oracle) for data analysis and processing. Usually, we use database tools like Navicat or the Excel import...
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.