INIT in C
Volume Number: 5
Issue Number: 10
Column Tag: C Workshop
Writing INITs in THINK C 
By J. Peter Hoddie, Palo Alto, CA
Note: Source code files accompanying article are located on MacTech CD-ROM orsource code disks.
Writing INITs Using THINK C: Introduction
THINK C provides a convenient and powerful environment for creating INITs.
INITs are code resources, stored in the System Folder, that are automatically run at
start up. THINK C allows the programmer to easily access global variables from an
INIT, as well as providing an inline assembler to deal with situations when C code alone
may not be sufficient. This article will present a simple, but complete, INIT created
with THINK C. In addition, the details of how THINK C handles code resources will be
presented along with some general rules for writing INITs. While the examples
presented here are written in THINK C, many of the techniques, tips, and discussions
apply to writing INITs in any language.
Building an INIT in THINK C is the same as building any other type of code
resource such as CDEF, LDEF, WDEF, or FKEY. You use “Set Project Type...” to
indicate that the project is a Code Resource, and you set the “File Type” field to INIT.
To be safe, you should use the Attributes field to set the lock bit and reset the purge bit
so that your INIT code won’t get moved or purged unexpectedly. Set the System Heap
bit to ensue that the INIT is loaded into the System Heap, not the Application Heap. If
you do not select the “Custom Headers” option THINK C installs some header code that
loads A0 with the address of your code resource and then branches to your “main”
routine. For the purposes of this article, the default THINK C header will suffice.
THINK C comes with a set of macros contained in the file “SetUpA4.h” which take
care of the necessary details so that you can access global variables from within your
INIT. THINK C maintains your global variables as part of the code resource that makes
up the INIT, placing the global variables at the end of the code resource. Since THINK C
generates code that accesses these variables with a word as an offset, the maximum
combined length of the code and all global variables is 32K. The “SetUpA4.h” file
generates a small portion of code, so it should only be included once.
INIT Installation
Most INITs consist of two main parts. The first is the installation code which is
executed at start up time. This code usually patches a few traps and returns control to
the system. The second portion of the INIT is the code that will be executed when the
patched trap is called. Some INITs do not patch any traps but may instead install VBL
tasks or load various drivers.
The Main procedure in your THINK C project is the installation code. It is called
immediately after your INIT is loaded. Main must perform several tasks so that the
trapped patches will function properly. The first task is to remember the address that
the INIT resource was loaded at so that global variables may be accessed. The following
code fragment illustrates the essential elements.
/* 1 */
#include
Handle myINITHandle
main()
{
Ptr myINITPtr;
asm {
move.L A0,myINITPtr;
}
RememberA0();
SetUpA4();
myINITHandle = RecoverHandle( myINITPtr );
DetachResource( myINITHandle);
...
RestoreA4();
}
The very first line of code in Main stores the contents of register A0 in the local
variable myINITPtr. This value must be stored in a local variable, as global variables
are not yet available at this point in the code. Once this is done, the macros
RememberA0 and SetUpA4 from the “SetUpA4.h” file are called. RememberA0 causes
the current value of A0 to be stored away (in a memory location reserved in the code
generated “SetUpA4.h”), and SetUpA4 causes A4 to be loaded with the address of our
INIT code resource. THINK C generates global variable references relative to A4 for
code resources, so global variables may be referenced after the call to SetUpA4. The
next step is to use the address of the INIT code resource that we saved (which is a
separate copy from the one saved by the call to RememberA0) to recover the handle to
our code resource and save that away in the global variable myINITHandle.
INITs are discussed in Inside Macintosh IV-256 which states that on entry to
your INIT code the operating system “saves all registers and places the handle to your
‘INIT’ resource in register A0.” This can lead to some confusion because the default
THINK C header for a code resource places a pointer to your code resource in register
A0. This destroys the handle to your INIT resource that the operating system put into
A0, thus the call to RecoverHandle is required to get the handle of the INIT code
resource.
The next line performs a DetachResource on the INIT code. This is necessary so
that your INIT will survive beyond system start up. When you return from your INIT
to the operating system, the resource file of your INIT is closed causing the INIT code
resource to be purged from memory. Calling DetachResource forces the Resource
Manager to forget that it ever knew about the INIT resource, so it is not purged.
Following the DetachResource call is any installation code for the INIT. This is
where trap patches (discussed below) are placed. Ending the installation procedure is
a call to RestoreA4 which simply restores the value to A4 that was present when
SetUpA4 was called. RememberA0 should only be called once in your entire INIT, at
the start of the installation procedure. SetUpA4 and RestoreA4 should always be called
in pairs, in the same procedure. SetUpA4 stores the old value of A4 on the stack so that
if you call RestoreA4 in a function other than the one that contained the SetUpA4 you
will likely die a quick and violent death.
If you did not set the Lock bit in the “Set Project Type...” dialog, you should lock
the INIT with a call such as
HLock(myINITHandle);
after the RecoverHandle call in your Main procedure. If you don’t make sure that your
INIT is locked and unpurgeable, it could very well be unexpectedly moved or removed
by the Memory Manager.
Accessing the INIT File After Start Up
As mentioned above, at start up time, after your INIT returns from its Main
procedure back to the operating system, the INIT’s resource files are closed.
Unfortunately, often times your INIT may need to access its resource or data fork at
some later time. As good Macintosh developers, we all want our users to be able to
rename their INITs. So it now becomes necessary to hunt down the name of our INIT
file during start up and store it away to later to access the file. This can be
accomplished with the following function which stores the name of the INIT file in the
variable “name” passed to it. The passed variable should probably be a global
variable.
/* 2 */
findMyName(name)
Str255 name;
{
FCBPBRec p;
p.ioCompletion = 0;
p.ioRefNum = CurResFile();
p.ioVRefNum = 0;
/* next line is required, but why? */
p.ioNamePtr = (StringPtr)name;
PBGetFCBInfo(&p, false);
BlockMove(p.ioNamePtr, &name,
1 + *(char *)(p.ioNamePtr) );
}
The above function should be called very early in your installation code as it
relies on the fact the the current resource files is the INIT. This will not be the case
if you have opened any other resource files. If you are going to the trouble of saving
the name of your INIT file you might also consider saving the current volume reference
number. Through System 6, all INITs are stored in the System folder. The volume
reference number of the System folder is easily found using the SysEnvirons call
described in IM V-5. It is possible in the future that Apple will create an “INIT
Folder” or that a developer will release a product that allows users to keep INITs in a
separate folder. Thus in self defense, it is probably a good idea to store the current
volume reference number at the same time you save the file name of your INIT if you
plan to access your INIT file again. The current volume reference number can be
found using the call GetVol as described In IM II-89.
A secondary method of accessing resources in your INIT file is to load them into
memory at start up time, and perform a DetachResource on each so they will not be
purged. This solution is fine for a few small resources. However, memory will be