views:

217

answers:

3

Hey guys,

I'm new to C++. I'm calling a C++ function from C# using a PINVOKE and wanting to get a string back as an out parameter. However I just get an empty string back. The int out parameter works fine.

Importing:

[DllImport ( @"UnamanagedAssembly.dll", CharSet = CharSet.Ansi)] 
public static extern int Activate(ref int numActivated,  StringBuilder eventsActivated);

extern "C" __declspec(dllexport) int Activate(int *p_NumActivated, char *p_EventsActivated) {return Activation::GetInstance()->Activate(p_NumActivated, p_EventsActivated);}

Calling my C++ function from C#:

int numActivated = 0;
StringBuilder eventsActivated = new StringBuilder();
int status = Activate(ref numActivated, eventsActivated);  

The C++ function:

int Activation::Activate(int *p_NumActivated, char *&p_EventsActivated)
{
    char *pTemp = "Hello";
    p_EventsActivated = pTemp;

    *p_NumActivated = 1;

    return 0;
}
A: 

Pardon my answer if it is wrong, but:

I dont think a StringBuilder is compatible with a char *&

Are you're confusing a StringBuilder with a char** ? * You might want to create a new string , or stringbuilder based upon the return from Activate, rather than try to pass Activate a place to put the data. (As you seem to be trying to do?)

(Activate is allocating space for "Hello", then returning the pointer to its location).

Generally, when marshalling between C++ or C objects, when sending strings, it is best to also send the length of the string, to ensure that proper length strings are passed between callers.

Alex Lim
A: 
[DllImport ( @"UnamanagedAssembly.dll", CharSet = CharSet.Ansi)]
public static extern int Activate(
    ref int numActivated, 
    [MarshalAs(UnmanagedType.LPStr)]StringBuilder eventsActivated);
allonym
Tried adding that, but that there's still something wrong. Cheers though!
TerryB
@allonym: It's a reference to a pointer to a char, not a simple pointer to char. What you've posted exhibits undefined behavior in about 10 different ways.
Billy ONeal
allonym
A: 

P/Invoke is only supported for C interfaces. YMMV if you're trying to get it to talk to C++ constructs. In this case, you'd have to find out specifically what the C++ compiler was translating the reference into, as C++ compilers are free to implement that in any means they wish.

If you want to be able to P/Invoke this, but don't want to have to use pointer semantics at the call site, you could do something like:

int Activation::Activate(int *p_NumActivated, char **p_EventsActivated)
{
    return Activate(p_NumActivated, *p_EventsActivated);
}

int Activation::Activate(int *p_NumActivated, char *&p_EventsActivated)
{
    char *pTemp = "Hello";
    p_EventsActivated = pTemp;

    *p_NumActivated = 1;

    return 0;
}

which exposes a plain pointer you should be able to more simply P/Invoke.

Note, however, that whatever string you marshal it too is probably not going to be modifiable by the function in question. This is because .NET strings are UTF-16 strings, while the C++ function is using ASCII strings, which are not completely compatible with each other. You'll probably have to convert the .NET string into ASCII, marshal it across, marshal it back, and then reconvert back into UTF-16.

Oh, one last thing: As written, you're going to need to pass a reference to an Activation object in order for that function to work. The position of that argument is going to be C++ compiler dependent though.

Billy ONeal
Cheers. I'll give that a go. What is the simplest way to pass a string of arbitrary length (or within some large max length anyway) from C++ to C#. It sounds like I'm doing something that's not as easy as I was I thought it would be :)
TerryB
@TerryB: It can be simple -- but it's much easier if the C++ code is also using UTF-16 -- that is, `wchar_t` and friends instead of `char`. C# is a unicode environment. C++ is not unless you work at making it so.
Billy ONeal
Note that wchar_t and friends doesn't automatically imply unicode. It might be using UCS2.
Arafangion
@Arafangion: For 99% of cases, there is no difference. The BMP covers all languages in modern use. But I guess if you want to be pedantic...
Billy ONeal
Unless you want to work with translators for asian langauges, they _can_ still use the BMP, but they won't have the full range of characters they might want to use.
Arafangion