Date: | September 7, 2017 / year-entry #202 |
Tags: | code |
Orig Link: | https://blogs.msdn.microsoft.com/oldnewthing/20170907-00/?p=96956 |
Comments: | 26 |
Summary: | The struct with no name. |
Windows header files take advantage of a language extension known as "anonymous structs" or "nameless structs". It looks like this: typedef struct _devicemode { TCHAR dmDeviceName[CCHDEVICENAME]; WORD dmSpecVersion; WORD dmDriverVersion; WORD dmSize; WORD dmDriverExtra; DWORD dmFields; union { struct { short dmOrientation; short dmPaperSize; short dmPaperLength; short dmPaperWidth; short dmScale; short dmCopies; short dmDefaultSource; short dmPrintQuality; }; // <--- mystery #1 struct { POINTL dmPosition; DWORD dmDisplayOrientation; DWORD dmDisplayFixedOutput; }; // <--- mystery #2 }; // <--- mystery #3 short dmColor; short dmDuplex; short dmYResolution; short dmTTOption; short dmCollate; TCHAR dmFormName[CCHFORMNAME]; WORD dmLogPixels; DWORD dmBitsPerPel; DWORD dmPelsWidth; DWORD dmPelsHeight; union { DWORD dmDisplayFlags; DWORD dmNup; }; // <--- mystery #4 DWORD dmDisplayFrequency; #if(WINVER >= 0x0400) DWORD dmICMMethod; DWORD dmICMIntent; DWORD dmMediaType; DWORD dmDitherType; DWORD dmReserved1; DWORD dmReserved2; #if (WINVER >= 0x0500) || (_WIN32_WINNT >= 0x0400) DWORD dmPanningWidth; DWORD dmPanningHeight; #endif #endif } DEVMODE, *PDEVMODE, *LPDEVMODE;
Members of structures and unions normally have names.
But in the Let's start with a smaller example. Consider this structure: struct simple { int a; union { int b; int c; } d; } x;
In this example, we have a structure called
A nameless union omits the name struct simple2 { int a; union { int b; int c; }; // <-- no name! } x2; This time, the contents are
See what happened there? Omitting the name on the union means that the members of the union are accessible without having to say the name of the union (which is a good thing, because that union has no name).
Nameless unions are available in C and C++,¹
and that's what is happening in the These extensions are supported by both the Visual Studio compiler as well as the GCC compiler. But what if your compiler doesn't? The answer lies in the actual definition in the header file. typedef struct _devicemodeW { WCHAR dmDeviceName[CCHDEVICENAME]; WORD dmSpecVersion; WORD dmDriverVersion; WORD dmSize; WORD dmDriverExtra; DWORD dmFields; union { struct { short dmOrientation; short dmPaperSize; short dmPaperLength; short dmPaperWidth; short dmScale; short dmCopies; short dmDefaultSource; short dmPrintQuality; } DUMMYSTRUCTNAME; // magic #1 struct { POINTL dmPosition; DWORD dmDisplayOrientation; DWORD dmDisplayFixedOutput; } DUMMYSTRUCTNAME2; // magic #2 } DUMMYUNIONNAME; // magic #3 short dmColor; short dmDuplex; short dmYResolution; short dmTTOption; short dmCollate; WCHAR dmFormName[CCHFORMNAME]; WORD dmLogPixels; DWORD dmBitsPerPel; DWORD dmPelsWidth; DWORD dmPelsHeight; union { DWORD dmDisplayFlags; DWORD dmNup; } DUMMYUNIONNAME2; // magic #4 DWORD dmDisplayFrequency; #if(WINVER >= 0x0400) DWORD dmICMMethod; DWORD dmICMIntent; DWORD dmMediaType; DWORD dmDitherType; DWORD dmReserved1; DWORD dmReserved2; #if (WINVER >= 0x0500) || (_WIN32_WINNT >= 0x0400) DWORD dmPanningWidth; DWORD dmPanningHeight; #endif #endif } DEVMODEW,*LPDEVMODEW,*PDEVMODEW;
There are magic symbols called
How curious.
If you then search the Windows header files for definitions of
these magic symbols, you find them here in // // For compilers that don't support nameless unions/structs // #ifndef DUMMYUNIONNAME #if defined(NONAMELESSUNION) || !defined(_MSC_EXTENSIONS) #define DUMMYUNIONNAME u #define DUMMYUNIONNAME2 u2 #define DUMMYUNIONNAME3 u3 #define DUMMYUNIONNAME4 u4 #define DUMMYUNIONNAME5 u5 #define DUMMYUNIONNAME6 u6 #define DUMMYUNIONNAME7 u7 #define DUMMYUNIONNAME8 u8 #define DUMMYUNIONNAME9 u9 #else #define DUMMYUNIONNAME #define DUMMYUNIONNAME2 #define DUMMYUNIONNAME3 #define DUMMYUNIONNAME4 #define DUMMYUNIONNAME5 #define DUMMYUNIONNAME6 #define DUMMYUNIONNAME7 #define DUMMYUNIONNAME8 #define DUMMYUNIONNAME9 #endif #endif // DUMMYUNIONNAME #ifndef DUMMYSTRUCTNAME #if defined(NONAMELESSUNION) || !defined(_MSC_EXTENSIONS) #define DUMMYSTRUCTNAME s #define DUMMYSTRUCTNAME2 s2 #define DUMMYSTRUCTNAME3 s3 #define DUMMYSTRUCTNAME4 s4 #define DUMMYSTRUCTNAME5 s5 #else #define DUMMYSTRUCTNAME #define DUMMYSTRUCTNAME2 #define DUMMYSTRUCTNAME3 #define DUMMYSTRUCTNAME4 #define DUMMYSTRUCTNAME5 #endif #endif // DUMMYSTRUCTNAME Ah, now the pieces all fall into place.
If you define the symbol
This means that if you indicate that you don't want the header files to use nameless unions, the nameless structures and unions magically get names! The names are not particularly exciting, but at least they have names. DEVICEMODE dm; dm.dmPosition = ...; // if nameless unions are enabled dm.u.s2.dmPosition = ...; // if nameless unions are disabled Notice that I didn't use any Microsoft insider information to solve this mystery. All the information you need is right there, if you just follow the symbol definitions. ¹ The history here is unclear. Wikipedia claims that anonymous unions are in C++ and C11, but Stack Overflow claims that C++ supports anonymous unions only because C did. So there's some sort of circular causality loop here. |
Comments (26)
Comments are closed. |
I was a little surprised I’d never noticed these dummy names. It turns out they are a recent addition. They are in the Windows 10 SDKs, but not the Windows 8 versions.
The common controls ones have been there since 1995.
I see. This is a recent addition to DEVMODE, not a recent innovation.
To explain my confusion: in the past I’ve extracted type information from the Windows headers and anonymous structs were the kind of detail that caused difficulties, so I was surprised I was unaware of a workaround. But it wasn’t a useful workaround because even as recently as Windows 8 it wasn’t used everywhere it was needed.
Anyway, it’s nice to see compatibility with other compilers improving.
No, these were not added recently: you can find them in SDKs published in the last Millennium!
WINNT.H has them at least since the SDK for Windows 7 and .NET 4.
See the VARIANT types for examples that are way older.
There are also macro’s to access the members, that will work both with and without NONAMELESSUNION defined (like V_VT to access the ‘vt’ field.
This makes me curious about how many compilers and compiler options Microsoft tests their SDK/API with. It seems like it’d be a daunting task with a substantial testing matrix.
The MinGW/GCC toolchain use their own set of Win32 header files because of stuff like __uuidof and other non-standard things. I don’t remember if MinGW was ever able to use the official SDK. Maybe a 20 year old SDK would work, who knows.
I always wondered why the SDK does not do something like #ifndef _MSC_VER #include #endif, then GCC could just ship that file with the correct defines mapping to their __attribute__((whatever)) stuff.
Tell me, what do you expect to happen if the standard windows headers were shipped with and used to compile Wine?
I never said anything about Wine, I’m talking about using the official headers with GCC/CLang/whatever to build normal applications. For Wine it is a legal issue, not a technical issue.
I went back and checked; mingw branched from cygwin, which quite possibly couldn’t use the official headers, and was used by wine about as soon as it could be.
Shipping the include files and using the include files are two different things. At one point one of the compilers shipped their own, but gave instructions on how to use the official include files if you legally obtained them.
Microsoft also ship clang with visual studio these days.
Wouldn’t be a easily parallelizable automated solution, though? Seems like something you could manage via CI. The initial configuration for the tests might be complex, but it’s all software; you’re not really relying on user interaction or anything like that. And the answer is a simple binary of whether it compiles or not, with some optional post-functional testing perhaps.
Oh, I’m sure a lot of it is automated. Still a lot of testing. And I assume they would need a lot of those post-compile functional checks to ensure that each and every API is being called with the right calling convention, all these various structs are laid out in memory exactly right, and so forth. There’s a lot of details and nuances that I could imagine being different.
Notice that I didn’t use any Microsoft insider information to solve this mystery. All the information you need is right there, if you just follow the symbol definitions.
But also notice that the symbol NONAMELESSUNION and its use/purpose is NOT documented on MSDN’s page, https://msdn.microsoft.com/en-us/library/windows/desktop/aa383745.aspx, where every developer and his dog would expect them.
Right. I’m saying that if you see them in the header file, you can chase down the definitions and figure out what they’re for based on their names.
Is your recommendation also applicable to (elsewhere not documented) structure definitions like RTL_VERIFIER_PROVIDER_DESCRIPTOR etc., or function prototype like RtlSetHeapInformation, RtlQueryHeapInformation etc. found in winnt.h?
Turning off nameless unions has no binary effect. The structure has the same layout either way. The only difference is how you refer to its members in source code. The resulting binary is identical. Calling undocumented functions or passing an undocumented structure affects the resulting binary. I can’t believe I have to explain this.
Stuff like this should be something that only the implementor should have to worry about IMO: Note that the page that you referenced discusses how the headers work with VC++. If your compiler ships with Windows headers and can produce PE files correctly, then it should be the responsibility of the compiler vendor to sort this out and document any requirements; otherwise you’re using the headers in an unsupported way and have to deal with any issues yourself.
Setting NONAMELESSUNION but forces the programmer to change the sources which reference such previously unnamed unions.
JFTR: the MSDN article talks about the Windows headers in general; Visual C++ is a subsection of it.
I remember asking in comp.lang.c why anonymous unions weren’t supported in ISO C, given their obvious usefulness. That was during the days of C99. Didn’t know they’ve since been added. Nor do I recall what answers I got (I seem to recall they were along the lines of “it’s not *that* useful a feature”).
What compilers are people using that don’t support anonymous structs and unions?
I have to trust my memory of an experience more than a decade ago, but IIRC, having used Visual C++ for many years, I assumed anonymous structs were part of the C++ language and was surprised to find out that while anonymous unions were part of the language, anonymous structs were not. I think I had to write a quick Linux program and found that GCC was either generating warnings or errors at my usage of them.
They were warnings for sure, and probably only with -pedantic; I used anonymous unions with gcc many times, they are a quite widespread extension (and a very convenient one for sure).
The C# compiler doesn’t support anonymous unions. Preserving exact parity between interop definitions and Win32 header files is harder without tricks like DUMMYUNIONNAME.
I want to try to get the C++ committee to approve anonymous structs. Most compilers already allow anonymous structs, though may warn about them being an extension. They’re clearly useful in certain context.
Gah! Nameless structs are a curse_ed evil from a primative time and should forever be banished from civilized society. It is the evil so great that one dares not speak its name (it has no name). Just say no.