home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-12-03 | 24.7 KB | 541 lines | [TEXT/R*ch] |
- Answers to Frequently Asked Questions about writing System Extensions
- on the Macintosh Computer. Version 1.0.1 11/94
-
- This document is Copyright © 1994 by Brian Stern.
- I can be contacted by email at <Jaeger@fquest.com> on Internet.
-
- The purpose of this FAQ list is to provide information on the writing
- of system extensions. This arcane art is difficult to learn and the
- existing information on the subject is spread around in various
- places. Hopefully the information here will help new and old INIT
- writers to write better INITs in less time.
-
- The questions in this document are broken into two sections: System
- Extensions and Trap Patches. While most extensions contain trap
- patches these subjects seemed different so I have separated them.
-
- FAQ lists like this one are an integral part of Usenet. When I
- started reading Usenet news I assure you that I knew nothing about
- writing extensions. I learned by posting questions to
- comp.sys.mac.programmer and by reading other's questions and
- responses. Let me take this space to give you a few tips on phrasing
- of questions to newsgroups.
-
- When asking a question try to make the title as descriptive as
- possible. Most people don't have time to read every question posted
- and will ignore posts whose titles are unclear or meaningless. Don't
- post questions titled 'HELP, My program doesn't work', or worse
- 'URGENT NEED HELP with program'. I'm sure it's urgent to you, but
- not to me. Always indicate that you're asking a questing by
- including a (you guessed it) question mark. Consider these titles:
- 'Programmers make big salaries' and 'Programmers make big salaries?'
- Don't waste people's time with the former when you mean the latter.
- Another way to indicate a question is like this: '[Q] Programmers
- make big salaries?'. Try to include one of the words 'who, what,
- when, where, why, or how' in the titles of your questions. Your
- questions are much more likely to be answered by someone who actually
- knows the answer if your title is clear.
-
- The code samples in this document were developed with Think C. You
- may need to make some changes to use them with other development
- systems. I've used inline assembly in many of the code samples. To
- my eye this makes things more clear since it's obvious what's going
- on. Not everyone agrees with this and not all development
- environments provide inline assembly. It's possible to rewrite most
- or all of this code by use of inline functions. If you're writing in
- Pascal or using CodeWarrior you'll have to do it that way.
-
- You'll be able to find information on general questions of Mac
- programming in the FAQ list maintained by Jon Watte at:
- ftp://nada.kth.se/pub/hacks/mac-faq/CSMP_PD_FAQ
-
- If you have questions about writing extensions that aren't addressed
- in this document or any comments about it feel free to send them to
- me. If I know the answer or can find it out it may find its way into
- a later version.
-
- This document may be distributed freely as long as no changes are
- made to it. It may not be distributed in ways in which the user must
- pay for the document, other than reasonable download costs or costs
- for the medium, without the consent of the author.
-
- Thanks to the following people who provided sample code and made
- constructive comments: Pete Gontier, Dair Grant, Chelly Green, Devon
- Hubbard, Peter Lewis, Jim Walker, Jim Wintermyre.
-
- ---------------------------------------------------------------------
- System Extension Writing FAQ Outline:
-
- System Extensions:
-
- [1] What is an INIT, exactly?
- [2] Should I write an INIT?
- [3] Can I write an INIT that makes the application menu into a
- hierarchical menu?
- [4] Do I need to know assembler to write an INIT?
- [5] Can you show me a sample INIT?
- [6] Can I have global variables in my INIT?
- [7] How do I debug an INIT?
- [8] How do I write a Control Panel-INIT combination?
- [9] How do I capture keystrokes?
- [10] How can my extension get time periodically?
- [11] How should an INIT manage memory?
- [12] How do I get my INIT to turn itself off?
- [13] How do I get my INIT to show its icon like all the other cool
- INITs do?
- [14] How do I show a dialog from my INIT?
- [15] How do I maintain compatibility with future systems?
- [16] Any tips for INIT writing?
-
- Trap Patches:
-
- [17] What exactly is a trap patch?
- [18] What's the difference between a head patch and a tail patch?
- [19] How do I patch a trap?
- [20] How do I patch a register-based trap?
- [21] Can you show me a tail patch?
- [22] How do I patch a selector-based trap?
- [23] How do I patch a trap on the PPC?
- [24] Can I write a fat trap?
- [25] Tips?
- [26] What other sources of information are available?
-
- ---------------------------------------------------------------------
-
- [1] What is an INIT, exactly?
-
- An INIT is a type of code resource that is loaded into memory and
- executed during the startup process. They're called INITs because
- they're resources of type 'INIT'. INITs load at the end of the
- startup process, after the hardware checks have been done and the
- system has been started. The span of time during which INITs load
- and execute is known as INIT time. Applications don't load until
- after INIT time and in most cases the first application to load is
- the Finder.
-
- Because they load before all applications, INITs can modify the
- system in a way that affects all applications. They can add new
- functionalities and modify the way in which many processes occur on
- the Mac. Apple often supplies new additions and updates to system
- software as INITs. This includes things such as the Drag-and-Drop
- Manager, Thread Manager, Speech Manager, and Sound Manager 3.0. At
- some point these new features are rolled into a new software release
- and eventually they are included in new ROMs, but they all start life
- as INITs.
-
- Apple recommends that the term 'system extension' be used when
- communicating with non-programmers. This name carries the
- implication that they extend the functionality of the system. You'll
- see the terms 'INIT', 'extension', and 'system extension' used to
- mean the same thing throughout this document.
-
- Extensions are loaded in a defined order. Resources of type 'INIT'
- can be found in files of type 'INIT' (System Extension), 'cdev'
- (Control Panel), 'RDEV' (Chooser Device), 'appe' (application
- extension), and 'scri' (script system extensions). In order to be
- loaded, the INIT resource must be in one of these file types in one
- of several locations in the System Folder.
-
- Under system 7 INITs are loaded first from the Extensions Folder in
- alphabetical order. INITs present in script system extension files
- (filetype 'scri') load before INITs present in system extension files
- (filetype 'INIT'). Next, any INITs in the Control Panels folder are
- loaded in alphabetical order. Any INITs present at the root level of
- the System Folder are loaded after that. This order of loading is
- important if an extension is dependent on the presence of another
- extension. For example, if an INIT resource in an INIT file named
- 'SpeakAll' is dependent on the presence of the 'Speech Manager'
- extension, it either needs to have its name changed to something that
- sorts after 'Speech Manager' or it has to be located in the Control
- Panels folder, since extensions in the Control Panels folder load
- after extensions in the Extensions Folder.
-
- In system 6 the Extensions Folder and Control Panels Folders don't
- exist. Extensions are simply loaded in alphabetical order from the
- root level of the System Folder.
-
- --
- [2] Should I write an INIT?
-
- System extensions are not easy to write. If this is your first
- attempt at Mac programming, the answer to this question is NO. Many
- experienced Mac programmers have never written one and don't intend
- to.
-
- There are a number of other ways to add functionality without writing
- an extension. If you want to add functionality to a single
- application then think about writing an FKEY. These are invoked by
- hitting cmd-shift-number and perform an action at that time only.
- The standard screen shot (cmd-shift-3) is one example.
-
- Another user-invokable means of adding functionality is plug-in
- modules. A number of commercial software packages support plug-ins
- that can add functionality in a straightforward manner. This
- includes PhotoShop, Quark Express, Hypercard and others.
-
- Another means of adding functionality is the use of a background-only
- application, AKA faceless-background application (fba). Fbas are
- applications without a user interface. One type of fba is known as
- an application extension. These are applications whose resource
- files are of type 'appe'. They are placed in the extensions folder
- (or the Startup Items Folder) and are started up after INIT time.
- They can communicate with other processes by Apple Events and by
- Gestalt selectors. If you don't need to patch a trap then an fba may
- be the way to add functionality to the system. Since an fba runs as
- a normal process there are some things that it can do that system
- extensions cannot do (or at least cannot do safely), like launch
- applications and send and receive Apple Events. An INIT resource in
- the resource fork of an application extension in the Extensions
- Folder will load normally at INIT time. See issue 9 of develop and
- the Tech Note PS 2 - Background-Only Applications for more info.
-
- Also consider an application that is started up by being placed in
- the startup items folder. The Screen Saver 'Dark Side of the Mac' is
- written in this way. Screen Savers are traditionally written as
- extensions because extensions can have access to the mouse location
- and all keyboard events. However an intelligently written
- application can do the same and be more compatible.
-
- If none of these things seems like it will work for you and you are
- thinking something like 'I want X to happen every time a resource is
- loaded', or otherwise add to or replace the system's functionality in
- some way then an extension is probably what you need.
-
- Remember that when developing system extensions you will be working
- without a net.
-
- --
- [3] Can I write an INIT that makes the application menu into a
- hierarchical menu?
-
- Every couple of weeks someone posts a message on
- comp.sys.mac.programmer entitled 'Neat idea for an init'. These
- posts go on to describe some non-programmer's idea of a great INIT.
- Invariably these posts fall into two groups: those that have already
- been done, and those that should never be done. Think long and hard
- about the design of an extension before starting to write it.
- Consider your target audience. If it's only yourself then you can do
- what you want. My first couple of extensions were just tests to see
- if I could actually do it. The answer to the above question is:
- Maybe you can but probably you shouldn't.
-
- --
- [4] Do I need to know assembler to write an extension?
-
- Yes. Strictly speaking, the simplest extension that just beeps at
- startup and doesn't hang around past INIT time requires no assembler.
- However, any extension that does anything useful will require some
- assembler in order to patch a trap or install a jGNEFilter. The
- extension can be written mostly in a high level language like C or
- Pascal, but there will be bits and pieces that need to be written in
- assembler to keep the stack happy or to access parameters passed in
- registers. Hopefully there will be enough sample code in this
- document to get you on your way if your assembler is weak.
-
- You will often need to inspect the disassembled source of your
- extension. If your development environment doesn't allow this the
- ResEdit Code Viewer will also allow you to inspect code resources.
- It can be found at ftp://ftp.apple.com/dts/mac/tools/resedit/resedit-
- extensions.hqx.
-
- --
- [5] Can you show me a sample INIT?
-
- Here's the code for the 'Hello World' of INITs.
-
- void main(void)
- {
- SysBeep( 5 );
- }
-
- Not very complicated is it? There are a number of additional things
- that are required to make this work:
-
- 1) Set the project type to code resource.
- 2) Set the resource type to 'INIT'.
- 3) Set the resource ID to something (>= 128), and set the resource
- name if desired.
- 4) Set the resource attributes to 'System Heap'.
- 5) Set the resource attributes to 'Locked'.
- 6) Set the filetype of the built code resource file to 'INIT' with
- any creator signature.
- 7) Set the filename of the built code resource file to 'Hello World'.
- 8) Include a 'sysz' resource indicating a size of 100K.
-
- Let me explain what each of these things does.
-
- 1) Because you're building a standalone code resource your compiler
- needs to know this. This is how the compiler knows to use A4
- addressing, rather than the A5 addressing used in applications.
-
- 2) As mentioned above, extensions are code resources of type 'INIT'.
-
- 3) Like any other resources they have IDs and can have names.
-
- 4) Setting the system heap flag places the code resource in the
- system heap. While not strictly required for this sample, in most
- other extensions we will want the code resource to remain in the
- system heap so this flag must be set. During the process that loads
- and executes INITs a small heap is created. Any memory allocation
- done by the INIT will, by default occur in this heap. Also any
- resources read into memory will, by default, go into this heap. If
- the system heap flag isn't set for the INIT itself then the INIT will
- be loaded into this temporary heap. When the INIT exits the heap is
- disposed and anything in it is lost. If an INIT intends to stay
- around past INIT time then it must have the system heap flag set.
-
- 5) The code resource should never move in memory so the Locked bit
- is set. When a resource with the Locked bit is loaded into memory it
- is loaded as low in the heap as possible. This is what we want since
- the INIT resource will never be unlocked. It is poor design to not
- set the Locked bit and to rely on the INIT to lock itself. In that
- case the INIT will not be loaded low in the system heap. This isn't
- fatal but will lead to fragmentation of the system heap.
-
- Some INITs try to use MoveHHi/HLock to move themselves or associated
- resources high in the system heap. MoveHHi is disabled for the
- system heap. Since the system heap is dynamically sizable the
- concept of the top of the heap is invalid. For this reason MoveHHi
- does nothing when the handle being referred to is within the system
- heap.
-
- 6) The filetype of 'INIT' gives you the generic extension icon, and
- of course allows the extension to load and execute at INIT time.
-
- 7) Set the file name to something like 'Hello World'.
-
- 8) A 'sysz' resource indicates to the INIT-loading code that your
- INIT requires a contiguous block of memory of the indicated size.
- The system heap will be expanded if necessary to make certain that a
- block of the requested size is available. If no 'sysz' recource is
- included the system assumes a block of 16K is sufficient. The format
- of the 'sysz' resource is simply a long integer indicating the
- blocksize. You can create one with ResEdit, or copy one from another
- INIT and edit it with the hex editor. This sample extension requires
- more than 16K because SysBeep allocates a sound channel and because
- you don't know what the size of the 'snd ' resource is for the system
- alert sound. If no 'sysz' resource has been included this INIT
- sometimes doesn't work because there isn't enough room to allocate
- the sound channel.
-
- Once these things have been done you can build the code resource and
- drag its file to the System Folder. The Finder should place it in
- the Extensions Folder for you and it should have the generic
- extension icon. When you restart you will hear the beep when the
- extension is loaded.
-
- Every extension must have a routine called 'main'. When the INIT
- resource is loaded into memory during INIT time the system jumps to
- the beginning of the code resource. The header of the code resource
- will normally contain a branch instruction that branches to the main
- routine. This routine does whatever it needs to do and then returns.
- In this 'Hello World' extension all that the main routine does is
- call SysBeep. In more substantial extensions the main routine will
- do things like patch traps, install gestalt selectors, and load
- resources into memory.
-
- --
- [6] Can I have global variables in my INIT?
-
- Yes you can. The Think environment and the CodeWarrior environment
- use A4-based addressing for global variables in code resources. The
- base address of the extension is found in register A0 on entry to the
- extension, and routines are provided to save this value and to set up
- and restore register A4 when the INIT is entered later. (In fact the
- standard header installed by Think C in code resources loads the
- address of the code resource into A0 by use of pc-relative
- addressing.) See the A4-Addressing section of your development
- environment's manual for more information.
-
- When writing extensions with the MPW compilers you may need to use an
- A5-based method for accessing global variables. See the Apple tech
- note 'StandAlone Code, ad nauseam' (#256) for more information on
- this method. ftp://ftp.apple.com/dts/mac/tn/platforms.tools.pt/pt-
- 35-stand-alone-code.hqx
-
- and
-
- "Another take on Globals in Standalone Code", Keith Rollin
- ftp://ftp.apple.com/dts/mac/docs/develop/develop.12.code/globals-in-
- standalone-code.hqx
-
- Here is some sample code using the Think C routines to illustrate
- this:
-
- /****Main*****************************************************/
- void
- main(void) //Main entry point of the extension
- {
- void *pToMe;
- Boolean initedOK;
-
- pToMe = GetA0(); //Save our address locally
-
- RememberA0(); //Save our base address
- SetUpA4(); //Set up A4-based addressing
-
- initedOK = InitAll(); //Patch traps etc.
- if ( initedOK )
- {
- //Make sure we stay around
- DetachResource( RecoverHandle( pToMe) );
- }
-
- RestoreA4(); //Reset A4 to its value on entry
- }
-
- This code sample also illustrates one method extensions can use to
- remain in memory after INIT time. Since extensions are code
- resources they will be removed from memory when their resource files
- are closed. This happens when the main routine exits. To avoid
- this, DetachResource must be called with the handle to the code
- resource. One common mistake with this is not having the code
- resource marked system heap. If it's not marked system heap it will
- be lost when the temporary heap is destroyed, whether it was detached
- or not. The GetA0 routine used above is defined in the tips section
- farther down in this document. Here are a few other methods for an
- extension to detach itself:
-
- void
- main(void) //Another method
- {
- asm
- {
- RecoverHandle //A0 already holdss our address
- move.L A0, -(A7) //DetachResource is stack-based
- DetachResource //so push A0 onto the stack
- }
- }
-
- void
- main(void) //This method uses no assembler
- {
- DetachResource( Get1Resource( 'INIT', kOurResID ) );
- }
-
- --
- [7] How do I debug an INIT?
-
- Debugging is generally the most time consuming, difficult, and all-
- around pain-in-the-neck part of producing good code. That goes
- double for INITs. Unfortunately many parts of INITs must be debugged
- with low level debuggers. The low level debuggers that I know about
- are MacsBug, TMON, and Jasik's Debugger. MacsBug is free and is
- available at ftp://ftp.apple.com/dts/mac/tools/macsbug/macsbug-6-
- 5d6.hqx. The two other low level debuggers are commercialware.
- Jasik's debugger has the ability to do source level debugging of code
- resources, like system extensions.
-
- In most cases, your low level debugger of choice will load before
- INIT time so it can be used to debug initialization of INITs. One
- exception to this is Jasik's Debugger, which loads in two parts. One
- of these is an extension that must load before your extension.
-
- The debugger can be invoked by calling the Debugger() or DebugStr()
- traps. Placing these trap calls in your code will drop you into the
- low level debugger so that you can step through your code and inspect
- registers or memory as needed.
-
- One useful technique is to take advantage of Macsbug's ability to
- process commands after a semicolon in a DebugStr call. The following
- function can display information that you would otherwise have to
- hunt down using hex offsets:
-
- pascal void SomeFunction (arguments)
- {
- // ...
-
- asm { MOVE.L fooP, A0 } // fooP can be any type
- asm { MOVE.L sizeof(*fooP), D0 }
- DebugStr ("\p ; dm rA0 rD0"); // dump (*fooP) in hex
-
- // ...
- }
-
- If you haven't placed Debugger() calls in your code then you will
- have to set a breakpoint in order to step through any trap patches or
- other parts of your extension. Here are a couple of methods to do
- that. If you have patched a trap, say GetResource, if you drop into
- MacsBug and type 'il GetResource' MacsBug will begin to disassemble
- at your patch. You can then set a breakpoint by typing 'br
- GetResource' or br theaddress' where theaddress is the address in hex
- of your patch or some part of it. Type 'brc' when you want to clear
- all breakpoints. Using the A-trap commands will also work. 'atb
- GetResource' will set a break and 'atc' will clear all A-trap
- breakpoints.
-
- If you want to set a breakpoint in some other part of your extension,
- say in a jGNEFilter, then you need another way of finding its
- location in memory. In MacsBug type 'hx syszone^' or just 'hx' to
- set the current heap to the system heap (where your extension is
- located). Type 'br ' (don't hit return yet) and then type cmd-D to
- view the names of all the routines in the system heap that were
- compiled with MacsBug names turned on (like yours, right?) Scroll
- down until you find the name of your routine. Hit enter and the
- command line should look like 'br yourRoutineName'. Hit enter again
- and the breakpoint will be set. You can also simply type 'br
- yourRoutineName' and MacsBug will find it for you. The zone must be
- set to the zone containing the routine that you want to break on for
- MacsBug to find it, so make sure to set the zone to the system zone.
-
- Identifying your extension in the system heap is dependent on
- compiling it with the MacsBug symbols option turned on. This inserts
- Ascii versions of the names of each function in the compiled code in
- such a way the MacsBug , and other debuggers can find these names.
- Jasik's debugger uses a different method for identifying your code
- that is based on SYM files, which are generated by your compiler.
- The SYM files allows Jasik's debugger to do source level debugging of
- INITs and other code resources.
-
- I have used a two-project method, when developing INITs and other
- code resources, that helps to cut down debugging time. Any extension
- consists of essentially two parts: the initialization portion and the
- implementation portion. The initialization portion detaches the
- resource as described above, shows the icon, and often patches traps.
- The implementation portion contains the actual trap patches. It is
- quite possible, and desirable, to test the implementation portion in
- the context of an application, rather than as an INIT. To accomplish
- this your project consists of three parts, which for a simple case
- would be three files.
-
- TesterApp Project:
- SetUpApplication.c Simple application shell that sets up
- the trap patches and provides a
- mechanism to call the traps
- TrapPatches.c Code for the trap patches
-
- INIT Project:
- SetUpINIt.c Standard INIT setup; contains main
- entry point; patches all necessary
- traps
- TrapPatches.c Code for the trap patches; same file as
- in the TesterApp Project
-
- The mechanism for calling the traps in SetUpApplication.c is usually
- a menu item. In some cases it might be a dialog with several
- buttons, each of which calls a particular trap. It isn't always
- necessary to patch traps in your TesterApplication. Simply calling
- the routines that implement the guts of the trap patches will often
- be just as good. Having two projects, one that builds the tester
- application and the other that builds the extension, allows you to
- save time debugging and to be able to build the extension at any
- time.
-
- Writing and testing extensions involves multiple rounds of
- 'compiling-installing the INIT-rebooting-Stepping through the INIT in
- a low level debugger'. Use of the two-project method will cut down
- this time.
-
- --
-
- --------------------comp.sys.mac.programmer.info---------------------
- comp.sys.mac.programmer.info is primarily for distributing FAQs,
- tutorials, news, and similar documents related to programming the
- Macintosh. To post, email csmp_info@xplain.com
- -----------------------about MacTech Magazine----------------------
- PO Box 250055, Los Angeles, CA 90025, 310-575-4343, Fax:310-575-0925
- For more info, anonymous ftp to ftp.netcom.com and cd to /pub/xplain
- or email to the following @xplain.com : custservice, editorial,
- adsales, marketing, accounting, pressreleases, progchallenge,
- publisher, info
-