From 08dc81499cfb5b7e58bd366642d7920b5dbb8761 Mon Sep 17 00:00:00 2001 From: tijoytk Date: Wed, 11 May 2016 10:00:52 -0700 Subject: Marshaling helpers for MarshalAs LPUTF8Str. --- .../MarshalAPI/String/StringMarshalingTest.cs | 27 ++++ .../MarshalAPI/String/StringMarshalingTest.csproj | 13 +- tests/src/Interop/MarshalAPI/String/project.json | 31 +--- .../StringMarshalling/UTF8/UTF8TestNative.cpp | 172 +++++++++------------ 4 files changed, 106 insertions(+), 137 deletions(-) (limited to 'tests/src/Interop') diff --git a/tests/src/Interop/MarshalAPI/String/StringMarshalingTest.cs b/tests/src/Interop/MarshalAPI/String/StringMarshalingTest.cs index f4ec24fe0e..4f5fbf2d7e 100644 --- a/tests/src/Interop/MarshalAPI/String/StringMarshalingTest.cs +++ b/tests/src/Interop/MarshalAPI/String/StringMarshalingTest.cs @@ -171,6 +171,32 @@ public class StringMarshalingTest } + public void TestUTF8String() + { + foreach (String srcString in TestStrings) + { + // we assume string null terminated + if (srcString.Contains("\0")) + continue; + + IntPtr ptrString = Marshal.StringToAllocatedMemoryUTF8(srcString); + string retString = Marshal.PtrToStringUTF8(ptrString); + + if (!srcString.Equals(retString)) + { + throw new Exception("Round triped strings do not match..."); + } + if (srcString.Length > 0) + { + string retString2 = Marshal.PtrToStringUTF8(ptrString, srcString.Length - 1); + if (!retString2.Equals(srcString.Substring(0, srcString.Length - 1))) + { + throw new Exception("Round triped strings do not match..."); + } + } + Marshal.FreeHGlobal(ptrString); + } + } public bool RunTests() { @@ -179,6 +205,7 @@ public class StringMarshalingTest StringToCoTaskMemUniToString(); StringToHGlobalAnsiToString(); StringToHGlobalUniToString(); + TestUTF8String(); return true; } diff --git a/tests/src/Interop/MarshalAPI/String/StringMarshalingTest.csproj b/tests/src/Interop/MarshalAPI/String/StringMarshalingTest.csproj index 9e3b782b59..d4528a574c 100644 --- a/tests/src/Interop/MarshalAPI/String/StringMarshalingTest.csproj +++ b/tests/src/Interop/MarshalAPI/String/StringMarshalingTest.csproj @@ -7,15 +7,16 @@ StringMarshalingTest 2.0 {F1E66554-8C8E-4141-85CF-D0CD6A0CD0B0} - Exe + exe Properties 512 {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} $(ProgramFiles)\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages ..\..\ - + true 7a9bfb7d $(DefineConstants);STATIC + true @@ -28,8 +29,8 @@ - - + + @@ -41,7 +42,7 @@ {c8c0dc74-fac4-45b1-81fe-70c4808366e0} CoreCLRTestLibrary - + - + \ No newline at end of file diff --git a/tests/src/Interop/MarshalAPI/String/project.json b/tests/src/Interop/MarshalAPI/String/project.json index fc8ccb4cc2..fc3f0262a9 100644 --- a/tests/src/Interop/MarshalAPI/String/project.json +++ b/tests/src/Interop/MarshalAPI/String/project.json @@ -1,34 +1,5 @@ { - "dependencies": { - "Microsoft.NETCore.Platforms": "1.0.1-rc2-23816", - "System.Collections": "4.0.10", - "System.Collections.NonGeneric": "4.0.1-rc2-23816", - "System.Collections.Specialized": "4.0.1-rc2-23816", - "System.ComponentModel": "4.0.1-rc2-23816", - "System.Console": "4.0.0-rc2-23816", - "System.Diagnostics.Process": "4.1.0-rc2-23816", - "System.Globalization": "4.0.10", - "System.Globalization.Calendars": "4.0.0", - "System.IO": "4.0.10", - "System.IO.FileSystem": "4.0.0", - "System.IO.FileSystem.Primitives": "4.0.0", - "System.Linq": "4.0.1-rc2-23816", - "System.Linq.Queryable": "4.0.1-rc2-23816", - "System.Reflection": "4.0.10", - "System.Reflection.Primitives": "4.0.0", - "System.Runtime": "4.1.0-rc2-23816", - "System.Runtime.Extensions": "4.0.10", - "System.Runtime.Handles": "4.0.0", - "System.Runtime.InteropServices": "4.1.0-rc2-23816", - "System.Runtime.Loader": "4.0.0-rc2-23816", - "System.Text.Encoding": "4.0.10", - "System.Threading": "4.0.10", - "System.Threading.Thread": "4.0.0-rc2-23816", - "System.Xml.ReaderWriter": "4.0.11-rc2-23816", - "System.Xml.XDocument": "4.0.11-rc2-23816", - "System.Xml.XmlDocument": "4.0.1-rc2-23816", - "System.Xml.XmlSerializer": "4.0.11-rc2-23816" - }, + "dependencies": {}, "frameworks": { "dnxcore50": {} }, diff --git a/tests/src/Interop/StringMarshalling/UTF8/UTF8TestNative.cpp b/tests/src/Interop/StringMarshalling/UTF8/UTF8TestNative.cpp index 77e4b9c8cb..f4e0f2f950 100644 --- a/tests/src/Interop/StringMarshalling/UTF8/UTF8TestNative.cpp +++ b/tests/src/Interop/StringMarshalling/UTF8/UTF8TestNative.cpp @@ -4,34 +4,43 @@ #include -// helper functions -#ifdef _WIN32 -char* UTF16ToUTF8(wchar_t * pszTextUTF16) +const int NSTRINGS = 6; +#ifdef _WIN32 +wchar_t *utf8strings[] = { L"Managed", +L"S\x00EEne kl\x00E2wen durh die wolken sint geslagen" , +L"\x0915\x093E\x091A\x0902 \x0936\x0915\x094D\x0928\x094B\x092E\x094D\x092F\x0924\x094D\x0924\x0941\x092E\x094D \x0964 \x0928\x094B\x092A\x0939\x093F\x0928\x0938\x094D\x0924\x093F \x092E\x093E\x092E\x094D", +L"\x6211\x80FD\x541E\x4E0B\x73BB\x7483\x800C\x4E0D\x4F24\x8EAB\x4F53", +L"\x10E6\x10DB\x10D4\x10E0\x10D7\x10E1\x10D8 \x10E8\x10D4\x10DB\x10D5\x10D4\x10D3\x10E0\x10D4,\x10E8\x10D4\x10DB\x10D5\x10D4\x10D3\x10E0\x10D4, \x10DC\x10E3\x10D7\x10E3 \x10D9\x10D5\x10DA\x10D0 \x10D3\x10D0\x10DB\x10EE\x10E1\x10DC\x10D0\x10E1 \x10E8\x10D4\x10DB\x10D5\x10D4\x10D3\x10E0\x10D4,\x10E1\x10DD\x10E4\x10DA\x10D8\x10E1\x10D0 \x10E8\x10D4\x10DB\x10D5\x10D4\x10D3\x10E0\x10D4, \x10E8\x10D4\x10DB\x10D5\x10D4\x10D3\x10E0\x10D4,\x10E8\x10D4\x10DB\x10D5\x10D4\x10D3\x10E0\x10D4,\x10E8\x10D4\x10DB\x10D5\x10D4\x10D3\x10E0\x10D4,\x10E8\x10E0\x10DD\x10DB\x10D0\x10E1\x10D0, \x10EA\x10D4\x10EA\x10EE\x10DA\x10E1, \x10EC\x10E7\x10D0\x10DA\x10E1\x10D0 \x10D3\x10D0 \x10DB\x10D8\x10EC\x10D0\x10E1\x10D0, \x10F0\x10D0\x10D4\x10E0\x10D7\x10D0 \x10D7\x10D0\x10DC\x10D0 \x10DB\x10E0\x10DD\x10DB\x10D0\x10E1\x10D0; \x10DB\x10DD\x10DB\x10EA\x10DC\x10D4\x10E1 \x10E4\x10E0\x10D7\x10D4\x10DC\x10D8 \x10D3\x10D0 \x10D0\x10E6\x10D5\x10E4\x10E0\x10D8\x10DC\x10D3\x10D4, \x10DB\x10D8\x10D5\x10F0\x10EE\x10D5\x10D3\x10D4 \x10DB\x10D0\x10E1 \x10E9\x10D4\x10DB\x10E1\x10D0 \x10DC\x10D3\x10DD\x10DB\x10D0\x10E1\x10D0, \x10D3\x10E6\x10D8\x10E1\x10D8\x10D7 \x10D3\x10D0 \x10E6\x10D0\x10DB\x10D8\x10D7 \x10D5\x10F0\x10EE\x10D4\x10D3\x10D5\x10D8\x10D3\x10D4 \x10DB\x10D6\x10D8\x10E1\x10D0 \x10D4\x10DA\x10D5\x10D0\x10D7\x10D0 \x10D9\x10E0\x10D7\x10DD\x10DB\x10D0\x10D0\x10E1\x10D0\x10E8\x10D4\x10DB\x10D5\x10D4\x10D3\x10E0\x10D4,\x10E8\x10D4\x10DB\x10D5\x10D4\x10D3\x10E0\x10D4,", +L"\x03A4\x03B7 \x03B3\x03BB\x03CE\x03C3\x03C3\x03B1 \x03BC\x03BF\x03C5 \x03AD\x03B4\x03C9\x03C3\x03B1\x03BD \x03B5\x03BB\x03BB\x03B7\x03BD\x03B9\x03BA\x03AE", +L"\0" +}; + + + +char* utf16_to_utf8(wchar_t *srcstring) { - if ((pszTextUTF16 == NULL) || (*pszTextUTF16 == L'\0')) { + if ((srcstring == NULL) || (*srcstring == L'\0')) { return 0; } - - size_t cchUTF16; - cchUTF16 = wcslen(pszTextUTF16) + 1; + size_t cchUTF16 = wcslen(srcstring) + 1; int cbUTF8 = WideCharToMultiByte(CP_UTF8, 0, - pszTextUTF16, + srcstring, (int)cchUTF16, NULL, 0/* request buffer size*/, NULL, NULL); - + char *pszUTF8 = (char*)CoTaskMemAlloc(sizeof(char) * (cbUTF8 + 1)); int nc = WideCharToMultiByte(CP_UTF8, // convert to UTF-8 0, //default flags - pszTextUTF16, //source wide string + srcstring, //source wide string (int)cchUTF16, // length of wide string pszUTF8, // destination buffer cbUTF8, // destination buffer size NULL, NULL); - + if (!nc) { throw; @@ -41,7 +50,7 @@ char* UTF16ToUTF8(wchar_t * pszTextUTF16) return pszUTF8; } -wchar_t* UTF8ToUTF16(const char *utf8) +wchar_t* utf8_to_utf16(const char *utf8) { // Special case of empty input string //wszTextUTF16 @@ -59,8 +68,8 @@ wchar_t* UTF8ToUTF16(const char *utf8) NULL, // unused - no conversion done in this step 0 // request size of destination buffer, in wchar_t's ); - - wszTextUTF16 = (wchar_t*)(CoTaskMemAlloc((cbUTF16 + 1 ) * sizeof(wchar_t) )); + + wszTextUTF16 = (wchar_t*)(CoTaskMemAlloc((cbUTF16 + 1) * sizeof(wchar_t))); // Do the actual conversion from UTF-8 to UTF-16 int nc = ::MultiByteToWideChar( CP_UTF8, // convert from UTF-8 @@ -78,40 +87,21 @@ wchar_t* UTF8ToUTF16(const char *utf8) wszTextUTF16[nc] = '\0'; return wszTextUTF16; } -#endif +char *get_utf8_string(int index) { + char *pszTextutf8 = utf16_to_utf8(utf8strings[index]); + return pszTextutf8; +} -LPSTR build_return_string(const char* pReturn) +void free_utf8_string(char *str) { - char *ret = 0; - if (pReturn == 0 || *pReturn == 0) - return ret; - - size_t strLength = strlen(pReturn); - ret = (LPSTR)(CoTaskMemAlloc(sizeof(char)* (strLength + 1))); - memset(ret, '\0', strLength + 1); - strncpy_s(ret, strLength + 1, pReturn, strLength); - return ret; + CoTaskMemFree(str); } -// this is the same set as in managed side , but here -// string need to be escaped , still CL applied some local and -// end up with different byte sequence. - -const int NSTRINGS = 6; -#ifdef _WIN32 -wchar_t *utf8Strings[] = { L"Managed", -L"S\x00EEne kl\x00E2wen durh die wolken sint geslagen" , -L"\x0915\x093E\x091A\x0902 \x0936\x0915\x094D\x0928\x094B\x092E\x094D\x092F\x0924\x094D\x0924\x0941\x092E\x094D \x0964 \x0928\x094B\x092A\x0939\x093F\x0928\x0938\x094D\x0924\x093F \x092E\x093E\x092E\x094D", -L"\x6211\x80FD\x541E\x4E0B\x73BB\x7483\x800C\x4E0D\x4F24\x8EAB\x4F53", -L"\x10E6\x10DB\x10D4\x10E0\x10D7\x10E1\x10D8 \x10E8\x10D4\x10DB\x10D5\x10D4\x10D3\x10E0\x10D4,\x10E8\x10D4\x10DB\x10D5\x10D4\x10D3\x10E0\x10D4, \x10DC\x10E3\x10D7\x10E3 \x10D9\x10D5\x10DA\x10D0 \x10D3\x10D0\x10DB\x10EE\x10E1\x10DC\x10D0\x10E1 \x10E8\x10D4\x10DB\x10D5\x10D4\x10D3\x10E0\x10D4,\x10E1\x10DD\x10E4\x10DA\x10D8\x10E1\x10D0 \x10E8\x10D4\x10DB\x10D5\x10D4\x10D3\x10E0\x10D4, \x10E8\x10D4\x10DB\x10D5\x10D4\x10D3\x10E0\x10D4,\x10E8\x10D4\x10DB\x10D5\x10D4\x10D3\x10E0\x10D4,\x10E8\x10D4\x10DB\x10D5\x10D4\x10D3\x10E0\x10D4,\x10E8\x10E0\x10DD\x10DB\x10D0\x10E1\x10D0, \x10EA\x10D4\x10EA\x10EE\x10DA\x10E1, \x10EC\x10E7\x10D0\x10DA\x10E1\x10D0 \x10D3\x10D0 \x10DB\x10D8\x10EC\x10D0\x10E1\x10D0, \x10F0\x10D0\x10D4\x10E0\x10D7\x10D0 \x10D7\x10D0\x10DC\x10D0 \x10DB\x10E0\x10DD\x10DB\x10D0\x10E1\x10D0; \x10DB\x10DD\x10DB\x10EA\x10DC\x10D4\x10E1 \x10E4\x10E0\x10D7\x10D4\x10DC\x10D8 \x10D3\x10D0 \x10D0\x10E6\x10D5\x10E4\x10E0\x10D8\x10DC\x10D3\x10D4, \x10DB\x10D8\x10D5\x10F0\x10EE\x10D5\x10D3\x10D4 \x10DB\x10D0\x10E1 \x10E9\x10D4\x10DB\x10E1\x10D0 \x10DC\x10D3\x10DD\x10DB\x10D0\x10E1\x10D0, \x10D3\x10E6\x10D8\x10E1\x10D8\x10D7 \x10D3\x10D0 \x10E6\x10D0\x10DB\x10D8\x10D7 \x10D5\x10F0\x10EE\x10D4\x10D3\x10D5\x10D8\x10D3\x10D4 \x10DB\x10D6\x10D8\x10E1\x10D0 \x10D4\x10DA\x10D5\x10D0\x10D7\x10D0 \x10D9\x10E0\x10D7\x10DD\x10DB\x10D0\x10D0\x10E1\x10D0\x10E8\x10D4\x10DB\x10D5\x10D4\x10D3\x10E0\x10D4,\x10E8\x10D4\x10DB\x10D5\x10D4\x10D3\x10E0\x10D4,", -L"\x03A4\x03B7 \x03B3\x03BB\x03CE\x03C3\x03C3\x03B1 \x03BC\x03BF\x03C5 \x03AD\x03B4\x03C9\x03C3\x03B1\x03BD \x03B5\x03BB\x03BB\x03B7\x03BD\x03B9\x03BA\x03AE", -L"\0" -}; +#else //Not WIndows -#else //test strings -const char *utf8Strings[] = { "Managed", +const char *utf8strings[] = { "Managed", "Sîne klâwen durh die wolken sint geslagen", "काचं शक्नोम्यत्तुम् । नोपहिनस्ति माम्", "我能吞下玻璃而不伤身体", @@ -119,8 +109,31 @@ const char *utf8Strings[] = { "Managed", "Τη γλώσσα μου έδωσαν ελληνική", "\0" }; + +char *get_utf8_string(int index) { + return (char*)utf8strings[index]; +} + +void free_utf8_string(char *str) +{ + // do nothing , we never allocated the temp buffer on non-windows +} + #endif +LPSTR build_return_string(const char* pReturn) +{ + char *ret = 0; + if (pReturn == 0 || *pReturn == 0) + return ret; + + size_t strLength = strlen(pReturn); + ret = (LPSTR)(CoTaskMemAlloc(sizeof(char)* (strLength + 1))); + memset(ret, '\0', strLength + 1); + strncpy_s(ret, strLength + 1, pReturn, strLength); + return ret; +} + // Modify the string builder in place, managed side validates. extern "C" DLL_EXPORT void __cdecl StringBuilderParameterInOut(/*[In,Out] StringBuilder*/ char *s, int index) { @@ -128,11 +141,7 @@ extern "C" DLL_EXPORT void __cdecl StringBuilderParameterInOut(/*[In,Out] String if (s == 0 || *s == 0) return; -#ifdef _WIN32 - char *pszTextutf8 = UTF16ToUTF8(utf8Strings[index]); -#else - char *pszTextutf8 = (char*)utf8Strings[index]; -#endif + char *pszTextutf8 = get_utf8_string(index); // do byte by byte validation of in string size_t szLen = strlen(s); @@ -151,48 +160,31 @@ extern "C" DLL_EXPORT void __cdecl StringBuilderParameterInOut(/*[In,Out] String s[i] = pszTextutf8[i]; } s[outLen] = '\0'; -#ifdef _WIN32 - CoTaskMemFree(pszTextutf8); -#endif + free_utf8_string(pszTextutf8); } //out string builder extern "C" DLL_EXPORT void __cdecl StringBuilderParameterOut(/*[Out] StringBuilder*/ char *s, int index) { - -#ifdef _WIN32 - char *pszTextutf8 = UTF16ToUTF8(utf8Strings[index]); -#else - char *pszTextutf8 = (char*)utf8Strings[index]; -#endif + char *pszTextutf8 = get_utf8_string(index); // modify the string inplace size_t outLen = strlen(pszTextutf8); for (size_t i = 0; i < outLen; i++) { s[i] = pszTextutf8[i]; } s[outLen] = '\0'; -#ifdef _WIN32 - CoTaskMemFree(pszTextutf8); -#endif + free_utf8_string(pszTextutf8); } // return utf8 stringbuilder -extern "C" DLL_EXPORT char* __cdecl StringBuilderParameterReturn(int index) { - -#ifdef _WIN32 - char *pszTextutf8 = UTF16ToUTF8(utf8Strings[index]); -#else - char *pszTextutf8 = (char*)utf8Strings[index]; -#endif +extern "C" DLL_EXPORT char* __cdecl StringBuilderParameterReturn(int index) +{ + char *pszTextutf8 = get_utf8_string(index); size_t strLength = strlen(pszTextutf8); LPSTR ret = (LPSTR)(CoTaskMemAlloc(sizeof(char)* (strLength + 1))); memcpy(ret, pszTextutf8, strLength); ret[strLength] = '\0'; - -#ifdef _WIN32 - CoTaskMemFree(pszTextutf8); -#endif - + free_utf8_string(pszTextutf8); return ret; } @@ -227,11 +219,7 @@ extern "C" DLL_EXPORT void _cdecl TestStructWithUtf8Field(struct FieldWithUtf8 f if (pszManagedutf8 == 0 || *pszManagedutf8 == 0) return; -#ifdef _WIN32 - pszNative = UTF16ToUTF8(utf8Strings[stringIndex]); -#else - pszNative = (char*)utf8Strings[stringIndex]; -#endif + pszNative = get_utf8_string(stringIndex); outLen = strlen(pszNative); // do byte by byte comparision for (size_t i = 0; i < outLen; i++) @@ -242,36 +230,24 @@ extern "C" DLL_EXPORT void _cdecl TestStructWithUtf8Field(struct FieldWithUtf8 f throw; } } -#ifdef _WIN32 - CoTaskMemFree(pszNative); -#endif + free_utf8_string(pszNative); } // test c# out keyword extern "C" DLL_EXPORT void __cdecl StringParameterRefOut(/*out*/ char **s, int index) { -#ifdef _WIN32 - char *pszTextutf8 = UTF16ToUTF8(utf8Strings[index]); -#else - char *pszTextutf8 = (char*)utf8Strings[index]; -#endif + char *pszTextutf8 = get_utf8_string(index); size_t strLength = strlen(pszTextutf8); *s = (LPSTR)(CoTaskMemAlloc(sizeof(char)* (strLength + 1))); memcpy(*s, pszTextutf8, strLength); (*s)[strLength] = '\0'; -#ifdef _WIN32 - CoTaskMemFree(pszTextutf8); -#endif + free_utf8_string(pszTextutf8); } //c# ref extern "C" DLL_EXPORT void __cdecl StringParameterRef(/*ref*/ char **s, int index) { -#ifdef _WIN32 - char *pszTextutf8 = UTF16ToUTF8(utf8Strings[index]); -#else - char *pszTextutf8 = (char*)utf8Strings[index]; -#endif + char *pszTextutf8 = get_utf8_string(index); size_t strLength = strlen(pszTextutf8); // do byte by byte validation of in string size_t szLen = strlen(*s); @@ -292,9 +268,7 @@ extern "C" DLL_EXPORT void __cdecl StringParameterRef(/*ref*/ char **s, int inde *s = (LPSTR)(CoTaskMemAlloc(sizeof(char)* (strLength + 1))); memcpy(*s, pszTextutf8, strLength); (*s)[strLength] = '\0'; -#ifdef _WIN32 - CoTaskMemFree(pszTextutf8); -#endif + free_utf8_string(pszTextutf8); } // delegate test @@ -302,13 +276,9 @@ typedef void (__cdecl * Callback)(char *text, int index); extern "C" DLL_EXPORT void _cdecl Utf8DelegateAsParameter(Callback managedCallback) { for (int i = 0; i < NSTRINGS; ++i) - { - char *pszNative = 0; -#ifdef _WIN32 - pszNative = UTF16ToUTF8(utf8Strings[i]); -#else - pszNative = (char*)utf8Strings[i]; -#endif + { + char *pszNative = get_utf8_string(i); managedCallback(pszNative, i); + free_utf8_string(pszNative); } } \ No newline at end of file -- cgit v1.2.3