home *** CD-ROM | disk | FTP | other *** search
-
- ΓòÉΓòÉΓòÉ 1. Title Page and Book Information ΓòÉΓòÉΓòÉ
-
- Real-World Programming for OS/2 2.1
-
- Derrel Blain
-
- Kurt Delimon
-
- Jeff English
-
-
- ΓòÉΓòÉΓòÉ 2. Copyright ΓòÉΓòÉΓòÉ
-
- This book is dedicated to our wives, Laurie, Beth, and September,
- each of whom is a professional in her own right.
- Thanks for enabling us to take the time out of
- our lives and the general business of living to spend
- even more time glued to our computers.
-
- (C) 1993 by Sams
-
- All rights reserved. No part of this book shall be reproduced, stored in a
- retrieval system, or transmitted by any means, electronic, mechanical,
- photocopying, recording, or otherwise, without written permission from the
- publisher. No patent liability is assumed with respect to the use of the
- information contained herein. Although every precaution has been taken in the
- preparation of this book, the publisher and author assume no responsibility for
- errors or omissions. Neither is any liability assumed for damages resulting
- from the use of the information contained herein. For information, address Sams
- Publishing, 11711 N. College Ave., Carmel, IN 46032.
-
- International Standard Book Number: 0-672-30300-0
-
- Library of Congress Catalog Card Number: 92-82105
-
- 96 95 94 93 4 3 2 1
-
- Interpretation of the printing code: the rightmost double-digit number is the
- year of the book's printing; the rightmost single-digit, the number of the
- book's printing. For example, a printing code of 93-1 shows that the first
- printing of the book occurred in 1993.
- Trademarks
-
- All terms mentioned in this book that are known to be trademarks or service
- marks have been appropriately capitalized. Sams Publishing cannot attest to the
- accuracy of this information. Use of a term in this book should not be regarded
- as affecting the validity of any trademark or service mark. OS/2 is a
- registered trademark of International Business Machines Corporation.
-
- Screen reproductions in this book were created by means of the program Collage
- Plus from Inner Media, Inc., Hollis, NH.
-
- Composed in AGaramond and MCPDigital by Prentice Hall Computer Publishing
-
- Printed in the United States of America
-
-
- ΓòÉΓòÉΓòÉ 3. Ordering Information ΓòÉΓòÉΓòÉ
-
- Sams
-
- To order this book or any Sams publication, call 1-800-428-5331 between 9m. and
- 5m. EST. For faster service please have your credit card available.
-
- 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.
-
-
- ΓòÉΓòÉΓòÉ 4. Foreword ΓòÉΓòÉΓòÉ
-
- In our five years of publishing for OS/2 application developers, we have
- learned what programmers need: real-life, function-filled, practical
- programming examples. Both new and intermediate developers seem to learn best
- from good source code samples. Often, they will cut and paste, building a new
- application on a working base, learning as they go.
-
- Real-World Programming for OS/2 2.1 understands and meets that need. This book
- is filled with practical, meaty tips and techniques about OS/2 programming. The
- authors deliver their considerable experience in the form of useful sample
- programs and narrative. They have tested their code on four popular OS/2
- compilers, and they have provided it on a diskette for the convenience of their
- readers.
-
- This book sets a new standard in practical programming. The authors are to be
- congratulated!
-
- Dick Conklin
- Editor
- OS/2 Developer Magazine
-
-
- ΓòÉΓòÉΓòÉ 5. Acknowledgments ΓòÉΓòÉΓòÉ
-
- We would like to say thanks to some of the many individuals who helped us in
- this project. Thanks to Karen Ali and Mike Polla of Watcom, David Intersimone
- and Dan Horn of Borland, Constantine of Zortech, and David Mooney and Maxine
- Houghton of IBM. Thanks also to Andy Cohen of Micrografx.
-
-
- ΓòÉΓòÉΓòÉ 6. Table of Contents ΓòÉΓòÉΓòÉ
-
- Foreword
-
- Programming for OS/2
- The Nature of This Book
- Compilers Used
- Building the Applications
- Installing Your Compiler
- Installing the Software
- Building a Sample Application
- Heading Out on Your Own
-
- OS/2 Application Window Fundamentals
- Windows
- Parent-Child Relationship
- Ownership
- Messages
- The Window Procedure
- Window Procedures Return Values
- SKELETON Application
- Component Files
- SKELETON.C
- The Main Procedure
- Creating the Application Window
- Message Queues
- Registering a Class
- Loading a String
- SKELETON.H
- SKELETON.RC
- Compiling SKELTON.RC
- SKELETON.DEF
- SKELETON.ICO
- Using the SKELETON Application
- Useful Window Reference Material
- Common Window Functions
- Standard Window Classes
- Class Styles
- Standard Window Styles
- Dialog Manager Styles
- Frame Control Flags
- Frame Contol Identifiers
- Predefined Window Handles
- Includes
-
- Window Management
- Introduction
- The Family Tree
- A Look at the Frame Window
- Window Characteristics
- Window Data
- Window Sizing and Positioning
- Window Focus and Activation
- Application Activation
- Window Subclassing
- Titlebar Painting
- Mouse Pointer
- Preventing Window Tracking
- Restricting a Window's Size
- Window Icons
- Message Processing
- WM_USER Messages
- Multiple Document Interface Application
- MINIMDI.RC
- TOOLBAR.DLG
- WININFO.DLG
- MINIMDI.C
- MINIMDI.H
- MINIMDI.DEF
- How MINIMDI Works
-
- 2.1 Common Dialogs
- Introduction to Common Dialogs
- Practice with the CONTROL Sample Application
- The File Menu
- The Font Menu
- Development of the Sample
- File Dialog
- Creating a Simple Application
- The FILEDLG Structure
- Using Nondefault Values
- Custom Template
- Multiple File Selection
- Save As Dialog
- Font Dialog
- Understanding the Basics of Fonts
- Font Representation
- The Common Font API
- Basic File Dialog
- Changing the Font
- Font as Logical Resource
- Making the Dialog Modeless
- Font Notification Messages
- Owner Draw Preview
- The CONTROL Sample Application
- Files to Build the CONTROL Sample
- COMMDLG.C
- COMMDLG.H
- COMMDLG.RC
- CUSTOM .DLG
- COMMDLG.DEF
-
- Menus
- Introduction
- Types of Menus
- Pull-Down and Cascaded Menus
- Pop-Up Menus
- Menu Components
- Menu Hierarchy
- Menu Window Styles
- Menu Items
- Specifying Menus in the RC File
- Menu Messages
- Menu Notification Messages
- Menu Item Selection
- Menu Application
- Menu Application Files
- MENU.C
- MENU.H
- MENUXTRA.H
- MENU.RC
- MENU.DEF
- Using the Menu Application
- Basic Menu and Changing Menu Items
- Changing the Menu Item Checkmark
- Bitmap Menu Items
- OWNERDRAW Menu Items
- Pop-Up Menu Window
- Replacing the Actionbar Menu
- Using the Same Submenu in Multiple Menus
- Changing the System Menu
- Menu Messages
- Menu Messages
- MM_QUERYSELITEMID_ 0x018a
- Menu Notification Messages
-
- Presentation Spaces and Drawing
- Introduction of GPI
- Presentation Spaces (PS)
- Types of Presentation Spaces
- WM_PAINT & WM_ERASEBACKGROUND
- Summary of Presentation Space Functions
- Cached Micro Presentation Spaces
- hPS WinGetPS (hWnd)
- hPS WinBeginPaint(hWnd,hPS,pRectl)
- Micro or Normal Presentation Spaces
- hPS GpiCreatePS (hAB,hDC,pSizel,lOptions)
- hPS WinBeginPaint(hWnd,hPS,pRectl)
- GpiAssociate(hPS,hDC)
- GpiDestroyPS(hPS)
- Device Contexts
- Device Capabilities
- Coordinates
- Default Presentation Space Attributes
- BUNDLES
- GpiSetAttrs (hPS,lPrimType,ulAttrMask, ulDefMask,pBundle)
- Area Bundle
- usSet
- usSymbol
- ptlRefPoint
- CHARBUNDLE
- LINEBUNDLE
- fxWidth
- lGeomWidth
- ulType
- ulEnd
- ulJoin
- MARKERBUNDLE
- usSet
- usSymbol
- sizefxCell
- Color Models
- Line and Arc Primitives
- Drawing a Thermometer
- Areas and Paths
- Areas
- Paths
- Resources
- Presentation Space Attribute Defaults
- Functions That Can Be Cal
- in a Path Bracket
- The Drawing Sample Application
- DRAW.H
- DRAW.C
- DRAW.DEF
- DRAW.RC
- THERMO.H
- THERMO.C
-
- Creating and Manipulating PM Fonts and Text
- Introduction
- Terminology
- Single-Byte and Double-Byte Characters
- Point Size
- Font Families
- Font Rendering
- Default Font
- Text Output Functions
- Text Extents and Spacing
- Common Dialog
- Enumeration
- The FONTMETRICS Structure
- Creating Fonts
- Scaling, Shearing, and Rotating
- Character Modes
- Rotation and Shear in PMFONT
- Character Bundle Structure
- Logical Inches
- Code Pages
- The PMFONT Application
- PMFONT.C
- PMFONT.H
- PMFONT.RC
- PMFONT.DLG
- PMFONT.DEF
- Font Class sFamilyClass
-
- Profile Management
- Introduction
- The Profile Management API
- Enumerating the Entries in the INI File
- Adding Entries
- Deleting Entries
- The INITOR Sample Application
- INITOR.C
- INITOR.DEF
- INITOR.H
- INITOR.RC
- Using INITOR
- Looking at System Initialization Files
- Using Your Own INI File
- Spooler Functions_Additional Profile API
-
- Memory Management
- The Memory Architecture of OS/2 2.1
- 16-Bit and 32-Bit Memory
- 32-Bit Memory
- The Memory Manager API
- NonShared Memory
- Suballocated Memory
- Shared Memory
- Converting from Unshared to Shared Memory
- The MEMMAP Application
- MEMMAP's Files
- MEMMAP.C
- MEMMAP.H
- MEMMAP.RC
- MEMMAP.DLG
- Using MEMMAP
- Understanding MEMMAP's Code
- The MEMORY Application
- MEMORY's Files
- MEMORY.C
- MEMORY.H
- MEMORY.RC
- ALLOCATE.DLG
- MEMORY.DLG
- MEMORY.DEF
- Using the MEMORY Sample Application
- Understanding MEMORY's Code
-
- Dynamic Link Libraries
- What Is a DLL and How Does It Work?
- How to Build a DLL
- DLLSAMP.C
- DLLSAMP.H
- DLLSAMP.DEF
- APPSAMP.H
- APPSAMP.RC
- APPSAMP.C
- The LIBPATH Statement
- Runtime Dynamic Linking
- DLLSAMP.H
- APPSAMP2.C
- Global versus Instance Data
- Initialization and Termination
- DLLINIT.ASM
- DosExitList
- OS2.INI
- Resource-Only DLLs
- Thermometer Application
- CONTROLS.H
- CONTROLS.DEF
- CTLINIT.ASM
- CONTROLS.C
- MEMDLL.H
- MEMDLL.DEF
- MEMINIT.ASM
- MEMINST.H
- MEMINST.C
- MEMDLL.C
- THERMO.RC
- THERMO.DLG
- THERMO.H
- THERMO.DEF
- THERMO.C
-
- Printing
- Introduction
- Some Printing Background: The DOS Days
- Device Independence
- Printing Under OS/2 1.x and Windows 3.x
- OS/2 2.1 Print Objects
- Spooler
- Determining Installed Devices
- SplEnumQueue
- PRQINFO3/PRQINFO6
- PRQINFO6 Only
- Identifying the Default Print Object
- Printer versus Job Properties
- Printer Properties
- Job Properites
- Posting the Job Properties Dialog: DevPostDeviceModes
- Creating an Information Device Context
- Querying Device Forms
- Determining Page and Printable Area Size
- Calculating Printer Offset
- Basic Printing
- Printing in an Application
- Tracking Your Print Job Using the Spooler API
- SplControlDevice
- SplCopyJob
- SplDeleteJob
- SplEnumJob
- SplHoldJob
- SplHoldQueue
- SplPurgeQueue
- SplQueryJob
- SplReleaseJob
- SplReleaseQueue
- SplSetJob
- Installing Private Device Drivers
- Converting Profile Quer
- to the Spooler Equivalents
- PM_SPOOLER
- PM_SPOOLER_PRINTER
- PM_SPOOLER_PRINTER_DESCR
- PM_SPOOLER_QUEUE_DESCR
- The PRNT Application
- PRNT.DLG
- DIALOG.H
- PRNT.C
- PRNTINFO.C
- PRNT.DEF
- PRNT.H
- PRNT.RC
-
- Threads and Semaphores
- Multitasking in OS/2
- Three Types of Multitasking
- Threads and Time Slices
- Priority Classes and Priority Levels
- OS/2 System Settings for Threads
- Working with Threads
- The Thread Function
- Threading Considerations
- Semaphores
- Event Semaphores
- Mutual Exclusion Semaphores
- Multiple Wait Semaphores
- Thread Demonstration Application
- THREADS.H
- THREADS.RC
- TID.DLG
- SORT.DLG
- THREADS.C
- Thread Observations
-
- Calling 16-Bit Code
- Introduction
- Tiled Memory
- Data Address Conversion
- Code Address Conversion
- Structure Alignment
- Thunks
- Thunk Application
- BOOK16.DEF
- BOOK16.C
- BOOK16.H
- THUNKS.DEF
- THUNKS.RC
- BOOKINFO.DLG
- THUNKS.H
- THUNKS.C
- THUNKASM.ASM
-
- Communications Basics
- Introduction
- Features of OS/2 Communications
- Hardware Settings
- Flow Control
- Communicating w
- the ASYNC Device Driver
- The DosDevIOCtl Function
- Phone Application
- Main Thread
- LED.C
- LED.H
- Files Required to Build the Phone Application
- PHONE.C
- PHONE.H
- PHONE.RC
- PHONE.DEF
- SETTINGS.DLG
- STATUS.DLG
- BUTTONS.DLG
- Understanding PHONE.C
-
- Miscellaneous Topics
- Miscellaneous Application
- MISC.C
- MISC.H
- MISC.RC
- MISC.DEF
- Obtaining Thread Information
- PIBTIB.C
- PIBTIB.H
- PIBTIB.DLG
- Querying System Information
- SYSINFO.C
- SYSTINFO.H
- SYSTINFO.DLG
- Determining Drive Types
- DRIVES.C
- DRIVES.H
- DRIVES.DLG
- File Searching
- FILESRCH.C
- FILESRCH.H
- FILESRCH.DLG
-
-
- ΓòÉΓòÉΓòÉ 7. Chapter1 - Programming for OS/2 ΓòÉΓòÉΓòÉ
-
- Programming for OS/2
-
-
- ΓòÉΓòÉΓòÉ 7.1. The Nature of This Book ΓòÉΓòÉΓòÉ
-
- More than likely you bought this book because you have a programming problem to
- solve. That's the reason we wrote it. We know from experience that the way to
- start programming on a new platform and the way to solve many programming tasks
- is not to stand back theorizing for a couple of weeks about how it should be
- done. Instead, you get a good example to work from, then tear into it with your
- own additions and changes. Now, more than likely, your first efforts will serve
- only as a stepping stone for later development, but you need that initial
- experience to build on.
-
- Trouble is, many times the software development kits do not have the room to
- provide examples for you to work with, and the last thing the world needs is
- another programming book showing you how to write another "Hello, World"
- program, only for OS/2. This book covers areas of programming that are largely
- ignored or poorly exemplified in software development kits and documentation.
- It is aimed at intermediate level programmers. This does not mean you must be
- an intermediate level programmer for OS/2, only that you should have the habit
- of analysis and be familiar with coding concepts that come with programming
- experience.
-
- Again, the emphasis is on developing code and techniques directly applicable to
- your programming efforts. Each sample application in this book is designed as a
- framework on which you can found your efforts. General areas of knowledge
- covered are message-based architecture, working with OS/2 windows, working with
- fonts, working with printers, creating DLLs, interprocess coordination, menus,
- and drawing. Although you can work from the front to the back, it is not
- necessary to follow only that path. We suggest that you first work through
- Chapters 2, "OS/2 Application Window Fundamentals," 3, "Window Management," and
- 4, "2.1 Common Dialogs," consecutively. After that, you should be able to dive
- into the chapters that apply to your particular need.
-
- The sample applications in this book are longer than usual, but we felt this
- was nec-essary to provide you with a useful framework. As a result, there is
- not a line-by-line discussion of each sample; otherwise, the book would have
- been huge! We suggest you first read the discussion about the concepts and
- important API entries at the beginning of each chapter. After that, load the
- working executable from the companion disk and spend some time working with it.
- This will help familiarize you with the general issues involved. Then, you can
- begin working through the code to see how the general concepts and behavior of
- the application have been realized in the code.
-
- The applications in this book use the graphical user interface facilities
- provided by the Presentation Manager API of OS/2. This continues to be an area
- in which many programmers need some assistance. Don't forget that OS/2 still
- allows character-based applications to run perfectly well in a window or
- full-screen session. These types of applications just aren't the focus of this
- book.
-
- This is a book designed to help you solve some of the more difficult issues
- involved in creating a nontrivial program for OS/2. We have tried to address
- the issues that programmers frequently face_things like how to open a file by
- using the common file dialogs and how to use multithreading.
-
- That's what the book is, but we should also say a word about what the book is
- not. This is not a programming manual about how to program in C. There simply
- isn't room. This book also is not simply a recapitulation of the OS/2 Technical
- Library. We have tried to distill the essential elements; we have tried to
- bring together things that are sometimes located all over the documentation in
- several different manuals; and we hope this helps; but really this book is a
- collection of applications that shows how to get something done_something
- usually fairly complicated. We tried to go far beyond the examples you will see
- both in the documentation for OS/2 and for the compiler that you will be using.
-
- You are welcome to build applications on the code that we supply with the book.
- We, however, do insist that you do not redistribute the software or the code
- from this book without substantial modification and addition on your part.
-
- If you have not built an application for OS/2 yet with your compiler, we
- certainly recommend working through one of the smaller examples supplied with
- the compiler before tackling one of the applications in this book. This will
- ensure that your environment is set up correctly and will save you a
- considerable amount of frustration.
-
- Each application on the disk that accompanies the book has a number of files
- associated with it. There is a compiled executable, along with a command file,
- a make file, and the C, DEF, RC, ICO, DLG, and all the other files necessary to
- build the application. To begin understanding the applications, we suggest you
- first use the compiled versions that are shipped on the disk.
-
-
- ΓòÉΓòÉΓòÉ 7.2. Compilers Used ΓòÉΓòÉΓòÉ
-
- We wrote the sample applications for this book to be compatible with the four
- major C compilers in the OS/2 environment. These are the Watcom, Borland,
- Zortech, and IBM C Set/2 compilers. Each has its own characteristics, which may
- make one more desirable than another.
-
-
- ΓòÉΓòÉΓòÉ 7.3. Building the Applications ΓòÉΓòÉΓòÉ
-
- The fact that a suite of applications could actually be made compatible across
- all these compilers says volumes about the stability of the platform and the
- tools developed for it. Each application has been provided with a make file
- that can be used to build the application for each of the compiler products.
- This process is described in greater detail in this chapter in the section
- "Building a Sample Application." You can, of course, use the integrated
- development environments provided with the compilers. Merely follow their
- instructions about where to place particular modules, switches, and so forth.
-
-
- ΓòÉΓòÉΓòÉ 7.4. Installing Your Compiler ΓòÉΓòÉΓòÉ
-
- Each of the compilers installs in a different way. You must follow the
- instructions for the compiler you plan to use. Further, we suggest that you
- practice with some of the smaller examples provided with your compiler. This
- will ensure that your development environment is set up correctly before you
- take on some of the larger projects that accompany this book. We strongly
- suggest you ensure that the PATH, INCLUDE, and LIBPATH environment variables
- are set correctly.
-
-
- ΓòÉΓòÉΓòÉ 7.5. Installing the Software ΓòÉΓòÉΓòÉ
-
- We have provided an installation program with the software. This installation
- program will copy the software from the companion distribution disk to your
- hard drive. It will also ensure that the proper directory structure to contain
- the software is set up on your hard drive. To use the installation program,
- start an OS/2 session, either full screen or in a window. Change to the drive
- containing the distribution disk. Then, type this command:
-
- INSTALL
-
- First, the installation program will present you with the screen shown in
- Figure 1.1. Figure 1.1. Opening screen shot file.
-
- You may choose which chapters to install. You may also select a check box to
- cause the installation program to place all the programs automatically in a
- folder on the desktop. You may also change the root directory under which the
- software will be installed. If you do not make a change to the default
- directory name, the software will be installed in a directory structure by
- chapter under a directory named \RWPOS2.
-
- Once the installation has begun, you will be presented with the dialog box
- shown in Figure 1.2. Figure 1.2. Dialog box file.
-
- This dialog box will change to show the progression of the installation.
-
-
- ΓòÉΓòÉΓòÉ 7.6. Building a Sample Application ΓòÉΓòÉΓòÉ
-
- After your compiler is installed on your system and the sample software is
- installed, you are almost ready to build the samples. Notice that executable
- versions of the samples are shipped for each chapter. You do not have to run
- the make utility to use the programs. We included the make files, however,
- because more than likely you will want to make additions and changes to the
- samples.
-
- First, before building any of the applications, you need to indicate which of
- the compilers you will be using to create the application. This is done by
- setting an environment variable for the compiler. This environment variable is
- used in the command file to determine which of the compilers to use to build
- the application for the GO command file. To set up the environment variable use
- this command:
-
- SET COMPILER=
-
- You may choose from these:
-
- BORLAND
- WATCOM
- IBMC
- ZORTECH
- CL386
-
- For example, if you are using the Watcom compiler, enter this command:
-
- SET COMPILER=WATCOM
-
- To make it even easier, we suggest making this line a part of your CONFIG.SYS
- file.
-
- There is a file common to all the applications. This is the About box. Before
- you can build any of the applications, this needs to be created with the
- compiler that you will be using. After you have set the environment variable,
- therefore, change to the \COMMON directory and enter the GO command. You are
- now ready to begin building the samples.
-
- Each sample is built by using the following:
- o The modules that make up the application code. These include C code
- modules, resources, and include files.
-
- o A file that is a list of dependencies for each sample. This file will
- have the extension .DEP.
-
- o A make file for each of the four compilers.
-
- o An OS/2 command file that starts the make process.
-
- The SKELETON application, for example, will have these files in its installed
- directory:
-
- SKELETON.DEP
- SKELETON.H
- SKELETON.DEF
- GO.CMD
- SKELETON.C
- SKELETON.RC
- SKELETON.OBJ
- SKELETON.RES
- SKELETON.MAP
- SKELETON.ICO
- SKELETON.EXE
-
- To build this sample, change to the directory that contains these files and
- enter the following command:
-
- GO
-
- This will cause the command file to start the build for the SKELETON
- application. The actual lines in the command file are as follows:
-
- @ECHO --------------------------------------------------
- @ECHO | Skeleton Application |
- @ECHO | Chapter 2 |
- @ECHO | Real-World Programming for OS/2 2.1 |
- @ECHO | Copyright (c) 1993 Blain, Delimon, and English |
- @ECHO --------------------------------------------------
- @if "%COMPILER%"=="" goto nocompiler
- @if %COMPILER%==CL386 set MAKECMD=nmake
- @if %COMPILER%==cl386 set MAKECMD=nmake
- @if %COMPILER%==BORLAND set MAKECMD=make
- @if %COMPILER%==borland set MAKECMD=make
- @if %COMPILER%==IBMC set MAKECMD=nmake
- @if %COMPILER%==ibmc set MAKECMD=nmake
- @if %COMPILER%==WATCOM set MAKECMD=wmake
- @if %COMPILER%==watcom set MAKECMD=wmake
- @if %COMPILER%==ZORTECH set MAKECMD=make
- @if %COMPILER%==zortech set MAKECMD=make
- %MAKECMD% -f ..\%COMPILER%.mak MAKEFILE=skeleton.dep
- @goto done
- :nocompiler
- @ECHO
- @ECHO ╨┐╨┐╨┐ COMPILER environment variable must be set to compiler name
- @ECHO
- :done
-
- The make file follows the usual structure. There is a make file for each of
- the compilers supported. Each of these is in the root directory where you
- installed the distributed files. Let's say you are using the Borland compiler.
- The make file for this compiler has these lines:
-
- #====================================================================
- # Borland C++ for OS/2
- # Compiler Definition File
- # Real-World Programming for OS/2 2.1
- # Copyright (c) 1993 Blain, Delimon, and English
- #====================================================================
-
- #=====================================================================
- # Inference rules
- #=====================================================================
-
- BORLANDEXEOPTS = -m -aa -Toe -L$(LIB)
- BORLANDDLLOPTS = -m -Tod -L$(LIB)
- ABOUT = ..\common\about.obj
-
- .c.obj :
- @echo ╨┐╨┐╨┐
- @echo ╨╛╨╛╨╛ Compiling $*.c using Borland options ╨┐╨┐╨┐
- @echo ╨┐╨┐╨┐
- bcc -c -K -w -D__BORLAND__ $(BORLANDOPTS) $*.c > $*.err
- type $*.err
-
- .asm.obj :
- @echo ╨┐╨┐╨┐
- @echo ╨╛╨╛╨╛ Assembling $*.asm using MASM386 options ╨┐╨┐╨┐
- @echo ╨┐╨┐╨┐
- tasm -ml -mx -d__TASM__ -oi -w2 $*.asm,$*.obj;
-
- .rc.res :
- @echo ╨┐╨┐╨┐
- @echo ╨╛╨╛╨╛ Running resource compiler on $*.rc ╨┐╨┐╨┐
- @echo ╨┐╨┐╨┐
- rc -r $*.rc
-
- .def.lib :
- @echo ╨┐╨┐╨┐
- @echo ╨╛╨╛╨╛ Creating import library from $*.def ╨┐╨┐╨┐
- @echo ╨┐╨┐╨┐
- implib $*.lib $*.def
-
- .obj.exe :
- @echo ╨┐╨┐╨┐
- @echo ╨╛╨╛╨╛ Building $*.exe using Borland options ╨┐╨┐╨┐
- @echo ╨┐╨┐╨┐
- tlink $(BORLANDEXEOPTS) c02 $(OBJS) $(ABOUT),$*.exe,$*.map,$(LIBS)
- c2 os2386,$*.def
-
- rc $*.res $*.exe
-
- .obj.dll :
- @echo ╨┐╨┐╨┐
- @echo ╨╛╨╛╨╛ Building $*.dll using Borland options ╨┐╨┐╨┐
- @echo ╨┐╨┐╨┐
- tlink $(BORLANDDLLOPTS) $(OBJS)
- $(BORLANDINITOBJ),$*.dll,$*.map,$(LIBS) os2386,$*.def
-
- rc $*.res $*.dll
-
- !include $(MAKEFILE)
-
- # MEMINST.OBJ must be built this way to force the _INSTANCEDATA segment
- # to be placed in a separate segment from the _DATA segment
- # The -oi option of tasm creates the correct kind of object record
- # for the linker
- meminst.obj : meminst.c
- bcc -c -K -w -S -D__BORLAND__ -zR_INSTANCEDATA -zTFAR_DATA $(BORLANDOPTS) $*.c >
- $*.err
- tasm -ml -mx -d__TASM__ -oi -w2 $*.asm,$*.obj;
- del $*.asm
-
- This file, in turn, depends on a file that lists the dependencies for the
- particular sam-ple being made. There is a dependency file for each sample. In
- Chapter 2, "OS/2 Application Window Fundamentals," in the SKELETON
- application, the dependency file is named SKELETON.DEP. It contains these
- lines:
-
- #====================================================================
- # Skeleton Dependencies Make File
- # Chapter 2
- # Real-World Programming for OS/2 2.1
- # Copyright (c) 1993 Blain, Delimon, and English
- #====================================================================
-
- #====================================================================
- # Dependencies and Build Items
- #====================================================================
-
- OBJS = skeleton.obj
-
- all: skeleton.exe
-
- skeleton.obj: skeleton.c skeleton.h
-
- skeleton.res: skeleton.rc skeleton.ico
-
- skeleton.exe: $(OBJS) skeleton.res skeleton.def
-
-
- ΓòÉΓòÉΓòÉ 7.7. Heading Out on Your Own ΓòÉΓòÉΓòÉ
-
- After you have built a few of the applications just as they are installed, you
- can begin to modify them to suit your own purposes. We suggest making small
- changes at first. This will keep you from getting too far off the track. Write
- if you get work.
-
-
- ΓòÉΓòÉΓòÉ 8. Chapter 9 - Memory Management ΓòÉΓòÉΓòÉ
-
- Memory Management
-
-
- ΓòÉΓòÉΓòÉ 8.1. The Memory Architecture of OS/2 2.1 ΓòÉΓòÉΓòÉ
-
- Many developers starting out programming for OS/2 2.1 have programmed for DOS.
- The clunky, segmented addressing architecture demanded by the chips on which
- DOS runs is notorious for its inefficiency and quirkiness. A 640K limitation
- and competing and confusing schemes such as extended and expanded memory are
- the result of this segmented architecture. OS/2 2.1, running on the 80386 chip,
- is capable of leaving all this behind.
-
- OS/2 2.1 has a linear address space. That is, the address of each byte that
- follows another is incremented by one. Some call this a "flat address space."
- Memory allocation in OS/2 2.1 takes advantage of the 32-bit protected mode of
- the Intel 80386 and 80486 processors. Because OS/2 2.1, as of this writing,
- runs on a chip that uses 32-bit pointers, 4GB of memory may be addressed. Not
- all this memory, however, is immediately available to an application, nor is
- the memory architecture as simple as the term "flat address space" implies.
-
- Remember, OS/2 2.1 is a true multitasking operating system. This means that a
- number of different processes, which may have any number of threads, occupy the
- system memory simultaneously along with the operating system. If any process
- could access any place in memory at will, confusion and corruption would often
- be the result. Further, if the physical address space taken by one application
- was large, it might limit another from running. More often than not this would
- be the case because although memory is cheap, it's not yet free. Most systems
- do not have 4GB laying about for use. Most office and home systems have 8
- physical megabytes available to the CPU, so some mechanism is necessary to
- allow applications to use a much larger virtual address space.
-
- In OS/2 2.1, each process can access 512M of linear address space. This area of
- memory is referred to as the process address space. The 512M limit is incurred
- by the need to support both 16-bit and 32-bit applications. This 512M of
- potential memory is, in fact, further limited to the amount of physical memory
- added to the amount of available hard disk memory.
-
- To make this large address space available to multiple applications in the much
- smaller space of physical memory available on most machines, OS/2 2.1 uses a
- paged memory system. This 512M of memory for each process is completely
- unrelated to the 512M available to other processes that may be running on the
- machine. The total amount of committed memory for all processes, however,
- cannot exceed the amount of physical memory plus the available hard disk space.
- The entire 4GB of address space is partitioned into private, shared, and system
- memory. Figure 9.1 illustrates how the address space is allocated to these
- three regions.
-
- Of the 64M reserved for private memory allocation, the first 64K is reserved
- for operating system overhead. The address range from 448M to 512M is reserved
- for shared memory. Addresses above 512M are in the OS/2 system space; these are
- where the operating system kernel is loaded. The remaining 384M between
- addresses 64M and 448M is allocated as needed for either private or shared
- memory.
- Figure 9.1. OS/2 address space.
-
- Private memory for each process grows upward (upward in value ) from the bottom
- 64K. This is true for every process running on the system. Shared memory starts
- at the top of the 512M address space and grows downward. Shared memory is
- memory shared across processes. Keep in mind that a process is different from a
- thread. A process may have many threads, but processes are individuals to
- themselves. Shared memory is used to share memory across processes as well as
- for Dynamic Link Library (DLL) code and data. Private memory is composed of the
- application's code, data, heap, stack, and allocated non-shared memory objects.
-
- Shared memory is allocated for all processes. For example, if several processes
- are running and one allocates 2M of shared memory, this shared memory is
- allocated from the shared memory address space for all the processes. This is
- true even if some of the processes do not have access to the allocated memory.
-
-
- ΓòÉΓòÉΓòÉ 8.2. 16-Bit and 32-Bit Memory ΓòÉΓòÉΓòÉ
-
- To fully understand the allocation scheme, you will find it helpful to first
- understand the 16-bit segmented allocation scheme. Prior versions of OS/2
- relied on the segmented memory model, and all memory was addressed by a 16-bit
- selector and 16-bit offset. Memory was allocated by blocks of up to 64K, and a
- 16-bit segment selector was returned. Memory was then accessed by combining the
- 16-bit selector with a 16-bit offset, with all offsets starting at 0. Memory
- was allocated and accessible immediately after allocation. This architecture
- limits each segment to 64K, so allocating blocks larger than 64K required the
- segment register to be changed to match the desired portion of allocated
- memory.
-
- OS/2 2.1 provides support for true 32-bit memory allocation and access, but it
- also still supports 16-bit segmented memory allocation for 16-bit applications.
- As it is possible for 32-bit applications and 16-bit applications to share data
- address spaces (shared memory) and also to pass memory addresses between each
- other, it is necessary to allow for differences in the addressing scheme. This
- support is provided by placing several restrictions on the 32-bit memory
- allocation process.
-
- In the 32-bit world, all addresses are near addresses into what is called the
- FLAT address space. The FLAT address space, sometimes called linear address
- space, is the entire 4GB of addressable memory. Because the 16-bit world
- requires all segment offsets to start at 0, you can see that only those
- addresses in the 32-bit space which have 0 as the low word of their address
- should map to segment boundaries. This is done in OS/2 by what is called memory
- tiling, or TILED memory, which restricts the base linear addresses to multiples
- of 64K. Figure 9.2 shows how the two addressing schemes can share the same
- memory space through tiling.
- Figure 9.2. Tiled memory architecture.
-
- Notice that all selectors have the low 3-bits set and that the memory
- referenced starts on a 64K multiple. Because the system reserves the first 64K,
- it is never possible to have a selector value of 0. The process of converting
- linear addresses to a 16-bit selector and offset and vice versa will be covered
- in Chapter 13, "Calling 16-Bit Code," which discusses the concept of thunking.
-
- OS/2 2.1 enables you to force memory allocation to follow the TILED memory
- restriction when memory is allocated. Using this option does reduce the amount
- of available memory address space because the entire range of 64K is reserved
- on such an allocation request. This is discussed later when we get into the
- memory management API.
-
-
- ΓòÉΓòÉΓòÉ 8.3. 32-Bit Memory ΓòÉΓòÉΓòÉ
-
- The fundamental unit of memory for OS/2 2.1 memory management is the page. A
- page of memory is always 4,096 bytes, 4K. If an application needs to allocate
- only 64 bytes of memory, this still causes a whole page, 4,096, to be allocated
- to contain that 64 bytes. If more than a page of memory is asked for, a page
- range is allocated. A page range is several contiguous pages.
-
- The total virtual memory space available to a process is divided into pages.
- Each of these pages may be in one of only three states at any particular
- moment. These three states are as follows:
- Free The page has not been allocated.
- Private The process has allocated the page for use.
- Shared The page has been allocated as memory to be shared between
- processes.
-
- Memory pages have an additional pair of characteristics. A memory page may be
- committed or uncommitted. Committed pages within memory objects have been
- allotted physical storage for the logical address range. Uncommitted pages are
- not yet backed by physical storage.
-
- Memory allocation merely reserves a range of memory addresses in the process
- address space. It does not use any physical memory until the memory is
- committed and referenced. Memory commitment is the process of physically
- allocating memory from the system and assigning it to the address space that
- has been allocated. Requests to commit memory are done on a page basis. A page
- in memory cannot be accessed until the page has been committed. Attempts to
- access uncommitted memory will result in a protection violation.
-
- Memory objects are allocated in units of 4K pages. All memory requests are
- resolved to a page boundary. All pages allocated are fully addressable. A
- request to allocate 1 byte of memory will result in a 4K page being allocated.
- The entire 4K is available for use by the application. A request to allocate
- 4,097 bytes of memory will result in two 4K pages being allocated. Again, the
- entire 8K is available for allocation. For small memory requests, this can
- result in a lot of unused memory being allocated, thus reducing the amount of
- memory available for use. We will discuss ways to handle such requests more
- efficiently later in this chapter.
-
- When an application no longer requires the use of a physical memory object, it
- can either be freed or decommitted. Decommitment allows the application to
- commit the memory again without the need for a memory allocation request.
-
- Notice that applications do not actually allocate "pages" for their use.
- Applications or processes allocate memory, which is then set aside by the
- operating system in terms of pages. Applications allocate and use memory as
- memory objects. Memory objects are one or more pages. Processes or
- applications may allocate more than one memory object. These memory objects
- have the following characteristics:
-
- o Each is allocated in increments of 4K.
-
- o Each has memory objects that are not relocatable.
-
- The operating system reserves a linear address range when an application asks
- it to allocate memory. This address range, however, is not backed by physical
- memory until the memory is committed. Any attempt to use uncommitted memory
- will cause an access violation.
-
- Memory may be committed when it is allocated, or it may be allocated as
- uncommitted and then committed at a later time. If the memory is committed
- when allocated, all of it is committed, and it is available for immediate use.
- If the memory is allocated as uncommitted, however, individual portions of the
- memory may be allocated at different times later. Allocated memory objects
- that are not committed are called by the peculiar term "sparse."
-
- You can see that the best practice for memory management is to allocate a
- sparse memory object initially for as much space as the process might need.
- Later, parts of the memory object may be committed as needed. When a memory
- object is allocated as uncommitted, no physical memory is involved, so it is
- not wasteful to allocate several extra megabytes. Better yet, this practice
- actually optimizes the use of memory within the system. Pages are not given
- memory storage to back them up until they are both committed and referenced.
- No memory is wasted for unreferenced, committed pages.
-
- Access to committed pages is further refined to have access rights: read,
- write, or execute. A fourth right, guard page, is also available but is not
- discussed here.
-
- At any time, an application may change the access rights of a committed page
- of memory. It may also commit and decommit allocated pages on demand. Figure
- 9.3 shows an initial allocation request for 16K of memory.
-
- Notice that an entire range of 64K of address space has been allocated. This
- is due to the current implementation of memory allocation in OS/2 2.1, which
- makes all memory allocation requests follow the TILED memory scheme.
-
- Before accessing any of this memory, the application would have to make a
- request to commit the necessary pages. Figure 9.4 shows the result of a
- request by an application to commit the first 8K of memory, starting at
- address 14F00000.
- Figure 9.3. 16K uncommitted memory allocation.
-
- Figure 9.4. Memory committment.
-
- At this point, we are free to access memory in the range 14F00000 to 14F01FFF.
- Attempts to address 14F02000 would generate a protection violation.
-
- Memory objects cannot be resized after allocation. An application, thus, must
- allocate the maximum amount of memory for a memory object and commit the
- memory as needed.
-
- Committing and decommitting pages in a range of allocated memory as they are
- needed is a more efficient way of managing memory than requesting memory
- allocation and commitment every time. This, however, requires the application
- to keep track of how many pages have been committed so that memory requests to
- commit and decommit do not fail.
-
- An easier way of handling such a situation is to allocate a memory object as a
- heap and use the memory suballocation functions to allocate and commit the
- memory as needed. The heap management functions in the operating system keep
- track of the committed and uncommitted pages and manage these pages as memory
- requests are made. Care must be taken when using a heap, as accessing memory
- above the requested amount of suballocated memory may write over memory
- allocated to another suballocated block of memory or destroy information
- maintained by the heap itself. Memory requests suballocated from a heap are
- rounded up to a multiple of 8 bytes.
-
-
- ΓòÉΓòÉΓòÉ 8.4. The Memory Manager API ΓòÉΓòÉΓòÉ
-
- The memory management functions in OS/2 are divided into three categories:
- nonshared, suballocated, and shared. Some functions that are discussed as a
- part of the nonshared memory API are also available for use with shared memory.
-
-
- ΓòÉΓòÉΓòÉ 8.5. NonShared Memory ΓòÉΓòÉΓòÉ
-
- NonShared memory is allocated with the DosAllocMem API:
-
- APIRET DosAllocMem (PPVOID ppBaseAddress, ULONG ulObjectSize,
- ULONG ulAllocationFlags)
-
- PPVOID ppBaseAddress Address of pointer to receive memory
- address.
- ULONG ulObjectSize Maximum size of memory object to be
- allocated.
- ULONG ulAllocationFlags Memory allocation flags:
- PAG_READ Allows read access.
- PAG_EXECUTE Allows execute access (executable code).
- PAG_WRITE Allows write access (includes read and execute
- access).
- PAG_COMMIT Commits all requested pages.
- OBJ_TILE Allocates memory on a 64K boundary.
-
- The following example allocates an 8K memory object:
-
- PBYTE pMem;
- APIRET RetCode;
- RetCode = DosAllocMem ((PPVOID) &pMem, 8192L, PAG_READ | PAG_WRITE);
-
- The memory object returned in pMem must be committed before it can be
- accessed. Memory can be committed at the same time it is allocated by using
- the PAG_COMMIT flag:
-
- RetCode = DosAllocMem ((PPVOID) &pMem, 8192L,
- PAG_READ | PAG_WRITE | PAG_COMMIT);
-
- The memory object returned in pMem can now be immediately accessed. Each page
- of a memory object is initialized to zeroes the first time the page is read or
- written. The flag fALLOC can be used to request memory that is readable,
- writable, and committed:
-
- #define fPERM (PAG_EXECUTE + PAG_READ + PAG_WRITE)
- #define fALLOC (OBJ_TILE + PAG_COMMIT + fPERM)
-
- Note: The current implementation of OS/2 2.1 always allocates memory as
- though the OBJ_TILE flag is set. If the PAG_COMMIT flag is set,
- however, only those pages required to satisfy the allocation request
- are committed. In the previous example, only the first four pages
- (8,192 bytes) would be committed, although 16 pages (64K) are
- allocated. This may change in a future release of OS/2 2.1.
-
- After the memory is allocated, it can be committed and decommitted as needed
- with the DosSetMem API:
-
- APIRET DosSetMem (PVOID pBaseAddress, ULONG ulRegionSize,
- ULONG ulAttributeFlags)
-
- PVOID pBaseAddress Base address of memory object.
- ULONG ulRegionSize Size of memory to set.
- ULONG ulAttributeFlags Memory attribute flags:
- PAG_COMMIT Commits pages specified in ulRegionSize.
- PAG_DECOMMIT Decommits pages specified in ulRegionSize starting at
- pBaseAddress.
- PAG_READ Read access.
- PAG_EXECUTE Execute access.
- PAG_WRITE Write access.
- PAG_DEFAULT Use default access.
-
- If you are committing memory, you must also specify PAG_DEFAULT or at least
- one of these_ PAG_READ, PAG_WRITE, or PAG_EXECUTE. PAG_DEFAULT sets the access
- rights of the page to the same rights as when the memory object was allocated.
-
- The following example allocates an 8K memory object and commits the first page
- of the object:
-
- PBYTE pMem;
- APIRET RetCode;
- if ( !(RetCode = DosAllocMem ((PPVOID) &pMem, 8192L,
- PAG_READ | PAG_WRITE)) )
- {
- RetCode = DosSetMem ((PVOID) pMem, 4096L,
- PAG_COMMIT | PAG_READ | PAG_WRITE);
- }
-
- Attempts to set the commitment state of a page of memory to a state it is
- already in will fail. The second call to DosSetMem in the following sequence
- will fail as the second page is already committed:
-
- RetCode = DosSetMem ((PVOID) pMem, 8192L,
- PAG_COMMIT | PAG_READ | PAG_WRITE);
- RetCode = DosSetMem ((PVOID) pMem+4096, 4096L,
- PAG_COMMIT | PAG_READ | PAG_WRITE);
- DosSetMem can also be used to change the access rights of committed pages of
- the memory object. The following example allocates and commits an 8K memory
- object and then changes the access right of the first page from readable and
- writable to only readable:
-
- PBYTE pMem;
- APIRET RetCode;
- if ( !(RetCode = DosAllocMem ((PPVOID) &pMem, 8192L, fALLOC)) )
- {
- RetCode = DosSetMem ((PVOID) pMem, 4096L, PAG_READ);
- }
-
- The first page is now readable, but the second page is still readable and
- writable. It is not possible to change the access rights for uncommitted
- memory.
-
- It is important for you to remember that the access right of PAG_EXECUTE can
- be set for a page of memory only if the memory was originally allocated with
- PAG_EXECUTE.
-
- When memory is no longer needed, you can release it with the DosFreeMem API:
-
- APIRET DosFreeMem (PVOID pBaseAddress)
-
- PVOID pBaseAddress Base address of memory object
-
- The following example allocates and commits an 8K memory object and then
- releases it when done:
-
- PBYTE pMem;
- APIRET RetCode;
- if ( !(RetCode = DosAllocMem ((PPVOID) &pMem, 8192L, fALLOC)) )
- {
- .
- .
- .
- RetCode = DosFreeMem ((PVOID) pMem);
- }
-
- Any range of memory can be queried to determine its current state with the
- DosQueryMem API:
-
- APIRET DosQueryMem (PVOID pBaseAddress, PULONG pulRegionSize,
- PULONG pulAllocationFlags)
-
- PPVOID ppBaseAddress Base address of memory object.
- PULONG pulRegionSize Address of variable to receive region size.
- PULONG pulAllocationFlags Address of variable to receive allocation
- flags.
-
- DosQueryMem will scan the pages of memory starting at pBaseAddress and will
- return the allocation flags of the memory in the range specified. On input,
- pulRegionSize points to a variable containing the size, in bytes, of the
- amount of memory to scan. The range of memory pages scanned will stop when a
- page with attribute flags different from the first page is encountered, the
- number of pages requested is scanned, or the end of the allocated object is
- reached. On return, the variable pointed to by pulRegionSize will contain the
- actual number of bytes scanned.
-
- The allocation flags include two new flags:
-
- PAG_FREE The memory pages are not allocated.
- PAG_BASE The first page specified is the first page of the
- allocated memory object.
- PAG_SHARED The memory pages are in a shared memory object. (This flag
- is returned only if the pages are committed.)
-
- The following example queries the second page to see whether it is committed
- and writable:
-
- ULONG ulMemSize = 4096L;
- ULONG ulFlags;
- if ( !(RetCode = DosQueryMem ((PVOID) (pMem+4096),
- &ulMemSize, &ulFlags)) )
- {
- if ( (ulFlags & (PAG_COMMIT | PAG_WRITE)) ==
- (PAG_COMMIT | PAG_WRITE)) )
- {
- .
- .
- .
- }
- }
-
- If every page of an allocated memory object has the same attributes, you can
- determine the amount of committed memory for the object with the following
- call:
-
- ULONG ulMemSize = 0xFFFFFFFF;
- ULONG ulFlags;
- if ( (RetCode = DosQueryMem (pMem, &ulMemSize, &ulFlags)) ||
- !(ulFlags & PAG_COMMIT) )
- ulMemSize = 0L;
-
- This example requests DosQueryMem to scan all memory pages starting at pMem.
- If DosQueryMem returns successful, we still need to check the flags because
- all memory may be uncommitted. The applications presented later demonstrate
- how to determine the amount of allocated and committed memory when all pages
- do not have the same attributes.
-
-
- ΓòÉΓòÉΓòÉ 8.6. Suballocated Memory ΓòÉΓòÉΓòÉ
-
- There are several drawbacks to allocating, committing, and managing the pages
- of memory that your application may need.
-
- o Your application must track which pages have been committed and must
- check this before each memory access.
-
- o Small memory requests result in large amounts of address space being
- reserved.
-
- o Small memory requests for committed pages result in at least 4K of
- physical memory being used.
-
- o Memory allocation requests for new blocks of memory will be slower than
- suballocating from already committed memory.
-
- Consider the following memory requests:
-
- DosAllocMem ((PPVOID) &pMem, 20L, fALLOC);
- DosAllocMem ((PPVOID) &pMem, 32L, fALLOC);
-
- This results in a total of 128K of address space being reserved (64K for each
- request) and 8K of physical memory being used (4K for each request). It would
- be more efficient if we could satisfy both requests in a single page of
- committed memory. This is made possible by using the heap management functions
- of OS/2. In using a heap, you allocate a block of memory and indicate to OS/2
- that the block is to be managed as a heap. OS/2 will then satisfy memory
- requests from this heap by suballocating portions of this memory and returning
- the address to the application. OS/2 takes care of making sure the pages of
- memory are committed as necessary and of freeing them as they become
- available. Your application should never call DosSetMem to change any
- attribute of the memory used for suballocation, as this will cause
- unpredictable results. As an alternative, it is also possible for your
- application to commit the pages of the heap and avoid having the heap
- functions do any memory commitment.
-
- The first step in using memory suballocation (heaps) is to allocate a block of
- memory for this purpose. This is done by using the DosAllocMem function. This
- allocated memory is initialized for memory suballocation with the DosSubSetMem
- API, as follows:
-
- APIRET DosSubSetMem (PVOID pOffset, ULONG ulFlags, ULONG ulSize)
-
- PVOID pOffset Base address of memory object.
- ULONG ulFlags Memory suballocation flags:
- DOSSUB_INIT Initializes the memory for suballocation.
- DOSSUB_SPARSE_OBJ Tells OS/2 to manage the commitment of the
- pages.
- DOSSUB_GROW Increases the size of the memory used for
- suballocation.
- DOSSUB_SERIALIZE More than one process will be suballocating
- memory from this block of memory.
-
- The suballocation management for the heap uses 64 bytes of this memory to
- maintain control information. In addition, all suballocation requests are
- satisfied in multiples of 8 bytes. The minimum size that can be set, thus, is
- 72 bytes.
-
- The DOSSUB_INIT flag is specified on the first call to DosSubSetMem so that
- the control information for the heap can be initialized.
-
- Specify the DOSSUB_SPARSE_OBJ flag if you want OS/2 to manage the commitment
- of the pages. All pages in the range specified must be initially uncommitted.
- If the DOSSUB_SPARSE_OBJ flag is not set, all pages in the range specified
- must be committed before the call to DosSubSetMem. Because the heap must be
- updated as memory is suballocated, all pages must have minimum access rights
- of PAG_READ and PAG_WRITE.
-
- The following example allocates 64K for use as a heap and initializes it such
- that OS/2 will manage the commitment of pages:
-
- if ( !(RetCode = DosAllocMem ((PPVOID) &pMem, 0x10000,
- PAG_READ | PAG_WRITE)) )
- {
- RetCode = DosSubSetMem (pMem,
- DOSSUB_INIT | DOSSUB_SPARSE_OBJ, 0x10000);
- }
-
- The DOSSUB_GROW flag can be used to increase the amount of memory available
- for suballocation. When using this flag, the setting of the DOSSUB_SPARSE_OBJ
- flag must match the setting that was used on the initial call to DosSubSetMem.
- The DOSSUB_INIT flag is ignored if DOSSUB_GROW is specified. This example
- allocates 128K for use as a heap, but only initializes the heap to use the
- first 64K.
-
- if ( !(RetCode = DosAllocMem ((PPVOID) &pMem, 0x20000,
- PAG_READ | PAG_WRITE)) )
- {
- RetCode = DosSubSetMem ((PVOID) pMem,
- DOSSUB_INIT | DOSSUB_SPARSE_OBJ, 0x10000);
- }
-
- Later, the range of memory used for the heap can be increased by calling
- DosSubSetMem as follows:
-
- RetCode = DosSubSetMem (pMem,
- DOSSUB_GROW | DOSSUB_SPARSE_OBJ, 0x20000);
-
- Notice that the base address used in the second call to DosSubSetMem must
- match that used in the first call.
-
- If more than one process is going to perform memory suballocation from the
- same heap, it is necessary to specify the DOSSUB_SERIALIZE flag. Because OS/2
- is a multitasking system, the system can interrupt one process, which could be
- in the middle of the code that is suballocating memory from the heap, to allow
- another process to continue and possibly call to have memory suballocated from
- the same heap. Because the first process may be in the middle of making
- updates to the control information for the heap and the second process may
- make updates as well, when the first process continues, the control
- information may be corrupted. Because the heap is then in a corrupted state,
- either the request to suballocate memory will never return or both processes
- will eventually cause a protection violation. By specifying the
- DOSSUB_SERIALIZE flag, OS/2 creates a semaphore for the heap and every request
- to suballocate or free memory from the heap starts with a request to gain
- access to the semaphore and releases it when it is done. This prevents more
- than one process from executing within the heap management code at the same
- time.
-
- The first process to create the heap must allocate the memory as shared memory
- (discussed later) and must use the DOSSUB_INIT and DOSSUB_SERIALIZE flags.
- Other processes that want to share the heap must specify the DOSSUB_SERIALIZE
- flags without the DOSSUB_INIT flag. We will use this in an example later when
- we discuss shared memory.
-
- When the heap has been initialized for suballocation, we can use the
- DosSubAllocMem API to request memory:
-
- APIRET DosSubAllocMem (PVOID pOffset, PPVOID ppBlockOffset,
- ULONG ulSize)
-
- PVOID pOffset Base address of the heap.
- PPVOID ppBlockOffset Address of pointer to receive memory
- address.
- ULONG ulSize Amount of memory for use by the heap.
-
- All memory requests are rounded up to the next multiple of 8 bytes. The
- following example requests 22 bytes from the heap allocated previously:
-
- PBYTE pSubMem;
- APIRET RetCode;
- RetCode = DosSubAllocMem (pMem, (PPVOID) &pSubMem, 22);
-
- When the suballocate memory is no longer needed, you can release it with the
- DosSubFreeMem API:
-
- APIRET DosSubFreeMem (PVOID pMem, PVOID pBlockOffset, ULONG ulSize)
-
- PVOID pMem Base address of the heap.
- PVOID pBlockOffset Address of pointer to receive memory
- address.
- ULONG ulSize Size of memory to suballocate.
-
- The amount of memory specified in ulSize will be rounded up to the next
- multiple of 8 bytes and, after rounding, must match the amount actually
- suballocated. The following example frees the memory requested in the previous
- example:
-
- RetCode = DosSubFreeMem (pMem, pSubMem, 24);
-
- When you are done with the heap, you must call the DosSubUnsetMem API to
- enable OS/2 to free the resources it has allocated to manage the heap:
-
- APIRET DosSubUnsetMem (PVOID pMem)
-
- PVOID pMem Base address of the heap.
-
- All calls to DosSubSetMem must be matched with an equivalent call to
- DosSubUnsetMem. The memory used by the heap can then be freed with a call to
- DosFreeMem after DosSubUnsetMem has been called. The following example ends
- the use of the heap and frees the memory we allocated earlier:
-
- if ( !(RetCode = DosSubUnsetMem ((PVOID) pMem)) )
- {
- RetCode = DosFreeMem ((PVOID) pMem);
- }
-
-
- ΓòÉΓòÉΓòÉ 8.7. Shared Memory ΓòÉΓòÉΓòÉ
-
- Until now we have talked about memory allocated for the private use of a
- process. Shared memory is memory that can be addressed by multiple processes.
- Shared memory is used when two or more processes need to share data or when
- data is copied to or from the OS/2 clipboard. While memory is allocated from
- the shared memory arena and the address space is reserved in the process
- address space of all processes, a process must explicitly gain access to the
- memory in order to access it.
-
- Once access to shared memory is obtained, the processes must coordinate among
- themselves whenever the memory is updated. This is usually done using
- semaphores. No coordination is required when just reading the shared memory.
-
- Shared memory is initially allocated by one process. So how do other
- applications know the address of the shared memory in order to access it? There
- are two methods for doing this. The first is to use named shared memory. In
- this case, shared memory is allocated, and a unique name is assigned to the
- memory. At that point, any application knowing the name of this memory can
- access it by referring to the same name. The second method is to use .i.unnamed
- shared memory;unnamed shared memory. In this case, shared memory is allocated,
- and the address of this memory is passed to the other applications. At that
- point, the other processes can access the memory. It is also possible for a
- process with access to a shared memory object to give access to another
- process.
-
- When each process with access to shared memory is finished using the memory,
- each process must free the shared memory. This is because the shared memory
- object maintains a count of the number of processes that have gained access to
- it, and the physical memory assigned to the shared memory object is not freed
- until all processes using the memory have freed it. Failure of each process to
- free the shared memory will result in reducing the address space available for
- shared memory.
-
- Shared memory is allocated by using the DosAllocSharedMem API:
-
- APIRET APIENTRY DosAllocSharedMem (PPVOID ppb, PSZ pszName,
- ULONG cb, ULONG flag)
-
- PPVOID ppb Address of pointer variable to receive the
- memory address.
- PSZ pszName Name of the named shared memory object.
- This name has the form \SHAREMEM\name. name
- can be any name that meets the criteria for
- a valid OS/2 filename.
- ULONG cb Maximum size of the memory object to be
- allocated.
- ULONG flag Memory allocation flags:
- PAG_READ Allows read access.
- PAG_EXECUTE Allows execute access (executable code).
- PAG_WRITE Allows write access (includes read and execute
- access).
- PAG_COMMIT Commit pages when allocated.
- OBJ_TILE Allocate on 64K boundary.
- OBJ_GETTABLE Memory is gettable by other processes.
- OBJ_GIVEABLE Memory is giveable to other processes.
-
- This function operates the same way as the DosAllocMem API, except that memory
- is allocated from the shared address space and there are some additional
- memory flags. To allocate named shared memory, the pszName field is set to the
- name of the object. The OBJ_GETTABLE and OBJ_GIVEABLE memory allocation flags
- are not allowed for named shared memory. To allocate unnamed shared memory,
- the pszName field is set to NULL and at least one of the OBJ_GETTABLE or
- OBJ_GIVEABLE flags must be specified. There are some macros defined in the
- BSEMEMF.H header file, which is included by the BSEDOS.H header file:
-
- #define fSHARE (OBJ_GETTABLE + OBJ_GIVEABLE)
- #define fPERM (PAG_EXECUTE + PAG_READ + PAG_WRITE)
- #define fALLOCSHR (OBJ_TILE + PAG_COMMIT + fSHARE + fPERM)
- #define fGETNMSHR (fPERM)
- #define fGETSHR (fPERM)
- #define fGIVESHR (fPERM)
-
- After shared memory has been committed, the protection flags and commit state
- of the pages of memory can be changed with the DosSetMem API just as they were
- for nonshared memory. Shared memory can also be queried as before using the
- DosQuerymem API. If a page of shared memory has been committed, the additional
- flag of PAG_SHARED will be returned for the shared pages when DosQueryMem is
- used. Once a page of shared memory has been committed, it cannot be
- decommitted.
-
- For named shared memory, the DosGetNamedSharedMem API is used by other
- processes to obtain access to the memory object:
-
- APIRET APIENTRY DosGetNamedSharedMem (PPVOID ppBaseAddress,
- PSZ pszSharedMemName, ULONG ulAttributeFlags)
-
- PPVOID ppBaseAddress Address of pointer to receive memory
- address.
- PSZ pszSharedMemName Name of the named shared memory object.
- This name has the form \SHAREMEM\name. name
- can be any name that meets the criteria for
- a valid OS/2 filename.
- ULONG ulAttributeFlags Desired access protection for the shared
- memory:
- PAG_READ Read access.
- PAG_EXECUTE Execute access (executable code).
- PAG_WRITE Write access (includes read and execute access).
-
- If a named shared memory object with the specified name exists, the process is
- given access to the memory, and its address is returned in ppBaseAddress. The
- following example shows one process allocating 32K of named shared memory and
- another process gaining access to it.
-
- Process 1:
-
- PVOID pMem;
-
- if (!DosAllocSharedMem (&pMem, "\\SHAREMEM\\OBJECT1", 0x8000L,
- fPERM | PAG_COMMIT | OBJ_TILE))
- {
- strcpy ((PSZ)pMem, "Hello Process 2");
- }
-
- Process 2:
-
- PVOID pMem;
- if (!DosGetNamedSharedMem (&pMem, "\\SHAREMEM\\OBJECT1", fPERM))
- {
- strcpy ((PSZ)pMem, "Hello to you Process 1");
- }
-
- For unnamed shared memory, there are two ways that memory access can be
- obtained by a second process. The first is for the second process to use the
- DosGetSharedMem API:
-
- APIRET APIENTRY DosGetSharedMem (PVOID pBaseAddress,
- ULONG ulAttributeFlags)
-
- PVOID pBaseAddress The base address of the shared memory
- object.
- ULONG ulAttributeFlags Desired access protection for the shared
- memory:
- PAG_READ Read access.
- PAG_EXECUTE Execute access (executable code).
- PAG_WRITE Write access (includes read and execute access).
-
- In order for the DosGetSharedMem request to succeed, the memory object must
- have been allocated with the OBJ_GETTABLE flag, and the requested
- ulAttributeFlags must be compatible with the current protection flags for the
- memory object. Note that the address passed in pBaseAddress must be the base
- address of the memory object. The following example shows one process
- allocating 32K of unnamed shared memory, passing the address to the second
- process, and the second process gaining access to it.
-
- Process 1:
-
- #define WM_USER_SHARED_MEM WM_USER+1
-
- PVOID pMem;
- HWND hWndProcess2;
-
- if (!DosAllocSharedMem (&pMem, NULL, 0x8000L,
- fPERM | PAG_COMMIT | OBJ_TILE | OBJ_GETTABLE |
- OBJ_GIVEABLE))
- {
- strcpy ((PSZ)pMem, "Hello Process 2");
- WinSendMsg (hWndProcess2, WM_USER_SHARED_MEM, (MPARAM)pMem, 0L);
- }
-
- Process 2:
-
- #define WM_USER_SHARED_MEM WM_USER+1
- PVOID pMem;
- MRESULT EXPENTRY MainWndProcProc (HWND hWnd, ULONG msg,
- MPARAM mp1, MPARAM mp2)
- {
- switch (msg)
- {
- ...
- case WM_USER_SHARED_MEM:
- pMem = (PVOID)mp1;
- if (!DosGetSharedMem (pMem, fPERM))
- strcpy ((PSZ)pMem, "Hello to you Process 1");
- return (0L);
- ...
- }
- return (WinDefWindowProc (hWnd, msg, mp1, mp2));
- }
-
- In this example, we used a message (WM_USER_SHARED_MEM) to pass the address of
- the memory object to Process 2. It could also have been done other ways using
- interprocess communication or through global data in a DLL using a single data
- segment.
-
- The second way for a process to gain access to unnamed shared memory is to
- have a process that currently has access to explicitly give access to another
- process. This is accomplished with the DosGiveSharedMem API:
-
- APIRET APIENTRY DosGiveSharedMem (PVOID pBaseAddress,
- PID idProcessID, ULONG ulAttributeFlags)
-
- PVOID pBaseAddress The base address of the shared memory
- object.
- PID idProcessID Process identifier of process to be given
- access.
- ULONG ulAttributeFlags Desired access protection for the shared
- memory:
- PAG_READ Read access.
- PAG_EXECUTE Execute access (executable code).
- PAG_WRITE Write access (includes read and execute access).
-
- In order for the DosGiveSharedMem request to succeed, the memory object must
- have been allocated with the OBJ_GIVEABLE flag, and the requested
- ulAttributeFlags must be compatible with the current protection flags for the
- memory object. Note again that the address passed in pBaseAddress must be the
- base address of the memory object. The following example shows one process
- allocating 32K of unnamed shared memory, giving access of it to another
- process, and then passing the address to the second process.
-
- Process 1:
-
- #define WM_USER_SHARED_MEM WM_USER+1
-
- PVOID pMem;
- PID pidProcess2;
- HWND hWndProcess2;
-
- if (!DosAllocSharedMem (&pMem, NULL, 0x8000L,
- fPERM | PAG_COMMIT | OBJ_TILE | OBJ_GETTABLE |
- OBJ_GIVEABLE))
- {
- strcpy ((PSZ)pMem, "Hello Process 2");
- if (!DosGiveSharedMem (pMem, pidProcess2, fPERM)) WinSendMsg
- (hWndProcess2, WM_USER_SHARED_MEM, (MPARAM)pMem, 0L);
- else
- DosFreeMem (pMem);
- }
-
- Process 2:
-
- #define WM_USER_SHARED_MEM WM_USER+1
- PVOID pMem;
- MRESULT EXPENTRY MainWndProcProc (HWND hWnd, ULONG msg,
- MPARAM mp1, MPARAM mp2)
- {
- switch (msg)
- {
- ...
- case WM_USER_SHARED_MEM:
- pMem = (PVOID)mp1;
- strcpy ((PSZ)pMem, "Hello to you Process 1"); return (0L);
- ...
- }
- return (WinDefWindowProc (hWnd, msg, mp1, mp2));
- }
-
- Again, in this example, we used a message (WM_USER_SHARED_MEM) to pass the
- address of the memory object to Process 2. Process 2 does not have to do
- anything to the memory object in order to use it since Process 1 has already
- given it access. The identifier for Process 2 must have been passed to Process
- 1 by using interprocess communication or through global data in a DLL by using
- a single data segment.
-
- It is important to note again that each process is responsible for calling
- DosFreeMem for each shared memory object it has access to when it is done
- using the memory.
-
- We showed in an earlier section how to improve the memory allocation
- performance and reduce the amount of memory management overhead on the
- application by using suballocated memory. Suballocated memory can also be used
- for shared memory objects, but you should use the DOSSUB_SERIALIZE flag when
- allocating the shared memory object. OS/2 creates a semaphore specifically for
- the shared memory object and automatically requests access to the semaphore
- for all memory suballocation requests and free operations. Following is an
- example of creating a shared memory object for use by memory suballocation and
- what is required by each process sharing this memory.
-
- Process 1 allocates a 128K block of shared memory and initializes it for
- memory suballocation.
-
- Process 1:
-
- PVOID pHeap;
-
- if (!DosAllocSharedMem (&pHeap, NULL, 0x20000L,
- fPERM | OBJ_GETTABLE) &&
- !DosSubSet (pHeap, DOSSUB_INIT | DOSSUB_SPARSE_OBJ |
- DOSSUB_SERIALIZE,
- 0x20000L)
- {
- PVOID pSubMem;
-
- if (!DosSubAlloc (pHeap, &pSubMem, 0x1000L,))
- {
- ...
- DosSubFree (pHeap, pSubMem, 0x1000L);
- }
- }
-
- Any processes wanting to share this memory and use the suballocation must
- first gain access to the memory and then call DosSubSet. The call to DosSubSet
- does not reinitialize the memory for suballocation but does give the process
- access to the semaphore created by OS/2 for serializing requests to the memory
- object.
-
- Process 2:
-
- PVOID pHeap;
-
- if (!DosGetSharedMem (pHeap, fPERM) &&
- !DosSubSet (pHeap, DOSSUB_SPARSE_OBJ | DOSSUB_SERIALIZE))
- {
- PVOID pSubMem;
-
- if (!DosSubAlloc (pHeap, &pSubMem, 0x1000L,))
- {
- ...
- DosSubFree (pHeap, pSubMem, 0x1000L);
- }
- }
-
- When each process is finished with the shared memory object, it must first
- call DosSubUnsetMem to release its access to the semaphore that OS/2 created.
- It must then call DosFreeMem to free the shared memory for the process.
-
- Process 1:
-
- /* Termination code for process */
- DosSubUnsetMem (pHeap);
- DosFreeMem (pHeap);
- ...
-
- Process 2:
-
- /* Termination code for process */
- DosSubUnsetMem (pHeap);
- DosFreeMem (pHeap);
- ...
-
-
- ΓòÉΓòÉΓòÉ 8.8. Converting from Unshared to Shared Memory ΓòÉΓòÉΓòÉ
-
- There are many times when you have a memory object that is allocated as a
- private memory object, and you need to pass the information to another process
- or even copy it to the clipboard. In those circumstances, it is necessary to
- move the data to shared memory. The following function shows the steps to take
- a private memory object and convert it to a shared memory object. This process
- simply requires us to determine the size of the private memory object, allocate
- a shared memory object of the same size, and copy the data. The function
- ConvertToSharedMemory assumes that the committed pages of the private data
- object consist of contiguous pages starting at the base address.
-
- PVOID ConvertToSharedMemory (PVOID pPrivateMem, BOOL bFree) {
- /* Convert the private memory pointed to by pPrivateMem to shared
- memory. If bFree is specified, free the memory pointed
- to by pPrivateMem after creating the shared memory
- object. Return NULL if the convert fails. */
-
- PVOID pSharedMem = NULL;
- ULONG ulSize;
- ULONG ulFlags;
-
- ulSize = 0xFFFFFFFFL;
-
- if ( !DosQueryMem (pPrivateMem, &ulSize, &ulFlag) &&
- (ulFlag & PAG_COMMIT) &&
- !DosAllocSharedMem (&pSharedMem, NULL, ulSize,
- (ulFlag & fALLOC) | fSHARE) )
- {
- memcpy (pSharedMem, pPrivateMem, ulSize);
- if (bFree)
- DosFreeMem (pPrivateMem);
- }
-
- return (pSharedMem);
- }
-
- In order to successfully convert the pointer, there must be committed memory in
- the private memory object, and the shared memory object must be successfully
- allocated:
-
- if ( !DosQueryMem (pPrivateMem, &ulSize, &ulFlag) &&
- (ulFlag & PAG_COMMIT) &&
- !DosAllocSharedMem (&pSharedMem, NULL, ulSize,
- (ulFlag & fALLOC) | fSHARE) )
-
- The flags used for the DosAllocSharedMem call are the existing flags for the
- private memory object (including the PAG_COMMIT) plus the fSHARE flags.
-
-
- ΓòÉΓòÉΓòÉ 8.9. The MEMMAP Application ΓòÉΓòÉΓòÉ
-
- This sample application demonstrates most features and API calls needed to
- query memory within OS/2.
-
-
- ΓòÉΓòÉΓòÉ 8.10. MEMMAP's Files ΓòÉΓòÉΓòÉ
-
- The files necessary to build the MEMMAP application are the following:
-
- MEMMAP.DEF
- MEMMAP.DLG
- MEMMAP.C
- MEMMAP.H
- MEMMAP.RC
- MEMMAP.ICO
-
-
- ΓòÉΓòÉΓòÉ 8.11. MEMMAP.C ΓòÉΓòÉΓòÉ
-
- /* ----------------------------------------------------------------
- MemMap Program
- Chapter 9
- Real-World Programming for OS/2 2.1
- Copyright (c) 1993 Blain, Delimon, and English
- ---------------------------------------------------------------- */
-
- #define INCL_WIN
- #define INCL_GPI
- #define INCL_DOSERRORS
- #include <os2.h>
- #include <stdio.h>
- #include "memmap.h"
- #include "..\..\common\about.h"
-
- /* Exported Functions */
-
- MRESULT EXPENTRY MainDlgProc (HWND,ULONG,MPARAM,MPARAM);
-
- /* Local Functions */
-
- VOID CenterWindow (HWND);
- VOID MemError (PSZ);
- ULONG ParseItem (PSZ,ULONG);
- VOID QueryMemory (VOID);
-
- HAB hab;
- CHAR szTitle[64];
- HWND hWndFrame;
- HWND hWndMemList;
- HWND hWndStartAddress;
- ULONG ulStartAddress = 0L;
- CHAR szStartAddress[9] = "00000000";
- PVOID pStartAddress = (PVOID)szStartAddress;
-
- /* Undocumented menu message */
- #define MM_QUERYITEMBYPOS 0x01f3
- #define MAKE_16BIT_POINTER(p) \
- ((PVOID)MAKEULONG(LOUSHORT(p),(HIUSHORT(p) << 3) | 7))
-
- int main()
- {
- HMQ hmq;
- QMSG qmsg;
-
- hab = WinInitialize (0);
- hmq = WinCreateMsgQueue (hab, 0);
-
- WinLoadString (hab, 0, ID_APPNAME, sizeof(szTitle), szTitle);
-
- hWndFrame = WinLoadDlg (HWND_DESKTOP, HWND_DESKTOP,
- MainDlgProc, 0, ID_APPNAME, NULL);
- WinSendMsg (hWndFrame, WM_SETICON,
- (MPARAM)WinLoadPointer (HWND_DESKTOP, 0, ID_APPNAME), NULL);
-
- while (WinGetMsg (hab, &qmsg, 0, 0, 0))
- WinDispatchMsg (hab, &qmsg);
-
- WinDestroyWindow (hWndFrame);
- WinDestroyMsgQueue (hmq);
- WinTerminate (hab);
- return (0);
- }
-
- VOID AddAboutToSystemMenu(HWND hWndFrame)
- {
- MENUITEM mi;
- HWND hWndSysSubMenu;
-
- WinSendMsg (WinWindowFromID (hWndFrame, FID_SYSMENU),
- MM_QUERYITEMBYPOS, 0L, MAKE_16BIT_POINTER(&mi));
- hWndSysSubMenu = mi.hwndSubMenu;
- mi.iPosition = MIT_END;
- mi.afStyle = MIS_SEPARATOR;
- mi.afAttribute = 0;
- mi.id = -1;
- mi.hwndSubMenu = 0;
- mi.hItem = 0;
- WinSendMsg (hWndSysSubMenu, MM_INSERTITEM, MPFROMP (&mi), NULL);
- mi.afStyle = MIS_TEXT;
- mi.id = IDM_ABOUT;
- WinSendMsg (hWndSysSubMenu, MM_INSERTITEM, MPFROMP (&mi),
- "About...");
- return;
- }
-
- VOID CenterWindow (HWND hWnd)
- {
- ULONG ulScrWidth, ulScrHeight;
- RECTL Rectl;
-
- ulScrWidth = WinQuerySysValue (HWND_DESKTOP, SV_CXSCREEN);
- ulScrHeight = WinQuerySysValue (HWND_DESKTOP, SV_CYSCREEN);
- WinQueryWindowRect (hWnd,&Rectl);
- WinSetWindowPos (hWnd, HWND_TOP, (ulScrWidth-Rectl.xRight)/2,
- (ulScrHeight-Rectl.yTop)/2, 0, 0, SWP_MOVE | SWP_ACTIVATE);
- return;
- }
-
- VOID MemError (PSZ pszMessage)
- {
- WinMessageBox (HWND_DESKTOP, hWndFrame, pszMessage,
- "Memory Map", 0, MB_ERROR | MB_OK);
- return;
- }
-
- ULONG ParseItem (PSZ szItem, ULONG CurPos)
- {
- ULONG EndPos = CurPos + 8;
- ULONG ulMem = 0;
- for (; CurPos < EndPos; CurPos++)
- {
- if (szItem[CurPos] != ' ')
- ulMem = ulMem*16 +
- ((szItem[CurPos] <= '9') ? (szItem[CurPos] - '0') :
- (szItem[CurPos] - 'A' + 10));
- }
- return (ulMem);
- }
-
- VOID QueryMemory ()
- {
- char szText[90];
- ULONG ulAddress,
- ulEndAddress,
- ulPage,
- ulSize,
- ulFlags;
- APIRET RetCode;
-
- WinSetPointer (HWND_DESKTOP,
- WinQuerySysPointer (HWND_DESKTOP, SPTR_WAIT, FALSE));
- WinEnableWindowUpdate (hWndMemList, FALSE);
- ulAddress = ulStartAddress;
- ulEndAddress = ulStartAddress + 0x02000000; /* 32 MB max */
- if (ulEndAddress > 0x20000000)
- ulEndAddress = 0x20000000;
- ulPage = 0L;
- sprintf (szText, "%08lx . . . . . . . . . . . . . . . . ",
- ulAddress);
- WinUpper (hab, 0, 0, szText);
- while (ulAddress < ulEndAddress)
- {
- ulSize = 0xFFFFFFFF;
- if (!(RetCode = DosQueryMem ((PVOID)ulAddress, &ulSize,
- &ulFlags)))
- {
- if (ulFlags == PAG_FREE)
- ulSize = 0x1000;
- else
- ulSize = (ulSize + 0xFFF) & 0xFFFFF000;
- if (ulFlags & PAG_BASE)
- szText[42] = '*';
- while (ulSize && (ulAddress < 0x20000000))
- {
- if (ulFlags & PAG_COMMIT)
- szText[10 + ulPage*2] = 'C';
- else if (!(ulFlags & PAG_FREE))
- szText[10 + ulPage*2] = 'A';
- ulAddress += 0x1000;
- ulSize -= 0x1000;
- ulPage++;
- if (ulPage == 16)
- {
- if (ulFlags & PAG_SHARED)
- sprintf (&szText[43], "%s", "Shared ");
- else if (ulFlags & PAG_FREE)
- sprintf (&szText[43], "%s", "Free ");
- else
- sprintf (&szText[43], "%s", "Private");
- if (WinInsertLboxItem (
- WinWindowFromID (hWndFrame, IDL_MEMLIST),
- LIT_END, szText) < 0)
- break;
- ulPage = 0;
- sprintf (szText,
- "%08lx . . . . . . . . . . . . . . . . ",
- ulAddress);
- WinUpper (hab, 0, 0, szText);
- }
- }
- }
- else if (RetCode == ERROR_INVALID_ADDRESS)
- {
- ulAddress += 0x10000;
- sprintf (&szText[43], "%s", "Invalid");
- WinInsertLboxItem (WinWindowFromID (hWndFrame, IDL_MEMLIST),
- LIT_END, szText);
- sprintf (szText,
- "%08lx . . . . . . . . . . . . . . . . ",
- ulAddress);
- WinUpper (hab, 0, 0, szText);
- }
- else
- {
- sprintf (szText, "Error querying memory address %08lx",
- ulAddress);
- MemError (szText);
- break;
- }
- }
- WinSetPointer (HWND_DESKTOP,
- WinQuerySysPointer (HWND_DESKTOP,SPTR_ARROW,FALSE));
- WinEnableWindowUpdate (hWndMemList, TRUE);
- }
-
-
- MRESULT EXPENTRY MainDlgProc (HWND hWnd, ULONG msg,
- MPARAM mp1, MPARAM mp2)
- {
- BOOL bHandled = TRUE;
- MRESULT mReturn = 0;
-
- switch (msg)
- {
- case WM_INITDLG:
- CenterWindow (hWnd);
-
- /* Add the About menu item to the system menu */
- AddAboutToSystemMenu (hWnd);
-
- hWndMemList = WinWindowFromID (hWnd, IDL_MEMLIST);
- hWndStartAddress = WinWindowFromID (hWnd, IDB_STARTADDRESS);
-
- WinSendMsg (hWndStartAddress, SPBM_SETTEXTLIMIT,
- (MPARAM)8, (MPARAM)0);
- WinSendMsg (hWndStartAddress, SPBM_SETARRAY,
- (MPARAM)&pStartAddress, (MPARAM)1L);
- WinSendMsg (hWndStartAddress, SPBM_SETCURRENTVALUE,
- (MPARAM)0L, (MPARAM)0L);
- WinSetFocus (HWND_DESKTOP, hWndStartAddress);
- WinPostMsg (hWnd, WM_COMMAND, (MPARAM)IDB_REFRESH, 0L);
- break;
-
- case WM_CONTROL:
- switch (LOUSHORT(mp1))
- {
- case IDL_MEMLIST:
- if (HIUSHORT(mp1) == LN_SELECT)
- {
- WinQueryLboxItemText (hWndMemList,
- WinQueryLboxSelectedItem((HWND)mp2),
- szStartAddress,9);
- ulStartAddress = ParseItem (szStartAddress, 0);
- WinSendMsg (hWndStartAddress, SPBM_SETARRAY,
- (MPARAM)&pStartAddress, (MPARAM)1L);
- }
- break;
-
- case IDB_STARTADDRESS:
- if (HIUSHORT(mp1) == SPBN_UPARROW)
- ulStartAddress = (ulStartAddress + 0x10000) & 0x1FFFFFFF;
- else if (HIUSHORT(mp1) == SPBN_DOWNARROW)
- ulStartAddress = (ulStartAddress - 0x10000) & 0x1FFFFFFF;
- sprintf (szStartAddress,"%08lx",ulStartAddress);
- WinUpper (hab, 0, 0, szStartAddress);
- WinSendMsg (hWndStartAddress, SPBM_SETARRAY,
- (MPARAM)&pStartAddress, (MPARAM)1L);
- break;
- }
- break;
-
- case WM_COMMAND:
- case WM_SYSCOMMAND:
- switch (LOUSHORT(mp1))
- {
- case IDB_REFRESH:
- WinSendDlgItemMsg (hWndFrame, IDL_MEMLIST, LM_DELETEALL,
- 0L, 0L);
- QueryMemory ();
- WinSendMsg (hWndMemList, LM_SELECTITEM,
- (MPARAM)0, (MPARAM)TRUE);
- break;
-
- case IDM_ABOUT:
- DisplayAbout (hWnd, szTitle);
- break;
-
- case SC_CLOSE:
- WinPostMsg (hWnd, WM_QUIT, 0L, 0L);
- break;
-
- default:
- bHandled = (msg == WM_COMMAND);
- }
- break;
-
- default:
- bHandled = FALSE;
- break;
- }
-
- if (!bHandled)
- mReturn = WinDefDlgProc (hWnd, msg, mp1, mp2);
-
- return (mReturn);
- }
-
-
- ΓòÉΓòÉΓòÉ 8.12. MEMMAP.H ΓòÉΓòÉΓòÉ
-
- /* ----------------------------------------------------------------
- MemMap Header File
- Chapter 9
- Real-World Programming for OS/2 2.1
- Copyright (c) 1993 Blain, Delimon, and English
- ---------------------------------------------------------------- */
-
- #define ID_APPNAME 1
-
- #define IDL_MEMLIST 10
- #define IDB_REFRESH 11
- #define IDT_PAGES 12
- #define IDB_STARTADDRESS 13
-
- #define IDM_ABOUT 100
-
-
- ΓòÉΓòÉΓòÉ 8.13. MEMMAP.RC ΓòÉΓòÉΓòÉ
-
- /* ----------------------------------------------------------------
- MemMap Resource File
- Chapter 9
- Real-World Programming for OS/2 2.1
- Copyright (c) 1993 Blain, Delimon, and English
- ---------------------------------------------------------------- */
-
- #include <os2.h>
- #include "memmap.h"
-
- ICON ID_APPNAME MEMMAP.ICO
-
- STRINGTABLE LOADONCALL MOVEABLE
- BEGIN
- ID_APPNAME "Memory Map Application"
- END
-
- rcinclude memmap.dlg
- rcinclude ..\..\common\about.dlg
-
-
- ΓòÉΓòÉΓòÉ 8.14. MEMMAP.DLG ΓòÉΓòÉΓòÉ
-
- /* ----------------------------------------------------------------
- MemMap Dialog
- Chapter 9
- Real-World Programming for OS/2 2.1
- Copyright (c) 1993 Blain, Delimon, and English
- ---------------------------------------------------------------- */
-
- DLGTEMPLATE ID_APPNAME LOADONCALL MOVEABLE DISCARDABLE
- BEGIN
- DIALOG "Memory Map", ID_APPNAME, 36, 17, 302, 165,
- WS_VISIBLE | FS_DLGBORDER,
- FCF_SYSMENU | FCF_TITLEBAR | FCF_DLGBORDER |
- FCF_TASKLIST | FCF_ICON;
- BEGIN
- LTEXT "Address", -1, 15, 145, 37, 8
- LTEXT "Page", -1, 139, 154, 24, 8
- LTEXT "0 1 2 3 4 5 6 7 8 9 A B C D E F", IDT_PAGES,
- 64, 145, 174, 8
- PRESPARAMS PP_FONTNAMESIZE, "10.System Monospaced"
- LTEXT "Type", -1, 248, 145, 22, 8
- LTEXT ". = Free A = Allocated C = Committed * = Base",
- -1, 77, 6, 207, 8
- LISTBOX IDL_MEMLIST, 10, 36, 282, 107
- PRESPARAMS PP_FONTNAMESIZE, "10.System Monospaced"
- PUSHBUTTON "Refresh", 11, 8, 4, 45, 14
- LTEXT "Start Address", DID_CANCEL, 9, 23, 60, 8
- CONTROL "", IDB_STARTADDRESS, 73, 20, 64, 12, WC_SPINBUTTON,
- SPBS_ALLCHARACTERS | SPBS_READONLY |
- SPBS_MASTER | SPBS_JUSTRIGHT |
- SPBS_FASTSPIN | WS_GROUP | WS_TABSTOP | WS_VISIBLE
- PRESPARAMS PP_FONTNAMESIZE, "10.System Monospaced"
- END
- END
-
-
- ΓòÉΓòÉΓòÉ 8.15. Using MEMMAP ΓòÉΓòÉΓòÉ
-
- Add the MEMMAP application icon to a window and open the program. You should
- see a display similar to Figure 9.5.
-
- The MEMMAP application merely displays a map of the memory available to the
- MEMMAP process. The memory will be marked Free, Allocated, Committed, or Base.
- Figure 9.5. MEMMAP application.
-
- Remember base memory is the starting address of a block of memory. If a process
- allocated two pages of memory, the first page will be marked as base, the
- second will not. Take a look at the memory map that MEMMAP displays. Each
- asterisk signals the beginning of a new allocated block. Gaps indicate large
- blocks of memory.
-
- Because the only memory available to the MEMMAP application is that which has
- been allocated for its own process, the map will not change much between
- differing instances of the application.
-
-
- ΓòÉΓòÉΓòÉ 8.16. Understanding MEMMAP's Code ΓòÉΓòÉΓòÉ
-
- The code for MEMMAP is similar to that of the MEMORY sample application, which
- is detailed following the MEMMAP application. The major difference lies in the
- QueryMemory function, which is detailed in following text. Work through
- understanding this smaller portion of both applications before going on to
- tackle the MEMORY application.
-
- The QueryMemory function will query 32M of memory starting at ulStartAddress.
- We stop at 32M because our list box can only hold so many entries. This
- function will check for all the features of paged memory allocation, as well as
- correct for a couple of OS/2 quirks.
-
- We begin by calculating our ending address and ensuring it is less than our
- 512M limit. We now loop until we have reached the end of our range. The call to
- DosQueryMem queries the maximum memory size starting at our current address.
- Remember that if the address queried is the first page of a memory object, the
- PAG_BASE flag is set. If the page is not currently allocated, the PAG_FREE flag
- is set. Also, the DosQueryMem call will query pages until it finds a page with
- a memory flag different from the first page queried or until it finds the first
- page of the next allocated memory object.
-
- Each call to DosQueryMem will start at the address in ulAddress and return the
- size of the memory pages that have the same flags as the first page at
- ulAddress. The ulFlags variable will contain those flags. Here, we discover a
- quirk in the DosQueryMem function. If the first page queried is free memory
- (PAG_FREE), the size of the memory object returned is not a believable value.
- In other words, we can be sure that only one page is marked as PAG_FREE. Do not
- use the size returned in ulSize as the size of contiguous PAG_FREE pages. For
- this reason, if the PAG_FREE bit is set, we reset ulSize to one 4K page;
- otherwise, we round the size to a 4K page boundary. At this point, we loop
- through the memory size returned, setting the appropriate page flags for
- allocated and committed pages.
-
- We also output the type of memory object each 64K block of memory represents
- (Shared, Free, or Private). At this point ulAddress contains the address of the
- first page past the memory object just processed. If the return code from
- DosQueryMem is ERROR_INVALID_ADDRESS, we are unable to query that address, so
- we skip that memory object and go to the next 64K block.
-
- Remember that a program can query memory only for the current process. It does
- not have access to another process's address space. This function, thus, only
- queries the memory allocated for the MemMap program. You, however, can use this
- function and add it to any program to query its allocated and committed memory.
-
-
- ΓòÉΓòÉΓòÉ 8.17. The MEMORY Application ΓòÉΓòÉΓòÉ
-
- This sample application demonstrates allocating, committing, freeing, and
- changing the access rights to blocks of memory. It also utilizes the
- techniques from the MEMMAP program to display the status of each page of an
- allocated memory object.
-
-
- ΓòÉΓòÉΓòÉ 8.18. MEMORY's Files ΓòÉΓòÉΓòÉ
-
- The files necessary to build the MEMORY application are the following:
-
- MEMORY.C
- MEMORY.H
- MEMORY.DEF
- MEMORY.RC
- ALLOCATE.DLG
- MEMORY.DLG
- MEMORY.ICO
-
-
- ΓòÉΓòÉΓòÉ 8.19. MEMORY.C ΓòÉΓòÉΓòÉ
-
- /* --------------------------------------------------------------------
- Memory Program
- Chapter 9
- Real-World Programming for OS/2 2.1
- Copyright (c) 1993 Blain, Delimon, and English
- ------------------------------------------------------------------ */
-
- #define INCL_WIN
- #include <os2.h>
- #include <stdio.h>
- #include "memory.h"
- #include "..\..\common\about.h"
-
- #define WAIT_POINTER WinSetPointer (HWND_DESKTOP, \
- WinQuerySysPointer (HWND_DESKTOP,SPTR_WAIT,FALSE));
- #define ARROW_POINTER WinSetPointer (HWND_DESKTOP, \
- WinQuerySysPointer (HWND_DESKTOP,SPTR_ARROW,FALSE));
-
- /* Exported Functions */
-
- MRESULT EXPENTRY AllocateDlgProc (HWND,ULONG,MPARAM,MPARAM);
- MRESULT EXPENTRY MainDlgProc (HWND,ULONG,MPARAM,MPARAM);
-
- /* Local Functions */
-
- VOID CenterWindow (HWND);
- VOID ChangeAccessRights (USHORT);
- VOID ChangeCommitState (BOOL);
- PSZ FormatMemListItem (PVOID,LONG,ULONG,ULONG);
- PSZ FormatPageListItem (PVOID,ULONG);
- VOID MemError (PSZ);
- ULONG ParseItem (PSZ,ULONG);
- VOID QueryMemory (SHORT);
-
- HAB hab;
- CHAR szTitle[64];
- char szListItem[40];
- char szPageItem[20];
- HWND hWndFrame; /* Application Frame window handle */
- HWND hWndMemList; /* Memory List box window handle */
- HWND hWndPageList; /* Page List box window handle */
- BOOL bCommitted = FALSE; /* Current settings in list box */
- BOOL bShared = FALSE;
- BOOL bRead = TRUE;
- BOOL bWrite = TRUE;
- BOOL bExecute = TRUE;
- BOOL bTiled = FALSE;
-
- #define FONTNAMELEN 21 /* length + 1 */
- CHAR szListboxFont[] = "10.System Monospaced";
-
- /* Undocumented menu message */
- #define MM_QUERYITEMBYPOS 0x01f3
- #define MAKE_16BIT_POINTER(p) \
- ((PVOID)MAKEULONG(LOUSHORT(p),(HIUSHORT(p) << 3) | 7))
-
- int main()
- {
- HMQ hmq;
- QMSG qmsg;
-
- hab = WinInitialize (0);
- hmq = WinCreateMsgQueue (hab, 0);
-
- WinLoadString (hab, 0, ID_APPNAME, sizeof(szTitle), szTitle);
-
- hWndFrame = WinLoadDlg (HWND_DESKTOP, HWND_DESKTOP,
- MainDlgProc, 0, ID_APPNAME, NULL);
- WinSendMsg (hWndFrame, WM_SETICON,
- (MPARAM)WinLoadPointer (HWND_DESKTOP, 0, ID_APPNAME), NULL);
-
- while (WinGetMsg (hab, &qmsg, 0, 0, 0))
- WinDispatchMsg (hab, &qmsg);
-
- WinDestroyWindow (hWndFrame);
- WinDestroyMsgQueue (hmq);
- WinTerminate (hab);
- return (0);
- }
-
- VOID AddAboutToSystemMenu(HWND hWndFrame)
- {
- MENUITEM mi;
- HWND hWndSysSubMenu;
-
- WinSendMsg (WinWindowFromID (hWndFrame, FID_SYSMENU),
- MM_QUERYITEMBYPOS, 0L, MAKE_16BIT_POINTER(&mi));
- hWndSysSubMenu = mi.hwndSubMenu;
- mi.iPosition = MIT_END;
- mi.afStyle = MIS_SEPARATOR;
- mi.afAttribute = 0;
- mi.id = -1;
- mi.hwndSubMenu = 0;
- mi.hItem = 0;
- WinSendMsg (hWndSysSubMenu, MM_INSERTITEM, MPFROMP (&mi), NULL);
- mi.afStyle = MIS_TEXT;
- mi.id = IDM_ABOUT;
- WinSendMsg (hWndSysSubMenu, MM_INSERTITEM, MPFROMP (&mi),
- "About...");
- return;
- }
-
- VOID CenterWindow (HWND hWnd)
- {
- ULONG ulScrWidth, ulScrHeight;
- RECTL Rectl;
-
- ulScrWidth = WinQuerySysValue (HWND_DESKTOP, SV_CXSCREEN);
- ulScrHeight = WinQuerySysValue (HWND_DESKTOP, SV_CYSCREEN);
- WinQueryWindowRect (hWnd,&Rectl);
- WinSetWindowPos (hWnd, HWND_TOP, (ulScrWidth-Rectl.xRight)/2,
- (ulScrHeight-Rectl.yTop)/2, 0, 0, SWP_MOVE | SWP_ACTIVATE);
- return;
- }
-
- PSZ FormatMemListItem (PVOID pMem, LONG lAllocRequest,
- ULONG ulAllocated, ULONG ulCommitted)
- {
- sprintf (szListItem,"%08lx %8lx %8lx %8lx",
- (ULONG)pMem, (ULONG)lAllocRequest, ulAllocated, ulCommitted);
- WinUpper (hab, 0, 0, szListItem);
- return (szListItem);
- }
-
- PSZ FormatPageListItem (PVOID pMem, ULONG ulFlags)
- {
- sprintf (szPageItem, "%08lx ", pMem);
- szPageItem[9] = (CHAR)(ulFlags & PAG_READ ? 'R' : ' ');
- szPageItem[10] = (CHAR)(ulFlags & PAG_WRITE ? 'W' : ' ');
- szPageItem[11] = (CHAR)(ulFlags & PAG_EXECUTE ? 'E' : ' ');
- szPageItem[12] = (CHAR)(ulFlags & PAG_COMMIT ? 'C' : ' ');
- szPageItem[13] = (CHAR)(ulFlags & PAG_SHARED ? 'S' : ' ');
- WinUpper (hab, 0, 0, szPageItem);
- return (szPageItem);
- }
-
- VOID MemError (PSZ pszMessage)
- {
- WinMessageBox (HWND_DESKTOP, hWndFrame, pszMessage,
- "Memory Allocation", 0, MB_ERROR | MB_OK);
- return;
- }
-
- ULONG ParseItem (PSZ szItem, ULONG CurPos)
- {
- ULONG EndPos = CurPos + 8;
- ULONG ulMem = 0;
- for (; CurPos < EndPos; CurPos++)
- {
- if (szItem[CurPos] != ' ')
- ulMem = ulMem*16 +
- ((szItem[CurPos] <= '9') ? (szItem[CurPos] - '0') :
- (szItem[CurPos] - 'A' + 10));
- }
- return (ulMem);
- }
-
- VOID ChangeAccessRights (USHORT usRights)
- {
- ULONG ulAddress;
- SHORT sSel = -1;
- ULONG ulFlags = 0L;
- BOOL bError = FALSE;
-
- WAIT_POINTER
- ulFlags = (ULONG)(usRights & 0x0007);
- while ((sSel = (SHORT)WinSendMsg (hWndPageList,
- LM_QUERYSELECTION, MPFROMSHORT(sSel),(MPARAM)NULL)) != LIT_NONE)
- {
- WinQueryLboxItemText (hWndPageList, sSel, szPageItem, 20);
- ulAddress = ParseItem(szPageItem, 0);
- if (!DosSetMem ((PVOID)ulAddress, 0x1000, ulFlags))
- {
- ULONG ulNewFlags;
- ULONG ulSize = 0x1000;
- DosQueryMem ((PVOID)ulAddress, &ulSize, &ulNewFlags);
- WinSetLboxItemText (hWndPageList, sSel,
- FormatPageListItem ((PVOID)ulAddress, ulNewFlags));
- }
- else
- {
- char szError[40];
- if (szPageItem[12] != 'C')
- {
- if (!bError)
- MemError (
- "Cannot change access rights of decommitted pages");
- bError = TRUE;
- }
- else
- {
- sprintf (szError, "Unable to change rights for page %08lx",
- ulAddress);
- MemError (szError);
- break;
- }
- }
- }
- ARROW_POINTER
- return;
- }
-
- VOID ChangeCommitState (BOOL bCommit)
- {
- ULONG ulAddress;
- ULONG ulFlags;
- ULONG ulCommitted;
- SHORT sSel = -1;
-
- WAIT_POINTER
- ulCommitted = ParseItem(szListItem, 27);
- while ((sSel = (SHORT)WinSendMsg (hWndPageList,
- LM_QUERYSELECTION, MPFROMLONG(sSel),(MPARAM)NULL)) != LIT_NONE)
- {
- WinQueryLboxItemText (hWndPageList, sSel, szPageItem, 20);
- ulAddress = ParseItem(szPageItem,0);
- if (bCommit)
- {
- /* If already committed nothing to do */
- if (szPageItem[12] == 'C')
- continue;
- ulFlags = PAG_COMMIT;
- if (szPageItem[9] == 'R')
- ulFlags |= PAG_READ;
- if (szPageItem[10] == 'W')
- ulFlags |= PAG_WRITE;
- if (szPageItem[11] == 'E')
- ulFlags |= PAG_EXECUTE;
- }
- else
- {
- /* If already decommitted nothing to do */
- if (szPageItem[12] == ' ')
- continue;
- ulFlags = PAG_DECOMMIT;
- }
- if (!DosSetMem ((PVOID)ulAddress, 0x1000, ulFlags))
- {
- /* Requery the item. Decommitting causes flags to be reset
- to default (flags defined when memory was allocated) */
- ULONG ulSize = 0x1000;
- DosQueryMem ((PVOID)ulAddress, &ulSize, &ulFlags);
- WinSetLboxItemText (hWndPageList, sSel,
- FormatPageListItem ((PVOID)ulAddress, ulFlags));
- if (bCommit)
- ulCommitted += 0x1000;
- else
- ulCommitted -= 0x1000;
- }
- else
- {
- char szError[40];
- sprintf (szError, "Unable to %s page %08lx",
- bCommit ? "commit" : "decommit", ulAddress);
- MemError (szError);
- break;
- }
- }
- sprintf (&szListItem[27], "%8lx", ulCommitted);
- WinUpper (hab, 0, 0, szListItem);
- WinSetLboxItemText (hWndMemList,
- WinQueryLboxSelectedItem(hWndMemList), szListItem);
- ARROW_POINTER
- return;
- }
-
- VOID QueryMemory (SHORT sSel)
- {
- ULONG ulAddress,
- ulAllocated,
- ulCommitted,
- ulSize,
- ulFlags;
- BOOL bError = FALSE;
- BOOL bFirst = TRUE;
-
- WAIT_POINTER
- WinQueryLboxItemText (hWndMemList, sSel, szListItem, 40);
- WinEnableWindowUpdate (hWndPageList, FALSE);
- WinSendMsg (hWndPageList, LM_DELETEALL, 0L, 0L);
-
- ulAddress = ParseItem (szListItem, 0);
- ulCommitted = 0L;
- ulAllocated = 0L;
- ulSize = 0xFFFFFFFF;
- while (!DosQueryMem ((PVOID)ulAddress, &ulSize, &ulFlags) &&
- (bFirst || !(ulFlags & (PAG_BASE | PAG_FREE))))
- {
- bFirst = FALSE;
- ulAllocated += ulSize;
- if (ulFlags & PAG_COMMIT)
- ulCommitted += ulSize;
- while (ulSize)
- {
- if (WinInsertLboxItem (hWndPageList, LIT_END,
- FormatPageListItem ((PVOID)ulAddress, ulFlags)) < 0)
- {
- if (!bError)
- MemError ("Unable to add any more list items");
- bError = TRUE;
- }
- ulAddress += 0x1000;
- ulSize -= 0x1000;
- }
- ulSize = 0xFFFFFFFF;
- }
- WinEnableWindowUpdate (hWndPageList, TRUE);
- WinSendMsg (hWndPageList, LM_SELECTITEM, (MPARAM)0L, (MPARAM)TRUE);
- sprintf (&szListItem[18], "%8lx %8lx", ulAllocated, ulCommitted);
- WinUpper (hab, 0, 0, szListItem);
- WinSetLboxItemText (hWndMemList, sSel, szListItem);
- ARROW_POINTER
-
- return;
- }
-
- MRESULT EXPENTRY AllocateDlgProc (HWND hWnd, ULONG msg,
- MPARAM mp1, MPARAM mp2)
- {
- LONG lMemAlloc;
-
- switch (msg)
- {
- case WM_INITDLG:
- CenterWindow (hWnd);
- WinCheckButton (hWnd,
- (bCommitted ? IDB_COMMITTED : IDB_UNCOMMITTED), TRUE);
- WinCheckButton (hWnd,
- (bShared ? IDB_SHARED : IDB_NONSHARED), TRUE);
- WinCheckButton (hWnd, IDB_READ, bRead);
- WinCheckButton (hWnd, IDB_WRITE, bWrite);
- WinCheckButton (hWnd, IDB_EXECUTE, bExecute);
- WinCheckButton (hWnd, IDB_TILED, bTiled);
- WinSendDlgItemMsg (hWnd, IDB_NUMBYTES, SPBM_SETTEXTLIMIT,
- (MPARAM)7, (MPARAM)0);
- WinSendDlgItemMsg (hWnd, IDB_NUMBYTES, SPBM_SETLIMITS,
- (MPARAM)0x00100000,(MPARAM)1L);
- WinSendDlgItemMsg (hWnd, IDB_NUMBYTES, SPBM_SETCURRENTVALUE,
- (MPARAM)1,(MPARAM)0L);
- WinSendDlgItemMsg (hWnd, IDB_REPEAT, SPBM_SETTEXTLIMIT,
- (MPARAM)4, (MPARAM)0);
- WinSendDlgItemMsg (hWnd, IDB_REPEAT, SPBM_SETLIMITS,
- (MPARAM)9999L,(MPARAM)1L);
- WinSendDlgItemMsg (hWnd, IDB_REPEAT, SPBM_SETCURRENTVALUE,
- (MPARAM)1,(MPARAM)0L);
- WinSetFocus (HWND_DESKTOP,
- WinWindowFromID (hWnd, IDB_NUMBYTES));
- return ((MRESULT)TRUE);
-
- case WM_COMMAND:
- switch (LOUSHORT(mp1))
- {
- case DID_OK:
- WinSendDlgItemMsg (hWnd, IDB_NUMBYTES, SPBM_QUERYVALUE,
- (MPARAM)&lMemAlloc, (MPARAM)0L);
- if (lMemAlloc)
- {
- ULONG flags = 0L;
- BOOL bOkay;
- PVOID pMem;
- ULONG ulRepeat;
-
- if (lMemAlloc < 0)
- lMemAlloc = -lMemAlloc;
- bCommitted = WinQueryButtonCheckstate (hWnd,
- IDB_COMMITTED);
- bShared = WinQueryButtonCheckstate (hWnd, IDB_SHARED);
- bRead = WinQueryButtonCheckstate (hWnd, IDB_READ);
- bWrite = WinQueryButtonCheckstate (hWnd, IDB_WRITE);
- bExecute = WinQueryButtonCheckstate (hWnd, IDB_EXECUTE);
- bTiled = WinQueryButtonCheckstate (hWnd, IDB_TILED);
- if (bCommitted)
- flags |= PAG_COMMIT;
- if (bShared)
- flags |= OBJ_GETTABLE | OBJ_GIVEABLE;
- if (bRead)
- flags |= PAG_READ;
- if (bWrite)
- flags |= PAG_WRITE;
- if (bExecute)
- flags |= PAG_EXECUTE;
- if (bTiled)
- flags |= OBJ_TILE;
-
- WinSendDlgItemMsg (hWnd, IDB_REPEAT, SPBM_QUERYVALUE,
- (MPARAM)&ulRepeat, (MPARAM)0L);
-
- while (ulRepeat--)
- {
- if (!bShared)
- bOkay = !DosAllocMem ((PPVOID)&pMem, lMemAlloc,
- flags);
- else
- bOkay = !DosAllocSharedMem ((PPVOID)&pMem, NULL,
- lMemAlloc, flags);
- if (bOkay)
- {
- SHORT sSel;
- sSel = (SHORT)WinInsertLboxItem (hWndMemList,
- LIT_SORTASCENDING,
- FormatMemListItem (pMem, lMemAlloc, 0L, 0L));
- QueryMemory (sSel);
- WinSendMsg (hWndMemList, LM_SELECTITEM,
- (MPARAM)sSel, (MPARAM)TRUE);
- }
- else
- {
- MemError ("Unable to allocate memory");
- break;
- }
- }
- }
-
- /* FALL THROUGH */
-
- case DID_CANCEL:
- WinDismissDlg (hWnd, TRUE);
- return (0);
- }
- break;
- }
- return (WinDefDlgProc (hWnd, msg, mp1, mp2));
- }
-
-
- MRESULT EXPENTRY MainDlgProc (HWND hWnd, ULONG msg,
- MPARAM mp1, MPARAM mp2)
- {
- SHORT sSel;
- RECTL Rectl;
- BOOL bHandled = TRUE;
- MRESULT mReturn = 0;
-
- static HWND hMenus[4] = { 0, 0, 0, 0 };
-
- switch (msg)
- {
- case WM_INITDLG:
- CenterWindow (hWnd);
- hWndMemList = WinWindowFromID (hWnd, IDL_MEMLIST);
- hWndPageList = WinWindowFromID (hWnd, IDL_PAGELIST);
-
- /* Set the system monospaced font for the listboxes */
- WinSetPresParam (hWndMemList,
- PP_FONTNAMESIZE, FONTNAMELEN, szListboxFont);
- WinSetPresParam (hWndPageList,
- PP_FONTNAMESIZE, FONTNAMELEN, szListboxFont);
-
- /* Load the popup menus */
- hMenus[0] = WinLoadMenu (HWND_OBJECT, 0, FREE_MENU);
- hMenus[1] = WinLoadMenu (HWND_OBJECT, 0, SELECT_MENU);
- hMenus[2] = WinLoadMenu (HWND_OBJECT, 0, STATE_MENU);
- hMenus[3] = WinLoadMenu (HWND_OBJECT, 0, RIGHTS_MENU);
-
- /* Add the About menu item to the system menu */
- AddAboutToSystemMenu (hWnd);
- break;
-
- case WM_CONTROL:
- switch (LOUSHORT(mp1))
- {
- case IDL_MEMLIST:
- if (HIUSHORT(mp1) == LN_SELECT)
- QueryMemory ((SHORT)WinQueryLboxSelectedItem((HWND)mp2));
- break;
- }
- break;
-
- case WM_COMMAND:
- case WM_SYSCOMMAND:
- switch (LOUSHORT(mp1))
- {
- case IDB_ALLOCATE:
- WinDlgBox (HWND_DESKTOP, hWnd, AllocateDlgProc,
- 0, IDD_ALLOCATE, NULL);
- break;
-
- case IDB_SELECT:
- case IDB_FREE:
- case IDB_STATE:
- case IDB_RIGHTS:
- WinQueryWindowRect (WinWindowFromID(hWnd, LOUSHORT(mp1)),
- &Rectl);
- WinMapWindowPoints (WinWindowFromID(hWnd, LOUSHORT(mp1)),
- hWnd, (PPOINTL)&Rectl, 2);
- WinPopupMenu (hWnd, hWnd,
- hMenus[LOUSHORT(mp1) / 100 - 1],
- Rectl.xLeft, Rectl.yTop,
- LOUSHORT(mp1) + 1,
- PU_HCONSTRAIN | PU_VCONSTRAIN | PU_KEYBOARD |
- PU_MOUSEBUTTON1 | PU_SELECTITEM);
- break;
-
- case IDM_SELECT_NONE:
- WinSendMsg (hWndPageList, LM_SELECTITEM, (MPARAM)LIT_NONE,
- 0L);
- break;
-
- case IDM_SELECT_ALL:
- {
- SHORT sCount;
- sCount = (SHORT)WinQueryLboxCount (hWndPageList);
- while (sCount--)
- WinSendMsg (hWndPageList, LM_SELECTITEM, (MPARAM)sCount,
- (MPARAM)TRUE);
- }
- break;
-
- case IDM_COMMIT_SELECTED:
- case IDM_DECOMMIT_SELECTED:
- ChangeCommitState (LOUSHORT(mp1) == IDM_COMMIT_SELECTED);
- break;
-
- case IDM_READ:
- case IDM_WRITE:
- case IDM_EXECUTE:
- case IDM_READWRITE:
- case IDM_READEXECUTE:
- case IDM_WRITEEXECUTE:
- case IDM_READWRITEEXECUTE:
- ChangeAccessRights (LOUSHORT(mp1));
- break;
-
- case IDM_FREE_SELECTED:
- if ( (sSel=(SHORT)WinQueryLboxSelectedItem (hWndMemList)) !=
- LIT_NONE)
- {
- char szMem[9];
- ULONG ulMem;
-
- WinQueryLboxItemText (hWndMemList, sSel, szMem, 9);
- ulMem = ParseItem (szMem,0);
- if (!DosFreeMem ((PVOID)ulMem))
- {
- WinDeleteLboxItem (hWndMemList, sSel);
- WinSendMsg (hWndPageList, LM_DELETEALL, 0L, 0L);
- if (sSel >= (SHORT)WinQueryLboxCount(hWndMemList))
- sSel--;
- WinSendMsg (hWndMemList, LM_SELECTITEM, (MPARAM)sSel,
- (MPARAM)TRUE);
- }
- else
- MemError ("Error freeing memory");
- }
- break;
-
- case IDM_FREE_ALL:
- {
- char szMem[9];
- ULONG ulMem;
- while (WinQueryLboxItemText (hWndMemList, 0, szMem, 9) > 0)
- {
- ulMem = ParseItem (szMem,0);
- if (!DosFreeMem ((PVOID)ulMem))
- WinDeleteLboxItem (hWndMemList, 0);
- else
- {
- MemError ("Error freeing memory");
- break;
- }
- }
- WinSendMsg (hWndPageList, LM_DELETEALL, 0L, 0L);
- break;
- }
-
- case IDM_ABOUT:
- DisplayAbout (hWnd, szTitle);
- break;
-
- case SC_CLOSE:
- WinDestroyWindow (hMenus[0]);
- WinDestroyWindow (hMenus[1]);
- WinDestroyWindow (hMenus[2]);
- WinDestroyWindow (hMenus[3]);
- WinPostMsg (hWnd, WM_COMMAND, (MPARAM)IDM_FREE_ALL, 0L);
- WinPostMsg (hWnd, WM_QUIT, 0L, 0L);
- break;
-
- default:
- bHandled = (msg == WM_COMMAND);
- }
- break;
-
- default:
- bHandled = FALSE;
- break;
- }
-
- if (!bHandled)
- mReturn = WinDefDlgProc (hWnd, msg, mp1, mp2);
-
- return (mReturn);
- }
-
-
- ΓòÉΓòÉΓòÉ 8.20. MEMORY.H ΓòÉΓòÉΓòÉ
-
- /* --------------------------------------------------------------------
- Memory Header File
- Chapter 9
- Real World Programming for OS/2
- Copyright (c) 1993 Blain, Delimon, and English
- -------------------------------------------------------------------- */
-
- #define ID_APPNAME 1
- #define IDD_ALLOCATE 2
-
- #define IDL_MEMLIST 10
- #define IDL_PAGELIST 11
- #define IDB_ALLOCATE 12
- #define IDB_FREE 100
- #define IDB_SELECT 200
- #define IDB_STATE 300
- #define IDB_RIGHTS 400
-
- #define IDB_NUMBYTES 20
- #define IDB_COMMITTED 21
- #define IDB_UNCOMMITTED 22
- #define IDB_SHARED 23
- #define IDB_NONSHARED 24
- #define IDB_READ 25
- #define IDB_WRITE 26
- #define IDB_EXECUTE 27
- #define IDB_TILED 28
- #define IDB_REPEAT 29
-
- #define FREE_MENU 100
- #define SELECT_MENU 200
- #define STATE_MENU 300
- #define RIGHTS_MENU 400
-
- #define IDM_FREE_SELECTED 101
- #define IDM_FREE_ALL 102
- #define IDM_SELECT_ALL 201
- #define IDM_SELECT_NONE 202
- #define IDM_COMMIT_SELECTED 301
- #define IDM_DECOMMIT_SELECTED 302
- #define IDM_READ 401
- #define IDM_WRITE 402
- #define IDM_READWRITE 403
- #define IDM_EXECUTE 404
-
-
- ΓòÉΓòÉΓòÉ 8.21. MEMORY.RC ΓòÉΓòÉΓòÉ
-
- /* --------------------------------------------------------------
- Memory Resource File
- Chapter 9
- Real-World Programming for OS/2 2.1
- Copyright (c) 1993 Blain, Delimon, and English
- -------------------------------------------------------------- */
-
- #include <os2.h>
- #include "memory.h"
-
- ICON ID_APPNAME MEMORY.ICO
-
- STRINGTABLE LOADONCALL MOVEABLE
- BEGIN
- ID_APPNAME "Memory Allocation Program"
- END
-
- rcinclude memory.dlg
- rcinclude allocate.dlg
- rcinclude ..\..\common\about.dlg
-
-
- ΓòÉΓòÉΓòÉ 8.22. ALLOCATE.DLG ΓòÉΓòÉΓòÉ
-
- /* ------------------------------------------------------------------
- Memory Allocation Dialog
- Chapter 9
- Real-World Programming for OS/2 2.1
- Copyright (c) 1993 Blain, Delimon, and English
- ------------------------------------------------------------------ */
-
- DLGTEMPLATE IDD_ALLOCATE LOADONCALL MOVEABLE DISCARDABLE
- BEGIN
- DIALOG "Allocate Memory", IDD_ALLOCATE, -5, 53, 264, 72, WS_VISIBLE, FCF_SYSMENU |
- FCF_TITLEBAR
- BEGIN
- LTEXT "Number of bytes", -1, 3, 58, 72, 8
- CONTROL "", IDB_NUMBYTES, 76, 56, 64, 12, WC_SPINBUTTON,
- SPBS_NUMERICONLY | SPBS_MASTER |
- SPBS_JUSTRIGHT | SPBS_FASTSPIN | WS_TABSTOP
- | WS_VISIBLE
- LTEXT "Repeat", -1, 144, 58, 36, 8
- CONTROL "", IDB_REPEAT, 181, 56, 30, 12, WC_SPINBUTTON,
- SPBS_NUMERICONLY | SPBS_MASTER |
- SPBS_JUSTRIGHT | SPBS_FASTSPIN | WS_TABSTOP
- | WS_VISIBLE
- GROUPBOX "Type", -1, 3, 24, 87, 31
- AUTORADIOBUTTON "Committed", IDB_COMMITTED, 9, 37, 58, 10,
- WS_TABSTOP
- AUTORADIOBUTTON "Uncommitted", IDB_UNCOMMITTED, 9, 27, 68, 10,
- WS_TABSTOP |
- GROUPBOX "Shareability", -1, 97, 24, 77, 31
- AUTORADIOBUTTON "Shared", IDB_SHARED, 101, 37, 44, 10,
- WS_TABSTOP
- AUTORADIOBUTTON "NonShared", IDB_NONSHARED, 101, 27, 61, 10,
- WS_TABSTOP
- GROUPBOX "Access Rights", -1, 181, 15, 77, 40
- AUTOCHECKBOX "Read", IDB_READ, 186, 37, 40, 10
- AUTOCHECKBOX "Write", IDB_WRITE, 186, 27, 40, 10
- AUTOCHECKBOX "Execute", IDB_EXECUTE, 186, 17, 46, 10
- AUTOCHECKBOX "Tiled", IDB_TILED, 102, 7, 34, 10, WS_GROUP
- DEFPUSHBUTTON "OK", DID_OK, 4, 4, 40, 14, WS_GROUP
- PUSHBUTTON "Cancel", DID_CANCEL, 47, 4, 40, 14
- END
- END
-
-
- ΓòÉΓòÉΓòÉ 8.23. MEMORY.DLG ΓòÉΓòÉΓòÉ
-
- /* ----------------------------------------------------------------
- Memory Dialog
- Chapter 9
- Real-World Programming for OS/2 2.1
- Copyright (c) 1993 Blain, Delimon, and English
- ---------------------------------------------------------------- */
-
- MENU FREE_MENU PRELOAD
- BEGIN
- MENUITEM "Free Selected Item", IDM_FREE_SELECTED
- MENUITEM "Free All Items", IDM_FREE_ALL
- END
-
- MENU SELECT_MENU PRELOAD
- BEGIN
- MENUITEM "Select All", IDM_SELECT_ALL
- MENUITEM "Select None", IDM_SELECT_NONE
- END
-
- MENU STATE_MENU PRELOAD
- BEGIN
- MENUITEM "Commit Selected Pages", IDM_COMMIT_SELECTED
- MENUITEM "Decommit Selected Pages", IDM_DECOMMIT_SELECTED
- END
-
- MENU RIGHTS_MENU PRELOAD
- BEGIN
- MENUITEM "Read", IDM_READ
- MENUITEM "Write", IDM_WRITE
- MENUITEM "Execute", IDM_EXECUTE
- MENUITEM "Read/Write", IDM_READWRITE
- MENUITEM "Read/Execute", IDM_READEXECUTE
- MENUITEM "Write/Execute", IDM_WRITEEXECUTE
- MENUITEM "Read/Write/Execute", IDM_READWRITEEXECUTE
- END
-
- DLGTEMPLATE ID_APPNAME LOADONCALL MOVEABLE DISCARDABLE
- BEGIN
- DIALOG "Memory Allocation", ID_APPNAME, 5, 11, 330, 153,
- WS_VISIBLE | FS_DLGBORDER,
- FCF_SYSMENU | FCF_TITLEBAR | FCF_DLGBORDER |
- FCF_TASKLIST | FCF_ICON;
- BEGIN
- LTEXT "Address", -1, 9, 137, 37, 8
- LTEXT "Requested", -1, 55, 137, 49, 8
- LTEXT "Allocated", -1, 109, 137, 41, 8
- LTEXT "Committed", -1, 156, 137, 48, 8
- LTEXT "Address", -1, 223, 137, 37, 8
- LTEXT "Flags", -1, 283, 137, 26, 8
- LISTBOX IDL_MEMLIST, 5, 22, 208, 114, WS_GROUP
- | WS_TABSTOP
- DEFPUSHBUTTON "Allocate", IDB_ALLOCATE, 4, 4, 50, 14,
- WS_GROUP | WS_TABSTOP
- PUSHBUTTON "Free", IDB_FREE, 57, 4, 50, 14,
- WS_TABSTOP
- LISTBOX IDL_PAGELIST, 222, 22, 102, 114,
- WS_GROUP | WS_TABSTOP | LS_MULTIPLESEL
- | LS_EXTENDEDSEL
- PUSHBUTTON "Select", IDB_SELECT, 171, 4, 50, 14,
- WS_GROUP | WS_TABSTOP
- PUSHBUTTON "State", IDB_STATE, 224, 4, 50, 14,
- WS_TABSTOP
- PUSHBUTTON "Rights", IDB_RIGHTS, 277, 4, 50, 14,
- WS_TABSTOP
- END
- END
-
-
- ΓòÉΓòÉΓòÉ 8.24. MEMORY.DEF ΓòÉΓòÉΓòÉ
-
- NAME MEMORY WINDOWAPI
- DESCRIPTION 'Memory Program (c) Blain, Delimon, & English, 1993'
- PROTMODE
- HEAPSIZE 4096
- STACKSIZE 16384
- EXPORTS MainDlgProc
- AllocateDlgProc
-
-
- ΓòÉΓòÉΓòÉ 8.25. Using the MEMORY Sample Application ΓòÉΓòÉΓòÉ
-
- The MEMORY application demonstrates committing, freeing, and changing the
- access rights to blocks of memory. Its use is also fairly explanatory. Remember
- though that you are making the changes only within a block of memory that is
- available to the MEMORY application. After starting the memory application,
- press the allocate button to allocate blocks of memory. The display for the
- allocated memory will appear similar to that shown in Figure 9.6.
- Figure 9.6. MEMORY application.
-
- Practice allocating both committed and uncommitted blocks of memory. Notice
- what state a particular block of memory must be in before its access rights can
- be changed. Notice also that the successive addresses for committed blocks of
- memory that are not shared grow upward. Addresses for successive blocks of
- shared memory grow downward. This behavior is in keeping with how these blocks
- of memory are allocated by the system.
-
-
- ΓòÉΓòÉΓòÉ 8.26. Understanding MEMORY's Code ΓòÉΓòÉΓòÉ
-
- Two macros will be used to set our current pointer. When we begin a
- time-consuming process, we will set the pointer to the SPTR_WAIT pointer and,
- when done, reset it to the SPTR_ARROW pointer:
-
- #define WAIT_POINTER WinSetPointer (HWND_DESKTOP, \
- WinQuerySysPointer (HWND_DESKTOP,SPTR_WAIT,FALSE));
- #define ARROW_POINTER WinSetPointer (HWND_DESKTOP, \
- WinQuerySysPointer (HWND_DESKTOP,SPTR_ARROW,FALSE));
-
- The CenterWindow function merely centers the dialog in the middle of the
- screen:
-
- VOID CenterWindow (HWND hWnd)
- {
- ULONG ulScrWidth, ulScrHeight;
- RECTL Rectl;
-
- ulScrWidth = WinQuerySysValue (HWND_DESKTOP, SV_CXSCREEN);
- ulScrHeight = WinQuerySysValue (HWND_DESKTOP, SV_CYSCREEN);
- WinQueryWindowRect (hWnd,&Rectl);
- WinSetWindowPos (hWnd, HWND_TOP, (ulScrWidth-Rectl.xRight)/2,
- (ulScrHeight-Rectl.yTop)/2, 0, 0, SWP_MOVE | SWP_ACTIVATE);
- return;
- }
-
- To format our memory list items in the list box, the FormatMemListItem function
- takes the memory address, amount of memory originally requested, amount of
- memory actually allocated, and amount of memory currently committed and formats
- them using the sprintf function:
-
- PSZ FormatMemListItem (PVOID pMem, LONG lAllocRequest,
- ULONG ulAllocated, ULONG ulCommitted)
- {
- sprintf (szListItem,"%08lx %8lx %8lx %8lx",
- (ULONG)pMem, (ULONG)lAllocRequest, ulAllocated, ulCommitted);
- WinUpper (hab, 0, 0, szListItem);
- return (szListItem);
- }
-
- Likewise, the FormatPageListItem function takes the memory address and current
- page flags and formats them using the sprintf function. The memory page flags
- are checked individually and set in the string:
-
- PSZ FormatPageListItem (PVOID pMem, ULONG ulFlags)
- {
- sprintf (szPageItem, "%08lx ", pMem);
- szPageItem[9] = (CHAR)(ulFlags & PAG_READ ? 'R' : ' ');
- szPageItem[10] = (CHAR)(ulFlags & PAG_WRITE ? 'W' : ' ');
- szPageItem[11] = (CHAR)(ulFlags & PAG_EXECUTE ? 'E' : ' ');
- szPageItem[12] = (CHAR)(ulFlags & PAG_COMMIT ? 'C' : ' ');
- szPageItem[13] = (CHAR)(ulFlags & PAG_SHARED ? 'S' : ' ');
- WinUpper (hab, 0, 0, szPageItem);
- return (szPageItem);
- }
- The MemError function is merely a consistent way for us to output our error
- messages using the WinMessageBox function:
-
- VOID MemError (PSZ pszMessage)
- {
- WinMessageBox (HWND_DESKTOP, hWndFrame, pszMessage,
- "Memory Allocation", 0, MB_ERROR | MB_OK);
- return;
- }
-
- When a list box item is selected, we need to determine what memory address has
- been selected. Because each list box item is formatted as a string, the
- ParseItem function can be used to convert any portion of a string from its
- hexadecimal string to an equivalent binary value. The first parameter szItem is
- the string to be parsed. The second parameter CurPos is the offset in szItem to
- start the conversion. The maximum size of our hexadecimal substrings is eight
- characters, so we look at the eight characters starting at position CurPos:
-
- ULONG ParseItem (PSZ szItem, ULONG CurPos)
- {
- ULONG EndPos = CurPos + 8;
- ULONG ulMem = 0;
- for (; CurPos < EndPos; CurPos++)
- {
- if (szItem[CurPos] != ' ')
- ulMem = ulMem*16 +
- ((szItem[CurPos] <= '9') ? (szItem[CurPos] - '0') :
- (szItem[CurPos] - 'A' + 10));
- }
- return (ulMem);
- }
-
- The ChangeAccessRights function is used to set the access rights of the
- selected memory pages. The only parameter to this function is the new access
- rights to be set. Our menu items have conveniently been defined such that the
- low byte of their identifier is the new access rights, so we can just AND this
- identifier with 0x0007 to get the new access rights. We then loop, querying the
- next selected item in the memory page list until there are no more selected.
- Querying the text for each item, we call ParseItem to retrieve the memory
- address for the selected page. The call to DosSetMem will attempt to set the
- access rights of the page. Because we can change the access rights only for a
- committed page, the DosSetMem call will fail if the page is not committed. If
- the DosSetMem call is successful, we query the access rights of the page and
- reformat the item to be updated in the list box. It is necessary to query the
- memory again because decommitting a page will reset the access rights to those
- defined when the memory was allocated.
-
- The ChangeCommitState function is used either to commit or decommit the
- selected memory pages. The only parameter is a flag indicating if the memory is
- to be committed or decommitted. Because the list box text for the memory list
- box has already been retrieved in QueryMemory, we can just call ParseItem on
- szListItem to get the current amount of committed memory for the selected item.
- This enables us to update the amount of committed memory as we make the
- updates.
-
- We then loop, querying the next selected item in the memory page list until
- there are no more selected. Querying the text for each item, we call ParseItem
- to retrieve the memory address for the selected page. Because we want to leave
- the access rights unchanged for the page, if we are committing memory, we must
- set the access rights to their current settings. We can either call DosQueryMem
- to get them or look at the cutrent flags in the list box string. The call to
- DosSetMem will attempt to commit or decommit the page. If the call to DosSetMem
- is successful, we query the memory to get its new flags. When a page is
- decommitted, its access rights are reset to those defined when the memory was
- allocated. We then update the amount of committed memory. When all selected
- pages have been processed, we must update the amount of committed memory in the
- memory list box.
-
- The QueryMemory function is used to retrieve the current flags for each page of
- memory for each allocated memory object. The input to this function is the
- index in the list box of the selected memory object. We begin by querying the
- text for the selected item and deleting all the items in the page list box. The
- call to WinEnableWindowUpdate tells PM not to repaint the list box as we are
- making our updates. This prevents the window from repainting every time we add
- a new item. When we are done, we will reenable the painting of the list box.
-
- The call to ParseItem converts the address portion of the string, so we have
- the base address for the memory object. We then repeatedly make calls to
- DosQueryMem to query the memory flags. Remember, if the address queried is the
- first page of a memory object, the PAG_BASE flag is set. If the page is not
- currently allocated, the PAG_FREE flag is set. Also, the DosQueryMem call will
- query pages until it finds a page with a different memory flag than the first
- page queried or it finds the first page of the next allocated memory object.
- Our loop will, thus, repeat until we either reach the end of the currently
- allocated object (PAG_FREE) or we reach the beginning of the next allocated
- memory object (PAG_BASE). We want to ignore the check for PAG_BASE on the first
- page because it will have the PAG_BASE flag set.
-
- Each call to DosQueryMem will start at the address in ulAddress and return the
- size of the memory pages that have the same flags as the first page at
- ulAddress. The ulFlags variable will contain those flags. Our total allocated
- count can be incremented by ulSize, and ulCommitted is incremented if the
- PAG_COMMIT flag is set. We then add a page entry to the pages list box for each
- page in the queried memory. When the memory object has been completely queried,
- we reenable the list box updates and select the first item in the list box. We
- also update the allocated and committed totals in the memory list box.
-
- AllocateDlgProc is the dialog function for the memory allocation dialog. When
- we receive the WM_INITDLG message, we can initialize all the controls in the
- dialog window. Because we save the last settings of each button from the last
- time the dialog was displayed, we use those values to either check or uncheck
- the buttons. Our spinners are initialized for their size, limits, and current
- value. Our dialog enables you to specify the amount of memory to allocate and
- its access rights, whether to commit the memory when it is allocated, whether
- it is private or shared memory, and whether it should be allocated out of the
- tiled arena. We also have the ability to repeat the memory request up to 9,999
- times. When the WM_COMMAND message is received with the OK button pressed, we
- query the values input and current state of the buttons. Then, for each memory
- allocation request (the repeat count), we make the call to either DosAllocMem
- (private memory allocation) or DosAllocSharedMem (shared memory allocation). If
- the allocation is successful, we add an entry to the memory list box and call
- QueryMemory to fill the pages list box. When all memory requests have been
- made, we exit the dialog.
-
- MainDlgProc is the dialog function for the main dialog of this program. When we
- receive the WM_INITDLG message, we query the handles and set the font for our
- two list boxes. We set the font to a monospaced font so that the columns in the
- list box align properly. The four different pop-up menus are also loaded at
- this time. If we receive a WM_CONTROL message from the memory list box and an
- item has been selected, we call QueryMemory to update the pages list box with
- the pages for the selected memory object. We receive a WM_COMMAND message for
- each button pressed in the dialog. If the command is IDB_ALLOCATE, we display
- the memory allocation dialog. If the command is IDB_SELECT, IDB_FREE,
- IDB_STATE, or IDB_RIGHTS, we display the pop-up menu directly above the button.
- The identifiers for the buttons and menus are the same. If IDM_SELECT_NONE is
- selected, we send the LM_SELECTITEM message to the pages list box with LIT_NONE
- specified so that no items are selected. If IDM_SELECT_ALL is selected, we send
- the LM_SELECTITEM message to the pages list box for each item in the list box.
- If one of the commit options is selected, we call ChangeCommitState. If one of
- the access rights options is selected, we call ChangeAccessRights. If
- IDM_FREE_SELECTED is selected, we call DosFreeMem on the selected memory object
- and remove it from the list box. After deleting the item, we select a new item.
- If IDM_FREE_ALL is selected, we call DosFreeMem on each item in the list box
- and remove it from the list box.