diff --git a/DllImportGenerator/Demo/Demo.csproj b/DllImportGenerator/Demo/Demo.csproj index fdba3fbbb354..4cee92efcd88 100644 --- a/DllImportGenerator/Demo/Demo.csproj +++ b/DllImportGenerator/Demo/Demo.csproj @@ -16,8 +16,4 @@ - - - - diff --git a/DllImportGenerator/Demo/Program.cs b/DllImportGenerator/Demo/Program.cs index 06715a76c7c0..f0490390f6ac 100644 --- a/DllImportGenerator/Demo/Program.cs +++ b/DllImportGenerator/Demo/Program.cs @@ -15,7 +15,7 @@ partial class NativeExportsNE public static partial void Sum(int a, ref int b); } - unsafe class Program + class Program { static void Main(string[] args) { @@ -31,13 +31,6 @@ static void Main(string[] args) c = b; NativeExportsNE.Sum(a, ref c); Console.WriteLine($"{a} + {b} = {c}"); - - SafeHandleTests tests = new SafeHandleTests(); - - tests.ReturnValue_CreatesSafeHandle(); - tests.ByValue_CorrectlyUnwrapsHandle(); - tests.ByRefSameValue_UsesSameHandleInstance(); - tests.ByRefDifferentValue_UsesNewHandleInstance(); } } } diff --git a/DllImportGenerator/DllImportGenerator.IntegrationTests/DllImportGenerator.IntegrationTests.csproj b/DllImportGenerator/DllImportGenerator.IntegrationTests/DllImportGenerator.IntegrationTests.csproj new file mode 100644 index 000000000000..2932ce632c21 --- /dev/null +++ b/DllImportGenerator/DllImportGenerator.IntegrationTests/DllImportGenerator.IntegrationTests.csproj @@ -0,0 +1,29 @@ + + + + net5.0 + false + Preview + true + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + diff --git a/DllImportGenerator/Demo/SafeHandleTests.cs b/DllImportGenerator/DllImportGenerator.IntegrationTests/SafeHandleTests.cs similarity index 84% rename from DllImportGenerator/Demo/SafeHandleTests.cs rename to DllImportGenerator/DllImportGenerator.IntegrationTests/SafeHandleTests.cs index bb21cfb565ca..fc7171005746 100644 --- a/DllImportGenerator/Demo/SafeHandleTests.cs +++ b/DllImportGenerator/DllImportGenerator.IntegrationTests/SafeHandleTests.cs @@ -1,22 +1,22 @@ - using System.Runtime.InteropServices; + using Microsoft.Win32.SafeHandles; using Xunit; -namespace Demo +namespace DllImportGenerator.IntegrationTests { partial class NativeExportsNE { public class NativeExportsSafeHandle : SafeHandleZeroOrMinusOneIsInvalid { - private NativeExportsSafeHandle() : base(true) - { - } + private NativeExportsSafeHandle() : base(ownsHandle: true) + { } protected override bool ReleaseHandle() { - Assert.True(NativeExportsNE.ReleaseHandle(handle)); - return true; + bool didRelease = NativeExportsNE.ReleaseHandle(handle); + Assert.True(didRelease); + return didRelease; } } @@ -57,7 +57,7 @@ public void ByRefSameValue_UsesSameHandleInstance() { using NativeExportsNE.NativeExportsSafeHandle handleToDispose = NativeExportsNE.AllocateHandle(); NativeExportsNE.NativeExportsSafeHandle handle = handleToDispose; - NativeExportsNE.ModifyHandle(ref handle, false); + NativeExportsNE.ModifyHandle(ref handle, newHandle: false); Assert.Same(handleToDispose, handle); } @@ -66,7 +66,7 @@ public void ByRefDifferentValue_UsesNewHandleInstance() { using NativeExportsNE.NativeExportsSafeHandle handleToDispose = NativeExportsNE.AllocateHandle(); NativeExportsNE.NativeExportsSafeHandle handle = handleToDispose; - NativeExportsNE.ModifyHandle(ref handle, true); + NativeExportsNE.ModifyHandle(ref handle, newHandle: true); Assert.NotSame(handleToDispose, handle); handle.Dispose(); } diff --git a/DllImportGenerator/DllImportGenerator.Test/CodeSnippets.cs b/DllImportGenerator/DllImportGenerator.UnitTests/CodeSnippets.cs similarity index 99% rename from DllImportGenerator/DllImportGenerator.Test/CodeSnippets.cs rename to DllImportGenerator/DllImportGenerator.UnitTests/CodeSnippets.cs index bb3594897326..06b8156a943c 100644 --- a/DllImportGenerator/DllImportGenerator.Test/CodeSnippets.cs +++ b/DllImportGenerator/DllImportGenerator.UnitTests/CodeSnippets.cs @@ -1,6 +1,6 @@ using System.Runtime.InteropServices; -namespace DllImportGenerator.Test +namespace DllImportGenerator.UnitTests { internal static class CodeSnippets { diff --git a/DllImportGenerator/DllImportGenerator.Test/CompileFails.cs b/DllImportGenerator/DllImportGenerator.UnitTests/CompileFails.cs similarity index 96% rename from DllImportGenerator/DllImportGenerator.Test/CompileFails.cs rename to DllImportGenerator/DllImportGenerator.UnitTests/CompileFails.cs index 3acbdace6ced..bf31ff47d743 100644 --- a/DllImportGenerator/DllImportGenerator.Test/CompileFails.cs +++ b/DllImportGenerator/DllImportGenerator.UnitTests/CompileFails.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; using Xunit; -namespace DllImportGenerator.Test +namespace DllImportGenerator.UnitTests { public class CompileFails { diff --git a/DllImportGenerator/DllImportGenerator.Test/Compiles.cs b/DllImportGenerator/DllImportGenerator.UnitTests/Compiles.cs similarity index 99% rename from DllImportGenerator/DllImportGenerator.Test/Compiles.cs rename to DllImportGenerator/DllImportGenerator.UnitTests/Compiles.cs index 3c66f36bbe6d..2275072099fa 100644 --- a/DllImportGenerator/DllImportGenerator.Test/Compiles.cs +++ b/DllImportGenerator/DllImportGenerator.UnitTests/Compiles.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; using Xunit; -namespace DllImportGenerator.Test +namespace DllImportGenerator.UnitTests { public class Compiles { diff --git a/DllImportGenerator/DllImportGenerator.Test/DllImportGenerator.Test.csproj b/DllImportGenerator/DllImportGenerator.UnitTests/DllImportGenerator.UnitTests.csproj similarity index 100% rename from DllImportGenerator/DllImportGenerator.Test/DllImportGenerator.Test.csproj rename to DllImportGenerator/DllImportGenerator.UnitTests/DllImportGenerator.UnitTests.csproj diff --git a/DllImportGenerator/DllImportGenerator.Test/ManualTypeMarshallingAnalyzerTests.cs b/DllImportGenerator/DllImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs similarity index 99% rename from DllImportGenerator/DllImportGenerator.Test/ManualTypeMarshallingAnalyzerTests.cs rename to DllImportGenerator/DllImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs index e049ce9d4e55..ae3aed147827 100644 --- a/DllImportGenerator/DllImportGenerator.Test/ManualTypeMarshallingAnalyzerTests.cs +++ b/DllImportGenerator/DllImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs @@ -3,9 +3,9 @@ using Xunit; using static Microsoft.Interop.ManualTypeMarshallingAnalyzer; -using VerifyCS = DllImportGenerator.Test.Verifiers.CSharpAnalyzerVerifier; +using VerifyCS = DllImportGenerator.UnitTests.Verifiers.CSharpAnalyzerVerifier; -namespace DllImportGenerator.Test +namespace DllImportGenerator.UnitTests { public class ManualTypeMarshallingAnalyzerTests { diff --git a/DllImportGenerator/DllImportGenerator.Test/TestUtils.cs b/DllImportGenerator/DllImportGenerator.UnitTests/TestUtils.cs similarity index 98% rename from DllImportGenerator/DllImportGenerator.Test/TestUtils.cs rename to DllImportGenerator/DllImportGenerator.UnitTests/TestUtils.cs index 49ca8e625f60..e5d79820f991 100644 --- a/DllImportGenerator/DllImportGenerator.Test/TestUtils.cs +++ b/DllImportGenerator/DllImportGenerator.UnitTests/TestUtils.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; using Xunit; -namespace DllImportGenerator.Test +namespace DllImportGenerator.UnitTests { internal static class TestUtils { diff --git a/DllImportGenerator/DllImportGenerator.Test/Verifiers/CSharpAnalyzerVerifier.cs b/DllImportGenerator/DllImportGenerator.UnitTests/Verifiers/CSharpAnalyzerVerifier.cs similarity index 98% rename from DllImportGenerator/DllImportGenerator.Test/Verifiers/CSharpAnalyzerVerifier.cs rename to DllImportGenerator/DllImportGenerator.UnitTests/Verifiers/CSharpAnalyzerVerifier.cs index 9ccff97f18b1..b6bd0f531648 100644 --- a/DllImportGenerator/DllImportGenerator.Test/Verifiers/CSharpAnalyzerVerifier.cs +++ b/DllImportGenerator/DllImportGenerator.UnitTests/Verifiers/CSharpAnalyzerVerifier.cs @@ -11,7 +11,7 @@ using Microsoft.CodeAnalysis.Testing; using Microsoft.CodeAnalysis.Testing.Verifiers; -namespace DllImportGenerator.Test.Verifiers +namespace DllImportGenerator.UnitTests.Verifiers { public static class CSharpAnalyzerVerifier where TAnalyzer : DiagnosticAnalyzer, new() diff --git a/DllImportGenerator/DllImportGenerator.Test/Verifiers/CSharpVerifierHelper.cs b/DllImportGenerator/DllImportGenerator.UnitTests/Verifiers/CSharpVerifierHelper.cs similarity index 97% rename from DllImportGenerator/DllImportGenerator.Test/Verifiers/CSharpVerifierHelper.cs rename to DllImportGenerator/DllImportGenerator.UnitTests/Verifiers/CSharpVerifierHelper.cs index 61c063975270..c51712a3ce0b 100644 --- a/DllImportGenerator/DllImportGenerator.Test/Verifiers/CSharpVerifierHelper.cs +++ b/DllImportGenerator/DllImportGenerator.UnitTests/Verifiers/CSharpVerifierHelper.cs @@ -3,7 +3,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -namespace DllImportGenerator.Test.Verifiers +namespace DllImportGenerator.UnitTests.Verifiers { internal static class CSharpVerifierHelper { diff --git a/DllImportGenerator/DllImportGenerator.sln b/DllImportGenerator/DllImportGenerator.sln index b2d908631a36..502dd987bd65 100644 --- a/DllImportGenerator/DllImportGenerator.sln +++ b/DllImportGenerator/DllImportGenerator.sln @@ -5,7 +5,7 @@ VisualStudioVersion = 16.0.30204.135 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DllImportGenerator", "DllImportGenerator\DllImportGenerator.csproj", "{0E14E01F-A582-4D22-8737-BF9DE5270435}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DllImportGenerator.Test", "DllImportGenerator.Test\DllImportGenerator.Test.csproj", "{B8CA13C4-F41B-4ABD-A9F3-63A02C53B96E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DllImportGenerator.UnitTests", "DllImportGenerator.UnitTests\DllImportGenerator.UnitTests.csproj", "{B8CA13C4-F41B-4ABD-A9F3-63A02C53B96E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Demo", "Demo\Demo.csproj", "{E478EE77-E072-4A42-B453-EBFDA7728717}" EndProject @@ -13,7 +13,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ancillary.Interop", "Ancill EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestAssets", "TestAssets", "{2CFB9A7A-4AAF-4B6A-8CC8-540F64C3B45F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NativeExports", "TestAssets\NativeExports\NativeExports.csproj", "{32FDA079-0E9F-4A36-ADA5-6593B67A54AC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeExports", "TestAssets\NativeExports\NativeExports.csproj", "{32FDA079-0E9F-4A36-ADA5-6593B67A54AC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DllImportGenerator.IntegrationTests", "DllImportGenerator.IntegrationTests\DllImportGenerator.IntegrationTests.csproj", "{162C204A-ED59-4EF3-A5FA-E58CC06FAB4D}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{69D56AC9-232B-4E76-B6C1-33A7B06B6855}" EndProject @@ -49,6 +51,10 @@ Global {6FD4AF19-0CAA-413C-A2BD-C888AA2E8CFB}.Debug|Any CPU.Build.0 = Debug|Any CPU {6FD4AF19-0CAA-413C-A2BD-C888AA2E8CFB}.Release|Any CPU.ActiveCfg = Release|Any CPU {6FD4AF19-0CAA-413C-A2BD-C888AA2E8CFB}.Release|Any CPU.Build.0 = Release|Any CPU + {162C204A-ED59-4EF3-A5FA-E58CC06FAB4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {162C204A-ED59-4EF3-A5FA-E58CC06FAB4D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {162C204A-ED59-4EF3-A5FA-E58CC06FAB4D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {162C204A-ED59-4EF3-A5FA-E58CC06FAB4D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/DllImportGenerator/TestAssets/NativeExports/ScalarOps.cs b/DllImportGenerator/TestAssets/NativeExports/Demo.cs similarity index 84% rename from DllImportGenerator/TestAssets/NativeExports/ScalarOps.cs rename to DllImportGenerator/TestAssets/NativeExports/Demo.cs index 4d2bb92006f6..6e82b412b6b7 100644 --- a/DllImportGenerator/TestAssets/NativeExports/ScalarOps.cs +++ b/DllImportGenerator/TestAssets/NativeExports/Demo.cs @@ -1,9 +1,8 @@ -using System; -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; namespace NativeExports { - public unsafe class ScalarOps + public static unsafe class Demo { [UnmanagedCallersOnly(EntryPoint = "sumi")] public static int Sum(int a, int b) diff --git a/DllImportGenerator/TestAssets/NativeExports/Handles.cs b/DllImportGenerator/TestAssets/NativeExports/Handles.cs index fae57f242fe5..0cc2ab4f9fe9 100644 --- a/DllImportGenerator/TestAssets/NativeExports/Handles.cs +++ b/DllImportGenerator/TestAssets/NativeExports/Handles.cs @@ -1,16 +1,18 @@ using System.Collections.Generic; using System.Runtime.InteropServices; - namespace NativeExports { public static unsafe class Handles { + /// + /// Using in tests. + /// private const nint InvalidHandle = -1; private static nint LastHandle = 0; - private static HashSet ActiveHandles = new HashSet(); + private static readonly HashSet ActiveHandles = new HashSet(); [UnmanagedCallersOnly(EntryPoint = "alloc_handle")] public static nint AllocateHandle() @@ -18,18 +20,6 @@ public static nint AllocateHandle() return AllocateHandleCore(); } - private static nint AllocateHandleCore() - { - if (LastHandle == int.MaxValue) - { - return InvalidHandle; - } - - nint newHandle = ++LastHandle; - ActiveHandles.Add(newHandle); - return newHandle; - } - [UnmanagedCallersOnly(EntryPoint = "release_handle")] public static byte ReleaseHandle(nint handle) { @@ -50,5 +40,17 @@ public static void ModifyHandle(nint* handle, byte newHandle) *handle = AllocateHandleCore(); } } + + private static nint AllocateHandleCore() + { + if (LastHandle == int.MaxValue) + { + return InvalidHandle; + } + + nint newHandle = ++LastHandle; + ActiveHandles.Add(newHandle); + return newHandle; + } } } \ No newline at end of file diff --git a/DllImportGenerator/readme.md b/DllImportGenerator/readme.md index 9ba80ca33220..30e05076eb5c 100644 --- a/DllImportGenerator/readme.md +++ b/DllImportGenerator/readme.md @@ -1,24 +1,31 @@ # DllImport Generator -See [P/Invoke source generator proposal](https://github.com/dotnet/runtime/blob/master/docs/design/features/source-generator-pinvokes.md) for design and goals. +Work on this project can be tracked and discussed via the [official issue](https://github.com/dotnet/runtime/issues/43060) in `dotnet/runtime`. The [P/Invoke source generator proposal](https://github.com/dotnet/runtime/blob/master/docs/design/features/source-generator-pinvokes.md) contains additional details. -## Additional work +## Example -Below are additional work items that are not presently captured in this repository. +The [Demo project](./DllImportGenerator/Demo) is designed to be immediately consumable by everyone. It demonstrates a simple use case where the marshalling code is generated and a native function call with only [blittable types](https://docs.microsoft.com/dotnet/framework/interop/blittable-and-non-blittable-types) is made. A managed assembly with [native exports](./DllImportGenerator/TestAssets/NativeExports) is used in the P/Invoke scenario. -### Required +### Recommended scenarios: +* Step into the `Demo` application and observe the generated code for the `Sum` functions. +* Find the implementation of the `sumrefi` function and set a breakpoint. Run the debugger and explore the stack. +* Add a new export in the `NativeExports` project and update the `Demo` application to call the new export. +* Try the above scenarios when building in `Debug` or `Release`. Consider the differences. -* Add `GeneratedDllImportAttribute` to the BCL. See [`GeneratedDllImportAttribute.cs`](./Ancillary.Interop/GeneratedDllImportAttribute.cs). -* Add support for calling [`SetLastError()`](https://docs.microsoft.com/windows/win32/api/errhandlingapi/nf-errhandlingapi-setlasterror) from managed code with the expected semantics. During P/Invoke transitions the runtime manipulates the associated thread's error code and thus breaks `SetLastError()` semantics. +## Designs -* APIs to handle [`SafeHandle`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.safehandle) manipulation. +- [Code generation pipeline](./designs/Pipeline.md) +- [Struct Marshalling](./designs/StructMarshalling.md) -### Optional +## Workflow -* A tool to compare the resulting IL from the generated source to that generated by the built-in IL Marshaller system. This would help with validation of what is being generated. +All features of the [`dotnet` command line tool](https://docs.microsoft.com/dotnet/core/tools/) are supported for the respective project types (e.g. `build`, `run`, `test`). A consistent cross-platform inner dev loop with an IDE is available using [Visual Studio Code](https://code.visualstudio.com/) when appropriate .NET extensions are loaded. -## Designs +On Windows, loading the [solution](./DllImportGenerator.sln) in [Visual Studio](https://visualstudio.microsoft.com/) 2019 or later will enable the edit, build, debug inner dev loop. All features of Visual Studio are expected to operate correctly (e.g. Debugger, Test runner, Profiler). -- [Code generation pipeline](./designs/Pipeline.md) -- [Struct Marshalling](./designs/StructMarshalling.md) +Most of the above options have [official tutorials](https://docs.microsoft.com/dotnet/core/tutorials/). It is an aim of this project to follow canonical workflows that are intuitive to all developers. + +### Testing assets + +This project has no explicit native build system and should remain that way. The [`DNNE`](https://github.com/AaronRobinsonMSFT/DNNE/) project is used to create native exports that can be called from the P/Invokes during testing.