-
Notifications
You must be signed in to change notification settings - Fork 90
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
Static 'Initialize' factory method on structs with cbSize fields #108
Comments
Note that Just wanted to throw that in because I suspect this library is going to use blitting where possible? details on the difference
public struct Sample
{
public int a;
public bool b;
public short c;
}
var sample = new Sample();
var blittingSize = sizeof(Sample); // 8
var blittingOffset = (byte*)&sample.c - (byte*)&sample; // 6
var marshalSize = Marshal.SizeOf<Sample>(); // 12
var marshalOffset = Marshal.OffsetOf<Sample>(nameof(Sample.c)); // 8 This happened to cause bugs twice in the WinForms interop cleanup ( |
That is horrifying. Thanks for enlightening me on this, @weltkante. |
As far as I'm aware blitting is used when you use pointers to or |
You may be right, but it's alarming to think that changing a p/invoke signature from |
https://docs.microsoft.com/en-us/dotnet/standard/native-interop/best-practices#blittable-types covers what is and is not blittable. In particular:
Arrays and strings as individual parameters have blittable contents and can be pinned and passed instead (assuming the
While
win32metadata should be defining purely blittable structs and P/Invokes with the correct packing information (outside any bugs) and so |
The forced pinning of references (especially For example, long frequency;
var result = QueryPerformanceFrequency(&frequency);
Debug.Assert(result != 0); // QPC can't fail on WinXP or later
return frequency; While if you instead had a P/Invoke of var result = QueryPerformanceFrequency(out long frequency);
Debug.Assert(result); // QPC can't fail on WinXP or later
return frequency; But this will actually generate something similar to (while the blittable signature doesn't require a shim and can have the transition logic inlined in newer versions of .NET): var result = QueryPerformanceFrequencyShim(out long frequency);
Debug.Assert(result); // QPC can't fail on WinXP or later
return frequency;
bool QueryPerformanceFrequencyShim(out long frequency) // will not be inlined
{
fixed (long* pFrequency = &frequency)
{
return QueryPerformanceFrequency(pFrequency) != 0;
}
} While if you manually defined the same "shim" over the blittable P/Invoke, it could be inlined and potentially optimized. |
Right, sorry, I always forget that blittable terminology no longer matches what it used to mean, |
Right, although that is slightly different. That is allowing pointers to unmanaged types to act as blittable and not incur marshalling (because its just a pointer and therefore the user is assumed to know what they're doing). In that scenario, the |
Ya, but that also means we lost precision in the metadata. We can't tell from But all that's aside from the question I posed originally, which is given a blittable struct that report different sizes from
That would be great. So in @weltkante's example where they are different, is it because he uses Regarding your shim and inlining thoughts, I didn't fully follow you. But here is what CsWin32 currently generates for your example method: internal static unsafe bool QueryPerformanceFrequency(out long lpFrequency)
{
fixed (long *lpFrequencyLocal = &lpFrequency)
{
bool __result = PInvoke.QueryPerformanceFrequency(lpFrequencyLocal);
return __result;
}
}
[DllImport("Kernel32", ExactSpelling = true, SetLastError = true)]
internal static extern unsafe bool QueryPerformanceFrequency([Out] long *lpFrequency); So we're in-between. The return type is |
Because that's how
There are numerous problems here including that the character type used for Windows APIs is a mapping of Additionally, the default behavior of It does result in more overhead on the generator, but that is something that it should be able to handle. The SAL annotations and/or docs are what need to tell you if the input is a
When the types are blittable, these two are equal; but just because they are equal does not mean you have blittable data because cases like
Yes,
Returning |
@tannergooding -- That's a good, succinct, and (importantly) testable requirement. Is there currently a unit test / CI validation step that checks that this is true for all win32metadata structs and P/Invokes? |
ClangSharp supports generating such tests. I am not aware if win32metadata is currently using this functionality. I'm also not aware if they have their own equivalent or not. |
Oops! Sorry, I lost track of which depot I was in. I'll open an issue (suggestion) in the win32metadata depot. |
While the win32metadata structs should all be blittable, that isn't always true in CsWin32. The structs we emit are only guaranteed to be blittable if you set |
The metadata now includes |
Starting in C# 10, parameterless struct constructors may be declared. Now that |
@mikebattista To adopt this to automated initializing the field, we may want CsWin32 to be able to determine whether a struct is a variable length struct, so that the |
Variable length meaning with a flexible array member as the last field? |
I suppose that's one manifestation of it. I'm not sure all variable length structs end with such a field. I vaguely recall a case where the docs simply indicate that the struct acts as a header to a larger buffer. |
See https://github.com/microsoft/win32metadata/blob/main/docs/projections.md#:~:text=Flexible%20array%20members%20at%20the%20end%20of%20a%20struct%20are%20decorated%20with%20%5BFlexibleArray%5D%20(%23912) for the flexible array member scenario. I'm not aware of anything else in the metadata. If it can't be programmatically scraped, then it would need to be manually attributed. |
Instead of:
What if we could write:
And
Initialize
would use eithersizeof
or a constant calculated at compile time whenever possible rather than callingMarshal.SizeOf
?(Allowing structs to have default constructors is being discussed for future C# versions.)
BITMAPINFOHEADER
hasbiSize
rather thancbSize
.The text was updated successfully, but these errors were encountered: