diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/StubHelpers.cs b/src/coreclr/src/System.Private.CoreLib/src/System/StubHelpers.cs index b507b28559dac2..6949bf13642323 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/StubHelpers.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/StubHelpers.cs @@ -537,6 +537,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/src/vm/corelib.h b/src/coreclr/src/vm/corelib.h index ad892439138409..915c9843666c11 100644 --- a/src/coreclr/src/vm/corelib.h +++ b/src/coreclr/src/vm/corelib.h @@ -1050,6 +1050,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/src/vm/ilmarshalers.cpp b/src/coreclr/src/vm/ilmarshalers.cpp index f224d7c477c7a4..53ef9451bbd3b2 100644 --- a/src/coreclr/src/vm/ilmarshalers.cpp +++ b/src/coreclr/src/vm/ilmarshalers.cpp @@ -1909,11 +1909,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..a29b99a01c4229 100644 --- a/src/tests/Interop/StringMarshalling/LPTSTR/LPTSTRTest.cs +++ b/src/tests/Interop/StringMarshalling/LPTSTR/LPTSTRTest.cs @@ -60,5 +60,20 @@ 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..522c80e872ff9e 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);