How do you convert a 'System::String ^' to 'TCHAR'?
Asked Answered
T

3

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?

Teocalli answered 14/4, 2011 at 17:14 Comment(1)
re: your flag--you need to email team@stackoverflow for help with that. Also, lesson learned?Mcmath
B
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.

Belted answered 14/4, 2011 at 17:21 Comment(1)
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.Advertence
H
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));
}
Hauger answered 14/4, 2011 at 17:37 Comment(3)
Same problem as Blindy, HGLOBALs are opaque structure which should be passed to GlobalLock in order to get a pointer to the contents.Advertence
If so, then Microsoft's documentation is wrong. msdn.microsoft.com/en-us/library/…Hauger
Ok, GlobalLock 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.Advertence
S
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.

Sauna answered 14/4, 2011 at 17:24 Comment(2)
both of these std's say that either wstring or string is not a member of std. am i missing a reference of kinds?Teocalli
You'll need to #include <string> in your C++ code to get the declaration of either.Sauna
B
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.

Belted answered 14/4, 2011 at 17:21 Comment(1)
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.Advertence

© 2022 - 2024 — McMap. All rights reserved.