By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
443,865 Members | 1,717 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 443,865 IT Pros & Developers. It's quick & easy.

accessing COM from C# with an IStream** parameter

P: n/a
Hallo,

I'm trying to call a COM function from C#.
The function has a parameter from the type array of IStream* (IStream**).
The COM component (out-proc-server (.exe)) has the following code:

(C++)

STDMETHODIMP CIntf::SendFiles(int noOfStreams, IUnknown** dataStreams)
{
for(int i = 0; i < noOfStreams; i++)
{
if(*(dataStreams + i))
{
IStream* iStream;
hr = (*(dataStreams + i))->QueryInterface(IID_IStream,
(void**)&iStream);
... ...
}
}
}

I can't change the code of the COM component.
If I'm calling the function from C# I get an E_NOINTERFACE error from
QueryInterface in the COM component.
Does anyone has an idea about the problem in my C# code?

I asked the same in the german newsgroup, but I got no answer. I hope you
are better here.

Best reguards
Ralf Beckers

(C#)

public void SendFiles(string file)
{

FileStream fs = new FileStream(file, FileMode.Open);
StreamReader sr = new StreamReader(fs, Encoding.Default);
string trc = sr.ReadToEnd();
sr.Close();
fs.Close();

byte[] data = Encoding.Default.GetBytes(trc);

IStorage storage;
ComTypes.IStream stream;

int err = StgCreateDocfile(null,
STGM.CREATE | STGM.READWRITE | STGM.TRANSACTED |
STGM.SHARE_EXCLUSIVE | STGM.DELETEONRELEASE,
0, out storage);

storage.CreateStream("TRTFiles.trt", (uint)(STGM.READWRITE |
STGM.SHARE_EXCLUSIVE), 0,0, out stream);
stream.Write(data, data.Length, IntPtr.Zero);
stream.Commit((int)STGC.OVERWRITE);

storage.Commit((int)STGC.OVERWRITE);
IntPtr ptr = Marshal.GetIUnknownForObject(stream);
IntPtr[] arr = { ptr };
IntPtr buffer = Marshal.AllocCoTaskMem( Marshal.SizeOf( typeof(IntPtr ))
* arr.Length );
Marshal.Copy(arr, 0, buffer, arr.Length );

object obj = buffer;
this.intf.SendFiles(1, ref obj);
Marshal.FreeCoTaskMem((IntPtr)buffer);
}
[DllImport("ole32.dll")]
static extern int StgCreateDocfile([MarshalAs(UnmanagedType.LPWStr)]string
pwcsName, STGM grfMode, uint reserved, out IStorage ppstgOpen);

[Flags]
public enum CSMFlags : short
{
NONE = 0,
EVENT_EN = 0x0100,
POLLED_EVENTS = 0x0001
}
[Flags]
public enum CSMFilterType : byte
{
ENABLE_MSG = 0x00,
BLOCK_MSG = 0x01,
NO_FILTER = 0x02
}
[Flags]
internal enum STGC : int
{
DEFAULT = 0,
OVERWRITE = 1,
ONLYIFCURRENT = 2,
DANGEROUSLYCOMMITMERELYTODISKCACHE = 4,
CONSOLIDATE = 8
}
[Flags]
internal enum STGM : int
{
DIRECT = 0x00000000,
TRANSACTED = 0x00010000,
SIMPLE = 0x08000000,
READ = 0x00000000,
WRITE = 0x00000001,
READWRITE = 0x00000002,
SHARE_DENY_NONE = 0x00000040,
SHARE_DENY_READ = 0x00000030,
SHARE_DENY_WRITE= 0x00000020,
SHARE_EXCLUSIVE = 0x00000010,
PRIORITY = 0x00040000,
DELETEONRELEASE = 0x04000000,
NOSCRATCH = 0x00100000,
CREATE = 0x00001000,
CONVERT = 0x00020000,
FAILIFTHERE = 0x00000000,
NOSNAPSHOT = 0x00200000,
DIRECT_SWMR = 0x00400000
}

[ComImport]
[Guid("0000000b-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown )]
interface IStorage
{
void CreateStream(
/* [string][in] */ string pwcsName,
/* [in] */ uint grfMode,
/* [in] */ uint reserved1,
/* [in] */ uint reserved2,
/* [out] */ out ComTypes.IStream ppstm);
void OpenStream(
/* [string][in] */ string pwcsName,
/* [unique][in] */ IntPtr reserved1,
/* [in] */ uint grfMode,
/* [in] */ uint reserved2,
/* [out] */ out ComTypes.IStream ppstm);
void CreateStorage(
/* [string][in] */ string pwcsName,
/* [in] */ uint grfMode,
/* [in] */ uint reserved1,
/* [in] */ uint reserved2,
/* [out] */ out IStorage ppstg);
void OpenStorage(
/* [string][unique][in] */ string pwcsName,
/* [unique][in] */ IStorage pstgPriority,
/* [in] */ uint grfMode,
/* [unique][in] */ IntPtr snbExclude,
/* [in] */ uint reserved,
/* [out] */ out IStorage ppstg);
void CopyTo(
/* [in] */ uint ciidExclude,
/* [size_is][unique][in] */ Guid rgiidExclude, // should this be an array?
/* [unique][in] */ IntPtr snbExclude,
/* [unique][in] */ IStorage pstgDest);
void MoveElementTo(
/* [string][in] */ string pwcsName,
/* [unique][in] */ IStorage pstgDest,
/* [string][in] */ string pwcsNewName,
/* [in] */ uint grfFlags);
void Commit(
/* [in] */ uint grfCommitFlags);
void Revert();
void EnumElements(
/* [in] */ uint reserved1,
/* [size_is][unique][in] */ IntPtr reserved2,
/* [in] */ uint reserved3,
/* [out] */ out IEnumSTATSTG ppenum);
void DestroyElement(
/* [string][in] */ string pwcsName);
void RenameElement(
/* [string][in] */ string pwcsOldName,
/* [string][in] */ string pwcsNewName);
void SetElementTimes(
/* [string][unique][in] */ string pwcsName,
/* [unique][in] */ ComTypes.FILETIME pctime,
/* [unique][in] */ ComTypes.FILETIME patime,
/* [unique][in] */ ComTypes.FILETIME pmtime);
void SetClass(
/* [in] */ Guid clsid);
void SetStateBits(
/* [in] */ uint grfStateBits,
/* [in] */ uint grfMask);
void Stat(
/* [out] */ out ComTypes.STATSTG pstatstg,
/* [in] */ uint grfStatFlag);
}

[ComImport]
[Guid("0000000d-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown )]
public interface IEnumSTATSTG
{
// The user needs to allocate an STATSTG array whose size is celt.
[PreserveSig]
uint
Next(
uint celt,
[MarshalAs(UnmanagedType.LPArray), Out]
ComTypes.STATSTG[] rgelt,
out uint pceltFetched
);
void Skip(uint celt);
void Reset();
[return:MarshalAs(UnmanagedType.Interface)]
IEnumSTATSTG Clone();
}

Jan 16 '08 #1
Share this Question
Share on Google+
4 Replies


P: n/a
Hi Ralf,

Based on my understanding, you are trying to pass an array of interface
pointers to a COM component, right?

To pass array in COM, you need SAFEARRAY.

Here's some key steps:

1) In your IDL:

[id(3), helpstring("method SendFiles2")] HRESULT SendFiles2([in,out]
SAFEARRAY(IUnknown*)* m);

2) Here's some example code on how to retrieve the element in a SAFEARRAY
with index 0:

STDMETHODIMP CSimpleObject::SendFiles2(SAFEARRAY ** m)
{
IUnknown* punk;
long elementNumber = 0;
HRESULT hr = S_OK;
hr = SafeArrayGetElement(*m, &elementNumber, &punk);
if (SUCCEEDED(hr)) {
IStream* pstream;
hr = punk->QueryInterface(IID_IStream, (void**)&pstream);
}
...

3) In C#, after you re-adding the reference, you should see this method now
has signature (ref Array). To call this method:

Array T = Array.CreateInstance(typeof(IStream), 1);
T.SetValue(stream, 0);
intf.SendFiles2(ref T);
Hope this helps.

Regards,
Walter Wang (wa****@online.microsoft.com, remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.

Jan 17 '08 #2

P: n/a
Hi Walter,

thanx for your answer.
But as I wrote in my question, I can't change anything at the interface of
the C++ COM component.
I need a way to use the existing interface without any modifications.

The interface is described in the IDL file as:

[
object,
uuid(A0926B7C-D57F-4F5E-9115-8F6ADB890DC1),
dual,
helpstring("IIntf Interface"),
pointer_default(unique)
]
interface IIntf : IDispatch
{
[id(21), helpstring("method SendFiles")]
HRESULT SendFiles(
[in] INT num,
[in, ref, size_is(num)] IUnknown** arr);

and my DevStudio generates:
using System;
using System.Runtime.InteropServices;

[ClassInterface(0)]
[TypeLibType(2)]
[Guid("A0926B7C-D57F-4F5E-9115-8F6ADB890DC1")]
public class CIntfClass :IIntf , CIntf
{
public CIntfClass ();
[DispId(1)]
public virtual void SendFiles(int num, ref object arr);
}

Regards
Ralf
""Walter Wang [MSFT]"" <wa****@online.microsoft.comschrieb im Newsbeitrag
news:mP**************@TK2MSFTNGHUB02.phx.gbl...
Hi Ralf,

Based on my understanding, you are trying to pass an array of interface
pointers to a COM component, right?

To pass array in COM, you need SAFEARRAY.

Here's some key steps:

1) In your IDL:

[id(3), helpstring("method SendFiles2")] HRESULT SendFiles2([in,out]
SAFEARRAY(IUnknown*)* m);

2) Here's some example code on how to retrieve the element in a SAFEARRAY
with index 0:

STDMETHODIMP CSimpleObject::SendFiles2(SAFEARRAY ** m)
{
IUnknown* punk;
long elementNumber = 0;
HRESULT hr = S_OK;
hr = SafeArrayGetElement(*m, &elementNumber, &punk);
if (SUCCEEDED(hr)) {
IStream* pstream;
hr = punk->QueryInterface(IID_IStream, (void**)&pstream);
}
..

3) In C#, after you re-adding the reference, you should see this method
now
has signature (ref Array). To call this method:

Array T = Array.CreateInstance(typeof(IStream), 1);
T.SetValue(stream, 0);
intf.SendFiles2(ref T);
Hope this helps.

Regards,
Walter Wang (wa****@online.microsoft.com, remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no
rights.
Jan 17 '08 #3

P: n/a
Hi Ralf,

I'm not sure if we could do that from .NET side but I will do some further
research.

Regards,
Walter Wang (wa****@online.microsoft.com, remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.

Jan 19 '08 #4

P: n/a
Hi Ralf,

I'm writing to check the status of this post. Please feel free to let me
know if there's anything else I can help. Thanks.
Regards,
Walter Wang (wa****@online.microsoft.com, remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.

Jan 25 '08 #5

This discussion thread is closed

Replies have been disabled for this discussion.