passing a safe Array as an out parameter to a .net component

I have a .Net component that passes a stringArray to a client program via an out parameter.

This works fine from a .net client

and I have successfully passed a SafeArray from MFCto a .net component as an [in] parameter.

What seems to evade me is returning returning an array of BSTR from a .net component via the CCW as an [out] parameter, back to this MFC client

Can anyone help?

regards

[580 byte] By [RobertMagowan] at [2008-2-15]
# 1
Robert Magowan wrote:
I have a .Net component that passes a stringArray to a client program via an out parameter.

This works fine from a .net client

and I have successfully passed a SafeArray from MFC to a .net component as an [in] parameter.

What seems to evade me is returning returning an array of BSTR from a .net component via the CCW as an [out] parameter, back to this MFC client

Can anyone help?

regards

Robert,

I believe for this to work in Interop, you will have to declare the parameter as a ref parameter, not an out parameter.

Hope this helps.

- Nicholas Paldino [.NET/C# MVP]
- mvp@spam.guard.caspershouse.com

casperOne at 2007-8-21 > top of Msdn Tech,.NET Development,.NET Base Class Library...
# 2
Yeah sure a ref parameter from c# becomes an [out] parameter in the generated TLI and TLH files when u import the type lib into the MFC app.

The thing is the .net procedure that I am calling from MFC is being executed correctly and sucessfully.

It is just that the ref string[] parameter passed back from the c# component does not seem to get marshaled correctly into the safe array in the MFC client.

The SafeArray that is used to recieve the marshaled ref string[] , just contains garbage.

Yes..The safearray has been set up to hold BSTRs

Has any one successfully done this before? If so could you send me a code sample. There doesnt seem to be a lot on the net wrt SafeArray Code samples

Cheers
Rob

RobertMagowan at 2007-8-21 > top of Msdn Tech,.NET Development,.NET Base Class Library...
# 3
Robert Magowan wrote:
Yeah sure a ref parameter from c# becomes an [out] parameter in the generated TLI and TLH files when u import the type lib into the MFC app.

The thing is the .net procedure that I am calling from MFC is being executed correctly and sucessfully.

It is just that the ref string[] parameter passed back from the c# component does not seem to get marshaled correctly into the safe array in the MFC client.

The SafeArray that is used to recieve the marshaled ref string[] , just contains garbage.

Yes..The safearray has been set up to hold BSTRs

Has any one successfully done this before? If so could you send me a code sample. There doesnt seem to be a lot on the net wrt SafeArray Code samples

Cheers
Rob


Rob,

What is your declaration in .NET? You don't have any attributes on the string array, do you?

- Nicholas Paldino [.NET/C# MVP]
- mvp@spam.guard.caspershouse.com

casperOne at 2007-8-21 > top of Msdn Tech,.NET Development,.NET Base Class Library...
# 4
My c# declaration for this is

public int TranslateID( ID_TRANS_CTX xTransCtx , string[] astrInputIdNames ,
ref string[] rastrOrigIdNames ,
ref string[] rastrTranslatedIdNames )

The first 2 parameters are used as inout parameters while the last 2 are used as output parameters.

The function is begin called correctly, and the correct values are being passed to it from MFC. It just seems that the output results are nonsense. I have tried using the MarshalAs attribute and Marchaling the [OUT] parameters as a SafeArray with subtype BSTR, but this has no effect.

I feel that the problem may lie on the client ( MFC) side of the equation, but have no clear idea of what is wrong

RobertMagowan at 2007-8-21 > top of Msdn Tech,.NET Development,.NET Base Class Library...
# 5
Could you also provide the TLH part of that TranslateID method (or maybe the IDL which you could recreate from the typelib with a tool like OleView) and the C++ method prototype of the MFC wrapper?

Bye,
SvenC

SvenC at 2007-8-21 > top of Msdn Tech,.NET Development,.NET Base Class Library...
# 6

Hi,
Here is the TLH file generated from th types lib... The function you are interested in is highlighed with a red background

//

// C++ source equivalent of Win32 type library C:\DEVELOPMENT\ENTERPRISE_5.1\Product Integration\IDTranslation\Translation API\C# Translation API\translation API\bin\Debug\translation API.tlb

// compiler-generated file created 04/27/05 at 11:15:22 - DO NOT EDIT!

#pragma once

#pragma pack(push, 8)

#include <comdef.h>

//

// Forward references and typedefs

//

struct __declspec(uuid("e6d0059d-00fe-34a0-8f7f-f5c091cfadaa"))

/* LIBID */ __translation_API;

struct ID_TRANS_CTX;

struct __declspec(uuid("27aa8457-b0ec-40df-afef-7a868bf8e867"))

/* dual interface */ ITranslateID;

struct /* coclass */ CTranslateID;

//

// Smart pointer typedef declarations

//

_COM_SMARTPTR_TYPEDEF(ITranslateID, __uuidof(ITranslateID));

//

// Type library items

//

struct __declspec(uuid("fbaa356f-dd9c-38b5-a5da-c2453049bdcc"))

ID_TRANS_CTX

{

long fromProduct;

long toProduct;

long elementType;

long ProjectNo;

long MNC;

long MCC;

};

struct __declspec(uuid("27aa8457-b0ec-40df-afef-7a868bf8e867"))

ITranslateID : IDispatch

{

//

// Wrapper methods for error-handling

//

long TranslateID (

struct ID_TRANS_CTX xTransCtx,

_bstr_t strIdNameToTranslate,

BSTR * rstrTranslatedIdName );

long TranslateID_2 (

struct ID_TRANS_CTX xTransCtx,

SAFEARRAY * astrInputIdNames,

SAFEARRAY * * rastrOrigIdNames,

SAFEARRAY * * rastrTranslatedIdNames );

long TranslateToAsset (

struct ID_TRANS_CTX xTransCtx,

_bstr_t strIdNameToTranslate,

long * riTranslatedIdKey );

long TranslateToAsset_2 (

struct ID_TRANS_CTX xTransCtx,

SAFEARRAY * astrInputIdNames,

SAFEARRAY * * rastrOrigIdNames,

SAFEARRAY * * raiTranslatedIdKeys );

long TranslateFromAsset (

struct ID_TRANS_CTX xTransCtx,

long iIdKeyToTranslate,

BSTR * rstrTranslatedIdName );

long TranslateFromAsset_2 (

struct ID_TRANS_CTX xTransCtx,

SAFEARRAY * aiInputIdKeys,

SAFEARRAY * * riOrigIdKeys,

SAFEARRAY * * rastrTranslatedIdNames );

long TranslateFromSubCell (

struct ID_TRANS_CTX xTransCtx,

long iCellKey,

long iLayerKey,

BSTR * rstrTranslatedIdName );

long TranslateFromSubCell_2 (

struct ID_TRANS_CTX xTransCtx,

SAFEARRAY * aiInputCellKeys,

SAFEARRAY * aiInputLayerKeys,

SAFEARRAY * * raiOrigCellKeys,

SAFEARRAY * * raiOrigLayerKeys,

SAFEARRAY * * rastrTranslatedIdNames );

//

// Raw methods provided by interface

//

virtual HRESULT __stdcall raw_TranslateID (

/*[in]*/ struct ID_TRANS_CTX xTransCtx,

/*[in]*/ BSTR strIdNameToTranslate,

/*[in,out]*/ BSTR * rstrTranslatedIdName,

/*[out,retval]*/ long * pRetVal ) = 0;

virtual HRESULT __stdcall raw_TranslateID_2 (

/*[in]*/ struct ID_TRANS_CTX xTransCtx,

/*[in]*/ SAFEARRAY * astrInputIdNames,

/*[in,out]*/ SAFEARRAY * * rastrOrigIdNames,

/*[in,out]*/ SAFEARRAY * * rastrTranslatedIdNames,

/*[out,retval]*/ long * pRetVal ) = 0;

virtual HRESULT __stdcall raw_TranslateToAsset (

/*[in]*/ struct ID_TRANS_CTX xTransCtx,

/*[in]*/ BSTR strIdNameToTranslate,

/*[in,out]*/ long * riTranslatedIdKey,

/*[out,retval]*/ long * pRetVal ) = 0;

virtual HRESULT __stdcall raw_TranslateToAsset_2 (

/*[in]*/ struct ID_TRANS_CTX xTransCtx,

/*[in]*/ SAFEARRAY * astrInputIdNames,

/*[in,out]*/ SAFEARRAY * * rastrOrigIdNames,

/*[in,out]*/ SAFEARRAY * * raiTranslatedIdKeys,

/*[out,retval]*/ long * pRetVal ) = 0;

virtual HRESULT __stdcall raw_TranslateFromAsset (

/*[in]*/ struct ID_TRANS_CTX xTransCtx,

/*[in]*/ long iIdKeyToTranslate,

/*[in,out]*/ BSTR * rstrTranslatedIdName,

/*[out,retval]*/ long * pRetVal ) = 0;

virtual HRESULT __stdcall raw_TranslateFromAsset_2 (

/*[in]*/ struct ID_TRANS_CTX xTransCtx,

/*[in]*/ SAFEARRAY * aiInputIdKeys,

/*[in,out]*/ SAFEARRAY * * riOrigIdKeys,

/*[in,out]*/ SAFEARRAY * * rastrTranslatedIdNames,

/*[out,retval]*/ long * pRetVal ) = 0;

virtual HRESULT __stdcall raw_TranslateFromSubCell (

/*[in]*/ struct ID_TRANS_CTX xTransCtx,

/*[in]*/ long iCellKey,

/*[in]*/ long iLayerKey,

/*[in,out]*/ BSTR * rstrTranslatedIdName,

/*[out,retval]*/ long * pRetVal ) = 0;

virtual HRESULT __stdcall raw_TranslateFromSubCell_2 (

/*[in]*/ struct ID_TRANS_CTX xTransCtx,

/*[in]*/ SAFEARRAY * aiInputCellKeys,

/*[in]*/ SAFEARRAY * aiInputLayerKeys,

/*[in,out]*/ SAFEARRAY * * raiOrigCellKeys,

/*[in,out]*/ SAFEARRAY * * raiOrigLayerKeys,

/*[in,out]*/ SAFEARRAY * * rastrTranslatedIdNames,

/*[out,retval]*/ long * pRetVal ) = 0;

};

struct __declspec(uuid("30734794-6d17-4983-b864-0e5a6552da4e"))

CTranslateID;

// interface _Object

// [ default ] interface ITranslateID

//

// Named GUID constants initializations

//

extern "C" const GUID __declspec(selectany) LIBID_translation_API =

{0xe6d0059d,0x00fe,0x34a0,{0x8f,0x7f,0xf5,0xc0,0x91,0xcf,0xad,0xaa}};

extern "C" const GUID __declspec(selectany) IID_ITranslateID =

{0x27aa8457,0xb0ec,0x40df,{0xaf,0xef,0x7a,0x86,0x8b,0xf8,0xe8,0x67}};

extern "C" const GUID __declspec(selectany) CLSID_CTranslateID =

{0x30734794,0x6d17,0x4983,{0xb8,0x64,0x0e,0x5a,0x65,0x52,0xda,0x4e}};

//

// Wrapper method implementations

//

#include "c:\development\enterprise_5.1\product integration\idtranslation\translation api\c# translation api\mfc_translate\debug\translation api.tli"

#pragma pack(pop)
In addition I enclose the MFC procedure that I am using to call this

void CMFC_TRANSLATEDlg:SurprisenBnClickedOk()

{

IUnknown* Unk = NULL;

ITranslateID* tt = NULL;

HRESULT hr;

SAFEARRAYBOUND rgsaBound[1];

rgsaBound[0].lLbound = 0;

rgsaBound[0].cElements = 1;

/*SAFEARRAYBOUND rgsaBound2[1];

rgsaBound2[0].lLbound = 0;

rgsaBound2[0].cElements = 4;*/

long bound[] = {0,0,0};

// IListPtr u(__uuidof(IList));

hr = CoInitialize(NULL);

hr = CoCreateInstance( CLSID_CTranslateID , NULL , CLSCTX_INPROC_SERVER , IID_IUnknown ,(void**) &Unk );

ID_TRANS_CTX z = { 3, 4, 3060 , 0 , 20, 10 };

SAFEARRAY* pSA;

SAFEARRAY* pSB = NULL;;

SAFEARRAY* pSC = NULL;

pSA = SafeArrayCreate( VT_VARIANT , 1 , rgsaBound );

_bstr_t Y = "D_SBA";

_bstr_t X = " ";

BSTR YY = Y.GetBSTR();

BSTR XX = X.GetBSTR();

hr = SafeArrayPutElement( pSA , &bound[0] , YY );

pSB = SafeArrayCreate( VT_BSTR , 1 , rgsaBound);

pSC = SafeArrayCreate( VT_BSTR , 1 , rgsaBound );

hr = Unk->QueryInterface( IID_ITranslateID , (void**) &tt );

hr = tt->TranslateID_2( z , pSA , &pSB , &pSC );

SafeArrayGetElement( pSC , &bound[2] , XX);

// spTranslate.CoCreateInstance( IID_CTRANSLATEID );

// spTranslate->TranslateID( z , "D_EA" , pX);;

OnOK();

}
Regards
Rob

RobertMagowan at 2007-8-21 > top of Msdn Tech,.NET Development,.NET Base Class Library...
# 7

Hi,
Here is the TLH file generated from th types lib... The function you are interested in is highlighed with a red background

//

// C++ source equivalent of Win32 type library C:\DEVELOPMENT\ENTERPRISE_5.1\Product Integration\IDTranslation\Translation API\C# Translation API\translation API\bin\Debug\translation API.tlb

// compiler-generated file created 04/27/05 at 11:15:22 - DO NOT EDIT!

#pragma once

#pragma pack(push, 8)

#include <comdef.h>

//

// Forward references and typedefs

//

struct __declspec(uuid("e6d0059d-00fe-34a0-8f7f-f5c091cfadaa"))

/* LIBID */ __translation_API;

struct ID_TRANS_CTX;

struct __declspec(uuid("27aa8457-b0ec-40df-afef-7a868bf8e867"))

/* dual interface */ ITranslateID;

struct /* coclass */ CTranslateID;

//

// Smart pointer typedef declarations

//

_COM_SMARTPTR_TYPEDEF(ITranslateID, __uuidof(ITranslateID));

//

// Type library items

//

struct __declspec(uuid("fbaa356f-dd9c-38b5-a5da-c2453049bdcc"))

ID_TRANS_CTX

{

long fromProduct;

long toProduct;

long elementType;

long ProjectNo;

long MNC;

long MCC;

};

struct __declspec(uuid("27aa8457-b0ec-40df-afef-7a868bf8e867"))

ITranslateID : IDispatch

{

//

// Wrapper methods for error-handling

//

long TranslateID (

struct ID_TRANS_CTX xTransCtx,

_bstr_t strIdNameToTranslate,

BSTR * rstrTranslatedIdName );

long TranslateID_2 (

struct ID_TRANS_CTX xTransCtx,

SAFEARRAY * astrInputIdNames,

SAFEARRAY * * rastrOrigIdNames,

SAFEARRAY * * rastrTranslatedIdNames );

long TranslateToAsset (

struct ID_TRANS_CTX xTransCtx,

_bstr_t strIdNameToTranslate,

long * riTranslatedIdKey );

long TranslateToAsset_2 (

struct ID_TRANS_CTX xTransCtx,

SAFEARRAY * astrInputIdNames,

SAFEARRAY * * rastrOrigIdNames,

SAFEARRAY * * raiTranslatedIdKeys );

long TranslateFromAsset (

struct ID_TRANS_CTX xTransCtx,

long iIdKeyToTranslate,

BSTR * rstrTranslatedIdName );

long TranslateFromAsset_2 (

struct ID_TRANS_CTX xTransCtx,

SAFEARRAY * aiInputIdKeys,

SAFEARRAY * * riOrigIdKeys,

SAFEARRAY * * rastrTranslatedIdNames );

long TranslateFromSubCell (

struct ID_TRANS_CTX xTransCtx,

long iCellKey,

long iLayerKey,

BSTR * rstrTranslatedIdName );

long TranslateFromSubCell_2 (

struct ID_TRANS_CTX xTransCtx,

SAFEARRAY * aiInputCellKeys,

SAFEARRAY * aiInputLayerKeys,

SAFEARRAY * * raiOrigCellKeys,

SAFEARRAY * * raiOrigLayerKeys,

SAFEARRAY * * rastrTranslatedIdNames );

//

// Raw methods provided by interface

//

virtual HRESULT __stdcall raw_TranslateID (

/*[in]*/ struct ID_TRANS_CTX xTransCtx,

/*[in]*/ BSTR strIdNameToTranslate,

/*[in,out]*/ BSTR * rstrTranslatedIdName,

/*[out,retval]*/ long * pRetVal ) = 0;

virtual HRESULT __stdcall raw_TranslateID_2 (

/*[in]*/ struct ID_TRANS_CTX xTransCtx,

/*[in]*/ SAFEARRAY * astrInputIdNames,

/*[in,out]*/ SAFEARRAY * * rastrOrigIdNames,

/*[in,out]*/ SAFEARRAY * * rastrTranslatedIdNames,

/*[out,retval]*/ long * pRetVal ) = 0;

virtual HRESULT __stdcall raw_TranslateToAsset (

/*[in]*/ struct ID_TRANS_CTX xTransCtx,

/*[in]*/ BSTR strIdNameToTranslate,

/*[in,out]*/ long * riTranslatedIdKey,

/*[out,retval]*/ long * pRetVal ) = 0;

virtual HRESULT __stdcall raw_TranslateToAsset_2 (

/*[in]*/ struct ID_TRANS_CTX xTransCtx,

/*[in]*/ SAFEARRAY * astrInputIdNames,

/*[in,out]*/ SAFEARRAY * * rastrOrigIdNames,

/*[in,out]*/ SAFEARRAY * * raiTranslatedIdKeys,

/*[out,retval]*/ long * pRetVal ) = 0;

virtual HRESULT __stdcall raw_TranslateFromAsset (

/*[in]*/ struct ID_TRANS_CTX xTransCtx,

/*[in]*/ long iIdKeyToTranslate,

/*[in,out]*/ BSTR * rstrTranslatedIdName,

/*[out,retval]*/ long * pRetVal ) = 0;

virtual HRESULT __stdcall raw_TranslateFromAsset_2 (

/*[in]*/ struct ID_TRANS_CTX xTransCtx,

/*[in]*/ SAFEARRAY * aiInputIdKeys,

/*[in,out]*/ SAFEARRAY * * riOrigIdKeys,

/*[in,out]*/ SAFEARRAY * * rastrTranslatedIdNames,

/*[out,retval]*/ long * pRetVal ) = 0;

virtual HRESULT __stdcall raw_TranslateFromSubCell (

/*[in]*/ struct ID_TRANS_CTX xTransCtx,

/*[in]*/ long iCellKey,

/*[in]*/ long iLayerKey,

/*[in,out]*/ BSTR * rstrTranslatedIdName,

/*[out,retval]*/ long * pRetVal ) = 0;

virtual HRESULT __stdcall raw_TranslateFromSubCell_2 (

/*[in]*/ struct ID_TRANS_CTX xTransCtx,

/*[in]*/ SAFEARRAY * aiInputCellKeys,

/*[in]*/ SAFEARRAY * aiInputLayerKeys,

/*[in,out]*/ SAFEARRAY * * raiOrigCellKeys,

/*[in,out]*/ SAFEARRAY * * raiOrigLayerKeys,

/*[in,out]*/ SAFEARRAY * * rastrTranslatedIdNames,

/*[out,retval]*/ long * pRetVal ) = 0;

};

struct __declspec(uuid("30734794-6d17-4983-b864-0e5a6552da4e"))

CTranslateID;

// interface _Object

// [ default ] interface ITranslateID

//

// Named GUID constants initializations

//

extern "C" const GUID __declspec(selectany) LIBID_translation_API =

{0xe6d0059d,0x00fe,0x34a0,{0x8f,0x7f,0xf5,0xc0,0x91,0xcf,0xad,0xaa}};

extern "C" const GUID __declspec(selectany) IID_ITranslateID =

{0x27aa8457,0xb0ec,0x40df,{0xaf,0xef,0x7a,0x86,0x8b,0xf8,0xe8,0x67}};

extern "C" const GUID __declspec(selectany) CLSID_CTranslateID =

{0x30734794,0x6d17,0x4983,{0xb8,0x64,0x0e,0x5a,0x65,0x52,0xda,0x4e}};

//

// Wrapper method implementations

//

#include "c:\development\enterprise_5.1\product integration\idtranslation\translation api\c# translation api\mfc_translate\debug\translation api.tli"

#pragma pack(pop)
In addition I enclose the MFC procedure that I am using to call this

void CMFC_TRANSLATEDlg:SurprisenBnClickedOk()

{

IUnknown* Unk = NULL;

ITranslateID* tt = NULL;

HRESULT hr;

SAFEARRAYBOUND rgsaBound[1];

rgsaBound[0].lLbound = 0;

rgsaBound[0].cElements = 1;

/*SAFEARRAYBOUND rgsaBound2[1];

rgsaBound2[0].lLbound = 0;

rgsaBound2[0].cElements = 4;*/

long bound[] = {0,0,0};

// IListPtr u(__uuidof(IList));

hr = CoInitialize(NULL);

hr = CoCreateInstance( CLSID_CTranslateID , NULL , CLSCTX_INPROC_SERVER , IID_IUnknown ,(void**) &Unk );

ID_TRANS_CTX z = { 3, 4, 3060 , 0 , 20, 10 };

SAFEARRAY* pSA;

SAFEARRAY* pSB = NULL;;

SAFEARRAY* pSC = NULL;

pSA = SafeArrayCreate( VT_VARIANT , 1 , rgsaBound );

_bstr_t Y = "D_SBA";

_bstr_t X = " ";

BSTR YY = Y.GetBSTR();

BSTR XX = X.GetBSTR();

hr = SafeArrayPutElement( pSA , &bound[0] , YY );

pSB = SafeArrayCreate( VT_BSTR , 1 , rgsaBound);

pSC = SafeArrayCreate( VT_BSTR , 1 , rgsaBound );

hr = Unk->QueryInterface( IID_ITranslateID , (void**) &tt );

hr = tt->TranslateID_2( z , pSA , &pSB , &pSC );

SafeArrayGetElement( pSC , &bound[2] , XX);

// spTranslate.CoCreateInstance( IID_CTRANSLATEID );

// spTranslate->TranslateID( z , "D_EA" , pX);;

OnOK();

}
Regards
Rob

RobertMagowan at 2007-8-21 > top of Msdn Tech,.NET Development,.NET Base Class Library...
# 8
Hi Robert,

This call seems wrong to me:
SafeArrayGetElement( pSC , &bound[2] , XX);

1) you must pass a BSTR reference to be filled:
SafeArrayGetElement( pSC , &bound[2] , (void*)&XX);

2) the third parameter of SafeArrayGetElement is an out param. So you would leak if you passed in a ref which is then overriden by the callee. Use SysFreeString(XX) before the call to SafeArrayGetElement.

3) You are leaking almost everything - might be just for the sample Wink
- the safearrays are not freed
- Unk and tt are not released
- XX and YY are not freed.
- CoInitialize is called but not CoUninitialize (you should not have to call this anyways in the middle of an MFC app. Normally this is done by the MFC initialization code.)

BUT another problem is: the .Net class interface seems to miss some marshal attributes to set up proper safearray handling: in arrays should be SAFEARRAY not SAFEARRAY* and out should be SAFEARRAY* not SAFEARRAY**. So basically for out arrays you will just write:

SAFEARRAY out1, out2;
tt->Translate2(a, b, &out1, &out2);

See that out1 and out2 are never initialized by the caller. They are out params filled by the callee.

HERE comes the last little caveat: OLE automation does not allow [out] params. You must use [in, out]. Which brings us back to having to initialize the SAFEARRAYs again before passing them as in/out params.

Search the MSDN for this topic "Default Marshaling for Arrays". This gives you the details for how to attribute the .Net class methods which you want to expose for COM clients.

HTH,
SvenC

SvenC at 2007-8-21 > top of Msdn Tech,.NET Development,.NET Base Class Library...

.NET Development

Site Classified