Hi.
My apologies if this question is trivial, I couldn't find the answers
on MDSN and it's the first time I've worked with input/output strings
across explicit P/Invoke.
My question is about memory management of input/output strings using
explicit P/Invoke.
Basically I send a fixed-size array of empty strings across P/Invoke
and the C++ library needs to populate the individual strings with data
of various lengths to be returned back up to the caller. The C++
library is NOT compliled with any "/clr" options.
Here's the C# declaration:
[DllImport("my.dll")]
internal extern static void MyMethod(
[In][Out][MarshalAs(UnmanagedType.LPArray,
ArraySubType=UnmanagedType.LPStr, SizeConst=2)]
String[] messages,
int numberOfMessages);
Here's the C++ declaration:
extern "C" __declspec(dllexport)
void MyMethod(
char* messages[],
int numberOfMessages);
Who is responsible for memory management of the individual strings -
CLR or C++ library?
Does the C++ library have to perform new/delete on each string?
If not, do I have to explicitly pass string lengths across P/Invoke to
avoid C++ buffer overrun?
Currently the code crashes when the C++ library uses strcpy() copy data
into the output strings.
Thanks very much for your help.
Chris Nusca
Bob Eaton - 25 May 2006 13:49 GMT
I'm not sure if this is *necessary*, but a better (read: safer) way to do
this is having your DLL return a SAFEARRAY of type BSTR. Here's a snippet
I've used (I don't know how to do it without ATL helper classes, though):
// on the C++ side, define your method as:
// in .cpp file implementation:
SAFEARRAY* MyMethod()
{
CComSafeArray<BSTR>* pSa = new CComSafeArray<BSTR>();
int nSize = numberOfMessages;
pSa->Create(nSize);
for(int i = 0; i < nSize; i++ )
{
// create "system" memory for returning back to caller (using CComBSTR)
CComBSTR strName = messages[i];
pSa->SetAt(i,strName);
}
return pSa->Detach();
}
// then on the .Net side, you can use:
string [] aMessages = MyMethod();
To get the returned string array back correctly.
I've left out some details because I'm not sure whether this works the same
for a DllImport vs. the way I use it--as a method of a COM class),
but I hope this gives you the hint you need.
Bob
> Hi.
>
[quoted text clipped - 40 lines]
>
> Chris Nusca
chris_nusca@hotmail.com - 25 May 2006 19:36 GMT
Thanks Bob.
I was hoping to stick with just ANSI strings (i.e. char* ). I'm
actually contemplating using char[MAX_PATH] instead of char* which
would make everything blittable and hopefully easier. If I can't get
that working I'll probably revert to your suggestion. Either way I'll
provide an update.
Regards
Chris
chris_nusca@hotmail.com - 29 May 2006 06:43 GMT
Hi,
I found the solution which is to explicitly pre-allocate the strings in
.NET as follows:
// do this for every input/output string
messages[i] = new String('\0', 256);
The C++ library receives a buffer of (256+1) characters which can be
read/written. The CLR handles allocation and deletion, so the only
extra work is to pass an extra input parameter containing the buffer
size.
Here is a nice MSDN article on the subject:
http://msdn2.microsoft.com/en-us/library/s9ts558h(VS.80).aspx
Regards,
Chris