Match Mono's behavior by changing the Auto character set to mean UTF-8 on non-Windows platforms (new behavior) and UCS-2/UTF-16 on Windows (current behavior).
Fixes #23464
Fixes dotnet/corefx#32442
Impact of breaking change: It is highly unlikely that anyone is actively using current behavior since it is inconsistent with Mono and doesn't match any native system APIs on non-Windows platforms (they're all UTF-8 based).
We will need to update our documentation to reflect this updated behavior.
{
public static partial class Marshal
{
+ public static string PtrToStringAuto(IntPtr ptr, int len)
+ {
+ return PtrToStringUTF8(ptr, len);
+ }
+
+ public static string PtrToStringAuto(IntPtr ptr)
+ {
+ return PtrToStringUTF8(ptr);
+ }
+
+ public static IntPtr StringToHGlobalAuto(string s)
+ {
+ return StringToHGlobalUTF8(s);
+ }
+
+ public static IntPtr StringToCoTaskMemAuto(string s)
+ {
+ return StringToCoTaskMemUTF8(s);
+ }
+
private static int GetSystemMaxDBCSCharSize() => 3;
private static bool IsWin32Atom(IntPtr ptr) => false;
return convertedBytes;
}
}
-}
\ No newline at end of file
+}
{
public static partial class Marshal
{
+ public static string PtrToStringAuto(IntPtr ptr, int len)
+ {
+ return PtrToStringUni(ptr, len);
+ }
+
+ public static string PtrToStringAuto(IntPtr ptr)
+ {
+ return PtrToStringUni(ptr);
+ }
+
+ public static IntPtr StringToHGlobalAuto(string s)
+ {
+ return StringToHGlobalUni(s);
+ }
+
+ public static IntPtr StringToCoTaskMemAuto(string s)
+ {
+ return StringToCoTaskMemUni(s);
+ }
+
private static unsafe int GetSystemMaxDBCSCharSize()
{
Interop.Kernel32.CPINFO cpInfo = default;
return nb;
}
}
-}
\ No newline at end of file
+}
return new string((char*)ptr, 0, len);
}
- public static string PtrToStringAuto(IntPtr ptr, int len)
- {
- // Ansi platforms are no longer supported
- return PtrToStringUni(ptr, len);
- }
-
- public static string PtrToStringAuto(IntPtr ptr)
- {
- // Ansi platforms are no longer supported
- return PtrToStringUni(ptr);
- }
-
public static unsafe string PtrToStringUTF8(IntPtr ptr)
{
if (ptr == IntPtr.Zero)
return hglobal;
}
- public static IntPtr StringToHGlobalAuto(string s)
+ private static unsafe IntPtr StringToHGlobalUTF8(string s)
{
- // Ansi platforms are no longer supported
- return StringToHGlobalUni(s);
+ if (s == null)
+ {
+ return IntPtr.Zero;
+ }
+
+ int nb = Encoding.UTF8.GetMaxByteCount(s.Length);
+
+ IntPtr pMem = AllocHGlobal(nb + 1);
+
+ int nbWritten;
+ byte* pbMem = (byte*)pMem;
+
+ fixed (char* firstChar = s)
+ {
+ nbWritten = Encoding.UTF8.GetBytes(firstChar, s.Length, pbMem, nb);
+ }
+
+ pbMem[nbWritten] = 0;
+
+ return pMem;
}
public static unsafe IntPtr StringToCoTaskMemUni(string s)
return pMem;
}
- public static IntPtr StringToCoTaskMemAuto(string s)
- {
- // Ansi platforms are no longer supported
- return StringToCoTaskMemUni(s);
- }
-
public static unsafe IntPtr StringToCoTaskMemAnsi(string s)
{
if (s == null)
case nltAnsi:
nlt = nltAnsi; break;
case nltUnicode:
- case nltAuto: // Since Win9x isn't supported anymore, nltAuto always represents unicode strings.
nlt = nltUnicode; break;
+ case nltAuto:
+#ifdef PLATFORM_WINDOWS
+ nlt = nltUnicode;
+#else
+ nlt = nltAnsi; // We don't have a utf8 charset in metadata yet, but ANSI == UTF-8 off-Windows
+#endif
+ break;
default:
hr = E_FAIL; goto ErrExit;
}
if (mappingFlags & pmNoMangle)
SetLinkFlags ((CorNativeLinkFlags)(GetLinkFlags() | nlfNoMangle));
- // XXX Tue 07/19/2005
// Keep in sync with the handling of CorNativeLinkType in
// PInvokeStaticSigInfo::PInvokeStaticSigInfo.
{
SetCharSet (nltAnsi);
}
- else if (charSetMask == pmCharSetUnicode || charSetMask == pmCharSetAuto)
+ else if (charSetMask == pmCharSetUnicode)
{
- // Since Win9x isn't supported anymore, pmCharSetAuto always represents unicode strings.
SetCharSet (nltUnicode);
}
+ else if (charSetMask == pmCharSetAuto)
+ {
+#ifdef PLATFORM_WINDOWS
+ SetCharSet(nltUnicode);
+#else
+ SetCharSet(nltAnsi); // We don't have a utf8 charset in metadata yet, but ANSI == UTF-8 off-Windows
+#endif
+ }
else
{
SetError(IDS_EE_NDIRECT_BADNATL);
{
*pNLTType = nltAnsi;
}
- else if (IsTdUnicodeClass(clFlags) || IsTdAutoClass(clFlags))
+ else if (IsTdUnicodeClass(clFlags))
{
*pNLTType = nltUnicode;
}
+ else if (IsTdAutoClass(clFlags))
+ {
+#ifdef PLATFORM_WINDOWS
+ *pNLTType = nltUnicode;
+#else
+ *pNLTType = nltAnsi; // We don't have a utf8 charset in metadata yet, but ANSI == UTF-8 off-Windows
+#endif
+ }
else
{
pAssembly->ThrowTypeLoadException(pInternalImport, cl, IDS_CLASSLOAD_BADFORMAT);
{
"name": "System.Runtime.InteropServices.Tests.ArrayWithOffsetTests.Ctor_Array_Offset",
"reason": "outdated"
+ },
+ {
+ "name": "System.Runtime.InteropServices.Tests.StringToCoTaskMemAutoTests.StringToCoTaskMemAuto_NonNullString_Roundtrips",
+ "reason": "outdated"
+ },
+ {
+ "name": "System.Runtime.InteropServices.Tests.StringToHGlobalAutoTests.StringToHGlobalAuto_NonNullString_Roundtrips",
+ "reason": "https://github.com/dotnet/coreclr/pull/23664"
}
]
}
public static extern int MarshalPointer_Int_InOut2([In, Out] ref int intValue);
}
+ class Auto
+ {
+ [DllImport("ExactSpellingNative", CharSet = CharSet.Auto, ExactSpelling = false)]
+ public static extern int Marshal_Int_InOut2([In, Out] int intValue);
+ }
+
public static int Main(string[] args)
{
int failures = 0;
int int8 = intManaged;
int intRet8 = Ansi.MarshalPointer_Int_InOut2(ref int8);
failures += Verify(intReturnAnsi, intNative, intRet8, int8);
+
+ Console.WriteLine("Method Auto.Marshal_Int_InOut: ExactSpelling = false. Verify CharSet.Auto behavior per-platform.");
+ int int9 = intManaged;
+ int intRet9 = Auto.Marshal_Int_InOut2(int9);
+#if PLATFORM_WINDOWS
+ failures += Verify(intReturnUnicode, intManaged, intRet9, int9);
+#else
+ failures += Verify(intReturnAnsi, intManaged, intRet9, int9);
+#endif
return 100 + failures;
}
S9Id,
IncludeOuterIntegerStructSequentialId,
S11Id,
+ AutoStringId,
IntWithInnerSequentialId,
SequentialWrapperId,
SequentialDoubleWrapperId,
static extern bool MarshalStructAsParam_AsSeqByValSequentialAggregateSequentialWrapper(AggregateSequentialWrapper wrapper);
[DllImport("MarshalStructAsParam")]
+ static extern int GetStringLength(AutoString str);
+
+ [DllImport("MarshalStructAsParam")]
static extern HFA GetHFA(float f1, float f2, float f3, float f4);
[DllImport("MarshalStructAsParam")]
failures++;
}
break;
-
+ case StructID.AutoStringId:
+ Console.WriteLine("\tCalling GetStringLength...");
+ {
+ AutoString autoStr = new AutoString
+ {
+ str = "Auto CharSet test string"
+ };
+ int actual = GetStringLength(autoStr);
+ if (actual != autoStr.str.Length)
+ {
+ Console.WriteLine($"\tFAILED! Managed to Native failed in GetStringLength. Expected {autoStr.str.Length}, got {actual}");
+ }
+ }
+ break;
case StructID.IntWithInnerSequentialId:
IntWithInnerSequential intWithInnerSeq = new IntWithInnerSequential
{
MarshalStructAsParam_AsSeqByVal(StructID.S9Id);
MarshalStructAsParam_AsSeqByVal(StructID.IncludeOuterIntegerStructSequentialId);
MarshalStructAsParam_AsSeqByVal(StructID.S11Id);
+ MarshalStructAsParam_AsSeqByVal(StructID.AutoStringId);
MarshalStructAsParam_AsSeqByVal(StructID.IntWithInnerSequentialId);
MarshalStructAsParam_AsSeqByVal(StructID.SequentialWrapperId);
MarshalStructAsParam_AsSeqByVal(StructID.SequentialDoubleWrapperId);
return TRUE;
}
+extern "C" DLL_EXPORT int GetStringLength(AutoString str)
+{
+#ifdef _WIN32
+ return (int)wcslen(str.str);
+#else
+ return (int)strlen(str.str);
+#endif
+}
+
+
extern "C" DLL_EXPORT HFA STDMETHODCALLTYPE GetHFA(float f1, float f2, float f3, float f4)
{
return {f1, f2, f3, f4};
return true;
}
+struct AutoString
+{
+#ifdef _WIN32
+ LPCWSTR str;
+#else
+ LPCSTR str;
+#endif
+};
+
struct HFA
{
float f1;
public long l2;
}
+[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
+public struct AutoString
+{
+ public string str;
+}
[StructLayout(LayoutKind.Sequential)]
public struct HFA