Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion src/CsWin32Generator/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public partial class Program
private bool verbose;
private string? assemblyName;
private FileInfo? assemblyOriginatorKeyFile;
private LanguageVersion languageVersion = LanguageVersion.CSharp13;

/// <summary>
/// Initializes a new instance of the <see cref="Program"/> class.
Expand Down Expand Up @@ -118,6 +119,11 @@ public async Task<int> Main(
Description = "Path to the strong name key file (.snk) for signing.",
};

var languageVersionOption = new Option<string?>("--language-version")
{
Description = "C# language version (e.g., 10, 11, 12, 13, Latest, Preview).",
};

var verboseOption = new Option<bool>("--verbose");

var rootCommand = new RootCommand("CsWin32 Code Generator - Generates P/Invoke methods and supporting types from Windows metadata.")
Expand All @@ -133,6 +139,7 @@ public async Task<int> Main(
referencesOption,
assemblyNameOption,
keyFileOption,
languageVersionOption,
verboseOption,
};

Expand All @@ -149,6 +156,7 @@ public async Task<int> Main(
this.assemblyName = parseResult.GetValue(assemblyNameOption);
this.assemblyOriginatorKeyFile = parseResult.GetValue(keyFileOption);
this.verbose = parseResult.GetValue(verboseOption);
string? languageVersionString = parseResult.GetValue(languageVersionOption);

// Check for errors before continuing.
if (parseResult.Errors.Count > 0)
Expand All @@ -161,6 +169,15 @@ public async Task<int> Main(
return 1;
}

if (languageVersionString is not null)
{
if (!LanguageVersionFacts.TryParse(languageVersionString, out this.languageVersion))
{
this.ReportError($"Invalid language version: {languageVersionString}");
return 1;
}
}

try
{
var result = await this.GenerateCode(
Expand Down Expand Up @@ -261,7 +278,7 @@ private Task<bool> GenerateCode(

// Create compilation context
CSharpCompilation? compilation = this.CreateCompilation(allowUnsafeBlocks: true, platform, references);
CSharpParseOptions? parseOptions = this.CreateParseOptions(targetFramework);
CSharpParseOptions? parseOptions = this.CreateParseOptions(targetFramework)?.WithLanguageVersion(this.languageVersion);
this.VerboseWriteLine($"Created compilation context with platform: {platform}, language version: {parseOptions?.LanguageVersion}");

// Load docs if available
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ public CsWin32CodeGeneratorTask(IToolExecutor? toolExecutor)
/// </summary>
public string? KeyFile { get; set; }

/// <summary>
/// Gets or sets the C# language version (e.g., 10, 11, 12, 13, Latest, Preview).
/// </summary>
public string? LangVersion { get; set; }

/// <summary>
/// Gets the generated source files.
/// </summary>
Expand Down Expand Up @@ -191,6 +196,7 @@ protected override string GenerateResponseFileCommands()
commandLine.AppendSwitchIfNotNull("--platform ", this.Platform);
commandLine.AppendSwitchIfNotNull("--assembly-name ", this.AssemblyName);
commandLine.AppendSwitchIfNotNull("--key-file ", this.KeyFile);
commandLine.AppendSwitchIfNotNull("--language-version ", this.LangVersion);
commandLine.AppendSwitch("--verbose ");

if (this.References?.Length > 0)
Expand Down
3 changes: 3 additions & 0 deletions src/Microsoft.Windows.CsWin32/Generator.Features.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public partial class Generator
private readonly bool canMarshalNativeDelegateParams;
private readonly bool overloadResolutionPriorityAttributePredefined;
private readonly bool unscopedRefAttributePredefined;
private readonly bool canUseComVariant;
private readonly INamedTypeSymbol? runtimeFeatureClass;
private readonly bool generateSupportedOSPlatformAttributes;
private readonly bool generateSupportedOSPlatformAttributesOnInterfaces; // only supported on net6.0 (https://github.com/dotnet/runtime/pull/48838)
Expand All @@ -30,6 +31,8 @@ public partial class Generator

internal bool CanUseIPropertyValue => this.canUseIPropertyValue;

internal bool CanUseComVariant => this.canUseComVariant;

private void DeclareOverloadResolutionPriorityAttributeIfNecessary()
{
// This attribute may only be applied for C# 13 and later, or else C# errors out.
Expand Down
1 change: 1 addition & 0 deletions src/Microsoft.Windows.CsWin32/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ public Generator(string metadataLibraryPath, Docs? docs, IEnumerable<string> add
this.getDelegateForFunctionPointerGenericExists = this.compilation?.GetTypeByMetadataName(typeof(Marshal).FullName)?.GetMembers(nameof(Marshal.GetDelegateForFunctionPointer)).Any(m => m is IMethodSymbol { IsGenericMethod: true }) is true;
this.generateDefaultDllImportSearchPathsAttribute = this.compilation?.GetTypeByMetadataName(typeof(DefaultDllImportSearchPathsAttribute).FullName) is object;
this.canUseIPropertyValue = this.compilation?.GetTypeByMetadataName("Windows.Foundation.IPropertyValue")?.DeclaredAccessibility == Accessibility.Public;
this.canUseComVariant = this.compilation?.GetTypeByMetadataName("System.Runtime.InteropServices.Marshalling.ComVariant") is not null;
if (this.FindTypeSymbolIfAlreadyAvailable("System.Runtime.Versioning.SupportedOSPlatformAttribute") is { } attribute)
{
this.generateSupportedOSPlatformAttributes = true;
Expand Down
11 changes: 8 additions & 3 deletions src/Microsoft.Windows.CsWin32/HandleTypeHandleInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ internal override TypeSyntaxAndMarshaling ToTypeSyntax(TypeSyntaxSettings inputs
return new TypeSyntaxAndMarshaling(IdentifierName(specialName));
}
}
else if (useComSourceGenerators && simpleName is "VARIANT")
else if (useComSourceGenerators && simpleName is "VARIANT" && this.Generator.CanUseComVariant)
{
return new TypeSyntaxAndMarshaling(QualifiedName(ParseName("global::System.Runtime.InteropServices.Marshalling"), IdentifierName("ComVariant")));
}
Expand Down Expand Up @@ -311,8 +311,13 @@ private static bool TryMarshalAsObject(TypeSyntaxSettings inputs, string name, [
marshalAs = new MarshalAsAttribute(UnmanagedType.IDispatch);
return true;
case "VARIANT":
marshalAs = new MarshalAsAttribute(UnmanagedType.Struct);
return true;
if (inputs.Generator?.UseSourceGenerators != true)
{
marshalAs = new MarshalAsAttribute(UnmanagedType.Struct);
return true;
}

break;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
GeneratorToolPath="$(CsWin32GeneratorToolPath)CsWin32Generator.dll"
TargetFramework="$(TargetFramework)"
Platform="$(Platform)"
LangVersion="$(LangVersion)"
References="@(ReferencePath)"
ContinueOnError="$(CsWin32ContinueOnError)"
Condition="@(CsWin32NativeMethodsTxt->Count()) > 0">
Expand Down
11 changes: 8 additions & 3 deletions test/CsWin32Generator.Tests/CsWin32GeneratorFullTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Microsoft.CodeAnalysis.CSharp;
using Xunit;

namespace CsWin32Generator.Tests;
Expand All @@ -12,11 +13,15 @@ public CsWin32GeneratorFullTests(ITestOutputHelper logger)
{
}

[Fact]
[Theory]
[Trait("TestCategory", "FailsInCloudTest")] // these take ~4GB of memory to run.
public async Task FullGeneration()
[InlineData("net8.0", LanguageVersion.CSharp12)]
[InlineData("net9.0", LanguageVersion.CSharp13)]
public async Task FullGeneration(string tfm, LanguageVersion langVersion)
{
this.fullGeneration = true;
await this.InvokeGeneratorAndCompile(TestOptions.None, "FullGeneration");
this.compilation = this.starterCompilations[tfm];
this.parseOptions = this.parseOptions.WithLanguageVersion(langVersion);
await this.InvokeGeneratorAndCompile($"FullGeneration_{tfm}_{langVersion}", TestOptions.None);
}
}
Loading
Loading