Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Apply [StructLayout(CharSet.Unicode)] to structs that contain the char type #410

Merged
merged 2 commits into from
Sep 18, 2021
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
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