diff --git a/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs b/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs index 33435681e9e937..54f5764989c3d9 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs @@ -508,6 +508,17 @@ internal static unsafe void ConvertToNative(string? strManaged, IntPtr nativeHom managed.Slice(0, numChars).CopyTo(native); native[numChars] = '\0'; } + + internal static unsafe string ConvertToManaged(IntPtr nativeHome, int length) + { + int end = SpanHelpers.IndexOf(ref *(char*)nativeHome, '\0', length); + if (end != -1) + { + length = end; + } + + return new string((char*)nativeHome, 0, length); + } } // class WSTRBufferMarshaler #if FEATURE_COMINTEROP diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 73ce65fd2d7642..8f6afcc3f73b6c 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -1069,6 +1069,7 @@ DEFINE_METHOD(CSTRMARSHALER, CLEAR_NATIVE, ClearNative, DEFINE_CLASS(FIXEDWSTRMARSHALER, StubHelpers, FixedWSTRMarshaler) DEFINE_METHOD(FIXEDWSTRMARSHALER, CONVERT_TO_NATIVE, ConvertToNative, SM_Str_IntPtr_Int_RetVoid) +DEFINE_METHOD(FIXEDWSTRMARSHALER, CONVERT_TO_MANAGED, ConvertToManaged, SM_IntPtr_Int_RetStr) DEFINE_CLASS(BSTRMARSHALER, StubHelpers, BSTRMarshaler) DEFINE_METHOD(BSTRMARSHALER, CONVERT_TO_NATIVE, ConvertToNative, SM_Str_IntPtr_RetIntPtr) diff --git a/src/coreclr/vm/ilmarshalers.cpp b/src/coreclr/vm/ilmarshalers.cpp index 5c7517b28847c1..f6f837a52257b5 100644 --- a/src/coreclr/vm/ilmarshalers.cpp +++ b/src/coreclr/vm/ilmarshalers.cpp @@ -1906,11 +1906,8 @@ void ILFixedWSTRMarshaler::EmitConvertContentsNativeToCLR(ILCodeStream* pslILEmi STANDARD_VM_CONTRACT; EmitLoadNativeHomeAddr(pslILEmit); - pslILEmit->EmitDUP(); - pslILEmit->EmitCALL(METHOD__STRING__WCSLEN, 1, 1); - pslILEmit->EmitCALL(METHOD__STUBHELPERS__CHECK_STRING_LENGTH, 1, 0); - - pslILEmit->EmitNEWOBJ(METHOD__STRING__CTOR_CHARPTR, 1); + pslILEmit->EmitLDC(m_pargs->fs.fixedStringLength); + pslILEmit->EmitCALL(METHOD__FIXEDWSTRMARSHALER__CONVERT_TO_MANAGED, 2, 1); EmitStoreManagedValue(pslILEmit); } diff --git a/src/tests/Interop/StringMarshalling/LPTSTR/LPTSTRTest.cs b/src/tests/Interop/StringMarshalling/LPTSTR/LPTSTRTest.cs index 8c789d180c693b..54054435fed23c 100644 --- a/src/tests/Interop/StringMarshalling/LPTSTR/LPTSTRTest.cs +++ b/src/tests/Interop/StringMarshalling/LPTSTR/LPTSTRTest.cs @@ -12,6 +12,8 @@ class LPTStrTest { private static readonly string InitialString = "Hello World"; + private static readonly string LongString = "0123456789abcdefghi"; + private static readonly string LongUnicodeString = "๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ง๐Ÿฑโ€๐Ÿ‘ค"; public static int Main() { @@ -58,7 +60,21 @@ private static void RunByValTStrTests() }; ReverseByValStringUni(ref uniStr); - Assert.AreEqual(Helpers.Reverse(InitialString), uniStr.str); + + ReverseCopyByValStringAnsi(new ByValStringInStructAnsi { str = LongString }, out ByValStringInStructSplitAnsi ansiStrSplit); + + Assert.AreEqual(Helpers.Reverse(LongString[^10..]), ansiStrSplit.str1); + Assert.AreEqual(Helpers.Reverse(LongString[..^10]), ansiStrSplit.str2); + + ReverseCopyByValStringUni(new ByValStringInStructUnicode { str = LongString }, out ByValStringInStructSplitUnicode uniStrSplit); + + Assert.AreEqual(Helpers.Reverse(LongString[^10..]), uniStrSplit.str1); + Assert.AreEqual(Helpers.Reverse(LongString[..^10]), uniStrSplit.str2); + + ReverseCopyByValStringUni(new ByValStringInStructUnicode { str = LongUnicodeString }, out ByValStringInStructSplitUnicode uniStrSplit2); + + Assert.AreEqual(Helpers.Reverse(LongUnicodeString[^10..]), uniStrSplit2.str1); + Assert.AreEqual(Helpers.Reverse(LongUnicodeString[..^10]), uniStrSplit2.str2); } } diff --git a/src/tests/Interop/StringMarshalling/LPTSTR/LPTStrTestNative.cpp b/src/tests/Interop/StringMarshalling/LPTSTR/LPTStrTestNative.cpp index 428ce488ddd1a0..c618a5db4fb6fc 100644 --- a/src/tests/Interop/StringMarshalling/LPTSTR/LPTStrTestNative.cpp +++ b/src/tests/Interop/StringMarshalling/LPTSTR/LPTStrTestNative.cpp @@ -47,3 +47,15 @@ extern "C" DLL_EXPORT void STDMETHODCALLTYPE ReverseByValStringUni(ByValStringIn { StringMarshalingTests::ReverseInplace(str->str); } + +extern "C" DLL_EXPORT void STDMETHODCALLTYPE ReverseCopyByValStringAnsi(ByValStringInStructAnsi str, ByValStringInStructAnsi* out) +{ + *out = str; + StringMarshalingTests::ReverseInplace(out->str); +} + +extern "C" DLL_EXPORT void STDMETHODCALLTYPE ReverseCopyByValStringUni(ByValStringInStructUnicode str, ByValStringInStructUnicode* out) +{ + *out = str; + StringMarshalingTests::ReverseInplace(out->str); +} diff --git a/src/tests/Interop/StringMarshalling/LPTSTR/LPTStrTestNative.cs b/src/tests/Interop/StringMarshalling/LPTSTR/LPTStrTestNative.cs index 022c3dc250eea8..443bc7615f31f8 100644 --- a/src/tests/Interop/StringMarshalling/LPTSTR/LPTStrTestNative.cs +++ b/src/tests/Interop/StringMarshalling/LPTSTR/LPTStrTestNative.cs @@ -14,6 +14,15 @@ public struct ByValStringInStructAnsi public string str; } + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct ByValStringInStructSplitAnsi + { + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)] + public string str1; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)] + public string str2; + } + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct ByValStringInStructUnicode { @@ -21,6 +30,15 @@ public struct ByValStringInStructUnicode public string str; } + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct ByValStringInStructSplitUnicode + { + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)] + public string str1; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)] + public string str2; + } + [DllImport(nameof(LPTStrTestNative), CharSet = CharSet.Unicode)] public static extern bool Verify_NullTerminators_PastEnd(StringBuilder builder, int length); @@ -36,4 +54,9 @@ public struct ByValStringInStructUnicode public static extern void ReverseByValStringAnsi(ref ByValStringInStructAnsi str); [DllImport(nameof(LPTStrTestNative))] public static extern void ReverseByValStringUni(ref ByValStringInStructUnicode str); + + [DllImport(nameof(LPTStrTestNative))] + public static extern void ReverseCopyByValStringAnsi(ByValStringInStructAnsi str, out ByValStringInStructSplitAnsi strOut); + [DllImport(nameof(LPTStrTestNative))] + public static extern void ReverseCopyByValStringUni(ByValStringInStructUnicode str, out ByValStringInStructSplitUnicode strOut); }