home *** CD-ROM | disk | FTP | other *** search
-
- ΓòÉΓòÉΓòÉ 1. Title Page and Book Information ΓòÉΓòÉΓòÉ
-
-
- Designing OS/2 Applications
-
- By David E. Reich
-
- Foreword by Lee Reiswig, President, IBM Personal Software Products Division
-
-
- Written by an IBM insider, this practical guide emphasizes the important design
- issues of software development for OS/2 applications. It shows you how to
- squeeze the most from 32-bit systems for dramatic increases in speed and power.
- Using examples and detailed scenarios, this book offers extensive coverage of
- the functions that are available in the system and shows you exactly how to
- exploit those functions to create the next generation of applications.
-
- About The Author
-
- David E. Reich has been a member of the OS/2 Development Team since 1987. In
- that time, he has worked on many parts of the operating system, supported
- customers and application developers and has traveled around the world teaching
- classes and giving seminars on OS/2. Mr. Reich has a Master's in Computer
- Science from the State University of New York at Albany.
-
-
- Section 1 discusses the benefits of OS/2 from the user's as well as the
- programmer's perspective.
-
- Section 2 discusses application design issues and covers details of OS/2
- internals including the OS/2 Kernel, Presentation Manager and the Workplace
- Shell.
-
- Section 3 covers the basic building blocks of OS/2 applications.
-
- Section 4 introduces details of the OS/2 development environment, application
- development tools, writing the user interface and building core functions as
- well as explores the advanced functions and non-English language support.
-
- Section 5 covers performance tuning issues for memory management, threads and
- window tuning.
-
- Section 6 discusses aspects of code testing, possible pitfalls of concurrent
- programming and how to contain code changes during testing.
-
- Section 7 talks about the importance of the installation program as well as the
- design implications and code reuse of such programs.
-
-
- ΓòÉΓòÉΓòÉ 2. Copyright and Trademarks ΓòÉΓòÉΓòÉ
-
-
- Designing OS/2 Applications
-
- By David E. Reich
-
- Foreword by Lee Reiswig, President, IBM Personal Software Products Division
-
-
- Copyright (C) 1993 by John Wiley & Sons, Inc.
-
-
- Library of Congress Cataloging-in-Publication Data
-
- Reich, David E.
- Designing OS/2 Applications / David E. Reich
- p. cm.
-
- Includes bibliographical references and index.
- ISBN 0-471-58889-X (pbk.)
- 1. Operating Systems (Computers) 2. OS/2 (Conputer file)
- 1. Title
- QA76.76.063R44 1993
-
- 055.4'469--dc20
-
- Excerpted with permission of John Wiley & Sons, Inc. and David E. Reich.
-
- IBM is a registered trademark of International Business Machines Corporation..
-
- Operating System/2, OS/2, Presentation Manager, Workplace Shell, Information
- Presentation Facility, Systems Application Architecture (SAA), Common User
- Access (CUA), PS/2 are trademarks or registered trademarks of International
- Business Machines Corporation.
-
- Intel is a registered trademark of Intel Corporation.
-
- Lotus is a trademark of Lotus Development Corporation.
-
- Aldus is a trademark of Aldus Corporation.
-
- PageMaker is a registered trademark of Aldus Corporation.
-
- CASE:PM is a trademark of Caseworks, Inc.
-
- Smalltalk is a registered trademark of Digitalk Inc.
-
- GPF is a trademark of Microformatic Inc.
-
- Microsoft is a registered trademark of Microsoft Corporation.
-
-
- ΓòÉΓòÉΓòÉ 3. How to Order ΓòÉΓòÉΓòÉ
-
- Designing OS/2 Applications
-
- By David E. Reich
-
- Foreword by Lee Reiswig, President, IBM Personal Software Products Division
-
-
- TO ORDER-
-
- Call 1-800-CALL-WILEY or
-
- Send $34.95 (prepaid orders are sent postage paid in the U.S.A.) check or money
- order to:
-
- John Wiley and Sons, Inc
- 605 Third Avenue
- New York, NY 10158
- Attn: S. Straub, 10th Floor
-
- Outside of the US, please write to the above
- address for information.
-
- Be sure to reference, "Designing OS/2 Applications" and ISBN number
- 0-471-58889-X.
-
- "Designing OS/2 Applications", can also be obtained via IBM Puborder number
- SC28-2701 or from the I.V. League.
-
- To receive a free copy of the PSP Product Catalog, or to order any of the books
- reviewed here, please call 1-800-342-6672. All orders are shipped next day
- air.
-
- 300 Pages
-
-
- ΓòÉΓòÉΓòÉ 4. Foreword ΓòÉΓòÉΓòÉ
-
-
- Designing OS/2 Applications
-
- By David E. Reich
-
- Foreword by Lee Reiswig, President, IBM Personal Software Products Division
-
-
- In April 1992, IBM opened a whole new world of computing with the introduction
- of 32-bit OS/2. OS/2 is the integrating platform. For the first time, users can
- run virtually any application written for DOS, Windows or OS/2 and run them
- simultaneously. Moreover, OS/2 provides the facilities for you, the application
- designers and developers, to create faster, more powerful, more flexible
- applications than ever before.
-
- All this power lies waiting for you to utilize in your full-fledged 32-bit
- applications. You are free of 64k segments and 640k boundaries, memory
- extenders, and other add-ons loaded on top of each other. You want memory? You
- got it. Up to 512 megabytes per application. You have the facilities of
- powerful graphical user and programming interfaces at your fingertips. One of
- the less tangible yet most powerful features of OS/2 is its preemptive,
- prioritized multithreading model. Other systems simulate multitaking through
- simple time slicing. OS/2 brings program execution to the thread, allowing you
- to write different parts of your application to run in parallel, creating
- better throughput for the application and higher productivity for the end user
- through more efficient use of the computer hardware. These, along with the
- other technological advances of OS/2, are wrapped in a state-of-the-art,
- object-oriented user shell.
-
- In working with developers all over the world, David has heard the questions,
- seen the hurdles, and helped overcome them. Through his work in the OS/2
- development organization as well as well as his work with developers, he has
- gained insight into what is needed to design and develop applications that
- exploit all that OS/2 has to offer.
-
- This book contains that insight and answers the questions of how to structure
- and design your applications to take the best advantage of OS/2's features. You
- don't have to use everything that is in OS/2 just because it is there.
- Throughout this book you will be shown which functions are more appropriate in
- different situations, and once you decide on a task, you are shown how to
- optimize your usage of system functions and resources.
-
- We at IBM are proud of our technical accomplishments with OS/2 and know that we
- need you and your applications to make our system really shine. David has the
- ability to explain the most technically involved topics in ways that everyone
- can understand. Use his experience, and this book, as you advisor in creating
- the 32-bit applications of the future.
-
- Lee Reiswig
-
- President, IBM Personal Software Products Division
-
-
- ΓòÉΓòÉΓòÉ 5. Table of Contents ΓòÉΓòÉΓòÉ
-
- This section contains the Table of Contents for, "Designing OS/2 Applications".
-
- Excerpts from Chapter 10 and 17 follow.
-
-
- Introduction
- About this book
- Overall Design
- Features
- Maintainability
- Testing and Code Change
- Install Programs
-
-
- Section I Why OS/2?
-
- Chapter 1 OS/2 As An End-user Platform
- Multiprocessing
- Using Several Applications at Once
- Sharing and Communicating Data
- MultiThreading
- Dividing Applications into Parallel Pieces
- Increased User Productivity
- Better Performance
- 32 bit Memory Management
- Flat Memory Model
- Independence from Physical Memory
- Up to 512 Meg per Program
- Paging vs Swapping
- Better Application Performance
- Better System Performance
- Intuitive User Interface
- User Can View Many Applications at Once
- Workplace Shell
- Consistent Behavior
- Lower Learning Curve
- Object oriented
- Works the Way People Work
- Contextual Help
- Learning is Easy
- Device Independence
- Generic Output Space - OS/2 manages resolution mapping
- New devices only need new drivers. Not new applications
- IBM Compatibility to Future Releases
- Summary
-
- Chapter 2 Why Program for OS/2?
- Powerful and flexible API
- Function Call Interface to all System Services
- Consistent, Easy To Code
- Portability, Flexibility, Expandability
- Easy to Modularize and Maintain
- System Coding Conventions
- Fast Prototyping
- Summary
-
- Chapter 3 OS/2 As A Development Platform
- Multitasking for development
- Debugger support
- Crash Protection
- Summary
-
-
- Section II Overall Application Design
-
- Chapter 4 Good Programs Have Good Up-Front Design
- Understanding the Target Environment
- 60% Design, 30% Code, 10% Test
- 60% Design
- 30% Code
- 10% Test
- Summary
-
- Chapter 5 OS/2 Kernel Architecture
- Overview of the Kernel
- Structure
- Scheduler/Dispatcher
- Loader
- System Services Flow
- Digging Deeper
- Protection Mechanism
- Process/Thread Model
- Priority
- Thread Management
- Memory Management
- File System
- Device Drivers
- DLL Mechanism
- Base Subsystems
- Summary
-
- Chapter 6 Presentation Manager, Graphics and the User Interface
- Presenting Data
- Presentation and Translation Flow
- Device Context
- Presentation Space
- Tracing a Drawing Call
- Graphics Engine
- Presentation Drivers
- Brute Force vs. Full-Function Drivers
- Printer Drivers
- Screen Drivers
- Window Manager
- Input
- Workplace Shell
- Summary
-
- Chapter 7 Features for your application
- What is the main function or objective of the application
- How Will the Application be Presented?
- Presentation Manager Graphics/Text
- AVIO windows
- Text-Based
- Workplace Shell Objects and Templates
- Choosing features to include
- Data communications with other programs (open application)
- Dynamic Data Exchange
- Clipboard
- Pipes and Queues
- Data Interchange Formats and Filters
- REXX hooks
- Printing
- Fonts and WYSIWYG
- Summary
-
- Chapter 8 Application Structure
- Isolate From Underlying Hardware
- Use OS/2's Device Independence
- Stick with Portable Languages and Tools
- Modular design and Threads
- Using Multiple Processes
- Using DLLs and Code Sharing
- Resource Sharing and Synchronization
- Keep Upgradability, Portability and Servicability in Mind
- Summary
-
-
- Section III Use building Blocks or your app will crumble
-
- Chapter 9 Block design and architecture
- Taking the black box approach
- Letting the operating system do it for you
- Summary
-
- Chapter 10 Designing the User Interface
- An excerpt for this chapter can be found in the sections below.
- Window Design
- CUA
- Presenting Data
- Window Controls
- Dialogs
- Application Defaults
- CASE Tools for PM
- WorkPlace Shell
- Objects and Templates
- Step 1---Simple Drag and Drop
- Step 2---Associations
- Step 3---Writing an Object
- Application Launching
- Single-Process Model Considerations
- Subset Function
- Object-Oriented Printing
- To Write an Object or Not to Write an Object?
- Summary
-
- Chapter 11 Where's the beef?
- Designing the core
- Modularizing the "worker" code
- Memory management
- Device drivers and device independence
- File layout
- HPFS and FAT features
- EXEs and DLLs
- INI files
- Multiprocess (multiprogram) applications
- Summary
-
-
- Section IV Making it happen
-
- Chapter 12 The Development Environment
- Source Code Control
- Platform
- Function
- Problem tracking
- Tree Structures
- Tools
- Summary
-
- Chapter 13 Prototyping the user interface
- Paint your windows
- Multithreading Considerations
- Handling long jobs
- Using PM's thread handling
- Object Window Messages
- Keep the user interface thread responsive
- Summary
-
- Chapter 14 Building the core function
- Memory manager package
- Memory suballocation
- 16 and 32 bit techniques and coexistence
- Multithreading
- Synchronizing threads
- Semaphores
- Critical Sections
- Should you even use another thread?
- IPC
- Queues
- Pipes
- Shared Memory
- File functions
- Internal File Formats
- Taking the LAN into account
- Using File System Structures
- Summary
-
- Chapter 15 Using Advanced Functions
- Clipboard
- Text Data
- Metafile Data
- Application Defined Data
- Dynamic Data Exchange (DDE)
- Printing
- Print Destinations
- Fonts
- Help Facilities
- Summary
-
- Chapter 16 Non English language support
- Flexibility in your Code
- Message Files
- Windows and Dialogs
- Stringtables
- Building Windows on the Fly
- Using Lengths and Proportions
- Structuring your development
- Summary
-
-
- Section V Performance
-
- Chapter 17 Base tuning
- Excerpt of this chapter can be found below in this document.
- Memory tuning
- Code and Data Working Sets
- Locality of Reference and Data Positioning
- Dynamic Link Considerations
- Messages and other Resources
- Throughput using threads
- Thread priority
- Packing the Executable
- DLL Placement
- Summary
-
- Chapter 18 Visual Tuning
- Window Tuning Tips
- Keeping Windows Around
- Fill Windows Invisibly
- Letting PM Manage Work
- Your own Multipurpose Classes
- Summary
-
-
- Section VI Testing and Code Change
-
- Chapter 19 Testing Methodology
- Scaffolding
- Testing units
- Testing Modules and Components
- Testing the System
- Summary
-
- Chapter 20 Code Change
- Code Change According to Design
- When There Are Too Many for Comfort
- Summary
-
-
- Section VII Installation Programs
-
- Chapter 21 Designing the Installation Program
- Just another (small) application
- User Interface
- Multithreading
- Multiple Installations
- Media Considerations
- Packing your Code
- "Series" Applications
- Summary
-
-
- Summary and Conclusion
-
-
- ΓòÉΓòÉΓòÉ 6. Chapter 10 Excerpt ΓòÉΓòÉΓòÉ
-
- This section contains an excerpt from Chapter 10. The section included is
- titled, "Workplace Shell".
-
- Expand this chapter using the "+" icons to read the section on the Workplace
- Shell.
-
-
- ΓòÉΓòÉΓòÉ 6.1. Chapter 10 Designing the User Interface ΓòÉΓòÉΓòÉ
-
- Expand this chapter using the "+" icons to read the section on the Workplace
- Shell.
-
-
- ΓòÉΓòÉΓòÉ 6.1.1. WorkPlace Shell ΓòÉΓòÉΓòÉ
-
- It is not only important to have a smooth flow within the windows of your
- application; it is equally important to make your application look like part of
- the operating system. This goes along with the integrated environment concept.
- The Workplace Shell (WPS) is an application launcher, as is any shell.
- However, the WPS provides much more to applications.
-
- It is just as important to use only the features of the shell that you need as
- it is to not overpower your application's function with fancy controls and
- unnecessary menus. The WPS is very powerful. You can create objects, new
- classes, and templates and implement many functions not previously available in
- the PC arena. You need to understand the architecture of the WPS and not overdo
- it with function; otherwise, just as if you had too many cluttered dialog
- windows, your application can appear clunky and hard to use.
-
- The main purpose of the shell is to start programs and manipulate data in a
- consistent manner. As you already know, the Workplace Shell is an
- object-oriented shell that allows the users to use a computer and work the way
- they do without a computer: with objects.
-
- A program is an object, as is a data file or a printer. To look at a data file,
- you must somehow tell the computer what that file is. Generally, it is a data
- file created by some application, such as a word processor or spreadsheet. The
- shell works with associations between objects. That is how it knows how to
- display objects. When an object is opened (with a double click), the object is
- opened with its default association. You will shortly see how these
- associations are built.
-
- Let's start with the basics of how the shell operates. Everything in the
- computer can be represented with a Workplace object. This object can be created
- by an application via WinCreateObject, or the user can create an object
- through templates, copying another object, creating a shadow of an existing
- object, and so on. Once the object is created, it can be manipulated by the
- user. How the object behaves is defined by the class it belongs to.
-
-
- ΓòÉΓòÉΓòÉ 6.1.1.1. Objects and Templates ΓòÉΓòÉΓòÉ
-
- As you have seen in Chapter 6, there are three general types of objects. There
- is the transient object, which has no information stored across reboots, and
- then there are the abstract and file system objects, which store their
- persistence in the .INI file and file system, respectively. How do these
- objects work?
-
- In general, you can think of a program reference object as an entry in a
- program starter list. You can create a program reference that points to an
- executable file. When this object is opened, the program is started, and you
- can open data files or print, as you have in any other system. This is the
- application-launching function present in any good user shell. However, the WPS
- is more than this, and by intelligently implementing functions provided by the
- shell, you can change people's impressions of applications. Applications, data
- files, and other objects will become part of the computer, part of the user's
- work environment.
-
- Do not think of your applications any longer as programs that need to be fed
- data. Think of documents, spreadsheets, or any other object that the user
- wishes to work with. Now that you have that firmly set in your mind, let's move
- to the choices you have in presenting these documents to your users.
-
-
- ΓòÉΓòÉΓòÉ 6.1.1.1.1. Step 1---Simple Drag and Drop ΓòÉΓòÉΓòÉ
-
- The first step most users will take in trying out the drag and drop functions
- of the Workplace Shell is to drag a data file to a program reference object and
- drop it, hoping that the application will start and load the data file. Once
- there, subsequent files would have to be loaded the old way, by asking the
- application to open them. This is a good first step and a nice introduction
- into the object-oriented world of the WPS.
-
- Let's look at how to make this work. The WPS cannot do all the work; the
- application must do some. In order for this function to work, the application
- must be able to take a "command line" parameter. That is simply how the shell
- does it. When an object is dropped on any other object---a program reference in
- this case---the shell will start the program (DosExecPgm) and pass the name of
- the dropped object as a parameter. Assuming the application knows how to use
- this information, the application will start and will do whatever it will with
- the file dropped.
-
- A good example of an application that just about always takes such a parameter
- is a text editor. Usually, you can type edit filename.ext (assuming the
- editor's name is edit) and it will start up, loading filename.ext as the file
- being edited. Some of the more complex applications that have been written do
- not accept such parameters and will not work within this scheme.
-
- As you will see, there are several ways to cause your application to present
- documents or data through the functions of the WPS. This is the simplest. My
- recommendation is to support this, even if you are using other methods as well.
- The reason behind this is that it takes a trivial amount of code to support
- this and gives your users more flexibility in how they view objects. The worst
- thing you can do is tell the users, "That's the way it is. It is good for you
- and you'll do it this way." Keep things flexible. If you can do it with a
- minimum of work, go for it.
-
- Any application that can handle a command line parameter can make use of the
- WPS drag and drop function this way.
-
-
- ΓòÉΓòÉΓòÉ 6.1.1.1.2. Step 2---Associations ΓòÉΓòÉΓòÉ
-
- The next more powerful function you can use is associations. An association is
- exactly what it sounds like: It associates an executable with type(s) of
- objects, either by object class or by file name extension. Associations can be
- created by the user or by the application.
-
- User-created associations are not relevant here, since we are dealing with
- keeping work away from the users and letting the applications do it.
- Application-created associations are straightforward.
-
- When an application is built, it can have a resource known as an association
- table ("assoctable"). The table is built using a resource definition with the
- ASSOCTABLE statement. When the resources are compiled into the executable
- program, the assoctable is built. The first time the shell wakes up the object
- representing this executable, it looks to see if there is an assoctable. If so,
- it builds the associations for the application.
-
- For example, let's say you are writing a word processor called "My Word
- Processor," and all of its documents will have the extension of .MWP. Listing
- 10.1 shows what the ASSOCTABLE statement might look like in the resource file.
-
-
- ASSOCTABLE
- BEGIN
- "MyApp Document", "*.MWP", AF\_DEFAULTOWNER,
- MYAPP.ICO "MyApp Backup Document", "*.MBK"
- END
-
- Listing 10.1 Sample association table in a resource definition file.
-
- When the program object is awakened for the first time by the shell, an
- association for the file type .MWP will be built, and a new template will be
- created in the Templates folder. Once this occurs, the user can then simply
- grab a template for an .MWP document and place it anywhere. When the user
- opens the object, the program will be started and the particular document will
- be loaded. Of course, this still relies on the command line parameter mechanism
- mentioned previously. The parameter-passing mechanism is the cornerstone of
- this function, and you can add to the ease of use further by adding the
- assoctable to the application.
-
- At this point, your users can drag a document to your program object and drop
- it there, or, if they wish, they can simply open the document by double
- clicking on it, and it will be loaded into the program.
-
-
- ΓòÉΓòÉΓòÉ 6.1.1.1.3. Step 3---Writing an Object ΓòÉΓòÉΓòÉ
-
- The next step is to write an object. This is not as difficult as it sounds, and
- it adds a new dimension to your application. There are some things you need to
- understand and be aware of when writing objects, but the power and flexibility
- you gain far outweigh the downsides.
-
-
- ΓòÉΓòÉΓòÉ 6.1.1.2. Application Launching ΓòÉΓòÉΓòÉ
-
- As you have seen, the main purpose of the shell is to launch applications. The
- Workplace Shell provides functions to allow these applications to communicate
- with other objects in the system---most importantly, the data file objects they
- will be manipulating. At first glance, it seems intuitive to write your
- application as an object. After all, when a file gets dropped or an object is
- opened, it is started. You would think that you would want to simply implement
- your program as an object.
-
- This is not a good idea. As you will see through the next few pages of
- discussion, there are better ways of implementing object functions in your
- application than writing it as an object. In actuality, the best way to write
- this function is to write an object that represents your data file(s). Each
- type of data file you will manipulate should be a separate type of object
- (unless of course they all behave the same way, in which case you only need one
- class of object).
-
- Remember that users deal with objects. Your job as an application designer is
- to hide the fact that they are dealing with programs at all. Users should be
- working with objects, such as documents and graphs. By writing objects that
- represent real-world objects, you allow users to learn your application easily.
-
- Objects that you will write (in the most general case, there will obviously be
- many special uses and exceptions to this) are a subclass of the data file
- object. When you create this class with WinCreateClass, you create a new
- template for the Templates folder. When the user "rips off" a new object from
- the appropriate template, you create a new object belonging to this class.
-
- The main method you will subclass in this new class of yours is wpOpen. When
- wpOpen is invoked in your object, you should call DosExecPgm to invoke the
- executable that is your application. You can also pass these "command line"
- parameters to tell the application what is going on and why it is being
- started. In the most general case, the user is simply trying to work with the
- document, and the application's job is to give it to him. As you will see
- shortly, this mechanism is useful for other object functions as well.
-
- Now you have some decisions to make. Firstly, what do you do if the user opens
- another document? Should you start another copy of the application code? It is
- easy enough to detect whether a copy is already running, but you need to define
- an object interface to the application code. You could choose to use shared
- memory, system semaphores, Dynamic Data Exchange, or any other IPC mechanism
- you like.
-
- There is no set answer as to what to do in these situations. It depends on your
- application and the functions you wish to provide to the user. Of course, you
- will most likely not want to start a separate copy of the application for each
- object opened, but you need to decide how you want to implement the
- communication between the object and the application.
-
- Recall the principles of object-oriented systems. The most important ones here
- are inheritance and the fact that all objects belonging to a class behave the
- same way. Once you decide what you want to do, you only have to write it once.
- Each instance of the class behaves the same way.
-
-
- ΓòÉΓòÉΓòÉ 6.1.1.3. Single-Process Model Considerations ΓòÉΓòÉΓòÉ
-
- Recall from Chapter 6 the single-process model of the Workplace Shell. All
- objects run in the Workplace process. If you were to write your application as
- an object, several things would happen.
-
- First, if your object has a bug in its code (that will never happen, we all
- write great code the first time, right?), it can take down the shell process.
- This is a drawback of the design of the shell, but for now, it is a situation
- that must be dealt with. As discussed in Chapter 6, the shell process will be
- restarted and objects will be restored to their previous state. However, the
- more code you write to run in the shell process, the more risk you are taking.
-
- The other more important consideration to note in writing your objects is that
- if the shell does need to be restarted, it is the whole process that needs to
- be restarted, since it is the process that is brought down. Other processes are
- not affected, but anything running within the Workplace process has to be
- restarted. If your application is part of, and is running in, the Workplace
- process, all updates to the object in the application will be lost. By using
- DosExecPgm to start the program when your object is called at wpOpen, the
- application is running in a separate process and is thus unaffected if the
- Workplace process has a problem.
-
- The bottom line is that you should write only what you have to as an object and
- not code the whole application as a DLL to run under the Workplace process.
- This also gives you the added advantage of inheriting many of the methods from
- the parent class (most often the data file object class) and only subclassing
- (or overriding) the methods you need to start and communicate with the
- application executable.
-
-
- ΓòÉΓòÉΓòÉ 6.1.1.4. Subset Function ΓòÉΓòÉΓòÉ
-
- Other features you may wish to add to your application are something I call
- subset functions. Subset functions are those that need the application to
- perform, but the user may not wish to view the object or manipulate the data.
-
-
- ΓòÉΓòÉΓòÉ 6.1.1.4.1. Object-Oriented Printing ΓòÉΓòÉΓòÉ
-
- An example of a subset function is a printing function. When the object---say,
- a document---is dragged and dropped on a print object, the document object is
- called at its wpPrintObject method. The default or inherited method is that of
- a plain text or printer-specific file, since the superclasses of the objects
- are made very generic. You will likely have data files in some format specific
- to the application, so a plain text or printer-specific file print would not
- work.
-
- Another factor is that by using the superclass's wpPrintObject inherited
- method, you are bound to the defaults set up for that printer. No customization
- is possible.
-
- When you write your document object class, you should override the
- wpPrintObject method and have some interface to the application that starts
- only a subset of the application. This is the reason I call this a subset
- function. An example of a subset function is the object-oriented printing you
- have just seen.
-
- An example of how to design this is that if your document object is called at
- wpPrintObject, you call DosExecPgm on the executable, passing in the file name
- to be printed and some sort of flag to indicate that the user does not want to
- do anything with the data other than print it. As a response, the application
- will start and load the specified data file but will not show it in its main
- window as if the user wanted to update the data. Rather, this flag will signal
- to the application to put up its print dialog.
-
- You may not want to go even that far and instead just have the application read
- in the data file and print it to the printer specified, using the application
- defaults. My recommendation would be to put up a print window, however. If the
- user wants to accept the application defaults, one more keystroke or mouse
- click will not make a difference, and you will be providing the flexibility to
- allow the user to change anything she wishes.
-
- Once the data has been completely spooled, the application terminates, where
- the user does not even know it ever started. The user simply dragged a file
- onto a printer, was asked to make sure this is the way he wanted it printed,
- and off it went. No more do users have to start the program, bring in the file,
- select to print the file, and then close the application. Now you're letting
- the users work with objects represented in the computer the same way they do
- with tangible objects.
-
-
- ΓòÉΓòÉΓòÉ 6.1.1.5. To Write an Object or Not to Write an Object? ΓòÉΓòÉΓòÉ
-
- The real question is how far to go when writing an object, or if you should
- even write an object at all. If your application is something very
- straightforward, such as a text editor, the choice is simple. By using an
- assoctable and taking command line parameters, you can accomplish all the
- things you need. Printing will work just fine, because you are using ASCII text
- files or in some cases, files with embedded printer control codes. If you are
- writing utility programs such as these, the shell already gives you most of the
- functions you need. That is the beauty of the object-oriented system. Inherit
- what you need; if you need nothing else, you're done.
-
- If you are going to be storing files in some format other than plain ASCII or
- require more complex function, then a custom object, representing data files,
- is probably the right way to go. This gives you the flexibility of drag and
- drop functions and data communications, as well as everything the previous
- method gives you. Again, by using the object-oriented principle of inheritance,
- simply override the methods you need, such as wpPrintObject, and let the
- superclass's methods handle the rest.
-
- I cannot stress enough that you should not implement your entire application as
- an object. There may be some cases where this is applicable, but for the
- majority of programs that will be written, this is undesirable both from an
- application standpoint and an overall system standpoint. Applications are
- really not objects. Documents, printers, FAX machines, and graphs are all
- objects and should be manipulated by "behind the scenes" application code.
- Computing is moving from an application-oriented environment to an
- object-oriented environment.
-
-
- ΓòÉΓòÉΓòÉ 7. Chapter 17 Excerpt ΓòÉΓòÉΓòÉ
-
- This section contains an excerpt from Chapter 17. The section included is
- titled, "Base Tuning", with subsections of, "Memory Tuning", and, "Dynamic Link
- Considerations."
-
- Expand this chapter using the "+" icons to read the section on the Workplace
- Shell.
-
-
- ΓòÉΓòÉΓòÉ 7.1. Chapter 17 Base tuning ΓòÉΓòÉΓòÉ
-
- Base tuning refers to all of the core functions of the application such as
- memory management, resource allocation, load time, and thread management. Back
- in Chapter 14 we discussed breaking tasks into threads and how best to parallel
- tasks. The OS/2 memory management package was explored along with some ways to
- structure your memory allocations. This chapter will dig deeper into
- structuring your memory allocations and managing your threads. You will see how
- to manage your threads' priorities based on their job in relation to the other
- threads in the system and the application.
-
-
- ΓòÉΓòÉΓòÉ 7.1.1. Memory tuning ΓòÉΓòÉΓòÉ
-
- Memory tuning was touched on in Chapters 11 and 14. Recall that in 32-bit OS/2
- all memory is allocated in 4K pages. Even a 2-byte allocation will give you a
- 4K page. By analyzing your code as you develop it you can determine working
- sets, locality of reference, and positioning of reference.
-
-
- ΓòÉΓòÉΓòÉ 7.1.1.1. Code and Data Working Sets ΓòÉΓòÉΓòÉ
-
- The first thing to look at is how your code works within itself. You do this by
- looking at both the logical structure of the code (how functions call others)
- and the LINK map. By looking at how the functions call each other you can
- determine how the code logically flows. By looking at the LINK map you can see
- the exact sizes of the functions and how the linker is working with your
- function "order" and is allocating code pages.
-
- The goal of your analysis is to minimize or even eliminate waste within the
- pages of code. The data can be managed using the techniques described in
- Chapter 14. You control the memory allocations for data. The real analysis
- takes place with the code.
-
-
- ΓòÉΓòÉΓòÉ 7.1.1.2. Locality of Reference and Data Positioning ΓòÉΓòÉΓòÉ
-
- Locality of reference refers to the real location of functions with respect to
- each other. Of course, you as a programmer cannot control where the pages are
- placed within physical memory, but you can control within which pages your code
- resides. The first step is to determine which functions reference which others
- and how often. Then, use the link map of your build to see how big each of
- these functions are. After that, the process is relatively simple. Take the
- functions that reference each other, and draw a hierarchy map. In this
- hierarchy map you should map out which functions reference each other. You
- should also write down each function's size. Now, understanding that
- everything is allocated in 4K pages you can use the .DEF file and LINK
- statements to group functions together that reference each other.
-
- The ideal goal is to keep the functions that reference each other most often on
- the same physical page of code if possible. Of course, this may not be possible
- due to the size of these functions or the number of functions that reference
- each other. Another possibility is to move some of the data structures
- associated with some of the functions to global data rather than instance data.
- This way the function will be smaller. By doing this you can also control the
- locality of the data with your internal memory manager. Of course, there will
- be times when you must have instance data automatically allocated with a
- function invocation such as with reentrant functions, but this approach can
- still be of value.
-
- In moving functions around to keep interfunction references within pages you
- can also minimize waste. When looking at the functions to keep together you
- should also look at their sizes. There really is no optimal way to figure out
- whether to move a function that is referenced more often to another page so
- that you can fit another two functions into the first page. You need to make
- those determinations based on the size, the function usage frequency, and your
- knowledge of how the code works. Chances are that if you are splitting hairs
- you won't generally be wrong. At that level of detail one choice versus another
- may mean nothing more than a few milliseconds.
-
- You can organize the functions on a strictly mathematical basis (how many
- references to a function versus how much is wasted moving to another code
- page), but the real key is knowing how the code is referenced. For example, if
- a particular feature of the application is used more often than others but the
- others would save space if moved to a lesser-used function's location, you need
- to see how much savings you get in either case. If the bottom line is nothing
- more than wasting a few K of memory throughout the application, the locality of
- reference of the functions is more important. The few K is not a big deal, but
- if the application has to keep bouncing the same few pages in and out of memory
- in a constrained system, your performance will be hampered.
-
- There is no formula for making these decisions. The function sizings and
- references are your guides along with your intimate knowledge of your
- application.
-
-
- ΓòÉΓòÉΓòÉ 7.1.2. Dynamic Link Considerations ΓòÉΓòÉΓòÉ
-
- The first thing to understand with dynamic linking by import (as opposed to
- DosLoadModule/DosGetProcAddr runtime dynamic linking) is that every function
- call requires fixups. Fixups are records that point to the real functions in
- DLLs. As outlined in Chapter 5, DLLs contain external reference records that
- point to functions in the DLLs. Each of these function calls must have some
- kind of fixup at runtime to gain addressability to the actual function.
-
- There is a series of tables associated with executables as well as with DLLs
- that cross-reference the functions in DLLs that are called by the application
- programs. These tables are stored in the extended executable file header and
- must be interpreted and resolved.
-
- The fixups are calculated at loadtime and are kept in swappable application
- memory. When the call is actually made to the function, the fixup tables, if
- not present, are brought back into memory, and the function call is resolved
- and executed. In general, this is not bad, but by understanding this, you can
- see ways to improve performance with some simple changes to your code. The way
- to do this is what I call API aliasing. What this means is to have only one
- function in the application that calls a particular DLL function. Whenever you
- want the services of that API you call your own function (the alias). You will
- create a function for each API function call you make in the application and
- give it a name such as MyDosAllocMem or MyDosCreateThread. It would take the
- same parameters as the real API and return information the same way the API
- does.
-
- The advantage of this is that there is only one fixup per API function used in
- the code, as opposed to having fixups all over your code for each API function
- call. This latter approach would just add size to the fixup tables, take time
- to resolve, and add size to the application working set (recall that the tables
- remain in the application's swappable memory).
-
- An important decision in aliasing the API functions is how many of them you
- actually call. If you use a particular API only a relatively few times (say,
- five or less) throughout the application, then the overhead of writing another
- function to alias the API is probably not worthwhile. However, for functions
- that are called on a regular basis an API alias will help your performance by
- reducing the size of the tables and the number of fixups required. In writing
- the API aliases you create a bunch of near, direct references to your own
- function throughout your code. This keeps each (indirect) invocation of the
- API out of the fixup tables since there is only one place in the application
- where the API is called. This makes your working set smaller, the executable
- file smaller, and the load time faster.
-
- Another technique you can use to reduce the working set of your application is
- to look for functions in your DLLs that you reference infrequently. DLL
- references to system code are really of no concern since most of that code is
- resident during system operation anyway.
-
- Your own DLL references can, however, be set up to optimize locality of
- reference for functions that call others within the same DLL. Earlier we
- discussed deciding which functions to put in DLLs. Performance also plays a
- part in this decision. If you have a function that can fit in a page close to
- its caller you may be better off in keeping that function in the .EXE rather
- than in the DLL. This is a moot point if the function needs to be shared
- between processes (which is the major reason for putting a function in a DLL
- anyway). The other half of this DLL optimization is to delay the loading of a
- DLL until it is needed.
-
- By loading a DLL when you call a function in it by name you take the
- performance hit at load time, which may be more desirable, but you'll also
- increase the working set size of your application because that DLL will be in
- use and loaded all the time. It can be swapped out in a constrained situation,
- but look at the net result. You'll load the DLL at the outset to take the hit
- at load time. However, if the code in the DLL is hardly ever used (or not used
- for a long time) it is likely to be paged out. If it is paged out when the
- actual call is made into it, it will need to be paged in again. Now you have
- loaded the page twice to use it once.
-
- By delaying the load by using DosLoadModule/DosGetProcAddr you load the DLL
- only when you need it. Since you'll take the performance hit if the code has to
- be paged in at the time a jump into it is made or if it is being loaded for the
- first time when the jump is made, at least you won't load it in at application
- load time as well. There may be times, such as with frequently used code, when
- you may want to place a function in a DLL despite this fact, but now you know
- the ups and downs of both approaches.
-
- Another important consideration is the life span of a DLL. That is when you
- reference a DLL, how long after the function is used should the DLL be kept
- around? If you are calling the functions by import or name you have no choice.
- The DLL is loaded for the life of the application. Of course, it (or parts of
- it) may be paged out, but that still consumes system resources. When it comes
- to DLLs loaded via DosLoadModule the choice is completely up to you. A good
- example is a "help" DLL.
-
- Help code is usually used for a short time and then released. However, if the
- user is in some sort of learning mode, such as when an application is first
- being learned and used, help is requested more often. You don't want to
- blindly free the DLL that contains the help code after each help function call,
- because if you make several calls close together you'll spend all the system
- resources loading and unloading that DLL. You also have to consider times when
- an experienced user may need to find only one piece of information and will not
- request help again during the application's execution. In that case you don't
- want the DLL hanging around. You could put in a switch for something such as
- an "expert mode" whereby the DLL would indeed be freed after each help call
- since an expert may not need it more than once or twice. Users who keep the
- expert mode switch off will keep the DLL loaded because they are likely to
- request help again.
-
- A help function is only one example. In general you should look at how and
- where your DLLs are used and how long they should remain loaded. Keep in mind
- that it takes resource to load and unload DLLs; you must weigh this against the
- size of the file. If you have a small DLL, for example, it may be feasible to
- keep the DLL loaded since it will take up only a small amount of swap space,
- whereas if the DLL is large you will need to make a trade-off decision. This
- may also lead you to make a set of smaller DLLs rather than one large one.
-
- All these facts must be considered when setting up your DLLs. It is easy to
- forget, though, that the main reason for putting code in a DLL is to share it
- between processes. If the code does not need to be shared you should simply
- keep it in the main executable file and manage where in the file it lives (via
- the .DEF file and LINK statements) as outlined earlier. The same method of
- managing the code in the executable file can be applied to the DLL as well.