473,602 Members | 2,764 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

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 (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.
Nov 17 '05 #1
8 15581
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 .InteropService s.ComTypes.IStr eam" 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.Expo rt(ms); // call IMimeBody.Expor t(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.WriteIn t64() 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.uti ls;

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.IStre am
ppstm) { ppstm = null; }

public void Read(byte[] pv, int cb, System.IntPtr pcbRead) {
int bytesRead = base.Read(pv, 0, cb);
// Marshal.WriteIn t64(pcbRead, (Int64)bytesRea d);
}
public void Write(byte[] pv, int cb, System.IntPtr pcbWritten) {
base.Write(pv, 0, cb);
// Marshal.WriteIn t64(pcbWritten, cb);
}
public void Seek(long dlibMove, int dwOrigin, System.IntPtr
plibNewPosition ) {
int pos = base.Seek(dlibM ove, (SeekOrigin)dwO rigin);
// Marshal.WriteIn t64(plibNewPosi tion, (Int64)pos);
}
public void SetSize(long libNewSize) { }
public void CopyTo(System.R untime.InteropS ervices.ComType s.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(lo ng libOffset, long cb, int dwLockType) { }
public void Stat(out System.Runtime. InteropServices .ComTypes.STATS TG
pstatstg, int grfStatFlag) {
pstatstg = new System.Runtime. InteropServices .ComTypes.STATS TG();
}
}
// 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 .InteropService s.ComTypes.IStr eam" 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(C omInterfaceType .InterfaceIsIUn known)]
public interface IStream
{
void Read([Out, MarshalAs(Unman agedType.LPArra y,
SizeParamIndex= 1)] byte[] pv, int cb, IntPtr pcbRead);
void Write([MarshalAs(Unman agedType.LPArra y, 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(lo ng 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.W rite(byte[] pv,int cb,System.IntPt r
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.WriteIn t64(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******** ******@TK2MSFTN GP12.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.W rite(byte[] pv,int cb,System.IntPt r
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.WriteIn t64(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.c s ----

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

namespace net.tjworld.uti ls
{
// 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
(ComInterfaceTy pe.InterfaceIsI Unknown)]
public interface IStream {
void Read([Out, MarshalAs(Unman agedType.LPArra y, SizeParamIndex= 1)] byte[]
pv, int cb, IntPtr pcbRead);
void Write([MarshalAs(Unman agedType.LPArra y, 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(lo ng 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( ClassInterfaceT ype.AutoDispatc h)]
public class IMemoryStream : MyMemoryStream, IStream {
public IMemoryStream() : base() { }
// convenience method for writing Strings to the stream
public void Write(string s) {
System.Text.ASC IIEncoding encoding = new System.Text.ASC IIEncoding();
byte[] pv = encoding.GetByt es(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.WriteIn t64(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.WriteIn t64(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(dlibM ove, (SeekOrigin)dwO rigin);
if(plibNewPosit ion != IntPtr.Zero) Marshal.WriteIn t64(plibNewPosi tion,
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(lo ng 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
14106
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 byteArray and afterwards see the changes in C#. (if you wanna know more details: the goal is to write the content of an existing char-array in c++ precisely into the passed byteArray, so that after the function has proceded the content of the...
1
2838
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 pass to it delegate to managed object method. I know how to do it from C# - DllImport etc. I suspect I can declare "stub" method using those attributes, so it will actually reference "C" method, and call this stub, letting marshaler to do the...
5
2710
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? Thanks in advance. David
7
8221
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 .Net functionality, i.e. calling methods in managed code. How do I pass CString variables for input and output usage, i.e. providing and retrieving char arrays? Regards, Klaus
5
3410
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 part attempting to do what is this article by Nish: http://www.voidnish.com/articles/ShowArticle.aspx?code=cbwijw I have to hook up a unmanaged callback to a managed method using IJW NOT P\Invoke. So I am employing this "Thunk" or "Bridge" class...
10
3933
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 my method. How can I do this?
1
3843
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 in read only mode, write only, binary and so on. I did not find any functions in C, but I found something similar in C++, the flags() member functio of an fstream object. I've tried to check the flags on ios_base::binary ios_base::in binary...
20
13305
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 handling object destruction. Thanks Justin
9
3542
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 3rd party unmanaged DLL will pass this reference into standard Win32 unmanaged static callback function in my code. Inside this unmanaged callback function I need to cast this unmnaged pointer that I have received from 3rd party back into the...
0
7920
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
8401
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
8404
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
0
6730
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
1
5867
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
3944
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
2418
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
1
1510
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
0
1254
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

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.