home *** CD-ROM | disk | FTP | other *** search
/ The Developer Connection…ice Driver Kit for OS/2 3 / DEV3-D1.ISO / docs / dsgnapp.inf (.txt) < prev    next >
Encoding:
OS/2 Help File  |  1993-08-10  |  39.3 KB  |  1,037 lines

  1.  
  2. ΓòÉΓòÉΓòÉ 1. Title Page and Book Information ΓòÉΓòÉΓòÉ
  3.  
  4.  
  5. Designing OS/2 Applications 
  6.  
  7. By David E. Reich 
  8.  
  9. Foreword by Lee Reiswig, President, IBM Personal Software Products Division 
  10.  
  11.  
  12. Written by an IBM insider, this practical guide emphasizes the important design 
  13. issues of software development for OS/2 applications. It shows you how to 
  14. squeeze the most from 32-bit systems for dramatic increases in speed and power. 
  15. Using examples and detailed scenarios, this book offers extensive coverage of 
  16. the functions that are available in the system and shows you exactly how to 
  17. exploit those functions to create the next generation of applications. 
  18.  
  19. About The Author 
  20.  
  21. David E. Reich has been a member of the OS/2 Development Team since 1987. In 
  22. that time, he has worked on many parts of the operating system, supported 
  23. customers and application developers and has traveled around the world teaching 
  24. classes and giving seminars on OS/2. Mr. Reich has a Master's in Computer 
  25. Science from the State University of New York at Albany. 
  26.  
  27.  
  28. Section 1 discusses the benefits of OS/2 from the user's as well as the 
  29. programmer's perspective. 
  30.  
  31. Section 2 discusses application design issues and covers details of OS/2 
  32. internals including the OS/2 Kernel, Presentation Manager and the Workplace 
  33. Shell. 
  34.  
  35. Section 3 covers the basic building blocks of OS/2 applications. 
  36.  
  37. Section 4 introduces details of the OS/2 development environment, application 
  38. development tools, writing the user interface and building core functions as 
  39. well as explores the advanced functions and non-English language support. 
  40.  
  41. Section 5 covers performance tuning issues for memory management, threads and 
  42. window tuning. 
  43.  
  44. Section 6 discusses aspects of code testing, possible pitfalls of concurrent 
  45. programming and how to contain code changes during testing. 
  46.  
  47. Section 7 talks about the importance of the installation program as well as the 
  48. design implications and code reuse of such programs. 
  49.  
  50.  
  51. ΓòÉΓòÉΓòÉ 2. Copyright and Trademarks ΓòÉΓòÉΓòÉ
  52.  
  53.  
  54. Designing OS/2 Applications 
  55.  
  56. By David E. Reich 
  57.  
  58. Foreword by Lee Reiswig, President, IBM Personal Software Products Division 
  59.  
  60.  
  61. Copyright (C) 1993 by John Wiley & Sons, Inc.
  62.  
  63.  
  64. Library of Congress Cataloging-in-Publication Data
  65.  
  66. Reich, David E.
  67.   Designing OS/2 Applications / David E. Reich
  68.     p.   cm.
  69.  
  70.   Includes bibliographical references and index.
  71.   ISBN 0-471-58889-X (pbk.)
  72.   1. Operating Systems (Computers)  2. OS/2 (Conputer file)
  73.  1. Title
  74.  QA76.76.063R44  1993
  75.  
  76.  055.4'469--dc20
  77.  
  78. Excerpted with permission of John Wiley & Sons, Inc. and David E. Reich. 
  79.  
  80. IBM is a registered trademark of International Business Machines Corporation.. 
  81.  
  82. Operating System/2, OS/2, Presentation Manager, Workplace Shell, Information 
  83. Presentation Facility, Systems Application Architecture (SAA), Common User 
  84. Access (CUA), PS/2 are trademarks or registered trademarks of International 
  85. Business Machines Corporation. 
  86.  
  87. Intel is a registered trademark of Intel Corporation. 
  88.  
  89. Lotus is a trademark of Lotus Development Corporation. 
  90.  
  91. Aldus is a trademark of Aldus Corporation. 
  92.  
  93. PageMaker is a registered trademark of Aldus Corporation. 
  94.  
  95. CASE:PM is a trademark of Caseworks, Inc. 
  96.  
  97. Smalltalk is a registered trademark of Digitalk Inc. 
  98.  
  99. GPF is a  trademark of Microformatic Inc. 
  100.  
  101. Microsoft is a registered trademark of Microsoft Corporation. 
  102.  
  103.  
  104. ΓòÉΓòÉΓòÉ 3. How to Order ΓòÉΓòÉΓòÉ
  105.  
  106. Designing OS/2 Applications 
  107.  
  108. By David E. Reich 
  109.  
  110. Foreword by Lee Reiswig, President, IBM Personal Software Products Division 
  111.  
  112.  
  113. TO ORDER- 
  114.  
  115. Call 1-800-CALL-WILEY or 
  116.  
  117. Send $34.95 (prepaid orders are sent postage paid in the U.S.A.) check or money 
  118. order to: 
  119.  
  120.     John Wiley and Sons, Inc
  121.     605 Third Avenue
  122.     New York, NY  10158
  123.     Attn: S. Straub, 10th Floor
  124.  
  125.     Outside of the US, please write to the above
  126.     address for information.
  127.  
  128. Be sure to reference, "Designing OS/2 Applications" and ISBN number 
  129. 0-471-58889-X. 
  130.  
  131. "Designing OS/2 Applications", can also be obtained via IBM Puborder number 
  132. SC28-2701 or from the I.V. League. 
  133.  
  134. To receive a free copy of the PSP Product Catalog, or to order any of the books 
  135. reviewed here, please call 1-800-342-6672.  All orders are shipped next day 
  136. air. 
  137.  
  138. 300 Pages 
  139.  
  140.  
  141. ΓòÉΓòÉΓòÉ 4. Foreword ΓòÉΓòÉΓòÉ
  142.  
  143.  
  144. Designing OS/2 Applications 
  145.  
  146. By David E. Reich 
  147.  
  148. Foreword by Lee Reiswig, President, IBM Personal Software Products Division 
  149.  
  150.  
  151. In April 1992, IBM opened a whole new world of computing with the introduction 
  152. of 32-bit OS/2. OS/2 is the integrating platform. For the first time, users can 
  153. run virtually any application written for DOS, Windows or OS/2 and run them 
  154. simultaneously. Moreover, OS/2 provides the facilities for you, the application 
  155. designers and developers, to create faster, more powerful, more flexible 
  156. applications than ever before. 
  157.  
  158. All this power lies waiting for you to utilize in your full-fledged 32-bit 
  159. applications. You are free of 64k segments and 640k boundaries, memory 
  160. extenders, and other add-ons loaded on top of each other. You want memory? You 
  161. got it. Up to 512 megabytes per application. You have the facilities of 
  162. powerful graphical user and programming interfaces at your fingertips. One of 
  163. the less tangible yet most powerful features of OS/2 is its preemptive, 
  164. prioritized multithreading model. Other systems simulate multitaking through 
  165. simple time slicing. OS/2 brings program execution to the thread, allowing you 
  166. to write different parts of your application to run in parallel, creating 
  167. better throughput for the application and higher productivity for the end user 
  168. through more efficient use of the computer hardware. These, along with the 
  169. other technological advances of OS/2, are wrapped in a state-of-the-art, 
  170. object-oriented user shell. 
  171.  
  172. In working with developers all over the world, David has heard the questions, 
  173. seen the hurdles, and helped overcome them. Through his work in the OS/2 
  174. development organization as well as well as his work with developers, he has 
  175. gained insight into what is needed to design and develop applications that 
  176. exploit all that OS/2 has to offer. 
  177.  
  178. This book contains that insight and answers the questions of how to structure 
  179. and design your applications to take the best advantage of OS/2's features. You 
  180. don't have to use everything that is in OS/2 just because it is there. 
  181. Throughout this book you will be shown which functions are more appropriate in 
  182. different situations, and once you decide on a task, you are shown how to 
  183. optimize your usage of system functions and resources. 
  184.  
  185. We at IBM are proud of our technical accomplishments with OS/2 and know that we 
  186. need you and your applications to make our system really shine. David has the 
  187. ability to explain the most technically involved topics in ways that everyone 
  188. can understand. Use his experience, and this book, as you advisor in creating 
  189. the 32-bit applications of the future. 
  190.  
  191. Lee Reiswig 
  192.  
  193. President, IBM Personal Software Products Division 
  194.  
  195.  
  196. ΓòÉΓòÉΓòÉ 5. Table of Contents ΓòÉΓòÉΓòÉ
  197.  
  198. This section contains the Table of Contents for, "Designing OS/2 Applications". 
  199.  
  200. Excerpts from Chapter 10 and 17 follow. 
  201.  
  202.  
  203.   Introduction
  204.    About this book
  205.    Overall Design
  206.    Features
  207.    Maintainability
  208.    Testing and Code Change
  209.    Install Programs
  210.  
  211.  
  212.   Section I  Why OS/2?
  213.  
  214.    Chapter 1  OS/2 As An End-user Platform
  215.      Multiprocessing
  216.       Using Several Applications at Once
  217.         Sharing and Communicating Data
  218.       MultiThreading
  219.         Dividing Applications into Parallel Pieces
  220.         Increased User Productivity
  221.         Better Performance
  222.      32 bit Memory Management
  223.       Flat Memory Model
  224.         Independence from Physical Memory
  225.         Up to 512 Meg per Program
  226.         Paging vs Swapping
  227.         Better Application Performance
  228.         Better System Performance
  229.      Intuitive User Interface
  230.       User Can View Many Applications at Once
  231.       Workplace Shell
  232.         Consistent Behavior
  233.         Lower Learning Curve
  234.         Object oriented
  235.         Works the Way People Work
  236.       Contextual Help
  237.         Learning is Easy
  238.      Device Independence
  239.       Generic Output Space - OS/2 manages resolution mapping
  240.       New devices only need new drivers. Not new applications
  241.      IBM Compatibility to Future Releases
  242.      Summary
  243.  
  244.    Chapter 2  Why Program for OS/2?
  245.      Powerful and flexible API
  246.       Function Call Interface to all System Services
  247.       Consistent, Easy To Code
  248.       Portability, Flexibility, Expandability
  249.       Easy to Modularize and Maintain
  250.       System Coding Conventions
  251.       Fast Prototyping
  252.      Summary
  253.  
  254.    Chapter 3  OS/2 As A Development Platform
  255.      Multitasking for development
  256.      Debugger support
  257.      Crash Protection
  258.      Summary
  259.  
  260.  
  261.   Section II  Overall Application Design
  262.  
  263.    Chapter 4  Good Programs Have Good Up-Front Design
  264.      Understanding the Target Environment
  265.      60% Design, 30% Code, 10% Test
  266.       60% Design
  267.       30% Code
  268.       10% Test
  269.      Summary
  270.  
  271.    Chapter 5  OS/2 Kernel Architecture
  272.      Overview of the Kernel
  273.       Structure
  274.         Scheduler/Dispatcher
  275.         Loader
  276.         System Services Flow
  277.       Digging Deeper
  278.         Protection Mechanism
  279.         Process/Thread Model
  280.         Priority
  281.         Thread Management
  282.         Memory Management
  283.         File System
  284.         Device Drivers
  285.         DLL Mechanism
  286.         Base Subsystems
  287.      Summary
  288.  
  289.    Chapter 6  Presentation Manager, Graphics and the User Interface
  290.      Presenting Data
  291.      Presentation and Translation Flow
  292.       Device Context
  293.       Presentation Space
  294.         Tracing a Drawing Call
  295.       Graphics Engine
  296.       Presentation Drivers
  297.         Brute Force vs. Full-Function Drivers
  298.         Printer Drivers
  299.         Screen Drivers
  300.       Window Manager
  301.         Input
  302.      Workplace Shell
  303.      Summary
  304.  
  305.    Chapter 7  Features for your application
  306.      What is the main function or objective of the application
  307.      How Will the Application be Presented?
  308.       Presentation Manager Graphics/Text
  309.       AVIO windows
  310.       Text-Based
  311.       Workplace Shell Objects and Templates
  312.      Choosing features to include
  313.       Data communications with other programs (open application)
  314.         Dynamic Data Exchange
  315.         Clipboard
  316.         Pipes and Queues
  317.         Data Interchange Formats and Filters
  318.       REXX hooks
  319.       Printing
  320.       Fonts and WYSIWYG
  321.      Summary
  322.  
  323.    Chapter 8  Application Structure
  324.      Isolate From Underlying Hardware
  325.       Use OS/2's Device Independence
  326.       Stick with Portable Languages and Tools
  327.      Modular design and Threads
  328.      Using Multiple Processes
  329.      Using DLLs and Code Sharing
  330.      Resource Sharing and Synchronization
  331.      Keep Upgradability, Portability and Servicability in Mind
  332.      Summary
  333.  
  334.  
  335.   Section III  Use building Blocks or your app will crumble
  336.  
  337.    Chapter 9  Block design and architecture
  338.      Taking the black box approach
  339.      Letting the operating system do it for you
  340.      Summary
  341.  
  342.    Chapter 10  Designing the User Interface
  343.     An excerpt for this chapter can be found in the sections below.
  344.      Window Design
  345.       CUA
  346.       Presenting Data
  347.         Window Controls
  348.         Dialogs
  349.         Application Defaults
  350.         CASE Tools for PM
  351.      WorkPlace Shell
  352.       Objects and Templates
  353.         Step 1---Simple Drag and Drop
  354.         Step 2---Associations
  355.         Step 3---Writing an Object
  356.       Application Launching
  357.       Single-Process Model Considerations
  358.       Subset Function
  359.         Object-Oriented Printing
  360.       To Write an Object or Not to Write an Object?
  361.      Summary
  362.  
  363.    Chapter 11  Where's the beef?
  364.      Designing the core
  365.       Modularizing the "worker" code
  366.       Memory management
  367.       Device drivers and device independence
  368.      File layout
  369.       HPFS and FAT features
  370.       EXEs and DLLs
  371.       INI files
  372.      Multiprocess (multiprogram) applications
  373.      Summary
  374.  
  375.  
  376.   Section IV  Making it happen
  377.  
  378.    Chapter 12  The Development Environment
  379.      Source Code Control
  380.       Platform
  381.       Function
  382.       Problem tracking
  383.      Tree Structures
  384.      Tools
  385.      Summary
  386.  
  387.    Chapter 13  Prototyping the user interface
  388.      Paint your windows
  389.      Multithreading Considerations
  390.       Handling long jobs
  391.         Using PM's thread handling
  392.         Object Window Messages
  393.       Keep the user interface thread responsive
  394.      Summary
  395.  
  396.    Chapter 14  Building the core function
  397.      Memory manager package
  398.       Memory suballocation
  399.       16 and 32 bit techniques and coexistence
  400.      Multithreading
  401.       Synchronizing threads
  402.         Semaphores
  403.         Critical Sections
  404.       Should you even use another thread?
  405.      IPC
  406.       Queues
  407.       Pipes
  408.       Shared Memory
  409.      File functions
  410.       Internal File Formats
  411.       Taking the LAN into account
  412.       Using File System Structures
  413.      Summary
  414.  
  415.    Chapter 15  Using Advanced Functions
  416.      Clipboard
  417.       Text Data
  418.       Metafile Data
  419.       Application Defined Data
  420.      Dynamic Data Exchange (DDE)
  421.      Printing
  422.       Print Destinations
  423.      Fonts
  424.      Help Facilities
  425.      Summary
  426.  
  427.    Chapter 16  Non English language support
  428.      Flexibility in your Code
  429.       Message Files
  430.       Windows and Dialogs
  431.         Stringtables
  432.         Building Windows on the Fly
  433.         Using Lengths and Proportions
  434.       Structuring your development
  435.      Summary
  436.  
  437.  
  438.   Section V  Performance
  439.  
  440.    Chapter 17  Base tuning
  441.     Excerpt of this chapter can be found below in this document.
  442.      Memory tuning
  443.       Code and Data Working Sets
  444.       Locality of Reference and Data Positioning
  445.      Dynamic Link Considerations
  446.      Messages and other Resources
  447.      Throughput using threads
  448.       Thread priority
  449.      Packing the Executable
  450.      DLL Placement
  451.      Summary
  452.  
  453.    Chapter 18  Visual Tuning
  454.      Window Tuning Tips
  455.       Keeping Windows Around
  456.       Fill Windows Invisibly
  457.      Letting PM Manage Work
  458.      Your own Multipurpose Classes
  459.      Summary
  460.  
  461.  
  462.   Section VI Testing and Code Change
  463.  
  464.    Chapter 19  Testing Methodology
  465.      Scaffolding
  466.      Testing units
  467.      Testing Modules and Components
  468.      Testing the System
  469.      Summary
  470.  
  471.    Chapter 20  Code Change
  472.      Code Change According to Design
  473.      When There Are Too Many for Comfort
  474.      Summary
  475.  
  476.  
  477.   Section VII  Installation Programs
  478.  
  479.    Chapter 21  Designing the Installation Program
  480.      Just another (small) application
  481.      User Interface
  482.      Multithreading
  483.      Multiple Installations
  484.      Media Considerations
  485.       Packing your Code
  486.      "Series" Applications
  487.      Summary
  488.  
  489.  
  490.    Summary and Conclusion
  491.  
  492.  
  493. ΓòÉΓòÉΓòÉ 6. Chapter 10 Excerpt ΓòÉΓòÉΓòÉ
  494.  
  495. This section contains an excerpt from Chapter 10. The section included is 
  496. titled, "Workplace Shell". 
  497.  
  498. Expand this chapter using the "+" icons to read the section on the Workplace 
  499. Shell. 
  500.  
  501.  
  502. ΓòÉΓòÉΓòÉ 6.1. Chapter 10   Designing the User Interface ΓòÉΓòÉΓòÉ
  503.  
  504. Expand this chapter using the "+" icons to read the section on the Workplace 
  505. Shell. 
  506.  
  507.  
  508. ΓòÉΓòÉΓòÉ 6.1.1. WorkPlace Shell ΓòÉΓòÉΓòÉ
  509.  
  510. It is not only important to have a smooth flow within the windows of your 
  511. application; it is equally important to make your application look like part of 
  512. the operating system. This goes along with the integrated environment concept. 
  513. The Workplace Shell (WPS) is an application launcher, as is any shell. 
  514. However, the WPS provides much more to applications. 
  515.  
  516. It is just as important to use only the features of the shell that you need as 
  517. it is to not overpower your application's function with fancy controls and 
  518. unnecessary menus. The WPS is very powerful. You can create objects, new 
  519. classes, and templates and implement many functions not previously available in 
  520. the PC arena. You need to understand the architecture of the WPS and not overdo 
  521. it with function; otherwise, just as if you had too many cluttered dialog 
  522. windows, your application can appear clunky and hard to use. 
  523.  
  524. The main purpose of the shell is to start programs and manipulate data in a 
  525. consistent manner. As you already know, the Workplace Shell is an 
  526. object-oriented shell that allows the users to use a computer and work the way 
  527. they do without a computer: with objects. 
  528.  
  529. A program is an object, as is a data file or a printer. To look at a data file, 
  530. you must somehow tell the computer what that file is. Generally, it is a data 
  531. file created by some application, such as a word processor or spreadsheet. The 
  532. shell works with associations between objects. That is how it knows how to 
  533. display objects. When an object is opened (with a double click), the object is 
  534. opened with its default association. You will shortly see how these 
  535. associations are built. 
  536.  
  537. Let's start with the basics of how the shell operates. Everything in the 
  538. computer can be represented with a Workplace object. This object can be created 
  539. by an application via  WinCreateObject, or the user can create an object 
  540. through templates, copying another object, creating a shadow of an existing 
  541. object, and so on. Once the object is created, it can be manipulated by the 
  542. user. How the object behaves is defined by the class it belongs to. 
  543.  
  544.  
  545. ΓòÉΓòÉΓòÉ 6.1.1.1. Objects and Templates ΓòÉΓòÉΓòÉ
  546.  
  547. As you have seen in Chapter 6, there are three general types of objects. There 
  548. is the transient object, which has no information stored across reboots, and 
  549. then there are the abstract and file system objects, which store their 
  550. persistence in the  .INI file and file system, respectively. How do these 
  551. objects work? 
  552.  
  553. In general, you can think of a program reference object as an entry in a 
  554. program starter list. You can create a program reference that points to an 
  555. executable file. When this object is opened, the program is started, and you 
  556. can open data files or print, as you have in any other system. This is the 
  557. application-launching function present in any good user shell. However, the WPS 
  558. is more than this, and by intelligently implementing functions provided by the 
  559. shell, you can change people's impressions of applications. Applications, data 
  560. files, and other objects will become part of the computer, part of the user's 
  561. work environment. 
  562.  
  563. Do not think of your applications any longer as programs that need to be fed 
  564. data. Think of documents, spreadsheets, or any other object that the user 
  565. wishes to work with. Now that you have that firmly set in your mind, let's move 
  566. to the choices you have in presenting these documents to your users. 
  567.  
  568.  
  569. ΓòÉΓòÉΓòÉ 6.1.1.1.1. Step 1---Simple Drag and Drop ΓòÉΓòÉΓòÉ
  570.  
  571. The first step most users will take in trying out the drag and drop functions 
  572. of the Workplace Shell is to drag a data file to a program reference object and 
  573. drop it, hoping that the application will start and load the data file. Once 
  574. there, subsequent files would have to be loaded the old way, by asking the 
  575. application to open them. This is a good first step and a nice introduction 
  576. into the object-oriented world of the WPS. 
  577.  
  578. Let's look at how to make this work. The WPS cannot do all the work; the 
  579. application must do some. In order for this function to work, the application 
  580. must be able to take a "command line" parameter. That is simply how the shell 
  581. does it. When an object is dropped on any other object---a program reference in 
  582. this case---the shell will start the program (DosExecPgm) and pass the name of 
  583. the dropped object as a parameter. Assuming the application knows how to use 
  584. this information, the application will start and will do whatever it will with 
  585. the file dropped. 
  586.  
  587. A good example of an application that just about always takes such a parameter 
  588. is a text editor. Usually, you can type  edit filename.ext (assuming the 
  589. editor's name is  edit) and it will start up, loading filename.ext as the file 
  590. being edited. Some of the more complex applications that have been written do 
  591. not accept such parameters and will not work within this scheme. 
  592.  
  593. As you will see, there are several ways to cause your application to present 
  594. documents or data through the functions of the WPS. This is the simplest. My 
  595. recommendation is to support this, even if you are using other methods as well. 
  596. The reason behind this is that it takes a trivial amount of code to support 
  597. this and gives your users more flexibility in how they view objects. The worst 
  598. thing you can do is tell the users, "That's the way it is. It is good for you 
  599. and you'll do it  this way." Keep things flexible. If you can do it with a 
  600. minimum of work, go for it. 
  601.  
  602. Any application that can handle a command line parameter can make use of the 
  603. WPS drag and drop function this way. 
  604.  
  605.  
  606. ΓòÉΓòÉΓòÉ 6.1.1.1.2. Step 2---Associations ΓòÉΓòÉΓòÉ
  607.  
  608. The next more powerful function you can use is associations. An association is 
  609. exactly what it sounds like: It associates an executable with type(s) of 
  610. objects, either by object class or by file name extension.  Associations can be 
  611. created by the user or by the application. 
  612.  
  613. User-created associations are not relevant here, since we are dealing with 
  614. keeping work away from the users and letting the applications do it. 
  615. Application-created associations are straightforward. 
  616.  
  617. When an application is built, it can have a resource known as an association 
  618. table ("assoctable"). The table is built using a resource definition with the 
  619. ASSOCTABLE statement. When the resources are compiled into the executable 
  620. program, the assoctable is built. The first time the shell wakes up the object 
  621. representing this executable, it looks to see if there is an assoctable. If so, 
  622. it builds the associations for the application. 
  623.  
  624. For example, let's say you are writing a word processor called "My Word 
  625. Processor," and all of its documents will have the extension of  .MWP. Listing 
  626. 10.1 shows what the  ASSOCTABLE statement might look like in the resource file. 
  627.  
  628.  
  629. ASSOCTABLE
  630.  BEGIN
  631.   "MyApp Document", "*.MWP", AF\_DEFAULTOWNER,
  632.    MYAPP.ICO "MyApp Backup Document", "*.MBK"
  633. END
  634.  
  635. Listing 10.1 Sample association table in a resource definition file.
  636.  
  637. When the program object is awakened for the first time by the shell, an 
  638. association for the file type  .MWP will be built, and a new template will be 
  639. created in the Templates folder.  Once this occurs, the user can then simply 
  640. grab a template for an  .MWP document and place it anywhere. When the user 
  641. opens the object, the program will be started and the particular document will 
  642. be loaded. Of course, this still relies on the command line parameter mechanism 
  643. mentioned previously. The parameter-passing mechanism is the cornerstone of 
  644. this function, and you can add to the ease of use further by adding the 
  645. assoctable to the application. 
  646.  
  647. At this point, your users can drag a document to your program object and drop 
  648. it there, or, if they wish, they can simply open the document by double 
  649. clicking on it, and it will be loaded into the program. 
  650.  
  651.  
  652. ΓòÉΓòÉΓòÉ 6.1.1.1.3. Step 3---Writing an Object ΓòÉΓòÉΓòÉ
  653.  
  654. The next step is to write an object. This is not as difficult as it sounds, and 
  655. it adds a new dimension to your application. There are some things you need to 
  656. understand and be aware of when writing objects, but the power and flexibility 
  657. you gain far outweigh the downsides. 
  658.  
  659.  
  660. ΓòÉΓòÉΓòÉ 6.1.1.2. Application Launching ΓòÉΓòÉΓòÉ
  661.  
  662. As you have seen, the main purpose of the shell is to launch applications. The 
  663. Workplace Shell provides functions to allow these applications to communicate 
  664. with other objects in the system---most importantly, the data file objects they 
  665. will be manipulating. At first glance, it seems intuitive to write your 
  666. application as an object. After all, when a file gets dropped or an object is 
  667. opened, it is started. You would think that you would want to simply implement 
  668. your program as an object. 
  669.  
  670. This is not a good idea. As you will see through the next few pages of 
  671. discussion, there are better ways of implementing object functions in your 
  672. application than writing it as an object. In actuality, the best way to write 
  673. this function is to write an object that represents your data file(s). Each 
  674. type of data file you will manipulate should be a separate type of object 
  675. (unless of course they all behave the same way, in which case you only need one 
  676. class of object). 
  677.  
  678. Remember that users deal with objects. Your job as an application designer is 
  679. to hide the fact that they are dealing with programs at all. Users should be 
  680. working with objects, such as documents and graphs.  By writing objects that 
  681. represent real-world objects, you allow users to learn your application easily. 
  682.  
  683. Objects that you will write (in the most general case, there will obviously be 
  684. many special uses and exceptions to this) are a subclass of the data file 
  685. object. When you create this class with WinCreateClass, you create a new 
  686. template for the Templates folder. When the user "rips off" a new object from 
  687. the appropriate template, you create a new object belonging to this class. 
  688.  
  689. The main method you will subclass in this new class of yours is wpOpen. When 
  690. wpOpen is invoked in your object, you should call  DosExecPgm to invoke the 
  691. executable that is your application. You can also pass these "command line" 
  692. parameters to tell the application what is going on and why it is being 
  693. started. In the most general case, the user is simply trying to work with the 
  694. document, and the application's job is to give it to him. As you will see 
  695. shortly, this mechanism is useful for other object functions as well. 
  696.  
  697. Now you have some decisions to make. Firstly, what do you do if the user opens 
  698. another document? Should you start another copy of the application code? It is 
  699. easy enough to detect whether a copy is already running, but you need to define 
  700. an object interface to the application code. You could choose to use shared 
  701. memory, system semaphores, Dynamic Data Exchange, or any other IPC mechanism 
  702. you like. 
  703.  
  704. There is no set answer as to what to do in these situations. It depends on your 
  705. application and the functions you wish to provide to the user. Of course, you 
  706. will most likely not want to start a separate copy of the application for each 
  707. object opened, but you need to decide how you want to implement the 
  708. communication between the object and the application. 
  709.  
  710. Recall the principles of object-oriented systems. The most important ones here 
  711. are inheritance and the fact that all objects belonging to a class behave the 
  712. same way. Once you decide what you want to do, you only have to write it once. 
  713. Each instance of the class behaves the same way. 
  714.  
  715.  
  716. ΓòÉΓòÉΓòÉ 6.1.1.3. Single-Process Model Considerations ΓòÉΓòÉΓòÉ
  717.  
  718. Recall from Chapter 6 the single-process model of the Workplace Shell. All 
  719. objects run in the Workplace process. If you were to write your application as 
  720. an object, several things would happen. 
  721.  
  722. First, if your object has a bug in its code (that will never happen, we all 
  723. write great code the first time, right?), it can take down the shell process. 
  724. This is a drawback of the design of the shell, but for now, it is a situation 
  725. that must be dealt with. As discussed in Chapter 6, the shell process will be 
  726. restarted and objects will be restored to their previous state.  However, the 
  727. more code you write to run in the shell process, the more risk you are taking. 
  728.  
  729. The other more important consideration to note in writing your objects is that 
  730. if the shell does need to be restarted, it is the whole process that needs to 
  731. be restarted, since it is the process that is brought down. Other processes are 
  732. not affected, but anything running within the Workplace process has to be 
  733. restarted. If your application is part of, and is running in, the Workplace 
  734. process, all updates to the object in the application will be lost.  By using 
  735. DosExecPgm to start the program when your object is called at wpOpen, the 
  736. application is running in a separate process and is thus unaffected if the 
  737. Workplace process has a problem. 
  738.  
  739. The bottom line is that you should write only what you have to as an object and 
  740. not code the whole application as a DLL to run under the Workplace process. 
  741. This also gives you the added advantage of inheriting many of the methods from 
  742. the parent class (most often the data file object class) and only subclassing 
  743. (or overriding) the methods you need to start and communicate with the 
  744. application executable. 
  745.  
  746.  
  747. ΓòÉΓòÉΓòÉ 6.1.1.4. Subset Function ΓòÉΓòÉΓòÉ
  748.  
  749. Other features you may wish to add to your application are something I call 
  750. subset functions. Subset functions are those that need the application to 
  751. perform, but the user may not wish to view the object or manipulate the data. 
  752.  
  753.  
  754. ΓòÉΓòÉΓòÉ 6.1.1.4.1. Object-Oriented Printing ΓòÉΓòÉΓòÉ
  755.  
  756. An example of a subset function is a printing function. When the object---say, 
  757. a document---is dragged and dropped on a print object, the document object is 
  758. called at its wpPrintObject method.  The default or inherited method is that of 
  759. a plain text or printer-specific file, since the superclasses of the objects 
  760. are made very generic. You will likely have data files in some format specific 
  761. to the application, so a plain text or printer-specific file print would not 
  762. work. 
  763.  
  764. Another factor is that by using the superclass's  wpPrintObject inherited 
  765. method, you are bound to the defaults set up for that printer. No customization 
  766. is possible. 
  767.  
  768. When you write your document object class, you should override the 
  769. wpPrintObject method and have some interface to the application that starts 
  770. only a subset of the application. This is the reason I call this a subset 
  771. function. An example of a subset function is the object-oriented printing you 
  772. have just seen. 
  773.  
  774. An example of how to design this is that if your document object is called at 
  775. wpPrintObject, you call  DosExecPgm on the executable, passing in the file name 
  776. to be printed and some sort of flag to indicate that the user does not want to 
  777. do anything with the data other than print it. As a response, the application 
  778. will start and load the specified data file but will not show it in its main 
  779. window as if the user wanted to update the data. Rather, this flag will signal 
  780. to the application to put up its print dialog. 
  781.  
  782. You may not want to go even that far and instead just have the application read 
  783. in the data file and print it to the printer specified, using the application 
  784. defaults.  My recommendation would be to put up a print window, however. If the 
  785. user wants to accept the application defaults, one more keystroke or mouse 
  786. click will not make a difference, and you will be providing the flexibility to 
  787. allow the user to change anything she wishes. 
  788.  
  789. Once the data has been completely spooled, the application terminates, where 
  790. the user does not even know it ever started. The user simply dragged a file 
  791. onto a printer, was asked to make sure this is the way he wanted it printed, 
  792. and off it went. No more do users have to start the program, bring in the file, 
  793. select to print the file, and then close the application. Now you're letting 
  794. the users work with objects represented in the computer the same way they do 
  795. with tangible objects. 
  796.  
  797.  
  798. ΓòÉΓòÉΓòÉ 6.1.1.5. To Write an Object or Not to Write an Object? ΓòÉΓòÉΓòÉ
  799.  
  800. The real question is how far to go when writing an object, or if you should 
  801. even write an object at all. If your application is something very 
  802. straightforward, such as a text editor, the choice is simple. By using an 
  803. assoctable and taking command line parameters, you can accomplish all the 
  804. things you need. Printing will work just fine, because you are using ASCII text 
  805. files or in some cases, files with embedded printer control codes. If you are 
  806. writing utility programs such as these, the shell already gives you most of the 
  807. functions you need. That is the beauty of the object-oriented system. Inherit 
  808. what you need; if you need nothing else, you're done. 
  809.  
  810. If you are going to be storing files in some format other than plain ASCII or 
  811. require more complex function, then a custom object, representing data files, 
  812. is probably the right way to go. This gives you the flexibility of drag and 
  813. drop functions and data communications, as well as everything the previous 
  814. method gives you. Again, by using the object-oriented principle of inheritance, 
  815. simply override the methods you need, such as  wpPrintObject, and let the 
  816. superclass's methods handle the rest. 
  817.  
  818. I cannot stress enough that you should not implement your entire application as 
  819. an object. There may be some cases where this is applicable, but for the 
  820. majority of programs that will be written, this is undesirable both from an 
  821. application standpoint and an overall system standpoint. Applications are 
  822. really not objects. Documents, printers, FAX machines, and graphs are all 
  823. objects and should be manipulated by "behind the scenes" application code. 
  824. Computing is moving from an application-oriented environment to an 
  825. object-oriented environment. 
  826.  
  827.  
  828. ΓòÉΓòÉΓòÉ 7. Chapter 17 Excerpt ΓòÉΓòÉΓòÉ
  829.  
  830. This section contains an excerpt from Chapter 17. The section included is 
  831. titled, "Base Tuning", with subsections of, "Memory Tuning", and, "Dynamic Link 
  832. Considerations." 
  833.  
  834. Expand this chapter using the "+" icons to read the section on the Workplace 
  835. Shell. 
  836.  
  837.  
  838. ΓòÉΓòÉΓòÉ 7.1. Chapter 17   Base tuning ΓòÉΓòÉΓòÉ
  839.  
  840. Base tuning refers to all of the core functions of the application such as 
  841. memory management, resource allocation, load time, and thread management. Back 
  842. in Chapter 14 we discussed breaking tasks into threads and how best to parallel 
  843. tasks. The OS/2 memory management package was explored along with some ways to 
  844. structure your memory allocations. This chapter will dig deeper into 
  845. structuring your memory allocations and managing your threads. You will see how 
  846. to manage your threads' priorities based on their job in relation to the other 
  847. threads in the system and the application. 
  848.  
  849.  
  850. ΓòÉΓòÉΓòÉ 7.1.1. Memory tuning ΓòÉΓòÉΓòÉ
  851.  
  852. Memory tuning was touched on in Chapters 11 and 14. Recall that in 32-bit OS/2 
  853. all memory is allocated in 4K pages. Even a 2-byte allocation will give you a 
  854. 4K page. By analyzing your code as you develop it you can determine working 
  855. sets, locality of reference, and positioning of reference. 
  856.  
  857.  
  858. ΓòÉΓòÉΓòÉ 7.1.1.1. Code and Data Working Sets ΓòÉΓòÉΓòÉ
  859.  
  860. The first thing to look at is how your code works within itself. You do this by 
  861. looking at both the logical structure of the code (how functions call others) 
  862. and the LINK map. By looking at how the functions call each other you can 
  863. determine how the code logically flows.  By looking at the LINK map you can see 
  864. the exact sizes of the functions and how the linker is working with your 
  865. function "order" and is allocating code pages. 
  866.  
  867. The goal of your analysis is to minimize or even eliminate waste within the 
  868. pages of code.  The data can be managed using the techniques described in 
  869. Chapter 14.  You control the memory allocations for data. The real analysis 
  870. takes place with the code. 
  871.  
  872.  
  873. ΓòÉΓòÉΓòÉ 7.1.1.2. Locality of Reference and Data Positioning ΓòÉΓòÉΓòÉ
  874.  
  875. Locality of reference refers to the real location of functions with respect to 
  876. each other. Of course, you as a programmer cannot control where the pages are 
  877. placed within physical memory, but you can control within which pages your code 
  878. resides.  The first step is to determine which functions reference which others 
  879. and how often. Then, use the link map of your build to see how big each of 
  880. these functions are. After that, the process is relatively simple.  Take the 
  881. functions that reference each other, and draw a hierarchy map. In this 
  882. hierarchy map you should map out which functions reference each other. You 
  883. should also write down each function's  size. Now, understanding that 
  884. everything is allocated in 4K pages you can use the  .DEF file and LINK 
  885. statements to group functions together that reference each other. 
  886.  
  887. The ideal goal is to keep the functions that reference each other most often on 
  888. the same physical page of code if possible. Of course, this may not be possible 
  889. due to the size of these functions or the number of functions that reference 
  890. each other.  Another possibility is to move some of the data structures 
  891. associated with some of the functions to global data rather than instance data. 
  892. This way the function will be smaller. By doing this you can also control the 
  893. locality of the data with your internal memory manager. Of course, there will 
  894. be times when you must have instance data automatically allocated with a 
  895. function invocation such as with reentrant functions, but this approach can 
  896. still be of value. 
  897.  
  898. In moving functions around to keep interfunction references within pages you 
  899. can also minimize waste. When looking at the functions to keep together you 
  900. should also look at their sizes. There really is no optimal way to figure out 
  901. whether to move a function that is referenced more often to another page so 
  902. that you can fit another two functions into the first page.  You need to make 
  903. those determinations based on the size, the function usage frequency, and your 
  904. knowledge of how the code works. Chances are that if you are splitting hairs 
  905. you won't generally be wrong. At that level of detail one choice versus another 
  906. may mean nothing more than a few milliseconds. 
  907.  
  908. You can organize the functions on a strictly mathematical basis (how many 
  909. references to a function versus how much is wasted moving to another code 
  910. page), but the real key is knowing how the code is referenced. For example, if 
  911. a particular feature of the application is used more often than others but the 
  912. others would save space if moved to a lesser-used function's location, you need 
  913. to see how much savings you get in either case. If the bottom line is nothing 
  914. more than wasting a few K of memory throughout the application, the locality of 
  915. reference of the functions is more important. The few K is not a big deal, but 
  916. if the application has to keep bouncing the same few pages in and out of memory 
  917. in a constrained system, your performance will be hampered. 
  918.  
  919. There is no formula for making these decisions. The function sizings and 
  920. references are your guides along with your intimate knowledge of your 
  921. application. 
  922.  
  923.  
  924. ΓòÉΓòÉΓòÉ 7.1.2. Dynamic Link Considerations ΓòÉΓòÉΓòÉ
  925.  
  926. The first thing to understand with dynamic linking by import (as opposed to 
  927. DosLoadModule/DosGetProcAddr runtime dynamic linking) is that every function 
  928. call requires fixups. Fixups are records that point to the real functions in 
  929. DLLs. As outlined in Chapter 5, DLLs contain external reference records that 
  930. point to functions in the DLLs. Each of these function calls must have some 
  931. kind of fixup at runtime to gain addressability to the actual function. 
  932.  
  933. There is a series of tables associated with executables as well as with DLLs 
  934. that cross-reference the functions in DLLs that are called by the application 
  935. programs. These tables are stored in the extended executable file header and 
  936. must be interpreted and resolved. 
  937.  
  938. The fixups are calculated at loadtime and are kept in swappable application 
  939. memory. When the call is actually made to the function, the fixup tables, if 
  940. not present, are brought back into memory, and the function call is resolved 
  941. and executed. In general, this is not bad, but by understanding this, you can 
  942. see ways to improve performance with some simple changes to your code.  The way 
  943. to do this is what I call API aliasing. What this means is to have only one 
  944. function in the application that calls a particular DLL function.  Whenever you 
  945. want the services of that API you call your own function (the alias). You will 
  946. create a function for each API function call you make in the application and 
  947. give it a name such as  MyDosAllocMem or MyDosCreateThread. It would take the 
  948. same parameters as the real API and return information the same way the API 
  949. does. 
  950.  
  951. The advantage of this is that there is only one fixup per API function used in 
  952. the code, as opposed to having fixups all over your code for each API function 
  953. call.  This latter approach would just add size to the fixup tables, take time 
  954. to resolve, and add size to the application working set (recall that the tables 
  955. remain in the application's swappable memory). 
  956.  
  957. An important decision in aliasing the API functions is how many of them you 
  958. actually call. If you use a particular API only  a relatively few times (say, 
  959. five or less) throughout the application, then the overhead of writing another 
  960. function to alias the API is probably not worthwhile. However, for functions 
  961. that are called on a regular basis an API alias will help your performance by 
  962. reducing the size of the tables and the number of fixups required.  In writing 
  963. the API aliases you create a bunch of near, direct references to your own 
  964. function throughout your code.  This keeps each (indirect) invocation of the 
  965. API out of the fixup tables since there is only one place in the application 
  966. where  the API is called.  This makes your working set smaller, the executable 
  967. file smaller, and the load time faster. 
  968.  
  969. Another technique you can use to reduce the working set of your application is 
  970. to look for functions in your DLLs that you reference infrequently. DLL 
  971. references to system code are really of no concern since most of that code is 
  972. resident during system operation anyway. 
  973.  
  974. Your own DLL references can, however, be set up to optimize locality of 
  975. reference for functions that call others within the same DLL.  Earlier we 
  976. discussed deciding which functions to put in DLLs.  Performance also plays a 
  977. part in this decision.  If you have a function that can fit in a page close to 
  978. its caller you may be better off in keeping that function in the  .EXE rather 
  979. than in the DLL. This is a moot point if the function needs to be shared 
  980. between processes (which is the major reason for putting a function in a DLL 
  981. anyway). The other half of this DLL optimization is to delay the loading of a 
  982. DLL until it is needed. 
  983.  
  984. By loading a DLL when you call a function in it by name you take the 
  985. performance hit at load time, which may be more desirable, but you'll also 
  986. increase the working set size of your application because that DLL will be in 
  987. use and loaded all the time. It can be swapped out in a constrained situation, 
  988. but look at the net result.  You'll load the DLL at the outset to take the hit 
  989. at load time. However, if the code in the DLL is hardly ever used (or not used 
  990. for a long time) it is likely to be paged out. If it is paged out when the 
  991. actual call is made into it, it will need to be paged in again. Now you have 
  992. loaded the page twice to use it once. 
  993.  
  994. By delaying the load by using  DosLoadModule/DosGetProcAddr you load the DLL 
  995. only when you need it. Since you'll take the performance hit if the code has to 
  996. be paged in at the time a jump into it is made or if it is being loaded for the 
  997. first time when the jump is made, at least you won't load it in at application 
  998. load time as well. There may be times, such as with frequently used code, when 
  999. you may want to place a function in a DLL despite this fact, but now you know 
  1000. the ups and downs of both approaches. 
  1001.  
  1002. Another important consideration is the life span of a DLL.  That is when you 
  1003. reference a DLL, how long after the function is used should the DLL be kept 
  1004. around?  If you are calling the functions by import or name you have no choice. 
  1005. The DLL is loaded for the life of the application. Of course, it (or parts of 
  1006. it) may be paged out, but that still consumes system resources. When it comes 
  1007. to DLLs loaded via DosLoadModule the choice is completely up to you. A good 
  1008. example is a "help" DLL. 
  1009.  
  1010. Help code is usually used for a short time and then released.  However, if the 
  1011. user is in some sort of learning mode, such as when an application is first 
  1012. being learned and used, help is requested more often.  You don't want to 
  1013. blindly free the DLL that contains the help code after each help function call, 
  1014. because if you make several calls close together you'll spend all the system 
  1015. resources loading and unloading that DLL.  You also have to consider times when 
  1016. an experienced user may need to find only one piece of information and will not 
  1017. request help again during the application's execution.  In that case you don't 
  1018. want the DLL hanging around.  You could put in a switch for something such as 
  1019. an "expert mode" whereby the DLL would indeed be freed after each help call 
  1020. since an expert may not need it more than once or twice.  Users who keep the 
  1021. expert mode switch off will keep the DLL loaded because they are likely to 
  1022. request help again. 
  1023.  
  1024. A help function is only one example. In general you should look at how and 
  1025. where your DLLs are used and how long they should remain loaded. Keep in mind 
  1026. that it takes resource to load and unload DLLs; you must weigh this against the 
  1027. size of the file. If you have a small DLL, for example, it may be feasible to 
  1028. keep the DLL loaded since it will take up only a small amount of swap space, 
  1029. whereas if the DLL is large you will need to make a trade-off decision. This 
  1030. may also lead you to make a set of smaller DLLs rather than one large one. 
  1031.  
  1032. All these facts must be considered when setting up your DLLs.  It is easy to 
  1033. forget, though, that the main reason for putting code in a DLL is to share it 
  1034. between  processes. If the code does not need to be shared you should simply 
  1035. keep it in the main executable file and manage where in the file it lives (via 
  1036. the  .DEF file and LINK statements) as outlined earlier. The same method of 
  1037. managing the code in the executable file can be applied to the DLL as well.