2

i asked a question here involving C++ and C# communicating. The problem got solved but led to a new problem.

this returns a String (C#)

return Marshal.PtrToStringAnsi(decryptsn(InpData));

this expects a TCHAR* (C++)

lpAlpha2[0] = Company::Pins::Bank::Decryption::Decrypt::Decryption("123456");

i've googled how to solve this problem, but i am not sure why the String has a carrot(^) on it. Would it be best to change the return from String to something else that C++ would accept? or would i need to do a convert before assigning the value?

Community
  • 1
  • 1
Andy
  • 2,248
  • 7
  • 34
  • 57
  • 1
    re: your flag--you need to email team@stackoverflow for help with that. Also, lesson learned? –  Apr 15 '11 at 13:11

3 Answers3

5

String has a ^ because that's the marker for a managed reference. Basically, it's used the same way as * in unmanaged land, except it can only point to an object type, not to other pointer types, or to void.

TCHAR is #defined (or perhaps typedefed, I can't remember) to either char or wchar_t, based on the _UNICODE preprocessor definition. Therefore, I would use that and write the code twice.

Either inline:

TCHAR* str;
String^ managedString
#ifdef _UNICODE
str = (TCHAR*) Marshal::StringToHGlobalUni(managedString).ToPointer();
#else
str = (TCHAR*) Marshal::StringToHGlobalAnsi(managedString).ToPointer();
#endif

// use str.

Marshal::FreeHGlobal(IntPtr(str));

or as a pair of conversion methods, both of which assume that the output buffer has already been allocated and is large enough. Method overloading should make it pick the correct one, based on what TCHAR is defined as.

void ConvertManagedString(String^ managedString, char* outString)
{
    char* str;
    str = (char*) Marshal::StringToHGlobalAnsi(managedString).ToPointer();    
    strcpy(outString, str);    
    Marshal::FreeHGlobal(IntPtr(str));
}

void ConvertManagedString(String^ managedString, wchar_t* outString)
{
    wchar_t* str;
    str = (wchar_t*) Marshal::StringToHGlobalUni(managedString).ToPointer();    
    wcscpy(outString, str);    
    Marshal::FreeHGlobal(IntPtr(str));
}
David Yaw
  • 27,383
  • 4
  • 60
  • 93
  • Same problem as Blindy, `HGLOBAL`s are opaque structure which should be passed to `GlobalLock` in order to get a pointer to the contents. – Ben Voigt Apr 15 '11 at 23:19
  • If so, then Microsoft's documentation is wrong. http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.stringtohglobalansi.aspx – David Yaw Apr 15 '11 at 23:33
  • Ok, [`GlobalLock`](http://msdn.microsoft.com/en-us/library/aa366584.aspx) is a no-op if `MEM_FIXED` is passed to `GlobalAlloc`. But MSDN doesn't say what flags `StringToHGlobalAnsi` uses internally. Relying on examples as if they were part of the specification is a notoriously bad idea. – Ben Voigt Apr 15 '11 at 23:52
4

The syntax String^ is C++/CLI talk for "(garbage collected) reference to a System.String".

You have a couple of options for the conversion of a String into a C string, which is another way to express the TCHAR*. My preferred way in C++ would be to store the converted string into a C++ string type, either std::wstring or std::string, depending on you building the project as a Unicode or MBCS project.

In either case you can use something like this:

std::wstring tmp = msclr::interop::marshal_as<std::wstring>( /* Your .NET String */ );

or

std::string tmp = msclr::interop::marshal_as<std::string>(...);

Once you've converted the string into the correct wide or narrow string format, you can then access its C string representation using the c_str() function, like so:

callCFunction(tmp.c_str());

Assuming that callCFunction expects you to pass it a C-style char* or wchar_t* (which TCHAR* will "degrade" to depending on your compilation settings.

Timo Geusch
  • 24,095
  • 5
  • 52
  • 70
2

That is a really rambling way to ask the question, but if you mean how to convert a String ^ to a char *, then you use the same marshaller you used before, only backwards:

char* unmanagedstring = (char *) Marshal::StringToHGlobalAnsi(managedstring).ToPointer();

Edit: don't forget to release the memory allocated when you're done using Marshal::FreeHGlobal.

Blindy
  • 65,249
  • 10
  • 91
  • 131
  • `HGLOBAL` is not a `char*`, and should not be cast to one. To get the pointer out of an `HGLOBAL`, use `GlobalLock`. Better yet, avoid `HGLOBAL` entirely. – Ben Voigt Apr 15 '11 at 23:14