Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
Reinstallable.c
/* |
* Sample reinstallable init |
* v 1.0 |
* |
* August 1993 Greg Robbins |
* |
* This sample INIT patches a trap globally yet is reinstallable: |
* it can be recompiled and run without rebooting. |
* |
* Usually, when an INIT patches a trap, changing the INIT requires |
* reinstalling the INIT in the Extensions folder and rebooting. |
* This INIT demonstrates a technique which allows new INIT code to |
* replace most of the old code without the developer having to reboot. |
* |
* This INIT just patches Standard File (_Pack3) and beeps when |
* a standard file dialog is raised. |
* |
* Demonstration: |
* |
* ¥ put the Reinstallable Init in the Extensions folder and reboot |
* ¥ bring up a standard file dialog; notice the beep |
* ¥ drag the Reinstallable twoBeep INIT file onto the |
* LaunchInits program |
* ¥ bring up a standard file dialog again and notice both beeps |
* |
* The trick is to keep a global handle in the system heap containing |
* the addresses of the routines we want executed for each |
* patched item. The handle to the globals is available via Gestalt. |
* |
* A permanently installed dispatcher routine (part of the init code) |
* is used to get the address of our patch routine and jump to it. |
* |
* The second time the init code runs, it will use the existing global |
* handle, and just update the addresses of our patch routines so the |
* dispatcher code jumps to the correct places. |
* |
* A couple of critical items: |
* - The init loaded at boot time contains the patch dispatchers, so |
* it must never be disposed of. We will simply strand it in the |
* heap when the first reinstallation occurs. |
* - If we change anything about which routines are patched, this invalidates |
* the global handle struct format, so we must reboot |
* - Similarly, if the dispatching code is changed, we must reboot |
* since only the dispatching code from the first time the init is |
* installed can be used. |
* - The dispatch and patch routines destroy registers, so they |
* may not be suitable for OS patches |
* |
* Globals: |
* |
* Since this code only uses a single true global (a handle to our |
* globals in the system heap) I used a simpler method than Think C's |
* A4 globals. The global handle is stored smack in the middle of the |
* GetGlobalsHandleFromStorage routine. Calling GGHFS with a handle |
* stores the handle there; calling GGHFS with no parameter returns |
* the stored handle. (The handle can also be retrieved from |
* Gestalt, but that is too slow a method for use in a trap patch.) |
* |
*/ |
#define SystemSevenOrLater 1 // leave out unnecessary glue code |
#include <Memory.h> |
#include <GestaltEqu.h> |
#include <StandardFile.h> |
#include <Resources.h> |
#include <Events.h> |
#include <SysEqu.h> |
#include <Traps.h> |
#define kOptionKeyCode 0x3A |
#define kReinstInitSelector 'Ri' // something suitably obscure |
// global types |
typedef struct Globals { |
Size globalsSize; // size of this struct |
Handle initHandle; // handle for this INIT if reinstalled |
// traps and hooks |
ProcPtr pack3TrueAddr; // old trap address |
ProcPtr pack3PatchAddr; // my patch routine address |
ProcPtr reinstInitGestaltFunctionAddr; |
// address of my Gestalt function |
// other globals can go here |
} Globals, *GlobalsPtr, **GlobalsHandle; |
// prototypes |
Boolean KeyIsDown(short); |
GlobalsHandle GetGlobalsHandleFromStorage(GlobalsHandle); |
void SetGlobalsHandleToStorage(GlobalsHandle *); |
void ReinstInitGestaltDispatch(void); |
void Pack3PatchDispatch(void); |
pascal OSErr ReinstInitGestaltFunction (OSType, long *); |
void Pack3Patch(void); |
// installation routine |
main() |
{ |
Handle initHandle, previousInitHandle; |
GlobalsHandle gHandle; |
Boolean installedFlag, firstInstallFlag; |
OSErr retCode; |
long gestaltResponse; |
// Think C points A0 at the start of the block; |
// use it to get the handle to the INIT resource |
asm { |
RecoverHandle |
MOVE.L A0, initHandle |
} |
// never hurts to lock the block of code we are in |
HLock(initHandle); |
// since this INIT uses the "lazy" installation method |
// (it's just a detached resource) it's critical that |
// the INIT resource had its system heap bit |
// set. I won't check for that here. |
// flag that this has not yet successfully installed |
installedFlag = false; |
// don't install if the Option key is depressed |
if (KeyIsDown(kOptionKeyCode)) |
goto Bail; |
// check that System 7 is available |
retCode = Gestalt(gestaltSystemVersion, &gestaltResponse); |
if (retCode != noErr || gestaltResponse < 0x0700) |
goto Bail; |
// find if my global struct already exists |
retCode = Gestalt(kReinstInitSelector, &gestaltResponse); |
if (retCode != noErr || gestaltResponse == 0) { |
// this is the first installation |
// install Gestalt selector to return handle of our globals |
retCode = NewGestalt(kReinstInitSelector, |
ReinstInitGestaltDispatch); |
if (retCode != noErr) goto Bail; |
// no existing globals, so allocate them |
gHandle = (GlobalsHandle) NewHandleSys(sizeof(Globals)); |
if (gHandle == nil) goto Bail; |
// record the size of the globals |
(**gHandle).globalsSize = sizeof(Globals); |
// since this is the first install, this INIT's handle |
// must never be disposed to protect our dispatchers |
(**gHandle).initHandle = nil; |
// we are not reinstalling the INIT |
firstInstallFlag = true; |
} |
else { |
// this is a reinstallation |
// get the handle for the existing globals |
gHandle = (GlobalsHandle) gestaltResponse; |
// ensure that the globals block is the current size |
if ((**gHandle).globalsSize != sizeof(Globals)) { |
SetHandleSize((Handle) gHandle, sizeof(Globals)); |
retCode = MemError(); |
if (retCode != noErr) goto Bail; |
} |
// update or reset any globals here |
// get rid of the last incarnation of the INIT code |
// (unless it was the first) |
previousInitHandle = (**gHandle).initHandle; |
if (previousInitHandle != nil) |
DisposeHandle(previousInitHandle); |
// save the handle to this version of the INIT |
(**gHandle).initHandle = initHandle; |
// we are reinstalling the INIT |
firstInstallFlag = false; |
} |
// okay, we're committed to installation |
installedFlag = true; |
// make the INIT float free |
DetachResource(initHandle); |
HLock(initHandle); |
// save the globals handle into the GetGlobalsHandleFromStorage code |
(void) GetGlobalsHandleFromStorage(gHandle); |
// patch _Pack3 trap (Standard File Package) |
if (firstInstallFlag) { |
// save the old trap address, then point the trap at my dispatch |
// function |
(**gHandle).pack3TrueAddr = (ProcPtr) GetToolTrapAddress(_Pack3); |
SetToolTrapAddress((long) Pack3PatchDispatch, _Pack3); |
} |
// store the address of my patch in the globals |
// so the dispatcher knows where to go |
(**gHandle).pack3PatchAddr = Pack3Patch; |
// store the address of my gestalt function in the globals |
(**gHandle).reinstInitGestaltFunctionAddr = ReinstInitGestaltFunction; |
Bail: |
// could do a ShowINIT here based on the installedFlag |
if (!installedFlag) DebugStr("\p Reinstallable INIT installation failed"); |
} |
// Dispatch routines |
// |
// These routines are the targets of any trap and hook addresses we |
// installed at INIT time (the Gestalt function as well) |
// |
// This code cannot change or move until reboot. We will strand the |
// first version of the INIT code that is installed to ensure |
// that these routines remain present. These routines are not |
// used in the reinstalled code (i.e. the routines from the first |
// install will continue to be called) |
// |
// If a lot of trap patches are in place, a more general dispatcher |
// might be appropriate |
void ReinstInitGestaltDispatch() |
{ |
// dispatch to my Gestalt routine |
// destroys D0, A0 |
asm { |
MOVE.L #0, -(SP) ; parameter to GetGlobalsHandle... |
JSR GetGlobalsHandleFromStorage |
ADDQ.L #4, SP ; dispose of parameter |
MOVE.L D0, A0 ; deref the globals handle |
MOVE.L (A0), A0 ; to get the routine address |
MOVE.L Globals.reinstInitGestaltFunctionAddr(A0), A0 |
JMP (A0) ; jump to the routine |
} |
} |
void Pack3PatchDispatch() |
{ |
// dispatch to my Pack3 trap patch |
// destroys A0, D0 |
asm { |
MOVE.L #0, -(SP) |
JSR GetGlobalsHandleFromStorage |
ADDQ.L #4, SP |
MOVE.L D0, A0 |
MOVE.L (A0), A0 |
MOVE.L Globals.pack3PatchAddr(A0), A0; |
JMP (A0) |
} |
} |
// |
// Patch routines and other meat code |
// |
void Pack3Patch() |
{ |
// patch to Standard File |
// just beeps before the dialog is raised |
SysBeep(10); |
asm { |
// get the address of the real standard file trap |
// and jump to it |
MOVE.L #0, -(SP) ; parameter to GetGlobalsHandle... |
JSR GetGlobalsHandleFromStorage |
ADDQ.L #4, SP ; dispose of parameter |
MOVE.L D0, A0 ; deref the globals handle |
MOVE.L (A0), A0 ; to get the real trap address |
MOVE.L Globals.pack3TrueAddr(A0), A0 |
JMP (A0) ; jump to it |
} |
} |
// this Gestalt function returns the address of the globals block |
pascal OSErr ReinstInitGestaltFunction (OSType selector, long * response) |
{ |
*response = (long) GetGlobalsHandleFromStorage(nil); |
return noErr; |
} |
// Keith Rollin's utility for using GetKeys from C |
// returns true if the Key with the given KeyCode is down |
Boolean KeyIsDown(short keyCode) |
{ |
union { |
KeyMap asMap; |
unsigned char asBytes[16]; |
} myMap; |
GetKeys(myMap.asMap); |
return ((myMap.asBytes[keyCode >> 3] >> |
(keyCode & 0x07)) & 1) != 0; |
} |
// GetGlobalsHandleFromStorage lets us magically store a handle |
// in the code's block of memory |
// |
// passing in a value for gHandle saves the value; calling this |
// routine with nil later retrieves the value |
GlobalsHandle GetGlobalsHandleFromStorage(GlobalsHandle gHandle) |
{ |
GlobalsHandle *gHandlePtr; |
// get the address of our handle storage space |
asm { |
BSR @1 ; push the address of the saved space |
DC.L 0 ; the saved handle will go here |
@1 |
MOVE.L (SP)+, gHandlePtr ; pop the address off of the stack |
} |
// use the pointer to store the handle if we were passed a handle |
if (gHandle) *gHandlePtr = gHandle; |
// return the handle that is there now |
return *gHandlePtr; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14