• The Old New Thing

    What was the purpose of the hPrevInstance parameter to WinMain?


    Once your average GUI program picks itself up off the ground, control begins at your WinMain function. The second parameter, hPrevInstance, is always zero in Win32 programs. Certainly it had a meaning at some point?

    Of course it did.

    In 16-bit Windows there was a function called GetInstanceData. This function took an HINSTANCE, a pointer, and a length, and copied memory from that instance into your current instance. (It's sort of the 16-bit equivalent to ReadProcessMemory, with the restriction that the second and third parameters had to be the same.)

    (Since 16-bit Windows had a common address space, the GetInstanceData function was really nothing more than a hmemcpy, and many programs relied on this and just used raw hmemcpy instead of using the documented API. Win16 was actually designed with the possibility of imposing separate address spaces in a future version - observe flags like GMEM_SHARED - but the prevalence of tricks like hmemcpy'ing your previous instance reduced this potential to an unrealized dream.)

    This was the reason for the hPrevInstance parameter to WinMain. If hPrevInstance was non-NULL, then it was the instance handle of a copy of the program that is already running. You can use GetInstanceData to copy data from it, get yourself up off the ground faster. For example, you might want to copy the main window handle out of the previous instance so you could communicate with it.

    Whether hPrevInstance was NULL or not told you whether you were the first copy of the program. Under 16-bit Windows, only the first instance of a program registered its classes; second and subsequent instances continued to use the classes that were registered by the first instance. (Indeed, if they tried, the registration would fail since the class already existed.) Therefore, all 16-bit Windows programs skipped over class registration if hPrevInstance was non-NULL.

    The people who designed Win32 found themselves in a bit of a fix when it came time to port WinMain: What to pass for hPrevInstance? The whole module/instance thing didn't exist in Win32, after all, and separate address spaces meant that programs that skipped over reinitialization in the second instance would no longer work. So Win32 always passes NULL, making all programs believe that they are the first one.

    And amazingly, it actually worked.

  • The Old New Thing

    What is the difference between HINSTANCE and HMODULE?


    They mean the same thing today, but at one time they were quite different.

    It all comes from 16-bit Windows.

    In those days, a "module" represented a file on disk that had been loaded into memory, and the module "handle" was a handle to a data structure that described the parts of the file, where they come from, and where they had been loaded into memory (if at all). On the other hand an "instance" represented a "set of variables".

    One analogy that might (or might not) make sense is that a "module" is like the code for a C++ class - it describes how to construct an object, it implements the methods, it describes how the objects of the class behave. On the other hand, an "instance" is like a C++ object that belongs to that class - it describes the state of a particular instance of that object.

    In C# terms, a "module" is like a "type" and an instance is like an "object". (Except that modules don't have things like "static members", but it was a weak analogy anyway.)

    Here's a diagram. (Recall that we discussed 16-bit HRSRC in a previous entry.)

    code segment descriptor USER32 code... USER32 data...
    code segment descriptor (not in memory)
    code segment descriptor USER32 code...
    data segment descriptor
    HRSRC (not in memory)
    HRSRC USER32 resource...
    HRSRC (not in memory)
    exports table

    In 16-bit Windows, all programs ran in a single address space, and if a DLL was used by five programs, it was loaded only once into memory. In particular, it got only one copy of its data segment. (In C++/C# terms, a DLL is like a "singleton class".)

    That's right, DLLs were system-global rather than per-process. The DLL did not get a separate copy of its data for each process that loaded it. If that was important to your DLL, you had to keep track of it yourself.

    In geek terms, there was only one "instance" of a DLL in the system.

    On the other hand, if you ran two copies of Notepad, each one got its separate set of variables - there were two "instances".

    code segment descriptor NOTEPAD code... NOTEPAD data...
    code segment descriptor (not in memory)
    data segment descriptor HINSTANCE
    HRSRC (not in memory) NOTEPAD data...
    HRSRC NOTEPAD resource...

    Both running copies of Notepad shared the NOTEPAD module (so the code and resources were shared), but each had its own copy of its variables (separate data segment). There were two "instances" of Notepad.

    The "instance" handles in the above diagrams are the data segments.

    Programs are identified by their the instance handle. You can't use the module handle, because the two copies of Notepad have the same module handle (since the same code is running in each). The thing that makes them different is that each has its own set of global variables.

    This is why the WinExec and ShellExecute functions return HINSTANCE: They are holdovers from 16-bit Windows, where HINSTANCEs were the way to identify running programs.

    The method by which code receives its HINSTANCE (i.e., knows where its global variables are) I will leave for a future article. It is somehow related to the now-obsolete MakeProcInstance function.

    When it came to design Win32, the question then arose, "What do we do with HINSTANCE and HMODULE for Win32?" Since programs ran in separate address spaces, you didn't have instance handles visible across process boundaries. So the designers took the only thing they had: The base address of the module. This was analogous to the HMODULE, since the file header describes the contents of the file and its structure. And it was also analogous to the HINSTANCE, since the data was kept in the data segment, which was mapped into the process directly.

    So in Win32, HINSTANCE and HMODULE are both just the base address of the module.

    Tomorrow, I'll talk about that mysterious hinstPrev parameter to WinMain.

  • The Old New Thing

    Do not underestimate the power of the game Deer Hunter


    During the run-up to Windows XP Service Pack 2 Beta in December of last year, there was a list of five bugs that the release management team decided were so critical that they were going to slip the beta until those bugs got fixed.

    The third bug on the list: Deer Hunter 4 won't run.

    Deer Hunter has the power to stop a beta.

  • The Old New Thing

    My first death threat


    Actual feedback submitted to the microsoft.com web site many years ago.

    id: 13726
    Date: 1996-07-29 17:27:41.997
    Name: ***********
    Email: *************
    Area: Windows 95
    PLEASE read this entire email as it is quite serious. I just discovered today that in the Windows 95 operating system, there are no switches, command line options, or any way whatsoever to have the XCOPY command include hidden/system files in it's operations. It is clear that at some point in the development of the Windows 95 product, that somebody made a conscious decision to implement the xcopy command in this manner. It is also clear from looking at the Windows NT XCOPY command that it can be implemented in the manner I describe. Therefore, let me give fair warning. This may not be easy, and I will expect no help from Microsoft in finding out who this person (or persons) was that made this decision, but....eventually I will find out who made this decision, and I will kill them. This is not an idle threat - I will pursue this matter until it is resolved...whoever is responsible for this incredibly ridiculous implementation of what would be an otherwise useful tool will die at my hands, hopefully in a bloody, painful fashion. You will not get away. -J*hn ******

    J*hn, if you're still out there... the switch for copying hidden files on Windows 95 is /H. Same as Windows NT.

    Please don't kill me.

  • The Old New Thing

    The dreaded "main" threading model


    In the absence of an explicit threading model for your COM object, you get the "main" threading model. The "main" threading model is little-known, and that's a good thing. It's a relic from the days before multi-threading.

    The first thread in a process to initialize COM becomes declared the "main" thread. (It might be the first thread to initialize COM in apartment model; I forget.) When a "main" threaded object is created, COM marshals the creation call to the main thread, creates the object, then marshals the result back to the creator's thread. Similarly, when you invoke any method on the object, the call is marshalled to the main thread, invoked, then the result is marshalled back.

    In other words, a "main" threaded object is like an apartment threaded object, with the additional constraint that the only apartment that can use it is the one that the "main" thread belongs to.

    As you can imagine, this is a horrific performance penalty in any multithreaded application, since there is so much marshalling going on. Even worse, it completely bottlenecks the main thread because there are now all these objects that must be serviced on that thread and no other thread.

    Even worse than worse, all this marshalling creates new opportunities for re-entrancy. While waiting for the main thread to do its thing, the calling thread will likely process messages, which means that you can receive a window message at a time when you didn't expect it.

    So why does this awful threading model exist at all?

    For backwards compatibility with COM objects written before multithreaded support was added to COM. Back in those days, there was only one thread, so COM objects could be extremely lazy with their synchronization. In fact, they didn't need any! If you have only one thread, then you certainly don't need to coordinate your actions with other threads because there are none.

    That's also why "main" threading model is the default. Threading models were invented when multithreading support was added to COM. Before then, there were no threads, so no threading models. All old objects therefore didn't specify a threading model in their registration.

    The only reason you should even be aware of this ancient threading model in the first place is that if you forget to specify a threading model in your object registration, you will get the dreaded "main" threading model by default.

    And then you will wonder why your application's performance is horrible, and why you have all these strange re-entrancy problems.

  • The Old New Thing

    Why is the default 8-bit codepage called "ANSI"?


    Reader Ben Hutchings wanted to know why the 8-bit codepage is called "ANSI" when it isn't actually ANSI.

    But instead of saying, "Oh well, some things mortals were never meant to know," he went and dug up the answer himself.

    A quick Google for Windows ANSI misnomer found me exactly what I was looking for [pdf]:

    "The term "ANSI" as used to signify Windows code pages is a historical reference, but is nowadays a misnomer that continues to persist in the Windows community. The source of this comes from the fact that the Windows code page 1252 was originally based on an ANSI draft, which became ISO Standard 8859-1. However, in adding code points to the range reserved for control codes in the ISO standard, the Windows code page 1252 and subsequent Windows code pages originally based on the ISO 8859-x series deviated from ISO. To this day, it is not uncommon to have the development community, both within and outside of Microsoft, confuse the 8859-1 code page with Windows 1252, as well as see "ANSI" or "A" used to signify Windows code page support.
  • The Old New Thing

    When you change the insides, nobody notices


    I find it ironic when people complain that Calc and Notepad haven't changed. In fact, both programs have changed. (Notepad gained some additional menu and status bar options. Calc got a severe workover.)

    I wouldn't be surprised if these are the same people who complain, "Why does Microsoft spend all its effort on making Windows 'look cool'? They should spend all their efforts on making technical improvements and just stop making visual improvements."

    And with Calc, that's exactly what happened: Massive technical improvements. No visual improvement. And nobody noticed. In fact, the complaints just keep coming. "Look at Calc, same as it always was."

    The innards of Calc - the arithmetic engine - was completely thrown away and rewritten from scratch. The standard IEEE floating point library was replaced with an arbitrary-precision arithmetic library. This was done after people kept writing ha-ha articles about how Calc couldn't do decimal arithmetic correctly, that for example computing 10.21 - 10.2 resulted in 0.0100000000000016.

    (These all came from people who didn't understand how computers handle floating point. I have a future entry planned to go into floating point representations in more detail.)

    Today, Calc's internal computations are done with infinite precision for basic operations (addition, subtraction, multiplication, division) and 32 digits of precision for advanced operations (square root, transcendental operators).

    Try it: 1 / 3 * 10000000000 - 3333333333 =. The result is one third exactly. Type 1/x - 3 = and you get zero back. (Of course, if you don't believe that, then repeat the sequence "* 10000000000 - 3333333333 =" until you're bored and notice that the answer always comes back as 0.33333333333333333333333333333333. If it were fixed-precision, then the 3's would eventually stop coming.)

    Thirty-two positions of precision for inexact results not good enough? The Power Calculator PowerToy uses the same arithmetic engine as Calc and lets you crank the precision to an unimaginable 512 digits.

    Anyway, my point is that - whether you like it or not - if you don't change the UI, nobody notices. That's so much effort is spent on new UI.

  • The Old New Thing

    How did those FILE_SHARE_* values come to be?


    Larry Osterman picks up the history ball and describes how those FILE_SHARE_* values came to be.

  • The Old New Thing

    Why did InterlockedIncrement/Decrement only return the sign of the result?


    If you read the fine print of the InterlockedIncrement and InterlockedDecrement functions, you'll see that on Windows NT 3.51 and earlier and on Windows 95, the return value only matches the sign of the result of the increment or decrement. Why is that?

    The 80386 instruction set supports interlocked increment and decrement, but the result of the increment/decrement operation is not returned. Only the flags are updated by the operation. As a result, the only information you get back from the CPU about the result of the operation is whether it was zero, positive, or negative. (Okay, you also get some obscure information like whether there were an even or odd number of 1 bits in the result, but that's hardly useful nowadays.)

    Since those operating systems supported the 80386 processor, their implementations of the InterlockedIncrement and InterlockedDecrement functions were limited by the capabilities of the processor.

    The 80486 introduced the XADD instruction which returns the original value of the operand. With this additional information, it now becomes possible to return the result of the operation exactly.

    Windows NT 4 dropped support for the 80386 processor, requiring a minimum of an 80486, so it could take advantage of this instruction. Windows 98 still had to support the 80386, so it couldn't.

    So how did Windows 98 manage to implement an operation that was not supported by the CPU?

    Windows 98 detected whether you had a CPU that supported the new XADD instruction. If not, then it used an alternate mechanism which was mind-bogglingly slow: It called a driver whenever you wanted to increment or decrement a variable. The driver would then emulate the XADD instruction by disabling interrupts and performing the operation in locked memory. Since Windows 98 was a uniprocessor operating system, it didn't have to worry about a second processor changing the memory at the same time; all it needed to ensure was that the single processor didn't get interrupted while it was performing the "atomic" operation.

  • The Old New Thing

    Chris Pratley's history lesson


    If you haven't read it yet, check out Chris Pratley's voluminous discourse on various aspects of the history of Word. It packs more history into one entry than I do all year.

    And that was a sequel! You can read the first half, too.

    In fact, the good stuff keeps on coming. just read it all.

Page 41 of 50 (499 items) «3940414243»