diff --git a/src/Generator/Types/Std/Stdlib.CSharp.cs b/src/Generator/Types/Std/Stdlib.CSharp.cs index a2db882d7..9047b97fe 100644 --- a/src/Generator/Types/Std/Stdlib.CSharp.cs +++ b/src/Generator/Types/Std/Stdlib.CSharp.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; @@ -6,6 +7,7 @@ using CppSharp.AST.Extensions; using CppSharp.Generators; using CppSharp.Generators.CSharp; +using Type = CppSharp.AST.Type; namespace CppSharp.Types.Std { @@ -145,7 +147,7 @@ public override void CSharpMarshalToNative(CSharpMarshalContext ctx) // would be really helpful to have ctx hold a Decl property representing the // "appropriate" Decl when we get here. When MarshalKind == NativeField, Decl would // be set to the Field we're operating on. - var fieldName = ctx.ReturnVarName.Substring(ctx.ReturnVarName.LastIndexOf("->") + 2); + var fieldName = ctx.ReturnVarName[Math.Max(ctx.ReturnVarName.LastIndexOf('.') + 1, ctx.ReturnVarName.LastIndexOf("->") + 2)..]; ctx.Before.WriteLine($"if (__{fieldName}_OwnsNativeMemory)"); ctx.Before.WriteLineIndent($"Marshal.FreeHGlobal({ctx.ReturnVarName});"); @@ -326,10 +328,24 @@ public override void CSharpMarshalToNative(CSharpMarshalContext ctx) var assign = basicString.Methods.First(m => m.OriginalName == "assign"); if (ctx.MarshalKind == MarshalKind.NativeField) { + string var; + if (ctx.ReturnVarName.LastIndexOf('.') > ctx.ReturnVarName.LastIndexOf("->")) + { + ctx.Before.WriteLine("throw new NotImplementedException(\"This method cannot currently be called because it would " + + "leave the object in an invalid state. See https://github.com/mono/CppSharp/issues/1777\");"); + + var = Generator.GeneratedIdentifier(ctx.ArgName); + ctx.Before.WriteLine($"fixed (void* {var} = &{ctx.ReturnVarName})"); + ctx.Before.WriteOpenBraceAndIndent(); + ctx.HasCodeBlock = true; + } + else + { + var = $"&{ctx.ReturnVarName}"; + } ctx.Return.Write($@"{qualifiedBasicString}Extensions.{ Helpers.InternalStruct}.{assign.Name}(new { - typePrinter.IntPtrType}(&{ - ctx.ReturnVarName}), "); + typePrinter.IntPtrType}({var}), "); if (ctx.Parameter.Type.IsTemplateParameterType()) ctx.Return.Write("(string) (object) "); ctx.Return.Write($"{ctx.Parameter.Name})"); diff --git a/tests/dotnet/CSharp/CSharp.CSharp.csproj b/tests/dotnet/CSharp/CSharp.CSharp.csproj index 6a6c4bf76..23c154b11 100644 --- a/tests/dotnet/CSharp/CSharp.CSharp.csproj +++ b/tests/dotnet/CSharp/CSharp.CSharp.csproj @@ -1,6 +1,7 @@  0108 + 10 diff --git a/tests/dotnet/CSharp/CSharp.Tests.cs b/tests/dotnet/CSharp/CSharp.Tests.cs index 98e080b09..6f312a7ce 100644 --- a/tests/dotnet/CSharp/CSharp.Tests.cs +++ b/tests/dotnet/CSharp/CSharp.Tests.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Reflection; using System.Runtime.InteropServices; -using System.Text; using CSharp; using NUnit.Framework; @@ -2024,4 +2023,17 @@ public void TestOptionalIntPtr() Assert.That(new CSharp.Optional(IntPtr.MaxValue) == new CSharp.Optional(IntPtr.MaxValue)); Assert.That(new CSharp.Optional(IntPtr.MaxValue) == IntPtr.MaxValue); } + + [Test] + [Ignore("https://github.com/mono/CppSharp/issues/1730")] + public void TestString() + { + var test = new CSharp.ValueType(); + Assert.AreEqual(string.Empty, test.StringMember); + Assert.AreEqual(null, test.CharPtrMember); + test.StringMember = "test"; + test.CharPtrMember = "test2"; + Assert.AreEqual("test", test.StringMember); + Assert.AreEqual("test2", test.CharPtrMember); + } } diff --git a/tests/dotnet/CSharp/CSharp.h b/tests/dotnet/CSharp/CSharp.h index 78a8ea7d4..79bb99c0d 100644 --- a/tests/dotnet/CSharp/CSharp.h +++ b/tests/dotnet/CSharp/CSharp.h @@ -1641,3 +1641,12 @@ class Optional { // We just need a method that uses various instantiations of Optional. inline void DLL_API InstantiateOptionalTemplate(Optional, Optional, Optional, Optional, Optional) { } + +CS_VALUE_TYPE class DLL_API ValueType { +public: + // Parameterless ctors are currently not generated for value types. + ValueType(int) { } + + std::string string_member; + const char* char_ptr_member; +};