problem in accessing a BSTR* from C#

  • Thread starter Thread starter Guest
  • Start date Start date
G

Guest

I use C# in .NET framework.
I have an ActiveX implemented in C++ that has a COM interface method that
gets as it’s out parameter a BSTR* . The interop translates this BSTR* into
C# string. From my managed code, I am calling that function with ‘out’
parameter of ‘string’ type. For normal size strings it works fine, but when
the string is very big (> 50,000 bytes), I get a null exception from the COM
interop.
What is the reason? How to solve this problem?

Thanks in advance.
 
But the Interop wrapper to the ActiveX is created automatically by VS.NET
when we drag the ActiveX into a form. we have no control on how it is being
done.
 
Hey Gilad,

Try using MarhsalAs attribute ont the function with BSTR.

Refer to MSDN for further information on the MarshalAs attribute.

-Moty-
 
Are you sure the ActiveX component is designed to deal with that large BSTR?

Willy.
 
I have tested it with an Un-managed client (MFC) and exactly the same string
that crashes .Net works fine in the MFC client.
 
Gilad Walden said:
I have tested it with an Un-managed client (MFC) and exactly the same
string
that crashes .Net works fine in the MFC client.


Maybe you should post your code, anyway, this works for me...

//C++

STDMETHODIMP CNoReg::GetLargeString(BSTR* large){
const int buffSize = 65000;
wchar_t* pwStr = new wchar_t[buffSize];
wmemset(pwStr, 0, buffSize);
// Fill with all 'A', leave room for null char.
for (int i = 0;i < buffSize-1; i++)
pwStr = L'A';
try {
CAtlString s(pwStr);
*large = s.AllocSysString();
}
catch(...){
return E_OUTOFMEMORY;
}
delete pwStr;
return S_OK;
}

//C#
string s = null;
LargeStringClass obj = new LargeStringClass ();
try {
obj.GetLargeString(out s);
...

Willy.
 
Thanks a lot! that worked. The way I allocated the BSTR in the ActiveX before
was:
STDMETHODIMP CNoReg::GetLargeString(BSTR* large)
{
std::string str;
// making Str bigger than 50,000...

*large= _bstr_t(str.c_str());
return S_OK;
}

Can you tell me what's the difference between that and using CAtlString?

Thanks,
Gilad Walden.
Willy Denoyette said:
Gilad Walden said:
I have tested it with an Un-managed client (MFC) and exactly the same
string
that crashes .Net works fine in the MFC client.


Maybe you should post your code, anyway, this works for me...

//C++

STDMETHODIMP CNoReg::GetLargeString(BSTR* large){
const int buffSize = 65000;
wchar_t* pwStr = new wchar_t[buffSize];
wmemset(pwStr, 0, buffSize);
// Fill with all 'A', leave room for null char.
for (int i = 0;i < buffSize-1; i++)
pwStr = L'A';
try {
CAtlString s(pwStr);
*large = s.AllocSysString();
}
catch(...){
return E_OUTOFMEMORY;
}
delete pwStr;
return S_OK;
}

//C#
string s = null;
LargeStringClass obj = new LargeStringClass ();
try {
obj.GetLargeString(out s);
...

Willy.
 
The difference is that your version contains a bug, you should never return
a BSTR by reference from a _bstr_t wrapper without copying/detaching the
wrapped BSTR first. The _bstr_t wrapper manages it's own lifecycle, so
basically what you are doing is:
- Construction of a _bstr_t object
- Conversion of your str to a BSTR
- Assignment of the BSTR to large
- Destruction of the BSTR
so what you return a pointer to a destructed object hence the undefined
behavior.

What you need to do when using the CRT _bstr_t wrapper (or any other COM
wrapper class like CComBSTR) is:
// return the detached underlying BSTR from the wrapper
*large= _bstr_t(str.c_str()).Detach();
// return a copy of the underlying BSTR
or copy the wrapped BSTR first like..
*large= _bstr_t(str.c_str()).Copy();

In both cases ownership of the BSTR is transfered to the caller, so it's up
to the caller to free the BSTR., this is done automatically by the COM
interop layer in the CLR.

Willy.



Gilad Walden said:
Thanks a lot! that worked. The way I allocated the BSTR in the ActiveX
before
was:
STDMETHODIMP CNoReg::GetLargeString(BSTR* large)
{
std::string str;
// making Str bigger than 50,000...

*large= _bstr_t(str.c_str());
return S_OK;
}

Can you tell me what's the difference between that and using CAtlString?

Thanks,
Gilad Walden.
Willy Denoyette said:
Gilad Walden said:
I have tested it with an Un-managed client (MFC) and exactly the same
string
that crashes .Net works fine in the MFC client.


Maybe you should post your code, anyway, this works for me...

//C++

STDMETHODIMP CNoReg::GetLargeString(BSTR* large){
const int buffSize = 65000;
wchar_t* pwStr = new wchar_t[buffSize];
wmemset(pwStr, 0, buffSize);
// Fill with all 'A', leave room for null char.
for (int i = 0;i < buffSize-1; i++)
pwStr = L'A';
try {
CAtlString s(pwStr);
*large = s.AllocSysString();
}
catch(...){
return E_OUTOFMEMORY;
}
delete pwStr;
return S_OK;
}

//C#
string s = null;
LargeStringClass obj = new LargeStringClass ();
try {
obj.GetLargeString(out s);
...

Willy.
 

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

Back
Top