home *** CD-ROM | disk | FTP | other *** search
-
- ΓòÉΓòÉΓòÉ 1. Title Page ΓòÉΓòÉΓòÉ
-
- Welcome to EDM/2 - The Electronic OS/2 Developers' Magazine!.
-
- Portions Copyright (C)1993 by Steve Luzynski.
-
- Issue #2 - April 1993.
-
- (Press 'Forward' to page.)
-
-
- ΓòÉΓòÉΓòÉ 2. Copyright Notice and other Legal Stuff ΓòÉΓòÉΓòÉ
-
- EDM/2 Copyright (C)1993 by Steve Luzynski. This publication may be freely
- distributed in electronic form provided that all parts are present in their
- original unmodified form. A reasonable fee may be charged for the physical act
- of distribution; no fee may be charged for the publication itself.
-
- All articles are copyrighted by their authors. No part of any article may be
- reproduced without permission from the original author.
-
- Neither this publication nor Steve Luzynski is affiliated with International
- Business Machines Corporation.
-
- OS/2 is a registered trademark of International Business Machines Corporation.
- Other trademarks are property of their respective owners. Any mention of a
- product in this publication does not constitute an endorsement or affiliation
- unless specifically stated in the text.
-
-
- ΓòÉΓòÉΓòÉ 3. From the Editor ΓòÉΓòÉΓòÉ
-
- Hi! and welcome again to EDM/2.
-
- This month was a big one for OS/2 developers. The new PDK CDROM, including all
- kinds of great OS/2 tools from IBM, was released at the incredible price of
- $15. Look for a new PM mail program from me soon now that I have the TCP/IP
- developer's kit...
-
- The other big news this month was the release of Borland C++ for OS/2. My copy
- came in on the 11th - about a week after I ordered it. I'm very impressed with
- this first offering from Borland...
-
- Borland C++ for OS/2
-
- In fact, I'm impressed enough that I'm going to talk about it with the rest of
- my space. Borland C++ came in Borland's new style packaging - a slick blue &
- white box. Included are seven disks and 8 manuals.
-
- I installed all 28 megs (including all sample code) and sat down to read the
- manuals while I waited. Apparently, Borland supplied a DOS based decompressor
- program - my guess is that the install time will drop dramatically after they
- rewrite their compression program for OS/2.
-
- After the install completed, I made all the changes listed in the README file.
- I would suggest that anyone installing it do the same - otherwise a few things
- won't work quite right. I would also suggest deleting the IBM Toolkit and its
- associated CONFIG.SYS lines before installing to keep things from getting
- confused. Besides, BC comes with everything the Toolkit includes EXCEPT the SOM
- reference (.INF) file. Copy it out if you need it.
-
- Most of the sample code was ported directly from the toolkit. I compiled one to
- make sure everything worked (it did) and started on converting one of my EMX
- programs to BC.
-
- Converting from EMX to BC
-
- The first thing I noticed when I compiled my program under BC was a lot of
- invalid conversions. EMX let me get away without explicitly casting some type
- conversions; BC demanded casts for most of these. I also found that NULL and 0L
- are not the same thing under BC; this required determining what it was I really
- meant when I had used NULL under EMX.
-
- After fixing these two problems (which were really the only ones I had), I
- immediately noticed how much faster Borland is at compiling - especially when
- using precompiled headers. It is also possible to continue editing while BC is
- compiling - something I could never satisfactorily do when using Emacs as my
- development environment.
-
- I would have liked to have shown before and after code; unfortunately, in my
- excitement I didn't save the old EMX version. Rest assured that it isn't really
- all that difficult - I suspect many of my problems were caused by using an OS/2
- 1.3 book as a reference, something I have since rectified.
-
- Etc.
-
- You may have noticed that this issue is a little smaller than the last one.
- This came about for a number of reasons:
-
- 1. March brought about Spring Break for many college students (and teachers.)
- These people are among the contributors to this magazine, and many of them
- decided that lying on a warm Florida beach would be more fun than sitting
- in front of a computer writing articles.
-
- 2. At least for me, the release of Borland C++ for OS/2 meant that I was
- spending a lot more time programming since it suddenly became a lot easier.
-
- 3. Since I too had spring break I was late in sending out guidelines and the
- deadline to the authors. As much as I hate to admit it, this is the biggest
- reason. I promise next time I'll be earlier.
- But have no fear, next month we'll be right back up to speed!
-
- As always, article ideas, criticism, and compliments are welcome at
- sal8@po.cwru.edu.
-
- Steve Luzynski, Editor.
-
-
- ΓòÉΓòÉΓòÉ 4. Corrections and Clarifications ΓòÉΓòÉΓòÉ
-
- Corrections
- and
- Clarifications
-
-
- ΓòÉΓòÉΓòÉ 4.1. From Raja Thiagarajan ΓòÉΓòÉΓòÉ
-
- Dear Editor:
-
- First of all, thanks for the nice job you did converting my article from raw
- ASCII to INF. You did an excellent job of covering my complete ignorance of
- VIEW.
-
- As long as I'm here, I've got a couple of clarifications I'd like to make to my
- "Unofficial Guide to Palette Manager" article.
-
- The first is a warning: TRIDENT VIDEO CARDS *DO* ALSO SUFFER FROM THE FATAL
- BUG. That is, if you're running the Service Pack and the Trident driver that
- was released shortly afterwards, changing the palette and then moving an icon
- will LOCK YOUR MACHINE, exactly as with ET4000s. Also as with ET4000s, the
- problem goes away if you use the 2.1 beta. Thanks to James Justice for testing
- and verifying this.
-
- Finally, I'd also like to say that I've discovered one more video platform that
- supports Palette Manager: If you install the 2.1 beta on IBM's new ThinkPad
- 700C, you can use a 640x480x256-color mode that supports Palette Manager.
- Unfortunately, it suffers from the Universal, GpiBitBlt, and Scaling bugs that
- I documented in my article. But it sure is nice to do pretty paletted graphics
- while lying on your stomach!
-
- Raja Thiagarajan / sthiagar@bronze.ucs.indiana.edu / 3-23-93
-
-
- ΓòÉΓòÉΓòÉ 4.2. From the Editor ΓòÉΓòÉΓòÉ
-
- Last month, in the last-minute rush to get my first issue out on time, I ended
- up forgetting to credit the authors for their articles on the title pages.
- Here's the complete list (and my apologies):
-
- o Larry Salomon - Questions and Answers
-
- o Steve Lacy - Advanced GPI
-
- o Raja Thaigarajan - Unofficial Guide to Palette Manager
-
- o Gavin Baker - Introduction to PM -- Part I
-
- o David Charlap - The Making of Minesweeper
- Again, my apologies to all who were needlessly confused.
-
- Steve Luzynski - Editor
-
-
- ΓòÉΓòÉΓòÉ 5. This Month's Features ΓòÉΓòÉΓòÉ
-
- o OS/2 Presentation Manager Drivers
-
- o Road Map for the WorkPlace Shell
-
- o Beginning Client/Server Programming:Named Pipes
-
-
- ΓòÉΓòÉΓòÉ 5.1. OS/2 Presentation Drivers in a Nutshell ΓòÉΓòÉΓòÉ
-
- OS/2 Presentation Drivers
- in a
- Nutshell
-
- Dave Raymer
- (dave@suite.com)
-
-
- ΓòÉΓòÉΓòÉ 5.1.1. Introduction ΓòÉΓòÉΓòÉ
-
- Historically, the general population has held the belief that computers are
- inherently difficult to use. One of the outgrowths of this popularly held
- belief has been the Graphical User Interface, or more commonly the GUI. The
- designers of OS/2, both on the Microsoft, and IBM side, strove to provide not
- only an easy to use interface, but an easy to program interface. This ease is
- measured by the robustness of the application programming interfaces (API's)
- that OS/2 supports, as well as the high degree of device independence that is
- provided by the display and printer subsystems.
-
- The OS/2 display and printer subsystems are built around the OS/2 Graphics
- Engine, and a set of executable programs collectively termed "Presentation
- Drivers." Presentation Drivers (PD's) are a special form of dynamic link
- library (DLL) that provide a predefined interface for the OS/2 Graphics Engine
- (PMGRE) to use in rendering. The display driver is one form of a presentation
- driver, and the printer/plotter driver is another. The primary difference
- between the two is that the display driver must provide support for a pointing
- device, but on the whole, the functionality of the two is essentially the same.
-
- In general, Joe and Suzi User interact with OS/2 through the WorkPlace Shell,
- which is an application that makes extensive use of two DLLs, PMWIN.DLL and
- PMGPI.DLL. These two DLLs are responsible for providing the API interface that
- application programmers use. In turn, PMWIN and PMGPI, use the PMGRE in a
- manner very similar to the shell's use of PMWIN and PMGPI. The PMGRE in turn
- uses the PD to produce the physical output that the user sees and interacts
- with, whether this output is on the video display, or a piece of paper. Figure
- 1 provides an annotated stack model of this simplified view of the OS/2 user
- interface.
-
- WorkPlace Shell <--- User interacts with
- PMWIN.DLL PMGPI.DLL <--- WorkPlace interacts with
- PMGRE.DLL <--- PMWIN/PMGPI interact with
- <presentation driver> <--- produces visual output
- <physical device> <--- displays visual output
-
- (fig. 1)
-
-
- ΓòÉΓòÉΓòÉ 5.1.2. A Brief Overview Of The Printer Subsystem ΓòÉΓòÉΓòÉ
-
- The OS/2 Print Subsystem consists of three primary components, the print
- manager, the print spooler, and the presentation driver. Under OS/2 version
- 2.x the print manager is implemented through WorkPlace Shell printer objects.
- The WPS printer object provides the user the ability to manage print jobs on a
- per printer basis, but is not the focus of this article, so little more will be
- said in regards to it.
-
- OS/2 provides two print spoolers, PMPRINT and PMPLOT, which differ in the fact
- that PMPLOT supplies a reverse vector clipping engine developed by Steve
- Matthews for Micrografx, Incorporated, and reimplemented by Wes Bell, also of
- Micrografx, for IBM, enabling HPGL plotters to produce correct output in what
- is primarily a raster oriented environment. The purpose of the print spooler
- is to build a meta-data file of an appli- cations request for hardcopy output,
- allowing the application to continue processing without without the need to
- wait for hardcopy output rendering to complete.
-
- The OS/2 print spoolers provide two types of meta-data files, the first is a
- result of opening the printer OD_QUEUED with the PM_Q_STD standard option. In
- this mode, the spooler will produce an intermediate data file that contains
- boundary information as well as the application supplied arguments to the
- rendering functions. The second format occurs when the printer is opened
- OD_QUEUED with the PM_Q_RAW option. This combination will produce an
- intermediate data file that is the raw printer data stream. However, the
- overall data path through the printer will remain the same.
-
- When used in conjunction with the spooler, the data is passed through the
- presentation driver twice. In the PM_Q_STD mode, the first pass is used to
- accumulate bounds and do basic error checking on the appli- ation supplied
- arguments, while the second pass, initiated by the spooler, will generate the
- hardcopy output. In the PM_Q_RAW format, the meta-data file created contains
- raw printer data stream, and will pass through the presentation driver
- unmodified on the second pass.
-
-
- ΓòÉΓòÉΓòÉ 5.1.3. The Presentation Driver ΓòÉΓòÉΓòÉ
-
- The OS/2 presentation driver is built around the concept of the dispatch table.
- The dispatch table belongs to the OS/2 graphics engine, and is passed to the
- driver at a specified point during the enable processing, so that the
- presentaion driver can override the default processing of certain key rendering
- routines. The enable processing for a presentation driver is handled through a
- procedure that is aliased to or called OS2_PM_DRV_ENABLE. The following snipet
- from a presentation driver module definition file's export section aliases a
- procedure "Enable" to OS2_PM_DRV_ENABLE, and places it at ordinal 200 in the
- driver. Placing the OS2_PM_DRV_ENABLE procedure at ordinal 200 is required.
- OS2_PM_DRV_ENABLE = Enable @200
-
- The prototype for the Enable procedure, for the CSet/2(tm) compiler package, is
- as follows. ULONG _System Enable(ULONG SubFunc,ULONG Param1,ULONG Param2);
-
- The returned value from the enable procedure varies from subfunction to
- subfunction.
-
- The following table lists the subfunctions.
-
- Name Subfunction Parameters Used
- ---------------------------------------------------------------
- Fill_Logical_Device (1) Param2
- Fill_Physical_Device (2) Param1
- Disable_Physical_Device (4) Param1
- Enable_DC_Begin (5) Param1
- Disable_DC_Complete (6) Param1
- Save_DC_State (7) Param1
- Restore_DC_State (8) both
- Reset_DC_State (9) both
- Enable_DC_Complete (10) both
- Disable_DC_Begin (11) both
-
- The handling of each of these subfunctions is covered in some detail in the
- OS/2 Toolkit white books ( specifically the I/O Subsystem Presentation Driver
- reference), available from IBM, therefore only a brief summary of each will be
- provided here.
-
- Fill_Logical_Device
- ---------------------
- This ENABLE subfunction is called when the driver is loaded. Its
- purpose is to initialize the logical device block, which includes
- setting some bits telling the engine whether the PD needs a physical
- device block (PDEV) per DC and to hook the PMGRE entry points that
- the PD wishes/needs to support.
-
-
- Fill_Physical_Device
- ----------------------
- This ENABLE subfunction is called each time a DevOpenDC is requested
- by the application. The purpose of this function is to create and
- allocate a physical device block, which should contain any information
- needed by the PD to perform I/O to the specified device. Typically
- these are things such as a Presentation Manager Anchor Block (HAB),
- a "private" memory heap, and a copy of the DEVOPENDATA, passed in
- to Enable() for this subfunction via Param1.
-
-
- Disable_Physical_Device
- -------------------------
- This ENABLE subfunction is called when a physical device block
- is to be deleted. The major purpose of this function is to release
- any memory allocated in Fill_Physical_Device, and to close the
- pathway to the device.
-
-
- Enable_DC_Begin
- -----------------
- This ENABLE subfunction is called each time a DC is created. It is
- also the first time the PD is presented the DC handle and is the time
- to allocate the internal DeviceContext. We are also presented with
- the corresponding physical device pointer returned from the
- Fill_Physical_Device subfunction.
-
-
- Disable_DC_Complete
- ---------------------
- This ENABLE subfunction is called when a DC has completed the disable
- process. The purpose of this function is to release any memory
- allocated for the PD's internal device context, often referred
- to as the "cookie."
-
-
- Save_DC_State
- ---------------
- This Enable subfunction is called when the engine wishes a device
- driver to save all DC information. This may be called multiple times
- and the driver should push the DDC in LIFO order.
-
- Restore_DC_State
- ------------------
- This ENABLE subfunction is called when the engine wishes the PD to
- POP a saved DC, or POP to a specified DC. The following table shows
- the action to be taken based on the contents of Param2.
-
- Param2 Action
- -------- -------------------------------------------------
- = 0 then error.
- > 0 it specifies which state to restore, and the
- others are lost, (if Number is 2, then the second
- DC is restored, the first DC is saved, and all
- others are lost.
- < 0 it specifies how many states to pop back. If the
- value is -1, then pop back to the first state
-
- (fig. 3)
-
- Reset_DC_State
- ----------------
- This Enable subfunction is called to reset a the current DC, returning
- it to is initialization state.
-
-
- Enable_DC_Complete
- --------------------
- This call informs the PD that the DC has been created by the Engine.
- Also it is the first time the PD receives the magic cookie returned
- from the Enable_DC_Begin subfunction. At this time, the PD should
- open the spooler if the current DC was openned OD_QUEUED. Also any
- metafiles or journaling is started here.
-
- Journaling is used primarily by raster devices that produce output
- through "banding." Even a low resolution printer, with 80 by 120
- dots per inch would require a substantial amount of memory to
- represent an entire page of output in memory. By journaling, or
- recording the calls into the driver, the driver can break the
- physical page into smaller sections, replaying the journal file
- for each section. Journaling was very useful in the 1.X releases
- of OS/2 to handle segmentation induced limitations. However, in
- Version 2.0+, it can be used to prevent a presentation driver from
- consuming a large amount of physical memory, which may lead to
- thrashing.
-
-
- Disable_DC_Begin
- ------------------
- This ENABLE subfunction is called before a the graphics engine
- begins disable processing. This allows the PD to do any final
- clean up of resources prior to the full disable. This is the
- point at which the PD ceases journaling, closes the spooler, etc.
-
-
- ΓòÉΓòÉΓòÉ 5.1.4. Producing Hardcopy Output ΓòÉΓòÉΓòÉ
-
- Once all ENABLE processing has been completed, the presentation driver is ready
- to begin producing output. In the handling of the Fill_Logical_Device Enable()
- subfunction, the PD hooks out a copy of the the PMGRE dispatch table. The
- heart of the OS/2 graphics engine is a dispatch routine that is used to access
- presentation drivers and internal PMGRE entry points via vectors. The dispatch
- table is an array of 32bit entries, each representing the addresses of entry
- points into the graphics engine. Logically, the presentation driver perform
- one of four actions on a given entry in the dispatch table.
-
- 1. Ignore it.
-
- 2. Copy it.
-
- 3. Hook it (replace it.)
-
- 4. Swap it (save and replace it.)
- The following macros are taken from my presentation driver template source
- code, and provide implementations of options two through four.
-
- #define COPY(Fun) \
- da##Fun = (PFNL)*(pDispatchTable + (NGre##Fun & 0xffL));
-
- #define HOOK(Fun) \
- *(pDispatchTable + (NGre##Fun & 0xffL)) = (ULONG)Fun;
-
- #define SWAP(Fun) \
- da##Fun = (PFNL)*(pDispatchTable + (NGre##Fun & 0xffL)); \
- *(pDispatchTable + (NGre##Fun & 0xffL)) = (ULONG)Fun;
-
- Note that the above macros assume that the presentation driver source code
- provides local storage for SWAP'd and COPY'd vectors in local address space.
- In my presentation driver template source code, this storage is provided by
- variables with the prefix "da" .
-
- The following are functions which a driver does NOT need to hook, but should
- save the PMGRE vector. By calling the vector directly, the overhead of the
- PMGRE dispatch mechanism is avoided. These entry points are COPY'd .
-
- COPY( Convert ); COPY( SelectClipPath );
- COPY( ConvertWithMatrix);
-
- The following entry points should be HOOK'd by the PD and processed completely.
- None of these involve PMGRE call-back, ie, your presentation driver must do all
- the work.
-
- HOOK( AccumulateBounds ); HOOK( ImageData );
- HOOK( Bitblt ); HOOK( LockDevice );
- HOOK( CreateLogColorTable ); HOOK( PolyScanline );
- HOOK( DeviceCreateBitmap ); HOOK( PolyShortLine );
- HOOK( DeviceDeleteBitmap ); HOOK( QueryColorData );
- HOOK( DeviceGetAttributes ); HOOK( QueryColorIndex );
- HOOK( DeviceQueryFonts ); HOOK( QueryDeviceBitmaps );
- HOOK( DeviceSelectBitmap ); HOOK( QueryDeviceCaps );
- HOOK( DeviceSetAttributes ); HOOK( QueryHardcopyCaps );
- HOOK( DeviceSetDCOrigin ); HOOK( QueryLogColorTable );
- HOOK( DeviceSetGlobalAttribute ); HOOK( QueryNearestColor );
- HOOK( DrawLinesInPath ); HOOK( QueryRealColors );
- HOOK( ErasePS ); HOOK( QueryRGBColor );
- HOOK( Escape ); HOOK( RealizeColorTable );
- HOOK( GetBitmapBits ); HOOK( RealizeFont );
- HOOK( GetBoundsData ); HOOK( ResetBounds );
- HOOK( GetCodePage ); HOOK( SetBitmapBits );
- HOOK( GetCurrentPosition ); HOOK( SetCodePage );
- HOOK( GetDCOrigin ); HOOK( SetLineOrigin );
- HOOK( GetLineOrigin ); HOOK( SetPel );
- HOOK( GetPairKerningTable ); HOOK( SetStyleRatio );
- HOOK( GetPel ); HOOK( UnlockDevice );
- HOOK( GetStyleRatio ); HOOK( UnrealizeColorTable );
-
- The following entry PMGRE entry points should also be supported by the
- presentation driver, but may also be safely passed back to the PMGRE supplied
- entry points if the processing involves complex clipping, non-device fonts,
- etc. These entry points may be safely SWAP'd by a printer presentation driver.
-
- SWAP( Arc ); SWAP( FullArcBoundary );
- SWAP( BeginArea ); SWAP( FullArcInterior );
- SWAP( BoxBoth ); SWAP( NotifyClipChange );
- SWAP( BoxBoundary ); SWAP( NotifyTransformChange );
- SWAP( BoxInterior ); SWAP( PartialArc );
- SWAP( CharString ); SWAP( PolyLine );
- SWAP( CharStringPos ); SWAP( PolyMarker );
- SWAP( DeviceQueryFontAttributes ); SWAP( QueryCharPositions );
- SWAP( EndArea ); SWAP( QueryTextBox );
- SWAP( FillPath ); SWAP( QueryWidthTable );
- SWAP( FullArcBoth ); SWAP( SetArcParameters );
- SWAP( SetCurrentPosition );
-
- The internals of each of the PD supplied entry points is dependent on the
- physical device type (printer or display, raster or vector), the algorithms and
- data structures chosen, and of course the developer. It is therefore not within
- the scope of this article to discuss them further. However, each of the
- external entry points to the presentation driver, and for that matter, the
- PMGRE have a common parameter, that is worth discussing. The last parameter to
- all entry points is a 32 bit unsigned value which contains the entry point id
- in the low 16 bits and a set of flags in the high order 16 bits. These bits
- should be checked at the beginning of each entry point procedure, as they
- contain additional control information governing exactly what the PD should do.
- For example, on the first pass through the driver, the COM_DRAW bit will be
- clear, while the COM_BOUND bit will be set. In this case, the PD need only
- accumulate bounds for the operation, and need not produce any physical output.
- Other bits of interest are used to indicate whether or not the driver is
- currently within a path or area bracket.
-
-
- ΓòÉΓòÉΓòÉ 5.1.5. A Few Final Notes ΓòÉΓòÉΓòÉ
-
- For a display PD, there are several additional entry points, related to the
- support of mouse and text cursors. Under V2.0+ of OS/2, the display driver
- also has the additional overhead of the Virtual Device Driver necessary to
- support the multiple virtual DOS machines (MVDMs) and WINOS2. Overall, display
- drivers are more difficult to write than printer drivers due to performance
- considerations. If you are interested in creating a display Presentation
- Driver, I would highly recommend contacting IBM through the Developers
- Assistance Program (the local IBM branch office should be able to help.)
-
- OS/2 Presentation Drivers are not overly complex, neither are they simple. To
- successfully create a PD requires careful thought and design as well as a
- strong knowledge of computer graphics in general. The use of modular and
- structured programming techniques, along with object oriented concepts (one
- need not use an object language to write object oriented code) will make the
- development cycle far less frustating, and much more rewarding. Remember that
- a little extra effort in the design phase can save a great deal of recoding in
- the testing phase.
-
-
- ΓòÉΓòÉΓòÉ 5.2. Road Map for the WorkPlace Shell ΓòÉΓòÉΓòÉ
-
- Road Map
- to the
- Work Place Shell
-
- David Campbell
- (campbell@campbell.saic.com)
-
-
- ΓòÉΓòÉΓòÉ 5.2.1. The Work Place Shell ΓòÉΓòÉΓòÉ
-
- The Work Place Shell (WPS) is the new user interface introduced with OS/2 2.0.
- This article will begin a series of articles dedicated to explaining the
- operation and use of the WPS. In preparing this initial article, I was
- overwhelmed by the amount of material I would have liked to include. There are
- so many aspects of the WPS to discuss. For example, Installation and
- Maintenance, Programming new WPS Classes, and future enhancements.
-
- Since the amount of material is so large, I will divide the topic into 2 or 3
- articles. The first article will discuss Installation and Maintenance. The
- second article will discuss programming issues facing developers who wish to
- write classes for the WPS. The third article will discuss future enhancements
- to the WPS. With the first article I will attempt to cover the following
- topics:
-
- o Evolution of the User Interface
- o Configuration and Maintenance of the WPS
- o Suggestions for Implementing the WPS
-
-
- ΓòÉΓòÉΓòÉ 5.2.2. Evolution of the User Interface ΓòÉΓòÉΓòÉ
-
- To better understand the WPS, it is necessary to be aware of the evolution of
- the user interface. The user interface has a very simple purpose, and that is
- to translate human initiated actions into computer operations and communicate
- these operations to the operating system. Traditionally their have been two
- different types of user interfaces. These include:
-
- 1. Character User Interface (CUI)
- 2. Graphical User Interface (GUI)
-
- Early user interfaces were CUI. That is they could only display the characters
- defined in the ASCII set. Examples of this type of interface are the command
- line interfaces provided with DOS 3.3 and early implementations of UNIX and
- VMS. This was limiting, but it was the only choice primarily because of 2
- hardware constraints. Early CPUs did not have the processing power to manage a
- GUI. Also, the video controllers and monitors were unable to display the high
- resolution necessary to implement a GUI.
-
- The CUI evolved into the GUI. The GUI provided vastly superior capabilities.
- It was now possible to display on the screen different fonts, images, and other
- graphical data. This type of interface required more CPU processing power, but
- the power of CPUs was increasing and the cost of CPUs was decreasing, so it was
- an acceptable penalty. With most current GUIs, emphasis is still on the
- 'application'. That is to say, the user interacts with applications. These
- applications in turn interact with the data that the user requests.
-
- The philosophy of this type of system is somewhat backward. The user should
- interface directly with the data, and the application should be evoked
- implicitly to act upon the data. This approach to user-data interaction lead to
- the development of object oriented user interfaces. The WPS is IBM's attempt at
- an object oriented user interface (OOUI). With the graphical interface, it
- became possible to represent 'objects' using icons. IBM has stated that that
- the current WPS is the template of future OOUIs. These future OOUIs will be
- grafted onto AIX and future operating systems. Therefore, the importance of
- becoming proficient at using this type of interface is obvious.
-
- To summarize:
-
- o To implement an OOUI effectively, the video subsystem must be operating in
- graphical mode. This is necessary to display detailed images which represent
- objects.
-
- o An OOUI places emphasis on the data or object, whereas current CUIs and GUIs
- place emphasis on the application.
-
-
- ΓòÉΓòÉΓòÉ 5.2.3. Configuration and Maintenance of the WPS ΓòÉΓòÉΓòÉ
-
- The user interface used by OS/2 is loaded by the base operating system at
- system startup. OS/2 can be configured to load any type of user interface. By
- default, the OS/2 installation procedure specifies the WPS as the user
- interface. In the config.sys file, there is a line which reads:
-
- PROTSHELL=c:\os2\pmshell.exe
-
- This line specifies the protected mode user interface. If you want to load
- another user interface, this is where you would place the new execution file.
-
- Interestingly, another environment variable affects the WPS. This environment
- variable is 'RUNWORKPLACE'. This environment variable is also used to specify
- the shell.
-
- The WPS is composed of objects. An object is an instance of a class. Each
- object is represented by an icon. Each class has its own methods, data, and
- class icon. The context menu of each class is different. The settings
- notebook of each class is different. Interacting with objects has several
- advantages. The operating system and user interface only have to be familiar
- with the classes. Each external device is represented as an object. For
- example, the keyboard, the mouse, the storage devices, and the printers. Each
- file and directory on storage devices are also represented by objects.
- Finally, each process and print job is represented by an object. In this way,
- the operating system has a common interface to everything it interacts with,
- because everything it interacts with is an object. The WPS is written using
- the System Object Model (SOM). SOM is written to help developers create
- classes more quickly, and from different languages. SOM forms a class
- hierarchy which is shown below.
-
- SOMObject
- Γö£ΓöÇ SOMClass
- Γö£ΓöÇ SOMClassMgr
- ΓööΓöÇ WPObject
- Γö£ΓöÇ WPAbstract
- Γöé Γö£ΓöÇ WPPrinter
- Γöé Γö£ΓöÇ WPProgram
- Γöé ΓööΓöÇ WPShadow
- Γö£ΓöÇ WPFileSystem
- Γöé Γö£ΓöÇ WPDataFile
- Γöé Γöé ΓööΓöÇ WPProgramFile
- Γöé ΓööΓöÇ WPFolder
- Γöé Γö£ΓöÇ WPDesktop
- Γöé Γö£ΓöÇ WPDrives
- Γöé ΓööΓöÇ WPStartup
- ΓööΓöÇ WPTransient
- Γö£ΓöÇ WPJob
- ΓööΓöÇ WPPort
-
- Note: Please note that the above class hierarchy is not complete. I have only
- included the most significant classes. Also note the class
- WPProgramFile. This class is actually derived from WPDataFile NOT
- WPFileSystem as most of the IBM documentation states.
-
- Due to the length of this article I will only discuss the classes which begin
- with WPObject. WPObject is the parent class of the 3 base classes:WPAbstract,
- WPFileSystem, and WPTransient. These 3 classes represent the foundation
- classes. Each base class is differentiated from the other base classes in the
- way in which the class data is maintained. Classes derived from WPAbstract
- classes store their instance data in the os2.ini file. Classes derived from
- WPFileSystem classes store their instance data as a file or directory on
- permanent storage. Classes derived from WPTransient classes do not store their
- instance data, therefore all WPTransient instance data is not maintained if the
- computer is restarted. This differentiation in storage types classifies
- WPAbstract and WPFileSystem as persistent, and WPTransient as being
- non-persistent.
-
- Each of these classes has some very common implementations. For example, the
- typical user creates a folder which contains programs and files. This
- implementation utilizes 3 WPS classes, WPProgram, WPDataFile and WPFolder. The
- folder used is an instance of WPFolder. An instance of WPFolder represents a
- directory on a diskette or hardrive. A folder is used as a container for other
- objects. Each file in the folder is an instance of WPDataFile. An instance of
- WPDataFile represents a physical file which exists either on a diskette or a
- hardrive. Each of the programs within the folder is an instance of WPProgram.
- Notice this is not the same class as WPProgramFile. Notice also that you do
- NOT want to copy the execution file into the folder. An instance of WPProgram
- is a reference to an execution file. This reference is used to specify the
- name of the execution file to execute, any parameters, the startup directory,
- and other information. An instance of this class typically uses the ICON
- resource within the execution file.
-
- To compare and contrast these 3 classes, we enumerate the settings notebook of
- each class. Notice the differences in the settings notebook for each class.
-
- An instance of WPProgram has 5 pages in its settings notebook.
-
- 1. Program
- 2. Session
- 3. Association
- 4. Window
- 5. General
-
- An instance of WPDataFile has 4 pages in its settings notebook.
-
- 1. Type
- 2. Menu
- 3. File
- 4. General
-
- An instance of WPFolder has 8 pages in its settings notebook.
-
- 1. View
- 2. Include
- 3. Sort
- 4. Background
- 5. Menu
- 6. File
- 7. Window
- 8. General
-
- Maintaining the WPS is critical to satisfactory use of the user interface. Due
- to the power and flexibility of the WPS, maintenance can be a somewhat
- difficult task. Fortunately, their are ways to effectively maintain the WPS to
- provide outstanding performance and usability. The number 1 thing is to keep
- your os2.ini and os2sys.ini files backed up. This can be done using 'RUN'
- commands in the config.sys file. You can use a 'RUN=C:\OS2\XCOPY OS2.INI
- C:\TEMP\OS2.INI' this will copy the os2.ini file before the WPS starts. Once
- the WPS starts, it opens the os2.ini file. The os2.ini file remains open until
- the system is shutdown. Therefore, it is impossible to make copies of the
- os2.ini file while the WPS is executing.
-
- Always properly shutdown the system. The WPS maintains several open files.
- Unexpectedly turning the computer off can have disastrous effects on the system
- configuration files.
-
- The next item of maintenance is concerned with maintaining the active class
- list, file types, file associations, and object handles. I have written
- several utilities in REXX to perform these functions. Information on these
- utilities is presented in the next section.
-
-
- ΓòÉΓòÉΓòÉ 5.2.4. Suggestions for Implementing the WPS ΓòÉΓòÉΓòÉ
-
- Learning to effectively use the WPS can be difficult. Upon initial
- introduction, the casual user tends to configure the WPS similar to the
- Microsoft Windows environment. For example, a software developer starts an
- editor to edit a source file. The user then saves the file and exits the
- editor. He then evokes the compiler to convert the source code to an
- executable file. He then runs the execution file.
-
- Here is an alternative. Create a folder on your desktop titled source. Within
- this folder create several data files representing your source code. For each
- of the data files, set the TYPE to either C code or REXX or whatever. Then
- create a program object (WPProgram) for the compiler. On the associations page
- of the program object, select C code or REXX. Now when you select the open
- menu of the data file, you should see an editor and a compiler. Now, to open
- the file to be edited, select the editor from the open menu. To open the file
- to be compiled, select the compiler from the open menu. In this way, you are
- interacting more with the data than with the application. For example, the
- data file can have 2 actions performed on it, edit and compile. We have
- created an open menu which has these options!
-
- I have created a set of REXX utilities for managing some of the aspects of the
- WPS. These utilities allow you to add a type, delete a type, enumerate all of
- the existing types, add an association, delete an association, enumerate all of
- the existing associations, get EA information, and set EA information. Several
- other utilities are needed, but I have not had the time to develop them. All
- of these utilities can be obtained via anonymous FTP from my machine:
-
- campbell.saic.com 139.121.81.146
-
- Please feel free to contact me if you have questions concerning this article or
- the WPS. I believe the WPS to be one of the most significant parts of OS/2
- 2.x. I also believe that without better understanding and knowledge of the
- WPS, OS/2 2.x's future is somewhat in doubt. It would be a shame for such a
- wonderful system to go unnoticed. Let's not let that happen!
-
-
- ΓòÉΓòÉΓòÉ 5.3. Beginning Client/Server Programming:Named Pipes. ΓòÉΓòÉΓòÉ
-
- Beginning Client/Server
- Programming:
- Named Pipes
-
- Steve Lacy
- (sl31@andrew.cmu.edu)
-
-
- ΓòÉΓòÉΓòÉ 5.3.1. Beginning Client/Server Programming: Named Pipes ΓòÉΓòÉΓòÉ
-
- This article is an introduction to client server applications, and thier
- interface in OS/2. Throughout this article, click on "Forward" to read the next
- paragraph. I've assumed that you've already browsed through the supplied
- source files, "client.c" and "server.c" just so you can see generally whats
- going on in this program, even though you might not know what the actual
- function calls do, you can look at the names (like DosCreateNPipe) and see just
- what they do, in this case, creating a named pipe.
-
-
- ΓòÉΓòÉΓòÉ 5.3.2. Introduction ΓòÉΓòÉΓòÉ
-
- One of the current hip words used when talking about OS/2 is "client/server"
- Well, as a programmer, I know I've wondered myself, "what exactly is a
- Client/Server program?" Well, from my experience, its not a black and white
- definiton, since some people seem to define one style to be client/server
- whereas other poeple wouldn't consider that particular example to be a
- client/server application.
-
- One thing to keep in mind is that the design of client/server applications is
- usually more work than the programming of them. Designing a multithreaded
- program which uses named pipes and semaphores elegantly is quite a task, we'll
- be getting into those topics in later articles.
-
- For our little example, we're going to be developing a "reverse this string"
- client server application. Basically, we'll have a server that accepts
- connections on a named pipe, reads in a string, and spits that string back out
- onto the pipe, reversed. Note that in our example we're not using a
- multithreaded server. This means that if two clients are trying to connect to
- the same pipe at the same time, that only one of them will get through, and
- that the other will have to wait for the other to finish, until it can
- continue.
-
- One thing is known for sure, that client server applications use the following
- OS/2 kernel functions: Named Pipes, Threads/Processes, Semaphores, and Shared
- Memory. Here's a brief description of four mechanisms
-
- In this article, I don't attempt to define client/server applications, but I do
- show you the basics as to what does define a "typical" client/server program,
- if such a beast does exist.
-
-
- ΓòÉΓòÉΓòÉ 5.3.2.1. Named Pipes ΓòÉΓòÉΓòÉ
-
- A named pipe is a mechanism for controlling inter-process (or inter-thread)
- communication under OS/2 2.0. Named pipes share a lot of the characteristics
- of UNIX sockets, but in my opinion, their programming interface is a lot more
- user friendly, and it doesn't have as much programming overhead as UNIX sockets
- do.
-
- In this article, we're going to dulge into the basics of named pipes, and the
- OS/2 functions used to program a named pipe application.
-
-
- ΓòÉΓòÉΓòÉ 5.3.2.2. Threads/Processes ΓòÉΓòÉΓòÉ
-
- A standard OS/2 program, something that you would write with the standard
- library of C functions, has what you call a "single thread of execution." A
- multithreaded program, as you might think, has multiple threads of execution.
- Basically, this means that your program is running in two different places at
- the same time. Starting a thread has very little overhead, since all the code
- for you program is already in memory. The overhead in starting a thread has
- been compared to the overhead for calling a standard C function. Your program,
- or some thread of your program, can start up another thread by issuing a
- DosCreateThread call.
-
- Processes are basically equivalent to "programs" One program can start up
- another program by issuing a DosStartSession call. Remember that there is some
- significant overhead in loading and starting another program, even if its
- another copy of the program that is currently executing. When processes are
- started, they always start with the main() function of your code.
-
-
- ΓòÉΓòÉΓòÉ 5.3.2.3. Semaphores ΓòÉΓòÉΓòÉ
-
- A semaphore is an inter-process or inter-thread signaling mechanism. There are
- three types of semaphores in OS/2, you don't particularly need to know about
- them now, so I won't go into the details. The way semaphores work is that you
- can either post or wait on an open semaphore. When you're waiting, you stop
- waiting until someone else posts. The neat thing is that it can be anyone
- whatsoever, in any process, in any thread.
-
-
- ΓòÉΓòÉΓòÉ 5.3.3. Design of the applications ΓòÉΓòÉΓòÉ
-
- This section deals with the design of our application, and gives an
- introduction to the Named pipe OS/2 services that we'll be using in our
- application. We have to keep in mind that dealing with named pipes is in fact
- a lot like dealing with standard files, that is, files on disk. For example,
- the client end of our program uses only the DosOpen , DosWrite , DosRead , and
- DosClose system calls, the exact same calls that would be used if we were
- writing to a file. We distinguish a named pipe from a file by the name that we
- send to the system calls. The name for pipes must be of the form "\pipe\*"
- Generally, its a good idea to try to pick pipe names that you don't think other
- people will be using too, since this would create problems. If you're really
- paranoid, you'll do something like include the process number (which is
- essentially unique) in the pipe name, so you know that no one else could
- possibly have the same name as you. For our example, that would be just a
- little bit of overkill, so we're just going to be using the name
- "\pipe\reverse\npipe" I find it a good idea to choose pipe names so that
- they're of the form "\pipe\application-name\pipe-name"
-
-
- ΓòÉΓòÉΓòÉ 5.3.3.1. Pipe Semantics. ΓòÉΓòÉΓòÉ
-
- If we're going to have interprocess communication, we have to design our
- application so that the communication takes place properly, that all the data
- is transmitted, and that there are "clean" open and close operations. Browsing
- through the documentation, the most obvious looking function calls are:
- DosCreateNPipe , DosConnectNPipe , and DosDisConnectNPipe. So, we have
- functions to create pipes, "connect" to them (whatever that may mean) and to
- disconnect from them. Thinking of pipes as files, the create routine creates
- the pipe, the connect waits for the client to connect to the pipe that we've
- just connected. When the entire process has finished, we disconnect from the
- pipe, and we can either end our application, or continue with the connect
- cycle.
-
- ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
- ΓöéServer Client Pipe status Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- Γöé... ... Nonexistant Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéDosCreateNPipe ... Created Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéDosConnectNPipe ... Blocking for Γöé
- Γöé connections Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- Γöé... DosOpen Opened Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéDosRead DosWrite Open Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéDosWrite DosRead Open Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- Γöé... DosClose Closing Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- Γöé... ... Closed Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéDosDisConnectNPipe ... Dosconnected (sameΓöé
- Γöé as created state) Γöé
- ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
-
-
- ΓòÉΓòÉΓòÉ 5.3.3.2. Server implementation ΓòÉΓòÉΓòÉ
-
- So, thats basically an outline of what our programs should do, but we still
- need a few little modifications. The main one being that we have to make sure
- that the DosDisconnectNPipe happens after the DosClose happens on the client
- side. The simples way to synchronize this it to add andother DosRead on the
- server side. What will happen is that the DosRead will fail with an End Of
- File (EOF) error, then we know that the pipe has defineately closed, and we can
- then do our DosDisConnectNPipe. The way that we know that DosRead has returned
- an end of file error is when it returns zero in the ulBytes field, which
- usually contains the number of bytes that we've read from the pipe. So,
- generally the code for our server looks like this:
-
- DosCreateNPipe(...);
- while (1) {
- DosConnectNPipe(...);
- /* Read the input data */
- DosRead(...);
- /* This is where we figure out what the output will be */
- DosWrite(...);
- /* Now we're reading for an end of file. */
- DosRead(...);
- /* If we're not at the end of the file that is, number of bytes read
- isn't zero, then something has happened */
- if (ulBytes!=0) error();
- rc=DosDisConnectNPipe(...);
- }
- Other than the parameters for the functions, thats the program. The reason
- that we have a while(1) in our program is that after one client has finished,
- we want the server to reset and be able to accept more connections from other
- clients.
-
-
- ΓòÉΓòÉΓòÉ 5.3.3.3. Client Implementation. ΓòÉΓòÉΓòÉ
-
- The client, as mentioned before, reads and writes to the created pipe --
- teating it as a file, not as a pipe. So, we open the file, write to it, read
- from it, and close it. Here's a brief version of the code:
-
- for (i=1;i<argc;i++) {
- DosWaitNPipe(...);
- DosOpen(...);
- DosWrite(...);
- DosRead(...);
- DosClose(...);
- }
- The only strange bit at this point should be the DosWaitNPipe call. This is
- OS/2 solution to a simple problem: What to do if someone else is already using
- the pipe? Well, you have to wait until the pipe frees up, that is, wait until
- the server has issued a DosCreateNPipe call.
-
-
- ΓòÉΓòÉΓòÉ 5.3.4. Conclusion ΓòÉΓòÉΓòÉ
-
- Well, from this you should be able to write a simple client/server program,
- basically by using the code supplied here along with the reference information
- in the next session. The OS/2 calls, although they may have long names, large
- numbers of arguments, or something else that you might find initially
- discouraging, they're actually quite easy to use. Just keep plugging, and have
- fun! Next time, we'll make the server actually do something, and we'll
- introduce semaphores.
-
-
- ΓòÉΓòÉΓòÉ 5.3.5. Reference Section ΓòÉΓòÉΓòÉ
-
- This is the reference section for this article. You should note that pipe
- handles and file handles are interchangable, so the file handlre returned by
- DosOpen can be passed to DosWaitNPipe, which requires a pipe handle as its
- argument.
-
-
- ΓòÉΓòÉΓòÉ 5.3.5.1. DosOpen ΓòÉΓòÉΓòÉ
-
- PSZ pszFileName;
- PHFILE ppshfFileHandle;
- PULONG pActionTaken;
- ULONG ulFileSize,ulFileAttribute,ulOpenFlag,ulOpenMode;
- PEAOP2 pEABuf;
- APIRET rc;
-
- rc=DosOpen(pszFileName,
- ppshfFileHandle,
- ulFileSize,
- ulFileAttribute,
- ulOpenFlag,
- ulOpenMode);
- pszFileName is the name of the file that we're opening.
- ppshfFileHandle is the place where the opened file's file handle ps placed.
- pActionTaken is the plce where the action taken value is placed. It is one if
- the following:
-
- FILE_EXISTED
- FILE_CREATED
- FILE_TRUNCATED
- ulFileSize is the initial size of the file, if you're creating one.
- ulFileAttribute is one or more of the following:
-
- FILE_ARCHIVED
- FILE_DIRECTORY
- FILE_SYSTEM
- FILE_HIDDEN
- FILE_READONLY
- FILE_NORMAL
-
- ulOpenFlag specifies an open action. It is one of the following:
-
- OPEN_ACTION_FAIL_IF_NEW
- OPEN_ACTION_CREATE_IF_NEW
- OPEN_ACTION_FAIL_IF_EXISTS
- OPEN_ACTION_OPEN_IF_EXISTS
- OPEN_ACTION_REPLACE_IF_EXISTS
- ulOpenMode specifies the mode for opening the file. It is one of the
- following:
-
- OPEN_FLAGS_DASD (direct open flag)
- OPEN_FLAGS_WRITE_THROUGH (if set, accesses don't go through cache)
- OPEN_FLAGS_FAIL_ON_ERROR
- OPEN_FLAGS_NO_CACHE
- OPEN_FLAGS_NO_LOCALITY (don't know info. about locality)
- OPEN_FLAGS_SEQUENTIAL (mostly sequential access file)
- OPEN_FLAGS_RANDOM (mostly random access file)
- OPEN_FLAGS_RANDOMSEQUENTIAL (random with some locality)
- OPEN_FLAGS_NOINHERIT (children don't inherit handle access)
- OPEN_SHARE_DENYREADWRITE
- OPEN_SHARE_DENYWRITE
- OPEN_SHARE_DENYREAD
- OPEN_SHARE_DENYNONE
- OPEN_ACCESS_READONLY
- OPEN_ACCESS_WRITEONLY
- OPEN_ACCESS_READWRITE
- pEABuf Is a pointer to Extended Attribute information.
-
- In most cases, you should specify OPEN_ACTION_OPEN_IF_EXISTS for ulOpenFlag
- and OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE for the ulOpenMode.
-
-
- ΓòÉΓòÉΓòÉ 5.3.5.2. DosClose ΓòÉΓòÉΓòÉ
-
- HFILE FileHandle;
- APIRET rc;
-
- rc=DosClose(FileHandle);
-
- Where FileHandle is the handle for the file that you want to close.
-
-
- ΓòÉΓòÉΓòÉ 5.3.5.3. DosRead ΓòÉΓòÉΓòÉ
-
- HFILE FileHandle;
- PVOID pBufferArea;
- ULONG ulBufferLength;
- PULONG pBytesRead;
- APIRET rc;
-
- rc=DosRead(FileHandle,pBufferArea,ulBufferLength,pBytesRead);
- Where FileHandle is the handle of the open file.
- pBufferArea is the place where the data is to be read into.
- ulBufferLength is the size of pBufferArea.
- pBytesRead is the number of bytes read from the file. (on return)
-
-
- ΓòÉΓòÉΓòÉ 5.3.5.4. DosWrite ΓòÉΓòÉΓòÉ
-
- HFILE FileHandle;
- PVOID pBufferArea;
- ULONG ulBufferLength;
- PULONG pBytesRead;
- APIRET rc;
-
- rc=DosWrite(FileHandle,pBufferArea,ulBufferLength,pBytesRead);
- Where FileHandle is the handle of the open file.
- pBufferArea is the place where the data is to be read from.
- ulBufferLength is the size of pBufferArea.
- pBytesRead is the number of bytes written to the file. (on return)
-
-
- ΓòÉΓòÉΓòÉ 5.3.5.5. DosCreateNPipe ΓòÉΓòÉΓòÉ
-
- PSZ pszFileName;
- PHPIPE pphpipePipeHandle;
- ULONG ulOpenMode,ulPipeMode,ulOutBufSize,ulInBufSize,ulTimeOut;
- APIRET rc;
-
- rc=DosCreateNPipe(pszFileName,
- pphpipePipeHandle,
- ulOpenMode,
- ulPipeMode,
- ulOutBufSize,
- ulInBufSize,
- ulTimeOut);
- Where pszFileName is the name of the pipe to be created.
- ulOpenMode is one or more of the following:
-
- NP_WRITEBEHIND
- NP_NOWRITEBEHIND
- NP_INHERIT
- NP_NOINHERIT
- NP_ACCESS_INBOUND
- NP_ACCESS_OUTBOUND
- NP_ACCESS_DUPLEX
-
- ulPipeMode is one or more of the following:
-
- NP_WAIT
- NP_NOWAIT
- NP_TYPE_BYTE
- NP_TYPE_MESSAGE
- NP_READMODE_BYTE
- NP_READMODE_MESSAGE
- You should also bitwise-or this with the number of instances of the pipe that
- you would like. In our case, this will always be one.
- ulOutBufSize is the size of the outgoing buffer.
- ulInBufSize is the size of the incoming buffer.
- ulTimeOut is the default timeout value, in microseconds, that reads and writes
- will use. Setting this to -1 gives indefinite timeout. In other words, reads
- and writes will wait forever.
-
-
- ΓòÉΓòÉΓòÉ 5.3.5.6. DosConnectNPipe ΓòÉΓòÉΓòÉ
-
- HPIPE PipeHandle;
- APIRET rc;
- rc=DosConnectNPipe(PipeHandle);
- Where PipeHandle is the pipe handle that we want to begin accepting connections
- on.
-
-
- ΓòÉΓòÉΓòÉ 5.3.5.7. DosDisConnectNPipe ΓòÉΓòÉΓòÉ
-
- HPIPE PipeHandle;
- APIRET rc;
- rc=DosDisConnectNPipe(PipeHandle);
- Where PipeHandle is the pipe handle that we want to stop accepting connections
- on.
-
-
- ΓòÉΓòÉΓòÉ 5.3.5.8. DosWaitNPipe ΓòÉΓòÉΓòÉ
-
- HPIPE PipeHandle;
- APIRET rc;
- rc=DosWaitNPipe(PipeHandle);
- Where PipeHandle is the pipe handle that we want to wait for an available
- connection on.
-
-
- ΓòÉΓòÉΓòÉ 6. Columns ΓòÉΓòÉΓòÉ
-
- o Questions and Answers
-
- o Introduction to PM
-
-
- ΓòÉΓòÉΓòÉ 6.1. Questions and Answers ΓòÉΓòÉΓòÉ
-
- Questions
- and
- Answers
-
- Larry Salomon
- (os2man@panix.com)
-
-
- ΓòÉΓòÉΓòÉ 6.1.1. Questions and Answers ΓòÉΓòÉΓòÉ
-
- Welcome to this month's "Questions and Answers"! Each month, I collect various
- questions sent to me via email and try to answer each directly; the ones that I
- feel contribute the most to developers, whether in terms of information or as a
- nifty trick to tuck into your cap, get published in this column.
-
- To submit a question, send mail to my email address - os2man@panix.com - and be
- sure to grant permission to publish your question (those that forget will not
- be considered for publication).
-
- Unfortunately, no questions were submitted this month so we will discuss some
- errors in the "Programming Reference" documentation. The following topics will
- be covered:
-
- o Cascading Menus
- o Dropping on a Printer
- o Message Box Help
-
-
- ΓòÉΓòÉΓòÉ 6.1.1.1. Cascading Menus ΓòÉΓòÉΓòÉ
-
- OS/2 2.0 added a new feature to menus, which is used extensively throughout the
- Workplace Shell. These are called "conditional cascading menus" (I will refer
- to them as CCM's), which look similar to "pull-rights", but have the following
- differences in appearance and behavior:
-
- o CCM's have a pushbutton instead of simply a right arrow, which looks like the
- right arrow on a scrollbar.
-
- o CCM's have a default selection. If you only click on the submenu name, the
- default action is taken.
-
- Unfortunately, there is no documentation discussing how to code this in your PM
- applications, with the exception of the mentioning of the menu style.
- Therefore, let us discuss this new style and introduce two new menu messages.
-
- MS_CONDITIONALCASCADE
-
- Setting this style indicates that the SUBMENU is to be a CCM. Unfortunately,
- using this in the resource file does not work. Instead, you must explicitly set
- it in your code like below:
-
- HWND hwndMenu;
- USHORT usSubmenu;
- MENUITEM miItem;
-
- WinSendMsg(hwndMenu,
- MM_QUERYITEM,
- MPFROM2SHORT(usSubmenu,TRUE),
- MPFROMP(&miItem));
-
- ulStyle=WinQueryWindowULong(miItem.hwndSubMenu,QWL_STYLE);
-
- ulStyle|=MS_CONDITIONALCASCADE;
-
- WinSetWindowULong(miItem.hwndSubMenu,QWL_STYLE,ulStyle);
-
- hwndMenu was loaded with WinLoadMenu (specifying HWND_OBJECT as the "frame
- window" in preparation for calling WinPopupMenu. usSubmenu is the resource id
- of the SUBMENU to convert to a CCM.
-
- MM_SETDEFAULTITEMID and MM_QUERYDEFAULTITEMID
-
- These two messages are used to set and query the default item in a CCM. They
- should be sent to the SUBMENU window handle (miItem.hwndSubMenu), but I'm not
- sure if the former must be sent to it (the latter must). They have the
- following form:
-
- MM_SETDEFAULTITEMID
-
- mpParm1
-
- sDefault (SHORT)
-
- Specifies the default menu item.
-
- bSearchSubmenus (BOOL)
-
- Specifies whether or not submenus should be searched or not.
-
- TRUE Search submenus
- FALSE Do not search submenus
-
- mpParm2 (BIT32)
-
- Reserved
-
- NULL Reserved value.
-
- Returns
-
- bSuccess (BOOL)
-
- Success indicator
-
- TRUE Successful completion
- FALSE Error occurred
-
- Notes
-
- Setting the default item id does not set the menu item's attribute to
- MIA_CHECKED. This responsibility is left to the programmer.
-
- MM_QUERYDEFAULTITEMID
-
- mpParm1 (BIT32)
-
- Reserved
-
- NULL Reserved value.
-
- mpParm2 (BIT32)
-
- Reserved
-
- NULL Reserved value.
-
- Returns
-
- sDefault (SHORT)
-
- Specifies the default item.
-
- 0 There is no default item associated with the submenu.
- Other Menu item id of the default item.
-
- Encapsulation
-
- All of this can be encapsulized into a single procedure. The code for this is
- shown below:
-
- BOOL setCascadeDefault(HWND hwndMenu,USHORT usSubmenu,USHORT usDefault)
- //-------------------------------------------------------------------------
- // This function sets the default menuitem for the specified CCM and checks
- // the menuitem.
- //
- // Input: hwndMenu - specifies the menu window handle.
- // usSubmenu - specifies the id of the cascade menu.
- // usDefault - specifies the id of the default menuitem.
- // Returns: TRUE if successful, FALSE otherwise.
- //-------------------------------------------------------------------------
- {
- MENUITEM miItem;
- ULONG ulStyle;
-
- WinSendMsg(hwndMenu,
- MM_QUERYITEM,
- MPFROM2SHORT(usSubmenu,TRUE),
- MPFROMP(&miItem));
- ulStyle=WinQueryWindowULong(miItem.hwndSubMenu,QWL_STYLE);
- ulStyle|=MS_CONDITIONALCASCADE;
- WinSetWindowULong(miItem.hwndSubMenu,QWL_STYLE,ulStyle);
-
- WinSendMsg(miItem.hwndSubMenu,
- MM_SETDEFAULTITEMID,
- MPFROM2SHORT(usDefault,FALSE),
- 0L);
- WinSendMsg(miItem.hwndSubMenu,
- MM_SETITEMATTR,
- MPFROM2SHORT(usDefault,FALSE),
- MPFROM2SHORT(MIA_CHECKED,MIA_CHECKED));
-
- return TRUE;
- }
-
-
- ΓòÉΓòÉΓòÉ 6.1.1.2. Dropping on a Printer ΓòÉΓòÉΓòÉ
-
- In the "Direct Manipulation" chapter of Volume 4 of the "Redbooks", it states
- that for the rendering mechanism DRM_PRINT the printer sends the source of the
- drag operation a DM_PRINTOBJECT message to print the object(s). That's all
- well and good, until you try to use the information in the "Programmer's
- Reference" to process this message. Well, direct manipulation is a real drag
- (pun intended) as it is, but even more so when the documentation is incorrect
- about a fundamental message used by the system.
-
- The documentation states that mpParm1 will point to the DRAGINFO structure
- corresponding to the drag operation in progress. Bzzz! Thanks for playing, and
- Bob will tell you what your consolation prize is. One can glean what the true
- value of this parameter is by looking at the name assigned in the documentation
- - pDragItem. You guessed it; it points to the DRAGITEM structure corresponding
- to an item that was dropped on the printer object.
-
- While this only makes sense, this poses an interesting design decision. If your
- application has a container and the user drags 10 items to the printer, you'll
- get 10 different DM_PRINTOBJECT messages. Since we all know that printing
- should be done in a separate thread, do we start 10 different threads to handle
- each message? What if 50 objects were dropped? 100?
-
- This can easily get out of hand, and the system can easily run out of resources
- (or it did when I originally implemented it this way for one of my
- applications), so an alternative method of accomplishing this will need to be
- developed; this is left to the programmer, since the solutions are various and
- are specific to an application.
-
- A second, more serious, problem is that of the second parameter, passed in
- mpParm2. It points to a PRINTDEST structure, which is defined in <os2def.h> as
- such:
-
- typedef struct _PRINTDEST {
- ULONG cb;
- LONG lType;
- PSZ pszToken;
- LONG lCount;
- PDEVOPENDATA pdopData;
- ULONG fl;
- PSZ pszPrinter;
- } PRINTDEST, *PPRINTDEST;
-
- This should save you a lot of time, since the DEVOPENSTRUC structure is already
- initialized and is disguised as a DEVOPENDATA structure. However, when you try
- to use the pdosData field in your call to DevOpenDC, you get a
- PMERR_INV_DRIVER_DATA error. This is particularly funny, since the printer
- specified this as the data to use, yet won't accept it if you use it.
-
- The only solution is to query the default driver data yourself using
- DevPostDeviceModes. But wait! You don't have the device name for the printer!
- Well, making the assumption that your machine doesn't have more than one
- printer attached to the queue you dropped the object on, you can use the
- SplQueryQueue function to determine the missing information. Sparing you the
- boring details, I'll cut-to-the-quick and will simply show you the procedure
- below:
-
- BOOL initPrinter(HAB habAnchor,PPRINTDEST ppdPrinter,PDEVOPENSTRUCT pdosPrinter)
- //-------------------------------------------------------------------------
- // This function will query the default driver data for the printer
- // specified in ppdPrinter. It is the caller's responsibility to free
- // the data using free().
- //
- // Input: habAnchor - anchor block of the calling thread.
- // ppdPrinter - pointer to the PRINTDEST structure passed via
- // the DM_PRINTOBJECT message.
- // Output: pdosPrinter - points to the variable which received the new
- // DEVOPENSTRUC structure.
- // Returns: TRUE if successful, FALSE otherwise.
- //-------------------------------------------------------------------------
- {
- ULONG ulNeeded;
- PPRQINFO3 ppiQueue;
- CHAR achDriver[64];
- CHAR achDevice[64];
- PCHAR pchPos;
-
- *pdosPrinter=*((PDEVOPENSTRUC)(ppdPrinter->pdopData));
-
- SplQueryQueue(NULL,
- pdosPrinter->pszLogAddress,
- 3,
- NULL,
- 0,
- &ulNeeded);
-
- ppiQueue=malloc(ulNeeded);
- if (ppiQueue==NULL) {
- return FALSE;
- } /* endif */
-
- SplQueryQueue(NULL,
- pdosPrinter->pszLogAddress,
- 3,
- ppiQueue,
- ulNeeded,
- &ulNeeded);
-
- strcpy(achDriver,ppiQueue->pszDriverName);
- free(ppiQueue);
-
- pchPos=strchr(achDriver,'.');
- if (pchPos!=NULL) {
- *pchPos=0;
- strcpy(achDevice,pchPos+1);
- } else {
- achDevice[0]=0;
- } /* endif */
-
- ulNeeded=DevPostDeviceModes(habAnchor,
- NULL,
- achDriver,
- achDevice,
- ppdPrinter->pszPrinter,
- DPDM_QUERYJOBPROP);
-
- pdosPrinter->pdriv=malloc(ulNeeded);
- if (pdosPrinter->pdriv==NULL) {
- return FALSE;
- } /* endif */
-
- DevPostDeviceModes(habAnchor,
- pdosPrinter->pdriv,
- achDriver,
- achDevice,
- ppdPrinter->pszPrinter,
- DPDM_QUERYJOBPROP);
-
- if ((ppdPrinter->fl & PD_JOB_PROPERTY)!=0) {
- DevPostDeviceModes(habAnchor,
- pdosPrinter->pdriv,
- achDriver,
- achDevice,
- NULL,
- DPDM_POSTJOBPROP);
- } /* endif */
-
- return TRUE;
- }
-
-
- ΓòÉΓòÉΓòÉ 6.1.1.3. Message Box Help ΓòÉΓòÉΓòÉ
-
- Message boxes are an easy way to communicate something to the user from your PM
- application. However, one can only say so much in a message box, so PM allows
- you to add online help for each one. To correspond a particular message with a
- help panel, the panel resource id is specified in the 5th parameter and MB_HELP
- must be specified in the flags.
-
- So how do you process the help requests? The documentation states that a help
- hook must be used, and that (because the Help Manager also uses a help hook) it
- should be installed before creating the help instance so that you can receive
- the help messages. Unfortunately, doing this won't work because the
- documentation is incorrect. If you look up the entry for WinSetHook, you'll
- read that this installs the hook at the head of the hook chain, so (doing a few
- abstract calculations in your head) you can see that setting your help hook
- before creating the help instance results in the Help Manager's hook being
- positioned before yours. Calling WinSetHook after WinAssociateHelpInstance
- will fix this problem and your help hook will start seeing the messages. Don't
- forget to return FALSE if the message isn't processed, or the Help Manager's
- hook won't see them!
-
- As if this wasn't enough, the documentation on the help hook is incorrect also.
- It states that the second parameter (sMode) can have one of the following four
- values:
-
- o HFM_MENU
- o HFM_MB
- o HFM_WINDOW
- o HFM_APPLICATION
-
- but none of these constants are defined in the toolkit! Instead, sMode can
- have one of the following three values:
-
- o HLPM_FRAME
- o HLPM_WINDOW
- o HLPM_MENU
-
- HLPM_WINDOW and HLPM_MENU seem to correctly behave according to the
- documentation for HFM_WINDOW and HFM_MENU, respectively. There is no
- corresponding constant for HFM_MB though; instead, message box help requests
- have the mode HLPM_WINDOW with the topic number specifying the help panel id
- used in the WinMessageBox call.
-
-
- ΓòÉΓòÉΓòÉ 6.2. Introduction to PM ΓòÉΓòÉΓòÉ
-
- Introduction to
- PM Programming
-
- Part II
-
- Gavin Baker
- (demogrb@lust.latrobe.edu.au)
-
-
- ΓòÉΓòÉΓòÉ 6.2.1. Introduction ΓòÉΓòÉΓòÉ
-
- Introduction
-
- Welcome to the second installment of our exciting series on PM programming!
- (Well, I thought I'd better start on a high note ...)
-
- In this article, I present a simple program which takes advantage of OS/2
- threads. It uses one thread to handle interacting with the user, and another
- thread to do all the work. It simply displays the current time in the middle
- of the window. It is obviously a trivial prorgam, but nonetheless serves to
- illustrate one possible use of threads.
-
- Basically you can have one program (process) doing more than one thing
- (threads) at once. A little like multi-multitasking... Anyway, in terms of the
- source code, a thread is just a function in your program which gets called in a
- special way which allows it to go off and run at the same time as the rest of
- the program. You can imagine how complicated things could get having to
- co-ordinate things, so OS/2 also provides IPC (Inter-Process Communication)
- functions to help you.
-
- The program uses other facets of OS/2 not discussed yet in this series
- (resources, and the Graphics Programming Interface [GPI]) so we will not spend
- too much time on them since the main focus is on threads. I have included the
- RC file which defines the dialog box and the menu, but I will leave off
- explaining resources to the next article.
-
-
- ΓòÉΓòÉΓòÉ 6.2.2. Scope ΓòÉΓòÉΓòÉ
-
- Scope
-
- I am assuming that you are a competent C programmer, and have a working
- knowledge of OS/2 from a user's perspective. The sample code here was produced
- with Borland C++ for OS/2, but should work with most other compilers.
-
-
- ΓòÉΓòÉΓòÉ 6.2.3. Trademarks etc. ΓòÉΓòÉΓòÉ
-
- Trademarks etc.
-
- Please note that any trademarks referred to in this article remain the property
- of their respective companies.
-
- #include <std_disclaimer.h>
-
-
- ΓòÉΓòÉΓòÉ 6.2.4. Processes & Threads ΓòÉΓòÉΓòÉ
-
- Processes & Threads
-
- A process in OS/2 terms is just an application or program. Because OS/2 is
- multi-tasking, you can run multiple processes (in their own address spaces) at
- the same time. A thread is an execution unit within a process. It becomes
- clearer in this diagram:
-
- Editor's note: Due to time constraints, this diagram was not available in time
- for publication. It will be made available at a later date.
-
- The red shows OS/2 itself. The green represents our program, and the yellow
- boxes are the threads within it. You would typically have one thread to handle
- the user interface (the main window procedure), one to do all the work, and
- perhaps one for printing. Threads can have different priorities, and there are
- some very powerful functions to enable threads to communicate with each other,
- to synchronise and co-ordinate events.
-
- Threads within a process share the address space, which means the variables and
- functions within the program have the same scope between two threads. For
- example, consider these two threads:
-
- void Thread1() void Thread2()
- { {
- while (1) while (1)
- i++; i--;
- } }
-
- Get the feeling we may not be getting anywhere...? These two threads are
- playing a "tug-of-war" with the poor old (overused) variable i. The first
- thread will be forever incrementing i, and the second thread will forever be
- undoing all that hard work. The variable i will stay around the same value, but
- won't be exactly zero due to the dynamic prioritising of threads that OS/2
- performs - if a Thread1 gets a tiny bit more CPU time, i will go up a little.
-
- Threads allow your programs to be much more efficient, and responsive to the
- user. Here I will show you how.
-
-
- ΓòÉΓòÉΓòÉ 6.2.5. The Code ΓòÉΓòÉΓòÉ
-
- The Code
-
- Right - now we'll go straight to the code. The general idea is to have the
- main program handle the user interactions, and during initialization we start
- the worker thread which carries on its merry way despite what the main thread
- may be doing.
-
- The way we acheive this is by creating the main window itself, and then have
- the second Worker thread create a dummy window (called an OBJECT WINDOW) which
- doesn't actually appear on screen, but gives us a window handle and a message
- queue to work with. This is not the only way to do this, but it is probably
- the simplest. The main window sends custom messages to the second dummy window
- on thread 2 to do all the work.
-
- Now here we specify which portions of the include files we need, and also
- include a few others.
-
- #define INCL_WIN
- #define INCL_GPI
- #define INCL_WINDIALOGS
-
- #include <os2.h>
- #include <process.h>
- #include <string.h>
- #include <time.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <dos.h>
- #include "step02.h"
-
- Now we need to pass messages between the two threads, so we define our own by
- starting at WM_USER and going up. This ensures we don't accidentally use a
- system-defined message.
-
- #define WM_BEGIN_PAINT WM_USER+1
- #define WM_END_PAINT WM_USER+2
- #define WM_ACK WM_USER+3
-
- Using global variables is generally considered a no-no, unless it can't be
- avoided. It makes things easier to implement and more self-contained if you
- keep globals to a bare minimum. We then supply prototypes for our functions.
-
- HWND hwndMain,
- hwndWorker;
-
- MRESULT EXPENTRY MainWndProc (HWND, ULONG, MPARAM, MPARAM);
- VOID WorkerThread ();
- MRESULT EXPENTRY WorkWndProc (HWND, ULONG, MPARAM, MPARAM);
- MRESULT EXPENTRY DlgProc(HWND, ULONG, MPARAM, MPARAM);
- VOID WorkPaint (HWND, HPS);
-
- The main function is not very big - all it does is set up some variables, the
- flags for the window, initialize things for PM, then register and create our
- window. After we exit our main message loop (by getting a WM_QUIT) we clean up
- and exit.
-
- int main (void)
- {
- HAB hab;
- HMQ hmq;
- HWND hwndFrame;
- QMSG qmsg;
- ULONG flFrameFlags = FCF_TITLEBAR | FCF_SYSMENU |
- FCF_SIZEBORDER | FCF_MINMAX |
- FCF_SHELLPOSITION | FCF_TASKLIST |
- FCF_MENU ;
-
- randomize();
-
- hab = WinInitialize (0);
- hmq = WinCreateMsgQueue (hab, 0);
-
- WinRegisterClass (hab, "STEP2", MainWndProc, CS_SIZEREDRAW, 0);
-
- hwndFrame = WinCreateStdWindow (HWND_DESKTOP, WS_VISIBLE,
- &flFrameFlags, "STEP2", NULL,
- 0, NULLHANDLE, ID_MAIN, &hwndMain);
-
- while (WinGetMsg (hab, &qmsg, 0, 0, 0))
- WinDispatchMsg (hab, &qmsg);
-
- WinDestroyWindow (hwndFrame);
- WinDestroyMsgQueue (hmq);
- WinTerminate (hab);
- return 0;
- }
-
- Now here is the Window Procedure for the main window itself. The special bit
- to notice here is the call to _beginthread when we get created (WM_CREATE).
- This is where the second thread gets started. Note we just pass it the name of
- the function, and that function will start executing from there all by itself.
- It can operate more or less like any other function, with a few considerations.
-
- MRESULT EXPENTRY MainWndProc (HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
- {
- FILEDLG fild;
-
- switch (msg)
- {
- case WM_CREATE:
- if (_beginthread (WorkerThread, 8192, NULL) == -1)
- {
- WinMessageBox (HWND_DESKTOP, hwnd,
- "Creation of second thread failed!", "Step 2",
- 0, MB_OK | MB_CUACRITICAL);
- return 0;
- }
-
- return 0;
-
- Here is the first user message we get. This message will be sent by the second
- thread's window procedure to the main one to say it is all setup and ready to
- go. We respond by telling it to go ahead and start painting the window.
-
- case WM_ACK:
- WinPostMsg(hwndWorker, WM_BEGIN_PAINT, 0, 0);
- return 0;
-
- When the main window gets resized, we stop the second thread from painting, and
- then restart it, causing it to update itself with the new size of the window.
- Note that we use WinSendMsg first, then WinPostMsg. We send the message first,
- which calls the target window procedure directly and will not continue until it
- returns (thus ensuring the message gets processed) and then we Post the message
- to restart in the second thread's message queue so it can keep going.
-
- case WM_SIZE:
- WinSendMsg(hwndWorker, WM_END_PAINT, 0, 0);
-
- WinPostMsg(hwndWorker, WM_BEGIN_PAINT, 0, 0);
- return 0;
-
- We get this message when the user drops a font or colour onto the window. We
- Invalidate the entire window and force a repaint so we can display with the new
- font/colours.
-
- case WM_PRESPARAMCHANGED:
- WinInvalidateRect(hwndMain,NULL,TRUE);
- return 0;
-
- If the main window needs to be painted, we simply make sure that the second
- thread gets on with it. Note again the different use of sending and posting
- the message.
-
- case WM_PAINT:
- WinSendMsg(hwndWorker, WM_END_PAINT, 0, 0);
-
- WinPostMsg(hwndWorker, WM_BEGIN_PAINT, 0, 0);
- return 0;
-
- In order to simplify things, PM does some of the painting for us if we like -
- returning true from this message makes PM erase the entire window with the
- default colour.
-
- case WM_ERASEBACKGROUND:
- return (MRESULT) TRUE;
-
- Now, whenever a user selects something from the menu, we get a WM_COMMAND
- message. We can then use the SHORT1FROMMP macro (which means extract a short
- value from the high word of a message parameter) to get the ID of the menu item
- selected. The first menu selection we process is the most important one - the
- About box. We call WinDlgBox which does a lot of work for us. It loads the
- dialog from the resource (check out STEP02.RC), runs the dialog procedure we
- specify (which works just like a window procedure) and will not return until
- the dialog is closed. This is called a modal dialog - it will not allow the
- user to select anything else until they finish with the dialog. Contrast this
- with a modeless dialog, which can be used at the same time as any other
- windows. These are often used for floating toolboxes, etc. We pass WinDlgBox
- the parent window (which will be the desktop), the owner window (our main
- window), a pointer to the dialog procedure which handles the messages, a handle
- to the module where the resource is located (by specifying NULLHANDLE OS/2
- looks in the EXE file), and any extra info we want to pass the dialog
- procedure.
-
- case WM_COMMAND:
- switch (SHORT1FROMMP(mp1))
- {
- case ID_ABOUT:
- WinDlgBox(HWND_DESKTOP,hwnd,(PFNWP)DlgProc,NULLHANDLE,
- DLG_ABOUT,NULL);
- return 0;
-
- Now we will use one of OS/2's standard dialogs - the File Open dialog. All we
- do is set up a structure with the appropriate options, and call WinFileDlg.
- Once it returns we can examine the FILEDLG struct for the file the user
- selected. This example only displays the dialog - it does nothing with what
- the user selected.
-
- case ID_FILEOPEN:
- memset(&fild, 0, sizeof(FILEDLG));
- fild.cbSize=sizeof(FILEDLG);
- fild.fl=FDS_OPEN_DIALOG | FDS_CENTER ;
- fild.pszIDrive="C:";
- WinFileDlg(HWND_DESKTOP,hwnd,&fild);
- return 0;
-
- If the user selects Exit from the File menu, we just send ourselves a WM_CLOSE
- message, which by default will shut down our application.
-
- case ID_FILEEXIT:
- WinPostMsg(hwnd, WM_CLOSE, 0, 0);
- return 0;
-
- Any other messages we don't need to worry about, so let PM handle them by
- passing them on to the default PM window procedure.
-
- default:
- return WinDefWindowProc(hwnd,msg,mp1,mp2);
- }
-
- Notice how we first destroy the second thread so it can clean up, before we
- close ourselves.
-
- case WM_DESTROY:
- WinSendMsg(hwndWorker, WM_DESTROY, mp1, mp2);
- return 0;
- }
- return WinDefWindowProc (hwnd, msg, mp1, mp2);
- }
-
- Now we get to the interesting bit - our Worker thread. It looks like a normal
- function, it just gets called differently. Although this example does not
- cover it, you must keep in mind that having multiple threads in the one program
- requires some forethought. You can't have two threads trying to write to the
- one data file, for example. We will explore this problem and how to solve it
- (using Semaphores) in a later article.
-
- VOID WorkerThread ()
- {
- HAB hab;
- HMQ hmq;
- HWND hwndObject,
- hwndParent;
- QMSG qmsg;
-
- This should look familiar - it looks very much like the main procedure. The
- only difference is that we specify HWND_OBJECT when we create the window. We
- need our own message queue so we can talk to the main window. Notice how we
- ACKnowledge the main window once we get created, and go into our message loop.
- All the work is actually done in the second thread's window procedure. One
- special thing to note is the call to _endthread at the end. This lets the C
- runtime library clean up after us, and shuts down the thread properly.
-
- hab = WinInitialize (0);
-
- hmq = WinCreateMsgQueue(hab, 0);
-
- WinRegisterClass(hab, "STEP2_B", WorkWndProc, 0, 0);
-
- hwndWorker = WinCreateWindow( HWND_OBJECT, "STEP2_B", "",
- 0, 0, 0, 0, 0, HWND_OBJECT, HWND_BOTTOM, 0, NULL, NULL );
-
- WinSendMsg( hwndMain, WM_ACK, 0, 0 );
-
- while( WinGetMsg ( hab, &qmsg, 0, 0, 0 ))
- WinDispatchMsg ( hab, &qmsg );
-
- WinPostMsg( hwndMain, WM_QUIT, 0, 0 );
-
- WinDestroyWindow( hwndWorker );
- WinDestroyMsgQueue( hmq );
- WinTerminate (hab);
- _endthread ();
- }
-
- This is the dialog procedure for the About box. It is just the same as a
- window procedure except that for the messages we don't process, we call
- WinDefDlgProc instead of WinDefWindowProc because certain things have to be
- handled differently. If the user presses a button in the dialog, we get a
- WM_COMMAND much the same as if they selected a menu item. We know that the OK
- button is the only button that will do anything so we don't bother checking and
- just close the dialog by calling WinDismissDlg. We pass it the handle of the
- dialog, and a BOOLean value which will be returned to the calling function
- (back at WinDlgBox) so we can tell if the user pressed OK or Cancel.
-
- MRESULT EXPENTRY DlgProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
- {
- switch (msg)
- {
- case WM_COMMAND:
- WinDismissDlg(hwnd,TRUE);
- return 0;
- default:
- return WinDefDlgProc(hwnd,msg,mp1,mp2);
- }
- }
-
- This is the window procedure for the "dummy" OBJECT window which the second
- thread creates. We have to set up a few things when we get CREATEd. Firstly,
- we ask PM for a timer. We give it a handle to the anchor block (which we get
- from our handle), the handle itself, an ID for the timer (you can have more
- than one), and the delay in milliseconds between timer "ticks". We ask it to
- send us a WM_TIMER once a second, so we can update our clock. The next thing
- we do is get ourselves a Presentation Space (PS), which is like a handle which
- is used when we want to draw or paint in the window. We then say that we want
- the background to be cleared when we draw things. Otherwise the time we
- display would overwrite itself and soon become garbled. I will not go into to
- much detail on the drawing side of things, as the GPI (Graphics Programming
- Interface) itself could fill a book (and has).
-
- MRESULT EXPENTRY WorkWndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
- {
- static BOOL Paint=FALSE;
- static HPS hps;
- SIZEL sizel;
-
- switch (msg)
- {
- case WM_CREATE:
- if (!WinStartTimer(WinQueryAnchorBlock(hwnd), hwnd, 1, 1000))
- WinMessageBox(HWND_DESKTOP,hwnd,"Could not start timer!",
- "Error",0,MB_CUACRITICAL | MB_OK);
- hps = WinGetPS(hwndMain);
- GpiSetBackMix(hps, BM_OVERPAINT);
- return 0;
-
- WM_BEGIN_PAINT is our first user message sent to us from the main window. All
- we do is set a flag saying it is OK to keep painting. If we get WM_END_PAINT
- then we stop painting for the moment (even while we are still getting WM_TIMER
- messages).
-
- case WM_BEGIN_PAINT:
- Paint = TRUE;
- return 0;
-
- case WM_END_PAINT:
- Paint = FALSE;
- return 0;
- Every second, we will get a WM_TIMER message, and all we do is check if it is
- Ok to paint, and then do so.
-
- case WM_TIMER:
- if (Paint)
- WorkPaint(hwndMain, hps);
- return 0;
-
- If we get closed, make sure we clean up by stopping our timer and releasing the
- PS we got to draw with. Cleanup is always very important, and could
- potentially cause some nasty bugs which may not be obvious. Always consult the
- PM Reference manual for functions you may use which require resources to be
- released or destroyed.
-
- case WM_DESTROY:
- WinStopTimer(WinQueryAnchorBlock(hwnd), hwnd, 0);
- WinReleasePS(hps);
- return 0;
- }
- return WinDefWindowProc (hwnd, msg, mp1, mp2);
- }
-
- This function does the painting for us. We get the size of the main window (a
- RECTangLe), then calculate its width and height.
-
- VOID WorkPaint (HWND hwnd, HPS hps)
- {
- ULONG x, y, cx, cy;
- RECTL rect;
- POINTL ptl;
- char s[42];
- struct time t;
-
- WinQueryWindowRect(hwnd, &rect);
-
- cx = rect.xRight - rect.xLeft;
- cy = rect.yTop - rect.yBottom;
-
- We check what the current time is, and compose our string.
-
- gettime(&t);
- sprintf(s,"Current Time: %2.2d:%2.2d%colon.%2.2d",t.ti_hour,t.ti_min,t.ti_sec);
-
- We want the time to be shown roughly in the middle of the screen, so we figure
- out where that is. We then randomly pick a colour and display the string at
- the specified point.
-
- ptl.x=(cx/3); ptl.y=(cy/2);
- GpiSetColor(hps, rand() % 16);
- GpiCharStringAt(hps, &ptl, strlen(s), s);
- }
-
-
- ΓòÉΓòÉΓòÉ 6.2.6. Prologue ΓòÉΓòÉΓòÉ
-
- Prologue
-
- Well, that's it. It's not a particularly exciting program, and it could
- probably be more efficient, but it does provide a general skeleton for a
- multi-threaded PM application. Study the structure of the program, in blocks.
- The main function which sets up and creates the window, the main window
- procedure which handles all the messages and delegates work through user
- messages to the worker thread. Then the worker thread which sets up the object
- window, and its corresponding window procedure which does all the work.
-
- Try a few things. First, drop a different font on the time. Then try a colour
- (this will only last until it updates itself, since it changes colour itself).
- Then bring up the About box and move it so you can see the time still updating
- itself.
-
- Something we have not discussed is thread states. Basically, a thread can
- either be running (the currently executing thread of which there can only ever
- be one), ready to run, or blocked (waiting for something to happen). This
- makes for very efficient programming. Imagine a terminal program. One thread
- can be handling the user interface, one doing general work, and another
- monitoring the serial port for input. It would call DosRead from COM1 for
- example. If there is nothing to read, it doesn't have to sit in a loop and
- poll the port. OS/2 will block the thread and drop its priority until DosRead
- returns with something, so it won't get any CPU time until it needs it.
-
- The possibilities for threads are endless - background recalculating,
- background printing, etc. are just some. See how threads could improve your
- programs.
-
-
- ΓòÉΓòÉΓòÉ 6.2.7. What Next? ΓòÉΓòÉΓòÉ
-
- What Next?
-
- There is a lot of ground to cover in this area, and we have only scratched the
- surface. But fear not - help is at hand! (You'll just have to wait until next
- month...)
-
- I welcome any feedback on this article (netmail preferred) - any comments or
- suggestions you may have, questions on this article, or things you would like
- to see in a future article. I hope you have learned something!
-
-
- ΓòÉΓòÉΓòÉ 6.2.8. Bibliography ΓòÉΓòÉΓòÉ
-
- Bibliography
-
- The following references were used in the preparation of this article:
-
- Γûá Borland C++ for OS/2 Documentation
-
- Γûá OS/2 Version 2.0 - The Redbooks
-
- Γûá OS/2 Version 2.0 Technical Library
-
-
- ΓòÉΓòÉΓòÉ 7. Future Attractions ΓòÉΓòÉΓòÉ
-
- Coming up in the future, we have:
-
- o Introduction to PM Part 3
-
- o Writing Installable File Systems
-
- o Getting Started with IPF
-
- o And much more!
-
-
- ΓòÉΓòÉΓòÉ 8. Contributors to this issue ΓòÉΓòÉΓòÉ
-
- o Gavin R Baker
-
- o David Campbell
-
- o Steve Lacy
-
- o Steve Luzynski
-
- o Dave Raymer
-
- o Larry Salomon
-
-
- ΓòÉΓòÉΓòÉ 8.1. Gavin R Baker ΓòÉΓòÉΓòÉ
-
- Gavin R Baker
-
- Gavin R Baker is the man behind ThinkSoft, a consulting firm based in
- Melbourne Australia which specialises in developing custom software. He has
- experience in Assembler, Pascal, C, C++, (and a lot of other languages), and
- has worked with Unix, DOS, Windows, OS/2, VMS and Pick operating systems. He is
- an active member of Team OS/2. When he isn't programming, he is also a
- musician, an actor, and wastes lots of time reading Net News. He can be
- contacted thusly:
-
- net: demogrb@lust.latrobe.edu.au
- cis: 100026,270
- bix: gbaker
-
-
- ΓòÉΓòÉΓòÉ 8.2. David Campbell ΓòÉΓòÉΓòÉ
-
- David has a degree in Mechanical Engineering. Currently he is working as a
- network designer and software developer. He is involved in everything from
- wide area token ring networks to developing an object oriented eMail system
- which works with IBM's TCP/IP for OS/2.
-
- David W. Campbell
-
- campbell@campbell.saic.com
-
- Work : (615) 481-2131
-
- Home : (615) 693-1479
-
-
- ΓòÉΓòÉΓòÉ 8.3. Steve Lacy ΓòÉΓòÉΓòÉ
-
- Steve Lacy is a third year computer science student at Carnegie Mellon
- University, and is currently overoccupied with school, and his job as an
- undergraduate research programmer for the Digital Mapping Lab at CMU. You can
- reach Steve via e-mail at sl31@andrew.cmu.edu. He would be glad to hear any and
- all of your comments about the previous article. Steve also spends his free
- time collecting CD's, and unfortunately spends far too much money in the
- process. Remember, diversity is knowledge, and personal gratification
- overrides the need for food in many cases.....
-
-
- ΓòÉΓòÉΓòÉ 8.4. Steve Luzynski ΓòÉΓòÉΓòÉ
-
- Steve Luzynski is the editor and creator of this magazine. He is currently a
- Computer Engineering student at Case Western Reserve University in Cleveland,
- OH, where he spends a lot of time being cold. Steve has yet to release any
- programs for OS/2 as a direct result of: 1) editing this magazine; and 2)
- having to waste time going to class when there are programs to write. Steve can
- by reached via e-mail at 'sal8@po.cwru.edu' or on Compuserve at 72677,2140.
-
- The old fashioned kind of mail can currently find Steve at:
-
- Steve Luzynski
- 11904 Carlton Road, Apt. 430D
- Cleveland, OH 44106
-
-
- ΓòÉΓòÉΓòÉ 8.5. Dave Raymer ΓòÉΓòÉΓòÉ
-
- Dave lives in Fort Worth, Texas with his wife, two sons, and five dogs. His
- current employer is Suite Software, a small privately held company which
- produces an object oriented, multi-platform distributed operating system. He
- has worked extensively in Windows/DOS, and OS/2; both at the device and
- application levels. Additionally he has worked in X-Windows/UNIX and NeXTSTEP,
- as well as in MVS and VMS.
-
- Dave can be reached in the following ways. Please do not hesitate to contact
- him for any reason. He is available for Q&A issues (preferably through
- e-mail.)
-
- e-mail:
- Internet -- dave@suite.com
- Prodigy -- shph80a
-
- voice/direct:
- Work -- (214)980-9900
- Home -- 6528 Levitt Drive
- Watauga, Texas, 76148
- (817)656-3813
-
-
- ΓòÉΓòÉΓòÉ 8.6. Larry Salomon ΓòÉΓòÉΓòÉ
-
- Larry Salomon wrote his first Presentation Manager application for OS/2 version
- 1.1 in 1989. Since that time, he has written numerous VIO and PM applications,
- including the Scramble applet included with OS/2 and the I-Brow/Magnify/Screen
- Capture trio included with the IBM Professional Developers Kit CD-ROM currently
- being distributed by IBM. Currently, he works for International Masters
- Publishers in Stamford, Connecticut and resides in Bellerose, New York with his
- wife Lisa.