Marshalling a SAFEARRAY from an ATL component into CS.

T

Tronster Hartley

I've written an ATL 7.1 component and have a C# 1.1 client that is
consuming it. The ATL component has a method with a [out,retval] that
returns a SAFEARRAY of INTs, but my C# component chokes on it.


The C# code:

// Display a message box with a value from the ATL control
private void GoButton_Click(object sender, System.EventArgs e)
{
SAServerLib.bobClass foo = new SAServerLib.bobClass();

object o = foo.Populate();
MessageBox.Show( o.ToString() );

int[] ch = (int[])o;
MessageBox.Show( ch.ToString() );
}


I receive an exception on:
int[] ch = (int[])o;

With the message being:
"An unhandled exception of type 'System.InvalidCastException' occurred
in Safearrays.exe

Additional information: Specified cast is not valid."


ATL code:

// Populate a safe array with the value "321".
STDMETHODIMP Cbob::populate(SAFEARRAY** saData)
{
SAFEARRAYBOUND rgsabound[1];
rgsabound[0].lLbound = 0;
rgsabound[0].cElements = 3;

*saData = SafeArrayCreate(VT_BSTR, 1, rgsabound);

long c = 3;
long lCount = 0L;
SafeArrayPutElement(*saData, &lCount, static_cast<void*>(&c) );

c = 2;
lCount++;
SafeArrayPutElement(*saData, &lCount, static_cast<void*>(&c) );

c = 1;
lCount++;
SafeArrayPutElement(*saData, &lCount, static_cast<void*>(&c) );

return S_OK;
}


All of this code is from a sample application I'm just trying to prove
the concept with. (Eventually I'll be returning SAFEARRAY's of custom
structures.)

I've googled and MSDNed for some information, but Any advice would be
greatly appreciated.

Cheers.
 
W

Willy Denoyette [MVP]

Please post your method declaration (midl).
I'm not clear how you declared your SAFEARRAY return argument, as far as I
know you can't return a safearray (retval)
Willy.
 
T

Tronster Hartley

You may be right in terms of SAFEARRAYs not able to be returned; the
MSDN article pointed to by Ollie Riches seems to show that an in or
in/out parameter is the only type that can be decorated for marshaling.
:( (I hope that isn't true.)




Below is the IDL:



import "oaidl.idl";
import "ocidl.idl";

[
uuid(0A15D60D-B87D-4B1C-AA85-07BF51CB6FF0),
version(1.0),
helpstring("SAServer 1.0 Type Library")
]
library SAServerLib
{
importlib("stdole2.tlb");
[
uuid(908FF2FF-CA80-4858-BD8D-89CFF5CAFEA3),
helpstring("_IbobEvents Interface")
]
dispinterface _IbobEvents
{
properties:
methods:
};

[
object,
uuid(1C4CD34D-8B6D-4571-B00A-4930FD1FFAF0),
dual,
nonextensible,
helpstring("Ibob Interface"),
pointer_default(unique)
]
interface Ibob : IDispatch{
[id(1), helpstring("method Populate")]
HRESULT Populate([out,retval] SAFEARRAY** saData);
};

[
uuid(D9C4E6D0-4A9F-4A8F-95EC-6ACACBACB67C),
helpstring("bob Class")
]
coclass bob
{
[default] interface Ibob;
[default, source] dispinterface _IbobEvents;
};
};


Please post your method declaration (midl).
I'm not clear how you declared your SAFEARRAY return argument, as far as I
know you can't return a safearray (retval)
Willy.

Tronster Hartley said:
I've written an ATL 7.1 component and have a C# 1.1 client that is
consuming it. The ATL component has a method with a [out,retval] that
returns a SAFEARRAY of INTs, but my C# component chokes on it.


The C# code:

// Display a message box with a value from the ATL control
private void GoButton_Click(object sender, System.EventArgs e)
{
SAServerLib.bobClass foo = new SAServerLib.bobClass();

object o = foo.Populate();
MessageBox.Show( o.ToString() );
int[] ch = (int[])o;
MessageBox.Show( ch.ToString() );
}


I receive an exception on:
int[] ch = (int[])o;

With the message being:
"An unhandled exception of type 'System.InvalidCastException' occurred in
Safearrays.exe

Additional information: Specified cast is not valid."


ATL code:

// Populate a safe array with the value "321".
STDMETHODIMP Cbob::populate(SAFEARRAY** saData)
{
SAFEARRAYBOUND rgsabound[1];
rgsabound[0].lLbound = 0;
rgsabound[0].cElements = 3;

*saData = SafeArrayCreate(VT_BSTR, 1, rgsabound);

long c = 3;
long lCount = 0L;
SafeArrayPutElement(*saData, &lCount, static_cast<void*>(&c) );
c = 2;
lCount++;
SafeArrayPutElement(*saData, &lCount, static_cast<void*>(&c) );
c = 1;
lCount++;
SafeArrayPutElement(*saData, &lCount, static_cast<void*>(&c) );
return S_OK;
}


All of this code is from a sample application I'm just trying to prove the
concept with. (Eventually I'll be returning SAFEARRAY's of custom
structures.)

I've googled and MSDNed for some information, but Any advice would be
greatly appreciated.

Cheers.
 
T

Tronster Hartley

Thank you Ollie.

I had not found that page in my searches. I'm gleaning information from
it now as I try to get this to work.

Cheers.


Ollie said:
have you check out this page on MSDN:

http://msdn.microsoft.com/library/d...uide/html/cpconDefaultMarshalingForArrays.asp

HTH

Ollie Riches

Tronster Hartley said:
I've written an ATL 7.1 component and have a C# 1.1 client that is
consuming it. The ATL component has a method with a [out,retval] that
returns a SAFEARRAY of INTs, but my C# component chokes on it.


The C# code:

// Display a message box with a value from the ATL control
private void GoButton_Click(object sender, System.EventArgs e)
{
SAServerLib.bobClass foo = new SAServerLib.bobClass();

object o = foo.Populate();
MessageBox.Show( o.ToString() );
int[] ch = (int[])o;
MessageBox.Show( ch.ToString() );
}


I receive an exception on:
int[] ch = (int[])o;

With the message being:
"An unhandled exception of type 'System.InvalidCastException' occurred in
Safearrays.exe

Additional information: Specified cast is not valid."


ATL code:

// Populate a safe array with the value "321".
STDMETHODIMP Cbob::populate(SAFEARRAY** saData)
{
SAFEARRAYBOUND rgsabound[1];
rgsabound[0].lLbound = 0;
rgsabound[0].cElements = 3;

*saData = SafeArrayCreate(VT_BSTR, 1, rgsabound);

long c = 3;
long lCount = 0L;
SafeArrayPutElement(*saData, &lCount, static_cast<void*>(&c) );
c = 2;
lCount++;
SafeArrayPutElement(*saData, &lCount, static_cast<void*>(&c) );
c = 1;
lCount++;
SafeArrayPutElement(*saData, &lCount, static_cast<void*>(&c) );
return S_OK;
}


All of this code is from a sample application I'm just trying to prove the
concept with. (Eventually I'll be returning SAFEARRAY's of custom
structures.)

I've googled and MSDNed for some information, but Any advice would be
greatly appreciated.

Cheers.
 
W

Willy Denoyette [MVP]

You should indicate the type of SAFEARRAY element, so your IDL should look
like this:

[id(1), helpstring("method Populate")] HRESULT Populate([out,
retval]SAFEARRAY(int)* psa);

STDMETHODIMP CBob::populate(SAFEARRAY** psa)
{

As such in C#, you can return into an int[]

int[] ia = obj.Populate();

Willy.



You may be right in terms of SAFEARRAYs not able to be returned; the MSDN
article pointed to by Ollie Riches seems to show that an in or in/out
parameter is the only type that can be decorated for marshaling. :( (I
hope that isn't true.)
Below is the IDL:



import "oaidl.idl";
import "ocidl.idl";

[
uuid(0A15D60D-B87D-4B1C-AA85-07BF51CB6FF0),
version(1.0),
helpstring("SAServer 1.0 Type Library")
]
library SAServerLib
{
importlib("stdole2.tlb");
[
uuid(908FF2FF-CA80-4858-BD8D-89CFF5CAFEA3),
helpstring("_IbobEvents Interface")
]
dispinterface _IbobEvents
{
properties:
methods:
};

[
object,
uuid(1C4CD34D-8B6D-4571-B00A-4930FD1FFAF0),
dual,
nonextensible,
helpstring("Ibob Interface"),
pointer_default(unique)
]
interface Ibob : IDispatch{
[id(1), helpstring("method Populate")]
HRESULT Populate([out,retval] SAFEARRAY** saData);
};

[
uuid(D9C4E6D0-4A9F-4A8F-95EC-6ACACBACB67C),
helpstring("bob Class")
]
coclass bob
{
[default] interface Ibob;
[default, source] dispinterface _IbobEvents;
};
};


Please post your method declaration (midl).
I'm not clear how you declared your SAFEARRAY return argument, as far as
I know you can't return a safearray (retval)
Willy.

Tronster Hartley said:
I've written an ATL 7.1 component and have a C# 1.1 client that is
consuming it. The ATL component has a method with a [out,retval] that
returns a SAFEARRAY of INTs, but my C# component chokes on it.


The C# code:

// Display a message box with a value from the ATL control
private void GoButton_Click(object sender, System.EventArgs e)
{
SAServerLib.bobClass foo = new SAServerLib.bobClass();

object o = foo.Populate();
MessageBox.Show( o.ToString() );
int[] ch = (int[])o;
MessageBox.Show( ch.ToString() );
}


I receive an exception on:
int[] ch = (int[])o;

With the message being:
"An unhandled exception of type 'System.InvalidCastException' occurred
in Safearrays.exe

Additional information: Specified cast is not valid."


ATL code:

// Populate a safe array with the value "321".
STDMETHODIMP Cbob::populate(SAFEARRAY** saData)
{
SAFEARRAYBOUND rgsabound[1];
rgsabound[0].lLbound = 0;
rgsabound[0].cElements = 3;

*saData = SafeArrayCreate(VT_BSTR, 1, rgsabound);

long c = 3;
long lCount = 0L;
SafeArrayPutElement(*saData, &lCount, static_cast<void*>(&c) );
c = 2;
lCount++;
SafeArrayPutElement(*saData, &lCount, static_cast<void*>(&c) );
c = 1;
lCount++;
SafeArrayPutElement(*saData, &lCount, static_cast<void*>(&c) );
return S_OK;
}


All of this code is from a sample application I'm just trying to prove
the concept with. (Eventually I'll be returning SAFEARRAY's of custom
structures.)

I've googled and MSDNed for some information, but Any advice would be
greatly appreciated.

Cheers.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top