TweetFollow Us on Twitter

INIT in C
Volume Number:5
Issue Number:10
Column Tag:C Workshop

Related Info: Control Panel OS Utilities

Writing INITs in THINK C

By J. Peter Hoddie, Palo Alto, CA

Note: Source code files accompanying article are located on MacTech CD-ROM or source 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 <SetUpA4.h>
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 unnecessarily tied up if you load many resources. Furthermore, you have no way of permanently modifying resources if they are all loaded into memory. A reasonable approach would be to keep any small resources that you access regularly in memory all the time, and only access the INIT resource file for rarely used or very large resources.

If you are loading resources at start up time that you intend to use at a later time, make sure that you set the System Heap bit on each one so that they are not loaded into the application heap. Furthermore, remember to call DetachResource on each resource, or they will be purged when your INIT’s resource file is closed by the operating system on return from installation.

Patching Traps - Introduction

Patching traps is a very powerful means of altering system behavior. While it is very powerful, it is also rather easy to make mistakes. A trap patch is installed by means of a call to NSetTrapAddress, usually after saving the old address of that trap by calling NGetTrapAddress. Traps are not usually completely replaced by a patch. Instead, the data passed to the trap is intercepted before the actual trap gets to it, or by modifying the data after the trap is finished, but before it returns to the calling program. In effect, installing a patch trap puts another level of code between the calling program and the various operating system managers. Because trap patches may get called very often (for example a patch on GetNextEvent, common in most screen saver and macro packages) they should execute quickly, so as not to slow the system down.

There is no simple formula for writing a function to behave as a trap patch. You must read the Inside Macintosh description of the trap you wish to patch with great care. There are two different kinds of traps, Operating System and Toolbox. Operating System calls pass their parameters in registers. Thus for Operating System traps, it is necessary to include some assembly language code. Toolbox traps use Pascal calling conventions, so they pass their parameters on the stack. THINK C can mimic Pascal calling conventions, so in most cases no assembly language is needed to patch Toolbox traps.

I know of no definitive way to tell a Toolbox trap from an Operating System trap except to look at its Inside Macintosh definition and see if it gives register usage. If there is no register description it is probably a Toolbox trap. Of course there are several parts Operating System traps that do not provide register information with the description. As an example, the new HFS calls were implemented through a single trap called HFSDispatch. A routine selector value is placed in register D0 to tell the operating system which routine is desired. The List Manager is a somewhat different example. All List Manager calls go through one trap, “Pack0”. A routine selector value is passed as a word on the stack to indicate which routine to actually call. However, the vast majority of calls are fairly straightforward to handle.

Patching a Toolbox Trap

As an example, we will now see how to patch BeginUpdate. This is trap A922, and is part of the Window Manager. Before going into the details of the trap patch code itself, it is necessary to actually install the patch. As mentioned above this is done with NGetTrapAddress and NSetTrapAddress. These are newer version of SetTrapAddress and GetTrapAddress, and should always be used as they allow you to specify whether the trap being patched is an Operating System or a Toolbox trap. The following code fragment, combined with the outline of Main above, shows how to install patch on BeginUpdate.

/* 3 */

#define BeginUpdateTrap 0xA922
long oldBeginUpdate;
main()
{
 /* start code */

 oldBeginUpdate = NGetTrapAddress 
 (BeginUpdateTrap, ToolTrap);
 NSetTrapAddress (newBeginUpdate, 
  BeginUpdateTrap, ToolTrap);

 /* end code */
}

The address of the original trap for BeginUpdate is stored in the global variable oldBeginUpdate, so that we can call the original routine from within the patch. Next, the address of the patch function, newBeginUpdate, is installed as the new address for BeginUpdate. The trap number of BeginUpdate is #defined to 0xA922 and used in the two calls. Some developers pass 0x0922, dropping the A. Inside Macintosh is not explicit about this matter, simply saying to pass the “trap number.” However, in Apple’s own INIT code, they usually pass the trap number with the preceding A.

The calling definition of BeginUpdate, as given in IM I-292, is:

 
BeginUpdate(wind: WindowPtr);

An equivalent function prototype for THINK C, to be included in the header of your program, would look like:

 pascal void newBeginUpdate(WindowPtr w);

The function to patch BeginUpdate would have an outline as follows:

/* 4 */

pascal void newBeginUpdate(w)
WindowPtr w;
{
 SetUpA4();
 /* pre-processing goes here */
 CallPascal(w, OldBeginUpdate);
 /* post-processing goes here */
 RestoreA4();
}

By using the prefix “pascal” we are telling THINK C to use Pascal calling conventions for this procedure. By giving the function the exact same definition as the trap we are patching, we can easily access its parameters. In fact the variable w is the WindowPtr for window that is to be updated. Thus we can access the entire window structure and make any necessary modifications. The calls to SetUpA4 and RestoreA4 should begin and end the trap patch so that all global variables will be available throughout the patch.

THINK C provides the CallPascal function so that we can call Pascal style routines without reverting to assembly language. The CallPascal functions are described in detail in the THINK C User’s Manual on pages 119-120. If the function that you are calling returns a value, other versions of the CallPascal function should be used, as described in the THINK C manual. In this use of CallPascal we are calling the original BeginUpdate routine that existed before we patched the BeginUpdate trap, so we simply pass the Window Pointer w and the address of the routine to CallPascal, and THINK C takes care of the details.

Patching An Operating System Trap

With a few exceptions, operating system traps do not store any parameters on the stack, rather they pass them in registers. Thus operating system trap patches must be handled differently than toolbox traps. Declare the function that will serve as the patch with no return value and no parameters in the header of the program, such as:

 void osTrapPatch(void);

The beginning of the function then includes some assembly code to save the contents of the parameter registers. Calling the original trap must be done with assembly code as well, first restoring the appropriate parameters into the registers, and then calling the original trap. Furthermore, some operating system traps return values in a register (commonly D0). This value must be saved on return from calling the original trap, and placed in the appropriate register immediately before returning from the patch.

SetFileInfo ( trap A00D, IM II-116) takes a parameter block pointer in register A0 and returns an error code as a word in register D0. The following example code assumes that the trap patch has been installed using the same procedure as shown above for BeginUpdate.

/* 5 */

#define SetFileInfoTrap 0xA00D
void newSetFileInfo(void);
long oldSetFileInfo; 

void newSetFileInfo()
{
 HFileParam *PBPtr;
 int saveD0;
 /* save original parameter block pointer */
 asm {
 move.L A0,PBPtr
 }
 /* make global variables available */
 SetUpA4();
 /* do any pre-processing */
 asm {
 /* set up the parameter block pointer */ 
 move.L PBPtr,A0
 /* get address of original trap */
 move.L oldSetFileInfo,A1
 /* call original trap */
 jsr    (A1)
 /* save return value, error code */
 move.W D0,saveD0
 }
 /* do any post-processing */
 /* restore original value of A4 */
 RestoreA4();
 /* set-up return value */
 asm {
 move.W saveD0,D0
 }
}

Coping with the Trap Dispatcher

When any trap is called, the Trap Dispatcher is invoked first. The Trap Dispatcher performs some general house keeping before calling the actual routine. One task it performs that can cause problems, is the storing of the actual trap number that was called in register D1. Apple has never documented this fact, so it may very well change. In self defense you may want to use some assembly language to save the value of D1 on entry to your patch, and restore its value immediately before calling the original trap. In fact, if you want to be very safe, you may want to save and restore other registers as well. The outline of the code for saving and restoring D1 follows.

/* 6 */

void patch()
{
 long saveD1;

 asm {
 move.L D1,saveD1
 }
 SetUpA4();
 /* any pre-processing */
 asm {
 move.L saveD1,D1
 }
 /* call the original trap */
 /* any post-processing */
 RestoreA4();
}

Note that saveD1 is a local variable because calling SetUpA4 could destroy the contents of D1. Before SetUpA4 is called only local variables are accessible. It is not necessary to restore the value of D1 before returning from the patch, although as a defensive measure you may want to.

Memory

When writing a trap patch you should check IM to see if the trap you are patching could move memory. If it can, then you can make all the Memory Manager calls you like. If the trap you are patching does not move memory according to IM then you better avoid calling the Memory Manager or any operating systems routines that could call the Memory Manager. If you move memory on an application when it isn’t expecting it, you will have created a worthless INIT.

If you intend to allocate memory, it is important that you consider whether you need to allocate memory in the system heap or the application heap. Any memory that is allocated in an application’s heap will be lost when that application quits, whereas system memory will remain until the machine is reset. During start up, if you allocate memory it may end up going into a temporary application heap that is set up for INITs. This is fine for temporary work, but not if you need to allocate a block of memory for later use. There has always been a way to allocate memory in the system heap, but it requires the use of assembly language. With THINK C you can create a few simple procedures for allocating memory in the System heap. Apple describes these in Tech Note #219, ”New Memory Manager Glue Routines,” and their definitions are given below. Note that once the handle or pointer is allocated in the system heap, standard Memory Manager calls can be used to manipulate them. Further discussion of this can be found on page 151 of the THINK C User’s Manual and IM II-32, 36.

/* 7 */

Handle NewHandleSys(length)
long length;
{
 asm {
 move.L length,D0
 NewHandleSYS
 move.L A0,D0
 }
}

Ptr NewPtrSys(length)
long length;
{
 asm {
 move.L length,D0
 NewPtr SYS
 move.L A0,D0
 }
}

Calls such as NewHandleSys and NewPtrSys are used for grabbing a block of memory. However, they do not guarantee the memory will actually be available at start up time. In Inside Macintosh IV, a tricky method of expanding the System Heap is described. Fortunately, in Volume V a simpler solution is presented although its description is less than detailed. Simply include a “sysz” resource with ID=0 in your INIT file. This resource consists of a single long word that specifies the number of bytes of memory your INIT needs. The operating system will attempt to grow the system heap by that amount before loading your INIT. If your INIT does not contain a “sysz” resource, the system heap is expanded by 16K. When you return from your INIT’s installation code, the system heap is compacted so that any memory that you didn’t explicitly allocate is lost. All the “sysz” mechanism does is to make sure that a certain amount of memory is available for allocation. It does not actually allocate or reserve the memory for your INIT.

Warning

Tech Note #212, “The Joys of Being 32-Bit Clean,” states “Make sure that any patch you do write is not a tail patch... You need to avoid tail patches because many of Apple’s System Software patches check the return address on the stack to see who called them. If you write a tail patch, you defeat these checks and may cause things to break in strange and less than wonderful ways.” Unfortunately, any trap patch written using the C function technique described here are effectively tail patches. However, I have yet to find a case where this actually causes a problem. Furthermore, in many cases it is impossible to obtain the desired result without writing a tail patch. It seems unlikely that Apple will declare war on tail patches in future Systems, as there are already tons of tail patches out there. However, it is possible that this could cause problems in some rare instances. Unfortunately, the alternative is to write lots more assembly code, and that isn’t a terribly appealing option.

Communicating with the User at Start Up

At installation time, many INITs may want to do something beyond simply installing a trap patch. In some cases, the INIT may need to interact with the user through a dialog. Apple is silent on this issue, although their own AppleShare INIT puts up an interactive dialog at start up. After some experimenting and disassembly I have come up with a way to handle dialogs at start up. The approach is very similar to what you do in a normal application. You must initialize various managers. The problem is that you should not initialize every manager, and there are some other matters to be taken care of. The sequence of calls below will set up the system so that the Dialog Manager may be used.

 InitFonts();
 InitWindows();
 TEInit();
 InitDialogs(0l);

There are still a few of catches though. QuickDraw requires that A5 point to a set of its global variables. As an INIT, we don’t automatically get our own set of QuickDraw globals. Fortunately, the operating system provides us with a set we can use. All you have to do is load A5 from the global variable CurrentA5. This should be done before initializing any of the managers. The following line of assembly does the job.

 asm {
 move.L CurrentA5,A5
 }

There is no need to restore A5 to its original value as the operating system saves all registers before calling your INIT, and restores them on return.

One unfortunate side effect of calling InitWindows is that the entire screen is redrawn, so any icons drawn by INITs loaded before yours are lost. If you only want to draw an INIT at start up to notify the user that your INIT has been installed, get a copy of Paul Mercer’s ShowINIT code (which is available as a THINK C project) and use it. Almost everyone who puts up an icon at start up uses Mercer’s code or a variant of it. ShowINIT makes sure icons don’t overlap and so on. A really useful piece of code.

There are two low memory globals that have not been initialized at the time INITs are loaded which can cause problems. These are DeskHook and DragHook which are both set to -1 when INITs are loaded. If the operating system tries to branch through these vectors, an address error is generated and the system error handler steps in. Simply setting these two globals to zero before initializing any of the managers solves the problem.

 DeskHook = 0l;
 DragHook = 0l;

It is possible, although highly unlikely (since these globals are no longer used under MultiFinder) , that a previous INIT or the operating system could have put a value into DeskHook and/or DragHook. For this reason, a more defensive method of handling these two globals is to test each to see if they contain an odd number. If so, set them to zero; otherwise assume that they are valid and leave them alone.

Uninstalling a Trap Patch

In most cases, once you have installed a trap patch you will not want to uninstall that trap patch. In some cases however, you may want to disable or remove the patch. Unfortunately, another INIT or application may have installed a patch on top of your trap patch. They have probably stored your entry address so that it can be called directly. Furthermore, some applications store the address of certain traps and call them directly to avoid the overhead of the Trap Dispatcher on each call. If you change the address of a trap after any other INIT or application has had the opportunity to execute, there is a chance that the system will be corrupted. In fact some INITs that users have found must load last make this mistake. The solution is not to change the trap address again, but to store a global state variable that indicates whether or not the INIT is active. The global variable can then be checked in each patch, and if the INIT is inactive, you can simply call the original trap without further intervention. There are more sophisticated ways to uninstall a patch which can even release most of the memory claimed by the INIT code, but they involve assembly language and/or self modifying code.

An Example

To illustrate some of the points made in this article, the following sample INIT has been provided. The INIT is a simple virus protection program. It intercepts two resource manager traps, ChangedResource and AddResource. Many viruses depend on these two calls to work. This INIT is not intended to be a full fledged virus protection program, but rather an example of how an INIT works. A list of resource types to watch is contained in the ‘ResT’ 256 resource. Initially this only contains CODE, INIT, and nVIR resources. This may be modified with ResEdit as desired. If the INIT detects a ChangedResource or AddResource call involving any resource type listed in the ‘ResT’ resource, it puts up a dialog warning the user about the pending action. The resource type, id number, name, and file name are displayed. The user may select OK to allow the operation to continue or Cancel to stop it. [This example is NOT meant for full virus protection; so don’t use it as such. This is an EXAMPLE on how to write an INIT; use it that was.-ed]

If the user holds down the mouse button when the INIT is loaded, it will not install itself. This feature can be useful when debugging the INIT and in cases where the user doesn’t want to load an INIT for a particular session. Many INITs currently use this approach. It would be easier for users if all INITs adopted such an approach.

The sample INIT illustrates the basic techniques described here, but does not get into some of the more complex techniques in the interest of brevity and clarity. The INIT should be compiled with the “Precompiled Headers” option on.

Listing:  example.c

#include <SetUpA4.h>

#define nil 0l

#define ChangedResourceTrap 0xA9AA
#define AddResourceTrap 0xA9AB

Handle queryDITL;/* DITL for unknown dialog */
Handle resourceTypes;/* handle of rsrc types to watch */
long oldChangedResource;  /* addr of original ChangedResource*/
long oldAddResource; /* address of original AddResource */
Str255 trash;    /* needed due to Apple bug ? */

pascal void NewChangedResource(Handle h);
pascal void NewAddResource(Handle h, ResType rType, int id, Str255 name);
Boolean userDialog();
Boolean inList(ResType type, Handle list);
Str255 *findFileName(int refNum);
void main(void);

pascal void NewChangedResource(h)
Handle h;
{
 int id;
 ResType type;
 Str255 resName, num;
 Boolean ok;

 SetUpA4();

 ok = true;
 GetResInfo(h, &id, &type, &resName);

 if (inList(type, resourceTypes) )
 ok = userDialog(“\pAttempt to Change Resource”,
 type, id, &resName, HomeResFile(h) );

 if (ok)
 CallPascal(h, oldChangedResource);
 else
 ResErr = resAttrErr;

 RestoreA4();
}

pascal void NewAddResource(h, rType, id, name)
Handle h;
ResType rType;
int id;
Str255 name;
{
 Boolean ok;

 SetUpA4();

 ok = true;

 if (inList(rType, resourceTypes) )
 ok = userDialog(“\pAttempt to Add Resource”,
 rType, id, name, CurResFile() );

 if (ok)
 CallPascal(h, rType, id, name, oldAddResource);
 else
 ResErr = addResFailed;

 RestoreA4();
}

Boolean userDialog(message, type, id, resName, file)
Str255 *message;
ResType type;
int id;
Str255 *resName;
int file;
{
 GrafPtr oldPort;
 Handle tempH;
 DialogPtr d;
 int i;
 Rect r;
 Str255 num;

 GetPort(&oldPort);

 tempH = queryDITL;
 HandToHand(&tempH);
 SetRect(&r, 90, 68, 444, 226);
 d = NewDialog(nil, &r, nil, true, 1, -1, false, nil, tempH);

 SetPort(d);
 MoveTo(20,20);
 DrawString(message);
 MoveTo(20,40);
 DrawString(“\pResource Type: “);
 DrawText(&type, 0, 4);
 MoveTo(20,60);
 DrawString(“\pResource ID: “);
 NumToString((long)id, num);
 DrawString(num);
 MoveTo(20,80);
 DrawString(“\pResource Name: “);
 DrawString(resName);
 MoveTo(20,100);
 DrawString(“\pFile Name: “);
 DrawString( findFileName( file ) );

 do {
 ModalDialog(nil, &i);
 } while ( i != 1 && i !=2 );

 DisposDialog(d);

 SetPort(oldPort);

 return( i == 1);
}

Boolean inList(type, list)
ResType type;
Handle list;
{
 int len;
 ResType *resPtr;

 len = GetHandleSize(list) >> 2;
 resPtr = (ResType *)*list;
 while ( len-- )
 if (*resPtr++ == type)
 return(true);
 return(false);
}

Str255 *findFileName(refNum)
int refNum;
{
 FCBPBRec p;

 p.ioCompletion = 0;
 p.ioRefNum = refNum;
 p.ioFCBIndx = 0;
 p.ioVRefNum = 0;
 p.ioNamePtr = (StringPtr)trash;
 PBGetFCBInfo(&p, false);

 return((Str255 *)p.ioNamePtr);
}

/* This block is called once. It saves the pointer
 to this code resource, and installs the patch. */
void main()
{
 Handle myHandle;
 Ptr myPtr;
 SysEnvRec world;
 Str255 *namePtr;

 asm {
 move.l A0, myPtr
 }
 RememberA0();
 SetUpA4();
 if(!Button()) {
 myHandle = RecoverHandle(myPtr);
 DetachResource(myHandle);

 resourceTypes = GetResource(‘ResT’, 256);
 DetachResource(resourceTypes);

 queryDITL = GetResource(‘DITL’, 256);
 DetachResource(queryDITL);

 oldChangedResource = NGetTrapAddress(ChangedResourceTrap,ToolTrap);
 NSetTrapAddress(NewChangedResource,ChangedResourceTrap,ToolTrap);

 oldAddResource = NGetTrapAddress(AddResourceTrap,ToolTrap);
 NSetTrapAddress(NewAddResource,AddResourceTrap,ToolTrap);
 }
 RestoreA4();
}

Listing: Example.r
resource ‘DITL’ (256, sysheap) {
 { /* [1] */
 {129, 270, 149, 330},
 Button {
 enabled,
 “OK”
 },
 /* [2] */
 {129, 189, 149, 249},
 Button {
 enabled,
 “Cancel”
 }
 }
};

resource ‘sysz’ (0) {
 0x0800
};

resource ‘ResT’ (256, sysheap) {
 { /* [1] */
 ‘nVIR’,
 /* [2] */
 ‘INIT’,
 /* [3] */
 ‘CODE’
 }
};

 
AAPL
$103.40
Apple Inc.
+0.93
MSFT
$44.92
Microsoft Corpora
+0.04
GOOG
$538.81
Google Inc.
+12.27

MacTech Search:
Community Search:

Software Updates via MacUpdate

Apple Security Update 2014-005 - For OS...
Apple Security Update is recommended for all users and improves the security of Mac OS X. For information on the security content of this update, please visit this website: http://support.apple.com/... Read more
EyeTV 3.6.6 - Watch and record TV on you...
EyeTV brings a rich TV experience to your Mac. Watch live TV on your Mac. Pause, rewind, and record whenever you want. EyeTV gives you powerful control over what you watch and how you watch it. Put... Read more
RapidWeaver 6.0 - Create template-based...
RapidWeaver is a next-generation Web design application to help you easily create professional-looking Web sites in minutes. No knowledge of complex code is required, RapidWeaver will take care of... Read more
NTFS 12.0.39 - Provides full read and wr...
Paragon NTFS breaks down the barriers between Windows and OS X. Paragon NTFS effectively solves the communication problems between the Mac system and NTFS, providing full read and write access to... Read more
RestoreMeNot 2.0.3 - Disable window rest...
RestoreMeNot provides a simple way to disable the window restoration for individual applications so that you can fine-tune this behavior to suit your needs. Please note that RestoreMeNot is designed... Read more
Macgo Blu-ray Player 2.10.9.1750 - Blu-r...
Macgo Mac Blu-ray Player can bring you the most unforgettable Blu-ray experience on your Mac. Overview Macgo Mac Blu-ray Player can satisfy just about every need you could possibly have in a Blu-ray... Read more
Apple iOS 8.1 - The latest version of Ap...
The latest version of iOS can be downloaded through iTunes. Apple iOS 8 comes with big updates to apps you use every day, like Messages and Photos. A whole new way to share content with your family.... Read more
TechTool Pro 7.0.5 - Hard drive and syst...
TechTool Pro is now 7, and this is the most advanced version of the acclaimed Macintosh troubleshooting utility created in its 20-year history. Micromat has redeveloped TechTool Pro 7 to be fully 64... Read more
PDFKey Pro 4.0.2 - Edit and print passwo...
PDFKey Pro can unlock PDF documents protected for printing and copying when you've forgotten your password. It can now also protect your PDF files with a password to prevent unauthorized access and/... Read more
Yasu 2.9.1 - System maintenance app; per...
Yasu was originally created with System Administrators who service large groups of workstations in mind, Yasu (Yet Another System Utility) was made to do a specific group of maintenance tasks... Read more

Latest Forum Discussions

See All

Reshape Review
Reshape Review By Jennifer Allen on October 22nd, 2014 Our Rating: :: SIMPLE SHAPESUniversal App - Designed for iPhone and iPad Match triangles together to form cubes in this fast-paced and twitchy game.   | Read more »
Miika (Games)
Miika 1.0.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0.0 (iTunes) Description: Miika is a beautiful 3D puzzle game based on camera perspectives combined with the use of optical illusions. Miika challenges... | Read more »
Infuse Pro (Photography)
Infuse Pro 3.0 Device: iOS Universal Category: Photography Price: $9.99, Version: 3.0 (iTunes) Description: ** All-new version 3 includes fully licensed and certified DTS® and DTS-HD® audio! ** | Read more »
Swap Heroes (Games)
Swap Heroes 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: **Half price for a limited time only** Swap Heroes is a casual turn-based strategy adventure. Form a group of heroes and guide them... | Read more »
Ghost Blade (Games)
Ghost Blade 1.1 Device: iOS Universal Category: Games Price: $4.99, Version: 1.1 (iTunes) Description: Get the most outstanding 3D Action Game from App Store NOW! For those who dare pursue dreams. Masterpiece of Yu Shi Game ---China... | Read more »
Fiete – A Day on a Farm Review
Fiete – A Day on a Farm Review By Amy Solomon on October 21st, 2014 Our Rating: :: A MEMORABLE EXPERIENCEUniversal App - Designed for iPhone and iPad Fiete – A day on a farm in an interactive app for young children full of... | Read more »
Tilt to Live: Gauntlet’s Revenge is Almo...
Tilt to Live: Gauntlet’s Revenge is Almost Here Posted by Jessica Fisher on October 21st, 2014 [ permalink ] One Man Left has announced the official release date of Tilt to Live: Gauntlet’s Re | Read more »
Starfly Review
Starfly Review By Jennifer Allen on October 21st, 2014 Our Rating: :: CHANGE OF PACEiPad Only App - Designed for the iPad Want a slightly different browsing experience? Starfly is a pretty personalized one but it’s also buggy.   | Read more »
Sago Mini Monsters Celebrates Halloween...
Sago Mini Monsters Celebrates Halloween with Fun Costumes and Special Treats. Posted by Jessica Fisher on October 21st, 2014 [ permal | Read more »
The Bot Squad: Puzzle Battles – Tips, Tr...
01000010 01101111 01110100 01110011 00100001: Curious to know what we though about all this robot-on-robot carnage? Then check out our Bot Squad review! The Bot Squad: Puzzle Battles offers nearly 200 stages based on its varied approach to tower... | Read more »

Price Scanner via MacPrices.net

AT&T accepting preorders for new iPads fo...
AT&T Wireless is accepting preorders for the new iPad Air 2 and iPad mini 3, cellular models, for $100 off MSRP with a 2-year service agreement: - 16GB iPad Air 2 WiFi + Cellular: $529.99 - 64GB... Read more
Apple offering refurbished Mac Pros for up to...
The Apple Store is offering Apple Certified Refurbished 2013 Mac Pros for up to $600 off the cost of new models. An Apple one-year warranty is included with each Mac Pro, and shipping is free. The... Read more
Select MacBook Airs $100 off MSRP, free shipp...
B&H Photo has 2014 a couple of MacBook Airs on sale for $100 off MSRP. Shipping is free, and B&H charges NY sales tax only. They also include free copies of Parallels Desktop and LoJack for... Read more
13-inch 2.5GHz MacBook Pro on sale for $100 o...
B&H Photo has the 13″ 2.5GHz MacBook Pro on sale for $999.99 including free shipping plus NY sales tax only. Their price is $100 off MSRP. Read more
Strong iPhone, Mac And App Store Sales Drive...
Apple on Monday announced financial results for its fiscal 2014 fourth quarter ended September 27, 2014. The Company posted quarterly revenue of $42.1 billion and quarterly net profit of $8.5 billion... Read more
Apple Posts How-To For OS X Recovery
OS X 10.7 Lion and later include OS X Recovery. This feature includes all of the tools you need to reinstall OS X, repair your disk, and even restore from a Time Machine backup. OS X Recovery... Read more
Mac OS X Versions (Builds) Supported By Vario...
Apple Support has posted a handy resource explaining which Mac OS X versions (builds) originally shipped with or are available for your computer via retail discs, downloads, or Software Update. Apple... Read more
Deals on 2011 13-inch MacBook Airs, from $649
Daily Steals has the Mid-2011 13″ 1.7GHz i5 MacBook Air (4GB/128GB) available for $699 with a 90 day warranty. The Mid-2011 13″ 1.7GHz i5 MacBook Air (4GB/128GB SSD) is available for $649 at Other... Read more
2013 15-inch 2.0GHz Retina MacBook Pro availa...
B&H Photo has leftover previous-generation 15″ 2.0GHz Retina MacBook Pros now available for $1599 including free shipping plus NY sales tax only. Their price is $400 off original MSRP. B&H... Read more
Updated iPad Prices
We’ve updated our iPad Air Price Tracker and our iPad mini Price Tracker with the latest information on prices and availability from Apple and other resellers, including the new iPad Air 2 and the... Read more

Jobs Board

Senior Event Manager, *Apple* Retail Market...
…This senior level position is responsible for leading and imagining the Apple Retail Team's global event strategy. Delivering an overarching brand story; in-store, Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
Project Manager / Business Analyst, WW *Appl...
…a senior project manager / business analyst to work within our Worldwide Apple Fulfillment Operations and the Business Process Re-engineering team. This role will work Read more
*Apple* Retail - Multiple Positions (US) - A...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
Position Opening at *Apple* - Apple (United...
…customers purchase our products, you're the one who helps them get more out of their new Apple technology. Your day in the Apple Store is filled with a range of Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.