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
1 change: 1 addition & 0 deletions src/Microsoft.Windows.CsWin32/Generator.Invariants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public partial class Generator
private const string SystemRuntimeCompilerServices = "System.Runtime.CompilerServices";
private const string SystemRuntimeInteropServices = "System.Runtime.InteropServices";
private const string NativeTypedefAttribute = "NativeTypedefAttribute";
private const string AlsoUsableForAttribute = "AlsoUsableForAttribute";
private const string InvalidHandleValueAttribute = "InvalidHandleValueAttribute";
private const string CanReturnMultipleSuccessValuesAttribute = "CanReturnMultipleSuccessValuesAttribute";
private const string CanReturnErrorsAsSuccessAttribute = "CanReturnErrorsAsSuccessAttribute";
Expand Down
36 changes: 36 additions & 0 deletions src/Microsoft.Windows.CsWin32/Generator.TypeDef.cs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,25 @@ private StructDeclarationSyntax DeclareTypeDefStruct(TypeDefinition typeDef, Typ
}
}

foreach (string alsoUsableForValue in this.GetAlsoUsableForValues(typeDef))
{
// Add implicit conversion operators for each AlsoUsableFor attribute on the struct.
var fullyQualifiedAlsoUsableForValue = $"{this.Reader.GetString(typeDef.Namespace)}.{alsoUsableForValue}";
if (this.TryGenerateType(fullyQualifiedAlsoUsableForValue))
{
IdentifierNameSyntax alsoUsableForTypeSymbolName = IdentifierName(alsoUsableForValue);
ExpressionSyntax valueValueArg = MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, valueParameter, fieldIdentifierName);

// public static implicit operator HINSTANCE(HMODULE value) => new HINSTANCE(value.Value);
members = members.Add(ConversionOperatorDeclaration(Token(SyntaxKind.ImplicitKeyword), alsoUsableForTypeSymbolName)
.AddParameterListParameters(Parameter(valueParameter.Identifier).WithType(name.WithTrailingTrivia(TriviaList(Space))))
.WithExpressionBody(ArrowExpressionClause(
ObjectCreationExpression(alsoUsableForTypeSymbolName).AddArgumentListArguments(Argument(valueValueArg))))
.AddModifiers(TokenWithSpace(SyntaxKind.PublicKeyword), TokenWithSpace(SyntaxKind.StaticKeyword)) // operators MUST be public
.WithSemicolonToken(SemicolonWithLineFeed));
}
}

switch (name.Identifier.ValueText)
{
case "PWSTR":
Expand Down Expand Up @@ -319,6 +338,23 @@ private IEnumerable<MemberDeclarationSyntax> CreateCommonTypeDefMembers(Identifi
.WithSemicolonToken(SemicolonWithLineFeed);
}

private List<string> GetAlsoUsableForValues(TypeDefinition typeDef)
{
List<string> alsoUsableForValues = new();
foreach (CustomAttributeHandle ah in typeDef.GetCustomAttributes())
{
CustomAttribute a = this.Reader.GetCustomAttribute(ah);
if (MetadataUtilities.IsAttribute(this.Reader, a, InteropDecorationNamespace, AlsoUsableForAttribute))
{
CustomAttributeValue<TypeSyntax> attributeData = a.DecodeValue(CustomAttributeTypeProvider.Instance);
string alsoUsableForValue = (string)(attributeData.FixedArguments[0].Value ?? throw new GenerationFailedException("Missing AlsoUsableFor attribute."));
alsoUsableForValues.Add(alsoUsableForValue);
}
}

return alsoUsableForValues;
}

private bool IsSafeHandleCompatibleTypeDef(TypeHandleInfo? typeDef)
{
return this.TryGetTypeDefFieldType(typeDef, out TypeHandleInfo? fieldType) && this.IsSafeHandleCompatibleTypeDefFieldType(fieldType);
Expand Down
5 changes: 3 additions & 2 deletions src/Microsoft.Windows.CsWin32/Generator.WhitespaceRewriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -331,9 +331,10 @@ ClassDeclarationSyntax or
StructDeclarationSyntax or
NamespaceDeclarationSyntax or
EnumDeclarationSyntax or
MethodDeclarationSyntax or
BaseMethodDeclarationSyntax or
IndexerDeclarationSyntax or
PropertyDeclarationSyntax)
PropertyDeclarationSyntax or
FieldDeclarationSyntax)
{
mutableMembers[i] = mutableMembers[i].WithLeadingTrivia(mutableMembers[i].GetLeadingTrivia().Insert(0, LineFeed));
}
Expand Down
9 changes: 9 additions & 0 deletions test/GenerationSandbox.Tests/BasicTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Windows.Win32.Devices.Display;
using Windows.Win32.Foundation;
using Windows.Win32.Media.DirectShow;
using Windows.Win32.Security.Cryptography;
using Windows.Win32.Storage.FileSystem;
using Windows.Win32.System.Com;
using Windows.Win32.System.Console;
Expand Down Expand Up @@ -35,6 +36,14 @@ public BasicTests(ITestOutputHelper logger)
new object[] { decimal.MaxValue },
};

[Fact]
public void AlsoUsableForImplicitConversion()
{
BCRYPT_KEY_HANDLE bcryptKeyHandle = new(new IntPtr(5));
BCRYPT_HANDLE bcryptHandle = bcryptKeyHandle;
Assert.Equal(bcryptKeyHandle, bcryptHandle);
}

[Fact]
public void GetTickCount_Nonzero()
{
Expand Down
3 changes: 2 additions & 1 deletion test/GenerationSandbox.Tests/NativeMethods.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
BOOL
BCRYPT_KEY_HANDLE
BOOL
BOOLEAN
CHAR
CreateCursor
Expand Down
12 changes: 12 additions & 0 deletions test/Microsoft.Windows.CsWin32.Tests/StructTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ public void CocreatableStructs()
Assert.Contains(classDecl.AttributeLists, al => al.Attributes.Any(a => a.Name.ToString().Contains("ComImport")));
}

[Theory]
[InlineData("BCRYPT_KEY_HANDLE", "BCRYPT_HANDLE")]
public void AlsoUsableForTypeDefs(string structName, string alsoUsableForStructName)
{
this.generator = this.CreateGenerator();
Assert.True(this.generator.TryGenerate(structName, CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
Assert.IsType<StructDeclarationSyntax>(this.FindGeneratedType(structName).Single());
Assert.IsType<StructDeclarationSyntax>(this.FindGeneratedType(alsoUsableForStructName).Single());
}

[Theory]
[InlineData("BOOL")]
[InlineData("HRESULT")]
Expand Down