EDN Admin
Well-known member
I have spent the last day searching documentation, reviewing forum posts, and googling to try to do something that I am guessing could be easily done with the right information.
I have a very large existing C++ application that has an already defined COM server with many methods exposed. I am trying to use those COM methods in a C# application (I am experienced in C++, but a C# newbie).
So in my VS2010 C# application, I add the COM server as a reference. COM Methods are visible in the object browser, and passing single-valued strings, floats, and ints seems to work fine.
But I am stumped trying to read SAFEARRAY values passed out of the C++ COM server into the C# application. Eventually I need to pass string arrays from C++ server to the C# app, but in just testing passing an array of floats, I have code that builds
but fails with the following exception when I try to cast the System.Object containing the array of floats to (float[]) ,
"exception {System.InvalidCastException: Unable to cast object of type System.Object[] to type System.Single[]."
With intellisence I can see that the Object contains the correct 8760 long array of floats, but I am unable to access that data in C#.
Here is the code on the C# side (d2RuleSet is an interface defined in the DOE2Com COM server). The exception above is thrown on the last line below.
DOE2ComLib.DOE2Com d2RuleSet;
d2RuleSet = new DOE2ComLib.DOE2Com();
System.Int32 i_Series =0;
System.Object pv_WeatherData;<br/>
float[] faWeatherData;
iOut = d2RuleSet.GetWeatherData(i_Series, out pv_WeatherData);<br/>
Type typeTest;<br/>
typeTest = pv_WeatherData.GetType();<br/>
int iArrayRank = typeTest.GetArrayRank();<br/>
Type typeElement = typeTest.GetElementType(); <br/>
faWeatherData = (float[])pv_WeatherData;
Below is the section in the idl file defining the C++ COM method
HRESULT GetWeatherData( [in] int iSeries, [out] VARIANT* pvWeatherData, [out,retval] int * piErrorCode);
Below is the C++ code where the VARIANT data is loaded.
void CDOE2BaseClass::GetWeatherData( int iSeries, VARIANT* pvWeatherData, int* piErrorCode)<br/>
{<br/>
*piErrorCode = 0;
if (iSeries < 0 || iSeries >= D2CWS_NumSeries)<br/>
*piErrorCode = 1;<br/>
else if (m_faWeatherData[iSeries] == NULL)<br/>
*piErrorCode = 3;<br/>
else<br/>
{<br/>
SAFEARRAYBOUND rgsaBound;<br/>
rgsaBound.lLbound = 0;<br/>
rgsaBound.cElements = 8760;
// First lets create the SafeArrays (populated with VARIANTS to ensure compatibility with VB and Java)<br/>
SAFEARRAY* pSAData = SafeArrayCreate( VT_VARIANT, 1, &rgsaBound );<br/>
if( pSAData == NULL ) {<br/>
#ifndef _DOE2LIB<br/>
_com_issue_error( E_OUTOFMEMORY);<br/>
#else<br/>
//RW_TO_DO - Throw custom Lib-version exception<br/>
OurThrowDOE2LibException(-1,__FILE__,__LINE__,0,"OUT OF MEMORY");<br/>
#endif //_DOE2LIB<br/>
}
for (long hr=0; hr<8760; hr++)<br/>
{<br/>
COleVariant vHrResult( m_faWeatherData[iSeries]
);<br/>
SafeArrayPutElement( pSAData, &hr, vHrResult );<br/>
}
// Now that we have populated the SAFEARRAY, assign it to the VARIANT pointer that we are returning to the client.<br/>
V_VT( pvWeatherData ) = VT_ARRAY | VT_VARIANT;<br/>
V_ARRAY( pvWeatherData ) = pSAData;<br/>
}<br/>
}
Thanks in advance for help with this problem, I feel like I spent too much time on what should be a simple problem. Also please post up any links or books that that cover interop between native C++ and C# well (I think I have already ping-ponged through
most of the Visual Studio/MSDN documentation, but maybe I missed something there too).
Thanks Again,
Tom
<
TommyVee
<br/>
<br/>
<br/>
View the full article
I have a very large existing C++ application that has an already defined COM server with many methods exposed. I am trying to use those COM methods in a C# application (I am experienced in C++, but a C# newbie).
So in my VS2010 C# application, I add the COM server as a reference. COM Methods are visible in the object browser, and passing single-valued strings, floats, and ints seems to work fine.
But I am stumped trying to read SAFEARRAY values passed out of the C++ COM server into the C# application. Eventually I need to pass string arrays from C++ server to the C# app, but in just testing passing an array of floats, I have code that builds
but fails with the following exception when I try to cast the System.Object containing the array of floats to (float[]) ,
"exception {System.InvalidCastException: Unable to cast object of type System.Object[] to type System.Single[]."
With intellisence I can see that the Object contains the correct 8760 long array of floats, but I am unable to access that data in C#.
Here is the code on the C# side (d2RuleSet is an interface defined in the DOE2Com COM server). The exception above is thrown on the last line below.
DOE2ComLib.DOE2Com d2RuleSet;
d2RuleSet = new DOE2ComLib.DOE2Com();
System.Int32 i_Series =0;
System.Object pv_WeatherData;<br/>
float[] faWeatherData;
iOut = d2RuleSet.GetWeatherData(i_Series, out pv_WeatherData);<br/>
Type typeTest;<br/>
typeTest = pv_WeatherData.GetType();<br/>
int iArrayRank = typeTest.GetArrayRank();<br/>
Type typeElement = typeTest.GetElementType(); <br/>
faWeatherData = (float[])pv_WeatherData;
Below is the section in the idl file defining the C++ COM method
HRESULT GetWeatherData( [in] int iSeries, [out] VARIANT* pvWeatherData, [out,retval] int * piErrorCode);
Below is the C++ code where the VARIANT data is loaded.
void CDOE2BaseClass::GetWeatherData( int iSeries, VARIANT* pvWeatherData, int* piErrorCode)<br/>
{<br/>
*piErrorCode = 0;
if (iSeries < 0 || iSeries >= D2CWS_NumSeries)<br/>
*piErrorCode = 1;<br/>
else if (m_faWeatherData[iSeries] == NULL)<br/>
*piErrorCode = 3;<br/>
else<br/>
{<br/>
SAFEARRAYBOUND rgsaBound;<br/>
rgsaBound.lLbound = 0;<br/>
rgsaBound.cElements = 8760;
// First lets create the SafeArrays (populated with VARIANTS to ensure compatibility with VB and Java)<br/>
SAFEARRAY* pSAData = SafeArrayCreate( VT_VARIANT, 1, &rgsaBound );<br/>
if( pSAData == NULL ) {<br/>
#ifndef _DOE2LIB<br/>
_com_issue_error( E_OUTOFMEMORY);<br/>
#else<br/>
//RW_TO_DO - Throw custom Lib-version exception<br/>
OurThrowDOE2LibException(-1,__FILE__,__LINE__,0,"OUT OF MEMORY");<br/>
#endif //_DOE2LIB<br/>
}
for (long hr=0; hr<8760; hr++)<br/>
{<br/>
COleVariant vHrResult( m_faWeatherData[iSeries]
);<br/>
SafeArrayPutElement( pSAData, &hr, vHrResult );<br/>
}
// Now that we have populated the SAFEARRAY, assign it to the VARIANT pointer that we are returning to the client.<br/>
V_VT( pvWeatherData ) = VT_ARRAY | VT_VARIANT;<br/>
V_ARRAY( pvWeatherData ) = pSAData;<br/>
}<br/>
}
Thanks in advance for help with this problem, I feel like I spent too much time on what should be a simple problem. Also please post up any links or books that that cover interop between native C++ and C# well (I think I have already ping-ponged through
most of the Visual Studio/MSDN documentation, but maybe I missed something there too).
Thanks Again,
Tom
<
TommyVee
<br/>
<br/>
<br/>
View the full article