1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
|
// ------------------------------------------------------------------------------------------- //
// clix.h
//
// http://www.nuclex.org/articles/5-cxx/10-marshaling-strings-in-cxx-cli
//
// Marshals strings between .NET and C++ using C++/CLI (Visual C++ 2005 and later only).
// Faster and cleaner than the System::Interop method because it uses garbage collected memory.
// Use at your own leisure. No warranties whatsoever provided.
//
// Original code by Markus Ewald (http://www.nuclex.org/articles/marshaling-strings-in-cxx-cli)
// Updated version including several improvements suggested by Neil Hunt
// ------------------------------------------------------------------------------------------- //
#pragma once
#include <string>
#include <vcclr.h>
// CLI extensions namespace
namespace clix {
/// <summary>Encoding types for strings</summary>
enum Encoding {
/// <summary>ANSI encoding</summary>
/// <remarks>
/// This is the default encoding you've most likely been using all around in C++. ANSI
/// means 8 Bit encoding with character codes depending on the system's selected code page.
/// <remarks>
E_ANSI,
/// <summary>UTF-8 encoding</summary>
/// <remarks>
/// This is the encoding commonly used for multilingual C++ strings. All ASCII characters
/// (0-127) will be represented as single bytes. Be aware that UTF-8 uses more than one
/// byte for extended characters, so std::string::length() might not reflect the actual
/// length of the string in characters if it contains any non-ASCII characters.
/// <remarks>
E_UTF8,
/// <summary>UTF-16 encoding</summary>
/// <remarks>
/// This is the suggested to be used for marshaling and the native encoding of .NET
/// strings. It is similar to UTF-8 but uses a minimum of two bytes per character, making
/// the number of bytes required for a given string better predictable. Be aware, however,
/// that UTF-16 can still use more than two bytes for a character, so std::wstring::length()
/// might not reflect the actual length of the string.
/// </remarks>
E_UTF16, E_UNICODE = E_UTF16
};
// Ignore this if you're just scanning the headers for informations :-)
/* All this template stuff might seem like overkill, but it is well thought out and enables
you to use a readable and convenient call while still keeping the highest possible code
efficiency due to compile-time evaluation of the required conversion path.
*/
namespace detail {
// Get C++ string type for specified encoding
template<Encoding encoding> struct StringTypeSelecter;
template<> struct StringTypeSelecter<E_ANSI> { typedef std::string Type; };
template<> struct StringTypeSelecter<E_UTF8> { typedef std::string Type; };
template<> struct StringTypeSelecter<E_UTF16> { typedef std::wstring Type; };
// Compile-time check whether a given type is a managed System::String
template<typename StringType> struct IsManagedString { enum { Result = false }; };
template<> struct IsManagedString<System::String ^> { enum { Result = true }; };
// Compile-time selection of two types depending on a boolean expression
template<bool expression> struct Select;
template<> struct Select<false> {
template<typename TrueType, typename FalseType> struct Type { typedef FalseType Result; };
};
template<> struct Select<true> {
template<typename TrueType, typename FalseType> struct Type { typedef TrueType Result; };
};
// Direction of the marshaling process
enum MarshalingDirection {
CxxFromNet,
NetFromCxx
};
// The actual marshaling code
template<MarshalingDirection direction> struct StringMarshaler;
// Marshals to .NET from C++ strings
template<> struct StringMarshaler<NetFromCxx> {
template<Encoding encoding, typename SourceType>
static System::String ^marshal(const SourceType &string) {
// Constructs a std::[w]string in case someone gave us a char * to choke on
return marshalCxxString<encoding, SourceType>(string);
}
template<Encoding encoding, typename SourceType>
static System::String ^marshalCxxString(
const typename StringTypeSelecter<encoding>::Type &cxxString
) {
typedef typename StringTypeSelecter<encoding>::Type SourceStringType;
size_t byteCount = cxxString.length() * sizeof(SourceStringType::value_type);
// Copy the C++ string contents into a managed array of bytes
array<unsigned char> ^bytes = gcnew array<unsigned char>(byteCount);
{ pin_ptr<unsigned char> pinnedBytes = &bytes[0];
memcpy(pinnedBytes, cxxString.c_str(), byteCount);
}
// Now let one of .NET's encoding classes do the rest
return decode<encoding>(bytes);
}
private:
// Converts a byte array based on the selected encoding
template<Encoding encoding> static System::String ^decode(array<unsigned char> ^bytes);
template<> static System::String ^decode<E_ANSI>(array<unsigned char> ^bytes) {
return System::Text::Encoding::Default->GetString(bytes);
}
template<> static System::String ^decode<E_UTF8>(array<unsigned char> ^bytes) {
return System::Text::Encoding::UTF8->GetString(bytes);
}
template<> static System::String ^decode<E_UTF16>(array<unsigned char> ^bytes) {
return System::Text::Encoding::Unicode->GetString(bytes);
}
};
// Marshals to C++ strings from .NET
template<> struct StringMarshaler<CxxFromNet> {
template<Encoding encoding, typename SourceType>
static typename detail::StringTypeSelecter<encoding>::Type marshal(
System::String ^string
) {
typedef typename StringTypeSelecter<encoding>::Type StringType;
// First, we use .NET's encoding classes to convert the string into a byte array
array<unsigned char> ^bytes = encode<encoding>(string);
// fix crash if empty string passed
if (bytes->Length == 0) return StringType();
// Then we construct our native string from that byte array
pin_ptr<unsigned char> pinnedBytes(&bytes[0]);
return StringType(
reinterpret_cast<StringType::value_type *>(static_cast<unsigned char *>(pinnedBytes)),
bytes->Length / sizeof(StringType::value_type)
);
}
template<> static std::wstring marshal<E_UTF16, System::String ^>(
System::String ^string
) {
// fix crash if empty string passed
if (string->Length == 0) return std::wstring();
// We can directly access the characters in the managed string
pin_ptr<const wchar_t> pinnedChars(::PtrToStringChars(string));
return std::wstring(pinnedChars, string->Length);
}
private:
// Converts a string based on the selected encoding
template<Encoding encoding> static array<unsigned char> ^encode(System::String ^string);
template<> static array<unsigned char> ^encode<E_ANSI>(System::String ^string) {
return System::Text::Encoding::Default->GetBytes(string);
}
template<> static array<unsigned char> ^encode<E_UTF8>(System::String ^string) {
return System::Text::Encoding::UTF8->GetBytes(string);
}
template<> static array<unsigned char> ^encode<E_UTF16>(System::String ^string) {
return System::Text::Encoding::Unicode->GetBytes(string);
}
};
} // namespace detail
// ----------------------------------------------------------------------------------------- //
// clix::marshalString()
// ----------------------------------------------------------------------------------------- //
/// <summary>Marshals strings between .NET managed and C++ native</summary>
/// <remarks>
/// This all-in-one function marshals native C++ strings to .NET strings and vice versa.
/// You have to specify an encoding to use for the conversion, which always applies to the
/// native C++ string as .NET always uses UTF-16 for its own strings.
/// </remarks>
/// <param name="string">String to be marshalled to the other side</param>
/// <returns>The marshaled representation of the string</returns>
template<Encoding encoding, typename SourceType>
typename detail::Select<detail::IsManagedString<SourceType>::Result>::Type<
typename detail::StringTypeSelecter<encoding>::Type,
System::String ^
>::Result marshalString(SourceType string) {
// Pass on the call to our nifty template routines
return detail::StringMarshaler<
detail::IsManagedString<SourceType>::Result ? detail::CxxFromNet : detail::NetFromCxx
>::marshal<encoding, SourceType>(string);
}
} // namespace clix
|