How to convert Platform::String to char*?
Asked Answered
M

6

18

How do I convert the contents of a Platform::String to be used by functions that expect a char* based string? I'm assuming WinRT provides helper functions for this but I just can't find them.

Thanks!

Marisamariscal answered 31/7, 2012 at 17:54 Comment(1)
You cannot ask for a conversion from UTF-16LE, unless you specify your target character encoding. What is it?Westbrooks
P
13

Platform::String::Data() will return a wchar_t const* pointing to the contents of the string (similar to std::wstring::c_str()). Platform::String represents an immutable string, so there's no accessor to get a wchar_t*. You'll need to copy its contents, e.g. into a std::wstring, to make changes.

There's no direct way to get a char* or a char const* because Platform::String uses wide characters (all Metro style apps are Unicode apps). You can convert to multibyte using WideCharToMultiByte.

Posehn answered 31/7, 2012 at 18:1 Comment(7)
Are there any Metro-specific "indirect" methods of converting to char* ?Marisamariscal
WideCharToMultiByte is callable from a Metro style app.Posehn
@JamesMcNellis If String is immutable, why does String::Begin return a char16 * and not a char16 const*? Is it legal to modify an individual character using this pointer?Those
@Prætorian: The documentation is wrong. Begin() and End() both return char16 const*. You can see their definitions in <vccorlib.h>. You may not modify the pointed-to characters (strings are reference counted, and if there are other owners and you modify the string, those other owners will be very surprised to discover that their string has changed). I'll see what I can do to get the documentation fixed. Thanks for the heads-up.Posehn
this doesn't answer the question. He asked how to convert from a Platform::String to a char* and there are ways to do this. WideCharToMultiByte works but someone new to the function would have no idea how to use it.Cornie
Also a wchar_t* from HString::GetRawBuffer is using the WRL and WindowsGetStringRawBuffer if using the true HSTRING.Orectic
@Eric: The OP never mentioned their destination character string encoding. Without that information, this answer is as complete as it gets (although it could have asked for the missing information). Then again, you could have left a comment on it, to explain why this is important just as well.Westbrooks
S
16

Here is a very simple way to do this in code w/o having to worry about buffer lengths. Only use this solution if you are certain you are dealing with ASCII:

Platform::String^ fooRT = "aoeu";
std::wstring fooW(fooRT->Begin());
std::string fooA(fooW.begin(), fooW.end());
const char* charStr = fooA.c_str();

Keep in mind that in this example, the char* is on the stack and will go away once it leaves scope

Sphygmoid answered 12/4, 2013 at 20:36 Comment(3)
For every problem, there is a solution, that's simple, elegant. And wrong. Like this one. Any character outside the range of ASCII characters will just be butchered to a random representation, depending on the executing threads current state. Don't use this solution. (Which is easy, because it doesn't even compile.)Westbrooks
Fixed the compiler error. PS: Still a nice way of converting if 100% sure that you only have to deal with ASCII charactersManciple
the std::string line generates compile messages is there any way to get around this? see reference to function template instantiation 'std::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string<std::_String_iterator<std::_String_val<std::_Simple_types<_Elem>>>,0>(_Iter,_Iter,const _Alloc &)' being compiled with [ _Elem=wchar_t, _Iter=std::_String_iterator<std::_String_val<std::_Simple_types<wchar_t>>>, _Alloc=std::allocator<char> ]Ortensia
P
13

Platform::String::Data() will return a wchar_t const* pointing to the contents of the string (similar to std::wstring::c_str()). Platform::String represents an immutable string, so there's no accessor to get a wchar_t*. You'll need to copy its contents, e.g. into a std::wstring, to make changes.

There's no direct way to get a char* or a char const* because Platform::String uses wide characters (all Metro style apps are Unicode apps). You can convert to multibyte using WideCharToMultiByte.

Posehn answered 31/7, 2012 at 18:1 Comment(7)
Are there any Metro-specific "indirect" methods of converting to char* ?Marisamariscal
WideCharToMultiByte is callable from a Metro style app.Posehn
@JamesMcNellis If String is immutable, why does String::Begin return a char16 * and not a char16 const*? Is it legal to modify an individual character using this pointer?Those
@Prætorian: The documentation is wrong. Begin() and End() both return char16 const*. You can see their definitions in <vccorlib.h>. You may not modify the pointed-to characters (strings are reference counted, and if there are other owners and you modify the string, those other owners will be very surprised to discover that their string has changed). I'll see what I can do to get the documentation fixed. Thanks for the heads-up.Posehn
this doesn't answer the question. He asked how to convert from a Platform::String to a char* and there are ways to do this. WideCharToMultiByte works but someone new to the function would have no idea how to use it.Cornie
Also a wchar_t* from HString::GetRawBuffer is using the WRL and WindowsGetStringRawBuffer if using the true HSTRING.Orectic
@Eric: The OP never mentioned their destination character string encoding. Without that information, this answer is as complete as it gets (although it could have asked for the missing information). Then again, you could have left a comment on it, to explain why this is important just as well.Westbrooks
I
8

You shouldn't cast a wide character to a char, you will mangle languages using more than one byte per character, e.g. Chinese. Here is the correct method.

#include <cvt/wstring>
#include <codecvt>

Platform::String^ fooRT = "foo";
stdext::cvt::wstring_convert<std::codecvt_utf8<wchar_t>> convert;
std::string stringUtf8 = convert.to_bytes(fooRT->Data());
const char* rawCstring = stringUtf8.c_str();
Inanimate answered 11/9, 2015 at 4:3 Comment(3)
Or one linear without using stdext char* raw = std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(fooRT->Data()).c_str();Synchronous
But using @Quest's method, the raw variable will point to deallocated memory (temporary object is gone after expression is evaluated) if used literally. Better use std::string utf8 = std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(fooRT->Data()) unless you are sure what you are doing.Booze
this solution doesn't generate any compiler messages!Ortensia
D
1

There's the String::Data method returning const char16*, which is the raw unicode string.

Conversion from unicode to ascii or whatever, i.e. char16* to char*, is a different matter. You probably don't need it since most methods have their wchar versions these days.

Dauphine answered 31/7, 2012 at 18:0 Comment(1)
Alas, for I do not live in a wchar world. Most of the code I'm working with is legacy code that expects 8 bit chars strings. :)Marisamariscal
M
1

A solution using wcstombs:

Platform::String^ platform_string = p_e->Uri->AbsoluteUri;
const wchar_t* wide_chars =  platform_string->Data();
char chars[512];
wcstombs(chars, wide_chars, 512);
Marengo answered 14/11, 2013 at 11:9 Comment(3)
wcstombs generates this warning 'wcstombs': This function or variable may be unsafe. Consider using wcstombs_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS.Ortensia
I guess the warning explain it all, using wcstombs_s is safer at the expense of some extra verbosity.Marengo
your comment provides no useful information, since myself and others coming here are not all C++/windows experts (I was trying to do some C# interop), I was relaying what the compiler told me while trying the answers on this page. maybe you could update your answer and provide more value?Ortensia
C
0

Solution 1:

#include <cvt/wstring>
#include <codecvt>

Platform::String^ tTextRT = "TestText";
stdext::cvt::wstring_convert<std::codecvt_utf8<wchar_t>> convert;
std::string stringUtf8 = convert.to_bytes(tTextRT->Data());
const char* rawCstring = stringUtf8.c_str();

But this solution generate error.

Error C4996 'stdext::cvt': warning STL4044: The contents of the stdext::cvt namespace are non-Standard extensions and will be removed in the future. The MultiByteToWideChar() and WideCharToMultiByte() functions can be used instead. You can define _SILENCE_STDEXT_CVT_DEPRECATION_WARNING or _SILENCE_ALL_MS_EXT_DEPRECATION_WARNINGS to suppress this warning.

Solution 2:

Platform::String^ testText = "Test Text";
std::wstring tTextW(testText->Begin());
std::string tTextA(tTextW.begin(), tTextW.end());
const char* charStr = tTextA.c_str();

But this solution has another Issue:

Any character outside the range of ASCII characters will just be destroyed to a random representation, depending on the executing threads current state.

Workable Solution:

#include <cvt/wstring>
#include <stringapiset.h>

Platform::String^ testText = "foo";
const wchar_t* pWStr = testText->Data();
int bufferSize = WideCharToMultiByte(CP_UTF8, 0, pWStr, -1, NULL, 0, NULL, NULL);
char* stringUtf8 = new char[bufferSize + 1];
memset(stringUtf8, 0, bufferSize + 1);
if (0 == WideCharToMultiByte(CP_UTF8, 0, pWStr, -1, stringUtf8, bufferSize, NULL, NULL))
{
    throw std::exception("Can't convert string to Unicode");
}
const char* rawCstring = std::string(stringUtf8).c_str();
delete[] stringUtf8;
Clearcut answered 30/11, 2023 at 6:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.