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.)

USER32 HMODULE USER32 HINSTANCE
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".

NOTEPAD HMODULE HINSTANCE
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.

Comments (15)
  1. Jack Mathews says:

    hinstPrev – In Win16, gave you the previously started HINSTANCE of a program so you could do things like keep one instance running. Since everything ran in one address space, you could dereference this memory fine.

    Since Win32 and Win64 are a convoluted set of anachronisms, they have an hinstPrev in WinMain that is always NULL. It has to be NULL because module base addresses are per-process, so your hinstPrev would both be useless and would be the same as the normal hinstance (because each of the processes would have the same base addresses in their respective address spaces).

    In Win32/64, if you want to know about or interact with other running versions of your process, you can use some form of inter process communication like named Mutexes, file mapping objects, or DDE.

    Next! :)

  2. asdf says:

    I have seen code (including code from Microsoft) that treated the HINSTANCE/HMODULE as the pointer to the dll/exe that was mapped to memory from MapViewOfFile and read the the PE struct through that. Is this a safe thing to do or will it likely be changed in a future windows?

  3. Raymond Chen says:

    asdf: Yes, the base address of the module is the Win32/64 HINSTANCE.

  4. Peter Montgomery says:

    It’s this sort of left over cruft from the 16 bit origins of Windows that makes me think it’s time for Microsoft to write a single 32/64 bit "wrapper" whose job would be to clean up this stuff. I know that MS has written all sorts of cracker macros and wrappers in the past – I’m talking about a single wrapper that cleans up ALL the leftover nonsense in the WinAPI.

    In (some people’s) perfect world, they would boot Hungarian from ALL interfaces, but at the very least lets get rid of the "lp" nonsense that makes NO sense anymore. They could also do things like eliminate the "HPrevInstance" parameter completely since it no longer does anything of use in the 32/64 bit world. The wrapper could present a cleaner, alternative API interface so that Win programmers could write to a new, clean interface that is 100% compatible with the old one. I know it’ll never happen, but I can dream, can’t I?

    Thanks,

    PeterM

    PS – Regarding my desire for losing Hungarian in the interfaces, I feel that if MS wants to code with it internally, then more power to them. However, having the interfaces littered with "lp" and other detritius that doesn’t even make sense anymore proves that Hungarian has a serious flaw. If the reason for Hungarian is to provide clarity to the programmer, then why do programmers today have to learn what a "long pointer" is (so that they understand what "lp" means in Hungarian) just so they know that they can IGNORE IT when they see it in the API?

  5. Ben Hutchings says:

    Peter: It’s called .NET with Windows.Forms. ;-)

  6. CW User says:

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

    YES!

    And can you please put few comments on this article: "Avoiding

    Multiple Instances of an Application" at: http://www.flounder.com/nomultiples.htm or give few pointers in that

    direction.

  7. Anonymous Coward says:

    I am curious what the oldest Windows code that will still compile and run is. Sadly I don’t have the very first Petzold, but I suspect that code may still compile and run.

    Will Windows/386 binaries still run?

  8. Mike Dunn says:

    >Will Windows/386 binaries still run?

    I once came upon a site that had old Windows binaries like Calc archived, and they mostly ran on today’s OSes. *Really* old apps (like for Win v1) had to have a bit flipped in their NE header and their graphics didn’t look 100% right, but they were still usable.

  9. John Elliott says:

    There are various Windows 1.x/2.x sample apps at

    <http://cd.textfiles.com/carousel/030A/&gt;.

    I tried a few on Windows XP. The ones in WIN2APPS.ZIP (for win 2.x) run; the ones in WINAPPS.ZIP (for Win 1.x) don’t (the DOS stub runs rather than the NE program). It doesn’t seem to be the subsystem ID, because SUPRFUSE.EXE has it set to 0 (rather than 2 for Win16) and it still runs.

    Then I tried setting the version number in the NE header to 1.0 and the DOS stub ran instead.

  10. John Elliott says:

    If the URL above fails, try http://cd.textfiles.com/carousel/030A/ – ie, no angle brackets.

    I just tried the same checks on Win98 and WinNT 3.51. On Win98, neither the Win 1.x nor the Win 2.x programs work – you get the DOS stub. On NT, the Win 2.x programs work and the Win 1.x ones say "The system cannot find the file USER".

  11. It used to mean something on 16-bit Windows.

  12. Did you ever get a chance to blankly stare at a screen similar to the above, trying to recollect what

Comments are closed.