Holy cow, I wrote a book!
Once your average GUI program picks itself up off the ground,
control begins at your
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.
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
In C# terms, a "module" is like a "type" and an instance is like an
(Except that modules don't have things like "static members",
but it was a weak analogy anyway.)
Here's a diagram.
we discussed 16-bit HRSRC in a previous entry.)
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
(In C++/C# terms, a DLL is like a "singleton class".)
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".
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
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
Tomorrow, I'll talk about that mysterious hinstPrev parameter to WinMain.