Date: | October 26, 2004 / year-entry #376 |
Tags: | other |
Orig Link: | https://blogs.msdn.microsoft.com/oldnewthing/20041026-00/?p=37473 |
Comments: | 20 |
Summary: | A colleague of mine nominated this code for Function of the Year. (This is the same person who was the first to report that a Windows beta used a suspect URL.) I have to admit that this code is pretty impressive. Of all the ways to check the operating system, you have to agree that... |
A colleague of mine nominated this code for Function of the Year. (This is the same person who was the first to report that a Windows beta used a suspect URL.) I have to admit that this code is pretty impressive. Of all the ways to check the operating system, you have to agree that sniffing at an undocumented implementation detail of memory-mapped files is certainly creative! // following the typographical convention that code // in italics is wrong int AreWeRunningOnWindowsNT() { HANDLE hFile, hFileMapping; BYTE *pbFile, *pbFile2; char szFile[MAX_PATH]; GetSystemDirectory(szFile, MAX_PATH); strcat(szFile, "\\MAIN.CPL"); hFile = CreateFile(szFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL); pbFile = (PBYTE) MapViewOfFile(hFileMapping, FILE_MAP_WRITE, 0, 0, 0); pbFile2 = (PBYTE) MapViewOfFile(hFileMapping, FILE_MAP_WRITE, 0, 65536, 0); if (pbFile + 65536 != pbFile2) return 1; return 0; } Nevermind that the function also leaves a file locked and leaks two handles and two views each time you call it!
What's more, this function may erroneously report
It can also erroneously report This particular function is from a library that is used by many popular multimedia titles. The quickest way to detect whether you are running on a Windows 95-series system or a Windows NT-series system is to use the hopefully-obviously-named function GetVersion. int AreWeRunningOnWindowsNT() { return (GetVersion() & 0x80000000) == 0; } [Raymond is currently on vacation; this message was pre-recorded.] |
Comments (20)
Comments are closed. |
It also has a potential buffer overflow.
It has many many bugs. What is an error?
My question is, how could someone who knows enough to use said functions be stupid enough to make that many errors in one function? This almost looks deliberate….
I vote this be sent to "The Daily WTF" for inclusion!
So I can avoid it..
I don’t think the buffer overflow is something to worry about. That would mean that the system directory is over 248 characters deep. I would bet that a number of things will get brokien if that were the case
Do you know what their reasoning for detecting the OS version this way?
Wow, that’s just scary.
Actually, it would be better to use GetVersionEx() Since GetVersion() has been superseded by GetVersionEx()
I just had an even scarier thought: what if this code is correct?
By which I mean, what if the thing they are trying to detect is not whether the system is NT, but whether is has this particular filemapping implementation quirk. The implication of which would be that somewhere they want to rely on it…
What about this
if ( !RegisterClass(&wndclass) )
{
MessageBox(NULL, TEXT("This program requires Windows NT"), szAppName, MB_ICONERROR) ;
return 0;
}
From Petzold. The idea being that the only reason RegisterClass would fail would be that you are running the Unicode build on Windows 9x/Me where RegisterClassW is a stub that returns FALSE.
Actually the world ends if %windir% isn’t an 8.3 compatible name, so the buffer overrun is definitely conceptual not tangible. The code /shouldn’t/ be written this way; the thing to keep you awake at night isn’t whether this function in particular will have a BO but rather whether all the other functions written by the same developer will…
And I thought the most common uses of GetVersion() were for programs to inform Windows 2000 users that they had to get SP3 (when Windows 2000 was new and its SP3 was years in the future), or to inform Windows 2000 users that they didn’t have Direct X, or to find other random reasons why they would refuse to run themselves under Windows 2000. Hmm… since this genuinely is an abuse of tools such as GetVersion(), one might wonder how many shims have been written in order to provide compatibility for these broken applications.
Tony, that may be the scariest thing I’ve heard all day…
Maybe this is to avoid the fact that a program can be lied to about the Windows version using various things such as the Application Compatibility Toolkit.
At least they didn’t write something to parse Kernel32.dll or some such.
We hear great scary stories from Raymond about the extent MS goes to in order to make Windows compatible with apps that made bad assumptions or relied on undocumented behavior. The fact that this guy is testing for NT (even though it’s poorly done) reminds us that ISVs go to lengths to be compatible with an ever-growing number of Windows versions. Some APIs have changed radically (e.g., printing), and occasionally we run into things that seem to work (or break) slightly differently on various versions of Windows.
Here’s one that hit me last night. I have an HBITMAP that I know is really a DIB Section. I use GetObject to retrieve a DIBSECTION that describes it. This bitmap happens to have an odd width. DIBs use DWORD alignment for scanlines, and I see that for almost every version of Windows dib.dsBm.bmWidthBytes is a multiple of 4 just like I’d expect. But on Windows 2000, it’s only a multiple of 2 as though it’s a WORD-aligned device-DEpendent bitmap. If I trust 2000 and use the WORD-aligned value for indexing into the pixels, it doesn’t work–the scanlines really are DWORD aligned.
Obviously, I can fix the problem by computing a DWORD-aligned stride without explicitly testing for the OS version. I can’t find any documentation that guarantees DWORD alignment for a DIB Section, only for DIBs, so what if the next version of Windows really does align the DIB Section scanlines to WORD boundaries? I would have to test OS versions to know whether or not to trust the alignment reported by GDI.
Adrian,
I am not a GDI programmer but the info on MSDN seems to indicate that it is word aligned:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_0cqa.asp
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_2h6a.asp
bmWidthBytes
Specifies the number of bytes in each scan line. This value must be divisible by 2, because the system assumes that the bit values of a bitmap form an array that is word aligned.
Of course I may be missing your point.
DIBs (including DIBSECTIONS) are always DWORD aligned. The only things that use WORD aligned buffers are DDBs using CreateBitmap, CreateBitmapIndirect, GetBitmapBits, and SetBitmapBits (I think that is a comprehensive list, MSDN kind of sucks to navigate). The rest of the bitmap functions use DWORD aligned stuff.