Skip to content

Commit

Permalink
Merge pull request #410 from microsoft/fix389
Browse files Browse the repository at this point in the history
Apply `[StructLayout(CharSet.Unicode)]` to structs that contain the `char` type
  • Loading branch information
AArnott authored Sep 18, 2021
2 parents cc651a5 + db245d9 commit b13bfcb
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 4 deletions.
2 changes: 1 addition & 1 deletion src/Microsoft.Windows.CsWin32/FastSyntaxFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ internal static SyntaxList<TNode> SingletonList<TNode>(TNode node)

internal static AttributeArgumentListSyntax AttributeArgumentList(SeparatedSyntaxList<AttributeArgumentSyntax> arguments = default) => SyntaxFactory.AttributeArgumentList(Token(SyntaxKind.OpenParenToken), arguments, Token(SyntaxKind.CloseParenToken));

internal static AttributeListSyntax AttributeList() => SyntaxFactory.AttributeList(Token(SyntaxKind.OpenBracketToken), null, SeparatedList<AttributeSyntax>(), Token(SyntaxKind.CloseBracketToken));
internal static AttributeListSyntax AttributeList() => SyntaxFactory.AttributeList(Token(SyntaxKind.OpenBracketToken), null, SeparatedList<AttributeSyntax>(), TokenWithLineFeed(SyntaxKind.CloseBracketToken));

internal static SyntaxList<TNode> List<TNode>()
where TNode : SyntaxNode => SyntaxFactory.List<TNode>();
Expand Down
20 changes: 17 additions & 3 deletions src/Microsoft.Windows.CsWin32/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1844,7 +1844,7 @@ private static string GetClassNameForModule(string moduleName) =>

private static AttributeSyntax FieldOffset(int offset) => FieldOffsetAttributeSyntax.AddArgumentListArguments(AttributeArgument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(offset))));

private static AttributeSyntax StructLayout(TypeAttributes typeAttributes, TypeLayout layout)
private static AttributeSyntax StructLayout(TypeAttributes typeAttributes, TypeLayout layout = default, CharSet charSet = CharSet.Ansi)
{
LayoutKind layoutKind = (typeAttributes & TypeAttributes.ExplicitLayout) == TypeAttributes.ExplicitLayout ? LayoutKind.Explicit : LayoutKind.Sequential;
var structLayoutAttribute = Attribute(IdentifierName("StructLayout")).AddArgumentListArguments(
Expand All @@ -1867,6 +1867,13 @@ private static AttributeSyntax StructLayout(TypeAttributes typeAttributes, TypeL
.WithNameEquals(NameEquals(nameof(StructLayoutAttribute.Size))));
}

if (charSet != CharSet.Ansi)
{
structLayoutAttribute = structLayoutAttribute.AddArgumentListArguments(
AttributeArgument(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, IdentifierName(nameof(CharSet)), IdentifierName(Enum.GetName(typeof(CharSet), charSet)!)))
.WithNameEquals(NameEquals(IdentifierName(nameof(StructLayoutAttribute.CharSet)))));
}

return structLayoutAttribute;
}

Expand Down Expand Up @@ -3231,6 +3238,7 @@ private StructDeclarationSyntax DeclareStruct(TypeDefinition typeDef)
IdentifierNameSyntax name = IdentifierName(this.Reader.GetString(typeDef.Name));
TypeSyntaxSettings typeSettings = this.fieldTypeSettings;

bool hasUtf16CharField = false;
var members = new List<MemberDeclarationSyntax>();
SyntaxList<MemberDeclarationSyntax> additionalMembers = default;
foreach (FieldDefinitionHandle fieldDefHandle in typeDef.GetFields())
Expand Down Expand Up @@ -3269,6 +3277,7 @@ private StructDeclarationSyntax DeclareStruct(TypeDefinition typeDef)
{
CustomAttributeHandleCollection fieldAttributes = fieldDef.GetCustomAttributes();
TypeHandleInfo fieldTypeInfo = fieldDef.DecodeSignature(SignatureHandleProvider.Instance, null);
hasUtf16CharField |= fieldTypeInfo is PrimitiveTypeHandleInfo { PrimitiveTypeCode: PrimitiveTypeCode.Char };
TypeSyntaxAndMarshaling fieldTypeSyntax = fieldTypeInfo.ToTypeSyntax(typeSettings, fieldAttributes);
var fieldInfo = this.ReinterpretFieldType(fieldDef, fieldTypeSyntax.Type, fieldAttributes);
additionalMembers = additionalMembers.AddRange(fieldInfo.AdditionalMembers);
Expand Down Expand Up @@ -3309,9 +3318,10 @@ private StructDeclarationSyntax DeclareStruct(TypeDefinition typeDef)
.WithModifiers(TokenList(TokenWithSpace(this.Visibility), TokenWithSpace(SyntaxKind.PartialKeyword)));

TypeLayout layout = typeDef.GetLayout();
if (!layout.IsDefault || (typeDef.Attributes & TypeAttributes.ExplicitLayout) == TypeAttributes.ExplicitLayout)
CharSet charSet = hasUtf16CharField ? CharSet.Unicode : CharSet.Ansi;
if (!layout.IsDefault || (typeDef.Attributes & TypeAttributes.ExplicitLayout) == TypeAttributes.ExplicitLayout || charSet != CharSet.Ansi)
{
result = result.AddAttributeLists(AttributeList().AddAttributes(StructLayout(typeDef.Attributes, layout)));
result = result.AddAttributeLists(AttributeList().AddAttributes(StructLayout(typeDef.Attributes, layout, charSet)));
}

if (this.FindGuidFromAttribute(typeDef) is Guid guid)
Expand Down Expand Up @@ -4830,6 +4840,10 @@ private ParameterSyntax CreateParameter(TypeHandleInfo parameterInfo, Parameter
}

fixedLengthStruct = fixedLengthStruct.AddMembers(conversionDecl);

// Make sure .NET marshals these `char` arrays as UTF-16.
fixedLengthStruct = fixedLengthStruct
.AddAttributeLists(AttributeList().AddAttributes(StructLayout(TypeAttributes.SequentialLayout, charSet: CharSet.Unicode)));
}

IdentifierNameSyntax indexParamName = IdentifierName("index");
Expand Down
16 changes: 16 additions & 0 deletions test/GenerationSandbox.Tests/BasicTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
using Windows.Win32.Foundation;
using Windows.Win32.Graphics.DirectShow;
using Windows.Win32.Storage.FileSystem;
using Windows.Win32.System.Console;
using Windows.Win32.System.ErrorReporting;
using Windows.Win32.System.SystemServices;
using Windows.Win32.UI.DisplayDevices;
using Xunit;
using Xunit.Abstractions;
Expand Down Expand Up @@ -413,4 +416,17 @@ public unsafe void GetProcAddress_String()
ticks = getTickCountPtr();
Assert.NotEqual(0u, ticks);
}

[Fact]
public void StructCharFieldsMarshaledAsUtf16()
{
Assert.Equal(128 * sizeof(char), Marshal.SizeOf<WER_REPORT_INFORMATION.__char_128>());
Assert.Equal(sizeof(char), Marshal.SizeOf<KEY_EVENT_RECORD._uChar_e__Union>());
}

[Fact]
public void CHAR_MarshaledAsUtf8()
{
Assert.Equal(1, Marshal.SizeOf<CHAR>());
}
}
3 changes: 3 additions & 0 deletions test/GenerationSandbox.Tests/NativeMethods.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
BOOL
CHAR
CreateFile
DISPLAYCONFIG_VIDEO_SIGNAL_INFO
EnumWindows
Expand All @@ -8,9 +9,11 @@ GetWindowText
GetWindowTextLength
HDC_UserSize
IEnumDebugPropertyInfo
KEY_EVENT_RECORD
LoadLibrary
MainAVIHeader
NTSTATUS
RM_PROCESS_INFO
WER_REPORT_INFORMATION
wglGetProcAddress
WPARAM

0 comments on commit b13bfcb

Please sign in to comment.