(Remember, he's not a real doctor.)
Bob Gunderson
Microsoft Developer Network Technology Group
Got a question? Dr. GUI's busy operating schedule won't allow him to answer all questions, but the Good Doctor will answer as many as he can on the next Microsoft Developer Network CD. Send your questions to:
Dr. GUI
Microsoft Developer Network
One Microsoft Way
Redmond, WA 98052-6399
Fax: 206-936-7329, Attn: Developer Network
Internet: devnetwk@microsoft.com
Dear Dr. GUI:
What does MakeProcInstance do? I've heard rumors that I don't have to call it for DLL functions. Is this true? Do I need MakeProcInstance if I let only one instance of my application run?
Dr. GUI replies:
Excellent questions! To explain what MakeProcInstance does, let me first describe how Microsoft Windows manages application and DLL data segments. For each application instance, Windows allocates a separate data segment that contains the application's global variables, static data, stack, and local heap. (Large-model applications add a twist to this, but we won't get into that here.) An instance handle is simply a unique identifier for this data segment.
DLLs, on the other hand, get only one data segment, regardless of the number of applications using the DLL. Windows allocates the data segment when it loads the DLL. If multiple applications (or multiple instances of the same application) share the DLL, Windows does not allocate additional data segments for the DLL.
Most applications and DLLs are written with the assumption that, when their code executes, the active data segment (the value of the DS register) is the application's or DLL's data segment. All applications, except large-model ones, use near data references to access data in their data segment, so when your application or DLL executes, it is imperative to have the proper DS register value set.
Let's say that you have two instances of your application running. You now have two data segments, one for each application instance. If Windows wants to call an exported function in one instance of your application (for example, the enumeration procedure for an EnumFonts function), how does it set the proper DS when it calls your function? It doesn't—this is where MakeProcInstance comes in. MakeProcInstance lets you attach a data segment to an instance of a callback function. You must call MakeProcInstance for every exported callback function your application uses. In general, a callback function is any function that Windows, another application, or a DLL calls. Window procedures are exempt from this rule, but dialog procedures are not because they are not true window procedures.
Your application calls MakeProcInstance and passes it the handle to the current instance (hinst) and the address of the callback function. Windows allocates a small piece of memory, called a thunk, and places the following two instructions in it:
MOV AX,[selector value for hinst]
JMP [function address]
Windows then passes the address of the thunk back to the application as the MakeProcInstance return value. The application uses the address of the thunk as the instance-specific address of the exported function.
Now that we have the proper DS value in the AX register when Windows calls the callback function, how does this value get into the DS register? That's where the function prolog and the Windows loader come into play. The compiler creates a standard function prolog for all far functions that looks like this:
PUSH DS
POP AX
NOP
.
.
.
MOV DS,AX
Note:
If you are using Microsoft C/C++ version 7.0 and the /GA compiler option, the compiler adds this prolog only to functions that use the __export modifier.
When you load the application, the Windows loader changes the prolog in all exported functions to look like this:
NOP
NOP
NOP
.
.
.
MOV DS,AX
So, the contents of the AX register at the time of the function call is stuffed into the DS register, assuming that all exported functions are called through a MakeProcInstance thunk. As I mentioned above, window procedures are exempt from this rule. When Windows dispatches a message to a window procedure, it sets the AX register to the proper selector based on the hinst parameter passed to CreateWindow when the window was created. Essentially, CreateWindow does an automatic MakeProcInstance for you.
To answer your last question: Yes, you must call MakeProcInstance for your callback functions even if you restrict your application to one instance. If you don't call MakeProcInstance, callbacks won't go through a thunk and the AX register won't be set to the proper DS value. AX will probably not contain a selector because it receives whatever was in the register at the time of the function call. The prolog will dutifully attempt to move this nonselector value into the DS register, causing a GP fault.
All rules have exceptions, and the rule of always calling MakeProcInstance is no different. If you use the Microsoft C/C++ version 7.0 compiler with the /GA option, you need not call MakeProcInstance because /GA causes the compiler to generate code that reloads DS from SS (the stack selector). This will only work for applications in which DS = SS, which is, by far, the majority of applications. The Borland C++ compiler provides the same feature with the smart callbacks switch, –WS.
Because DLLs do not have multiple data segments, the Windows loader modifies the prolog of all exported functions in a DLL to look like this:
MOV AX,[data segment selector]
.
.
.
MOV DS,AX
So, the value of AX at the time of the function call does not matter in a DLL. That's why you need not use MakeProcInstance on callback functions in a DLL.
Dear Dr. GUI:
The documentation for the DestroyIcon function in the Microsoft Windows version 3.0 Software Development Kit (SDK) Reference, Volume 1 specifically warned that the function must only be used to destroy icons created with the CreateIcon function. The Windows version 3.1 SDK Programmer's Reference, Volume 2: Functions, on the other hand, states that DestroyIcon can be used on icons that were created with either CreateIcon or LoadIcon. Which is correct?
Dr. GUI replies:
Oops. You just caught a documentation bug. The DestroyIcon function must only be used on icons that you created using CreateIcon. The Windows version 3.0 SDK Programmer's Reference, Volume 1 was correct.
Dear Dr. GUI:
I have an application that needs to call a function in a DLL. Because of version dependencies, I can't guarantee that the function exists in the DLL that is installed. So, instead of implicitly linking to the function, I use LoadLibrary and GetProcAddress. So far, so good. I've always heard that using ordinal values with GetProcAddress is much faster than strings, not to mention that it eliminates the need to deal with a text string for the name of the function, but I found the following comment in the Windows version 3.1 SDK Programmer's Reference, Volume 2: Functions:
"If the lpszProcName parameter is an ordinal value and a function with the specified ordinal does not exist in the module, GetProcAddress can still return a non-NULL value. In cases where the function may not exist, specify the function by name rather than ordinal value."
OK, so I'll keep using the function name rather than the ordinal. My question is, why?
Dr. GUI replies:
Time for a quick spelunking expedition into the guts of the kernel and the executable file format. Exported functions in an executable file (.EXE or .DLL) contain two tables that are used to resolve exported entry points in the executable—the name table and the entry table. The name table is used to resolve function names to an ordinal value. For example, let's say your application exports a function with the following .DEF file lines:
EXPORT
ReallyCoolFunction @4
The application will have an entry in the name table that can be used to resolve the string "ReallyCoolFunction" to the value 4. If you were to call GetProcAddress with the string "ReallyCoolFunction", Windows would first scan through the name table looking for the string. This is the part of GetProcAddress that is slow, and I understand why you want to eliminate this string search.
Now comes the interesting part. To resolve the ordinal value into a function address, GetProcAddress uses the entry table. The entry table contains a description of every exported function in the executable, ordered by ordinal value. If the executable was not explicitly built for protected mode only, the entry table also contains an entry for every far function in the executable. This real mode leftover is the reason for the warning in the Windows version 3.1 SDK Programmer's Reference. The far functions are added to the entry table as if they were exported functions, using any unused ordinals. Consider the following .DEF file excerpt:
EXPORT
FirstFunction @3
NextFunction @6
If the application was not built as a protected-mode-only application, the slots in the name table corresponding to ordinal value 1, 2, 4, 5, 7... would be filled with descriptions for functions defined as far. Windows in protected mode does not need to track movable code segments (since the selectors never change), so the linker does not need to add far function descriptors in the entry table. Use the PROTMODE statement in the .DEF file to direct the linker to make a protected-mode-only executable.
Now you see the problem. You would expect GetProcAddress called with an ordinal value of 4, in the preceding example, to return a NULL value indicating the function doesn't exist. Instead, it will return the address of some far function.
If you know that the exported function you are trying to get the address of is in an application or .DLL build for protected mode only, you can ignore the documentation warning.
Dear Dr. GUI:
I'm trying to make a DROPDOWNLIST combo box that drops the list box when the user tabs to the control and presses the DOWN ARROW key. How do I do this?
Dr. GUI replies:
Take a look at the CB_SETEXTENDEDUI combo box message. This is a new message for Windows version 3.1 that allows the application to change the behavior of the combo box to do just what you want.