Current version

v1.10.4 (stable)

Navigation

Main page
Archived news
Downloads
Documentation
   Capture
   Compiling
   Processing
   Crashes
Features
Filters
Plugin SDK
Knowledge base
Contact info
 
Other projects
   Altirra

Archives

Blog Archive

"If I were king for a day, and giving answers to the test...."

(Read More) to see my answers to the Win32 programmer brain teasers I posed last time. If you haven't seen the questions yet, you may want to look at the original blog post again before you read the spoilers.

As usual, I might make mistakes, so if you think one of my answers is incorrect, feel free to comment.

Avery's Win32 Brain Teasers (answers)

#1:
Consider the two C++ expressions (x>=y) and !(x<y). Is there a functional difference between them? Why might you choose one over the other?

I forgot to say that x and y are floating point variables, so full credit if you say that operator>=() is not available. You'll see the latter construct often in STL-like code, where weak and strict orderings are always defined in terms of less-than comparisons, and all other inequalities are derived by negation and operand exchange.

If they are floating-point values, the difference is significant because the two will differ in result if one of the values is a NaN (not a number). Well, and assuming you are using at least Visual Studio .NET, or a compiler that implements NaN comparisons properly. This becomes really important if you are trying to write safety code. If you attempt to do a bounded array lookup like this:

if (f > 4.0f) f = 4.0f;
if (f < 0.0f) f = 0.0f;
int i = (int)f;
return values[i] + (values[i+1] - values[i])*(f - i);

...you might be surprised when it still crashes on a NaN, because any comparison involving a NaN always returns false. Then again, if the array contains floats, the address computation might shift bits out of the integer indefinite that results (80000000) and convert that to zero, and then the routine will just compute a NaN for the next poor sucker to consume.

#2:
Explain the significance of at least three of the following constants:

#3:
When could the following code cause a deadlock?

MessageBox(hwnd, "Hello!", "Test", MB_OK);

When you call it from the DllMain() of a DLL, especially if a thread is spun up somewhere along the way, which then causes thread attach notifications to attempt to be sent... under loader lock. Whoops. And since MessageBox() spawns a modal message loop, nearly anything can happen (think hooks). Asserts on exit are great for tripping this....

#4:
A colleague is receiving the following link error.

linkerr.obj : error LNK2001: unresolved external symbol "public: int __thiscall Foo::GetCurrentTime(void)" (?GetCurrentTime@Foo@@QAEHXZ)
linkerr.exe : fatal error LNK1120: 1 unresolved externals

The method Foo::GetCurrentTime() appears to be both declared and defined correctly as a non-inline method, and other modules in the same project are calling it without problems. Yet, attempting to call it from the module that the colleague is working with results in this link error. Explain a possible reason for the error and a strategy for fixing it.

The modules that currently define or use the method include <windows.h>. Your colleague's doesn't. The link error is caused by this line in the nested header <winbase.h>:

#define GetCurrentTime()                GetTickCount()

Everyone is really seeing GetTickCount(), except him. This is especially fun with the semi-random ANSI/Unicode #defines. I hear a bug like this has bitten MFC one or two times, because some MFC routines have names that hit a #define, and no one noticed for a long time because MFC code usually includes <windows.h>.

#5:
Identify the most likely usage for memory pointed to by the following addresses in 32-bit Windows NT (not Win9x/ME):

Addresses are a bit different for Win9x. The executable still starts at 00400000 -- the OS can't relocate it anyway -- but system DLLs are in the shared range at 80000000-BFFFFFFF, and the main thread stack can't be that low due to the 1MB DOS arena at address zero.

#6:
Which implementation of a general-purpose memcpy() would be faster for large blocks: one that uses type (char) for memory movement, or one that uses type (double)? Why? You may assume that pointers and sizes are nicely aligned so that boundary fixups are not necessary.

This was a trick question. The answer is (char), because it's the one that actually works. If you attempt to write a memcpy() for arbitrary bit patterns that uses (double) on x86, it may either crash when it sees a bit pattern that corresponds to a NaN, or even worse, silently change a signaling NaN (SNaN) into a quiet NaN (QNaN) by setting the highest significand bit. Whether or not this happens can depend on your compiler's mood -- when I tested this, VC6 changed the copy loop to use regular integer moves, which would avoid this, but VC8 still used floating-point loads and stores. You didn't need bit 51, did you?

The chances of getting away with this are better if SSE2 code generation is enabled, as the SSE/SSE2 load instructions do not flag exceptions on NaNs like the x87 ones do. Even then, though, you can't depend on the compiler generating SSE2 moves, as sometimes it will still use x87 instructions in 32-bit for various reasons, and it's still a great bear trap given that the code looks portable.

You can actually do copies safely with the FPU using FILD/FISTP to load and store 64-bit integers. On the original Pentium, this was one of the few ways to do a 64-bit write transaction, and one of the faster ways to write to uncached memory. This is no longer the case, though, with fast string operations and MMX, and in any case it's not exactly what the compiler would generate from type (double).

#7:
What is wrong with this implementation of IUnknown::AddRef()? When will it work without any problems, and when will it fail? You may assume that COM aggregation is not involved.

ULONG STDMETHODCALLTYPE Foo::AddRef() {
    return ++mRefCount;
}

Partial credit if you said that it may fail on a single CPU system, and will always eventually fail on a multi-CPU system, because there is a race condition in the increment that can goof if multiple threads call AddRef() on the same object. IUnknown::AddRef() must be thread-safe in COM, and thus the code should have used InterlockedIncrement() or equivalent.

Full credit if you also noted that it will work perfectly fine if mRefCount is actually an instance of an object whose operator++() does a thread-safe increment. (Highly recommended.)

#8:
Write a function to return the size of a Win32 GDI bitmap header, given a (const BITMAPINFOHEADER *).

If you said sizeof(BITMAPINFOHEADER), nice try. If you said sizeof(BITMAPINFO), go back to start. You've been reading too many MSDN samples.

This is one question I'm pretty sure I don't have a complete answer to, because there are so many special cases that have to be handled. Off the top of my head:

Good luck with this one, as I'm sure there are more oddball cases. Give yourself full credit if you got at least a couple of these.

#9:
Describe a reliable, efficient way to wait for a worker thread to exit from a user interface thread, i.e. a thread that has windows.

If you said Sleep(), you not only failed, but I'm going to have you expelled.

If you said "wait for a signal/semaphore" or "wait for a message," not quite. I said thread exit, not when the worker's job is finished. The distinction's important if you absolutely need to guarantee that the thread has exited... say, if you're going to unload the DLL that holds its code. (There's FreeLibraryAndExitThread() for that case, but pretend something else is going to happen besides just freeing a DLL.)

If you said PostQuitMessage() and a modal loop, you still failed, but I'll give you points for creativity.

If you said GetExitCodeThread(), good try, but there's that little problem of its return value being overloaded for both status and the thread's return value. And you're polling.

If you said WaitForSingleObject() on the thread, that would be a better way... but again, uh, don't you have to pump messages? WaitForSingleObject(hThread, 0) is indeed a safer way to check for thread exit, but it's still polling.

MsgWaitForMultipleObjects() or MsgWaitForMultipleObjectsEx() is the way to go here, because it allows you to wait for either thread exit or a message to arrive.

#10:
Write a function that determines the HMODULE of its own DLL or EXE. Complications: You cannot call GetModuleHandle() or GetModuleHandleEx(), use a cached value, or reference __ImageBase, and the implementation must not be hardcoded to a particular DLL or EXE.

Call VirtualQuery() on your own function pointer, or any other static object within your module. Cast the AllocationBase field of the MEMORY_BASIC_INFORMATION structure to (HMODULE).

I'll give you partial credit for creativity and for taking the unnecessarily hard road if you used ToolHelp or PSAPI to iterate over the process's module list.

Why would you want to do this? Some UI operations, particularly registering window classes and creating windows, require your HMODULE or HINSTANCE (the two are equivalent). If you're making a static library, it's best if you can avoid having to depend on whether you are in a DLL or EXE, or force clients to pass the HMODULE to you. GetModuleHandleEx() is a cleaner way to do this -- as long as you have Windows XP. Otherwise, the VirtualQuery() trick can come in handy. __ImageBase is a really clean way to do it, but it was only introduced in VS.NET, and it's specific to the VC++ linker.

Comments

This blog was originally open for comments when this entry was first posted, but was later closed and then removed due to spam and after a migration away from the original blog software. Unfortunately, it would have been a lot of work to reformat the comments to republish them. The author thanks everyone who posted comments and added to the discussion.