problem in accessing a BSTR* from C#

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.
 
G

Guest

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.
 
M

Moty Michaely

Hey Gilad,

Try using MarhsalAs attribute ont the function with BSTR.

Refer to MSDN for further information on the MarshalAs attribute.

-Moty-
 
W

Willy Denoyette [MVP]

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

Willy.
 
G

Guest

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

Willy Denoyette [MVP]

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.
 
G

Guest

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.
 
W

Willy Denoyette [MVP]

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

Top