home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 32 Periodic / 32-Periodic.zip / edmi1-2.zip / EDMI2.INF (.txt) next >
OS/2 Help File  |  1993-04-12  |  89KB  |  2,203 lines

  1.  
  2. ΓòÉΓòÉΓòÉ 1. Title Page ΓòÉΓòÉΓòÉ
  3.  
  4.           Welcome to EDM/2 - The Electronic OS/2 Developers' Magazine!.
  5.  
  6.                   Portions Copyright (C)1993 by Steve Luzynski.
  7.  
  8.                              Issue #2 - April 1993.
  9.  
  10.                            (Press 'Forward' to page.)
  11.  
  12.  
  13. ΓòÉΓòÉΓòÉ 2. Copyright Notice and other Legal Stuff ΓòÉΓòÉΓòÉ
  14.  
  15. EDM/2 Copyright (C)1993 by Steve Luzynski. This publication may be freely 
  16. distributed in electronic form provided that all parts are present in their 
  17. original unmodified form. A reasonable fee may be charged for the physical act 
  18. of distribution; no fee may be charged for the publication itself. 
  19.  
  20. All articles are copyrighted by their authors. No part of any article may be 
  21. reproduced without permission from the original author. 
  22.  
  23. Neither this publication nor Steve Luzynski is affiliated with International 
  24. Business Machines Corporation. 
  25.  
  26. OS/2 is a registered trademark of International Business Machines Corporation. 
  27. Other trademarks are property of their respective owners. Any mention of a 
  28. product in this publication does not constitute an endorsement or affiliation 
  29. unless specifically stated in the text. 
  30.  
  31.  
  32. ΓòÉΓòÉΓòÉ 3. From the Editor ΓòÉΓòÉΓòÉ
  33.  
  34. Hi! and welcome again to EDM/2. 
  35.  
  36. This month was a big one for OS/2 developers. The new PDK CDROM, including all 
  37. kinds of great OS/2 tools from IBM, was released at the incredible price of 
  38. $15. Look for a new PM mail program from me soon now that I have the TCP/IP 
  39. developer's kit... 
  40.  
  41. The other big news this month was the release of Borland C++ for OS/2. My copy 
  42. came in on the 11th - about a week after I ordered it. I'm very impressed with 
  43. this first offering from Borland... 
  44.  
  45. Borland C++ for OS/2 
  46.  
  47. In fact, I'm impressed enough that I'm going to talk about it with the rest of 
  48. my space. Borland C++ came in Borland's new style packaging - a slick blue & 
  49. white box. Included are seven disks and 8 manuals. 
  50.  
  51. I installed all 28 megs (including all sample code) and sat down to read the 
  52. manuals while I waited. Apparently, Borland supplied a DOS based decompressor 
  53. program - my guess is that the install time will drop dramatically after they 
  54. rewrite their compression program for OS/2. 
  55.  
  56. After the install completed, I made all the changes listed in the README file. 
  57. I would suggest that anyone installing it do the same - otherwise a few things 
  58. won't work quite right. I would also suggest deleting the IBM Toolkit and its 
  59. associated CONFIG.SYS lines before installing to keep things from getting 
  60. confused. Besides, BC comes with everything the Toolkit includes EXCEPT the SOM 
  61. reference (.INF) file. Copy it out if you need it. 
  62.  
  63. Most of the sample code was ported directly from the toolkit. I compiled one to 
  64. make sure everything worked (it did) and started on converting one of my EMX 
  65. programs to BC. 
  66.  
  67. Converting from EMX to BC 
  68.  
  69. The first thing I noticed when I compiled my program under BC was a lot of 
  70. invalid conversions. EMX let me get away without explicitly casting some type 
  71. conversions; BC demanded casts for most of these. I also found that NULL and 0L 
  72. are not the same thing under BC; this required determining what it was I really 
  73. meant when I had used NULL under EMX. 
  74.  
  75. After fixing these two problems (which were really the only ones I had), I 
  76. immediately noticed how much faster Borland is at compiling - especially when 
  77. using precompiled headers. It is also possible to continue editing while BC is 
  78. compiling - something I could never satisfactorily do when using Emacs as my 
  79. development environment. 
  80.  
  81. I would have liked to have shown before and after code; unfortunately, in my 
  82. excitement I didn't save the old EMX version. Rest assured that it isn't really 
  83. all that difficult - I suspect many of my problems were caused by using an OS/2 
  84. 1.3 book as a reference, something I have since rectified. 
  85.  
  86. Etc. 
  87.  
  88. You may have noticed that this issue is a little smaller than the last one. 
  89. This came about for a number of reasons: 
  90.  
  91.  1. March brought about Spring Break for many college students (and teachers.) 
  92.     These people are among the contributors to this magazine, and many of them 
  93.     decided that lying on a warm Florida beach would be more fun than sitting 
  94.     in front of a computer writing articles. 
  95.  
  96.  2. At least for me, the release of Borland C++ for OS/2 meant that I was 
  97.     spending a lot more time programming since it suddenly became a lot easier. 
  98.  
  99.  3. Since I too had spring break I was late in sending out guidelines and the 
  100.     deadline to the authors. As much as I hate to admit it, this is the biggest 
  101.     reason. I promise next time I'll be earlier. 
  102.  But have no fear, next month we'll be right back up to speed! 
  103.  
  104. As always, article ideas, criticism, and compliments are welcome at 
  105. sal8@po.cwru.edu. 
  106.  
  107. Steve Luzynski, Editor. 
  108.  
  109.  
  110. ΓòÉΓòÉΓòÉ 4. Corrections and Clarifications ΓòÉΓòÉΓòÉ
  111.  
  112.                                    Corrections
  113.                                        and
  114.                                  Clarifications
  115.  
  116.  
  117. ΓòÉΓòÉΓòÉ 4.1. From Raja Thiagarajan ΓòÉΓòÉΓòÉ
  118.  
  119. Dear Editor: 
  120.  
  121. First of all, thanks for the nice job you did converting my article from raw 
  122. ASCII to INF. You did an excellent job of covering my complete ignorance of 
  123. VIEW. 
  124.  
  125. As long as I'm here, I've got a couple of clarifications I'd like to make to my 
  126. "Unofficial Guide to Palette Manager" article. 
  127.  
  128. The first is a warning: TRIDENT VIDEO CARDS *DO* ALSO SUFFER FROM THE FATAL 
  129. BUG. That is, if you're running the Service Pack and the Trident driver that 
  130. was released shortly afterwards, changing the palette and then moving an icon 
  131. will LOCK YOUR MACHINE, exactly as with ET4000s. Also as with ET4000s, the 
  132. problem goes away if you use the 2.1 beta. Thanks to James Justice for testing 
  133. and verifying this. 
  134.  
  135. Finally, I'd also like to say that I've discovered one more video platform that 
  136. supports Palette Manager: If you install the 2.1 beta on IBM's new ThinkPad 
  137. 700C, you can use a 640x480x256-color mode that supports Palette Manager. 
  138. Unfortunately, it suffers from the Universal, GpiBitBlt, and Scaling bugs that 
  139. I documented in my article. But it sure is nice to do pretty paletted graphics 
  140. while lying on your stomach! 
  141.  
  142. Raja Thiagarajan / sthiagar@bronze.ucs.indiana.edu / 3-23-93 
  143.  
  144.  
  145. ΓòÉΓòÉΓòÉ 4.2. From the Editor ΓòÉΓòÉΓòÉ
  146.  
  147. Last month, in the last-minute rush to get my first issue out on time, I ended 
  148. up forgetting to credit the authors for their articles on the title pages. 
  149. Here's the complete list (and my apologies): 
  150.  
  151. o Larry Salomon - Questions and Answers 
  152.  
  153. o Steve Lacy - Advanced GPI 
  154.  
  155. o Raja Thaigarajan - Unofficial Guide to Palette Manager 
  156.  
  157. o Gavin Baker - Introduction to PM -- Part I 
  158.  
  159. o David Charlap - The Making of Minesweeper 
  160.  Again, my apologies to all who were needlessly confused. 
  161.  
  162. Steve Luzynski - Editor 
  163.  
  164.  
  165. ΓòÉΓòÉΓòÉ 5. This Month's Features ΓòÉΓòÉΓòÉ
  166.  
  167. o OS/2 Presentation Manager Drivers 
  168.  
  169. o Road Map for the WorkPlace Shell 
  170.  
  171. o Beginning Client/Server Programming:Named Pipes 
  172.  
  173.  
  174. ΓòÉΓòÉΓòÉ 5.1. OS/2 Presentation Drivers in a Nutshell ΓòÉΓòÉΓòÉ
  175.  
  176.                             OS/2 Presentation Drivers
  177.                                       in a
  178.                                     Nutshell
  179.  
  180.                                    Dave Raymer
  181.                                 (dave@suite.com)
  182.  
  183.  
  184. ΓòÉΓòÉΓòÉ 5.1.1. Introduction ΓòÉΓòÉΓòÉ
  185.  
  186. Historically, the general population has held the belief that computers are 
  187. inherently difficult to use.  One of the outgrowths of this popularly held 
  188. belief has been the Graphical User Interface, or more commonly the GUI. The 
  189. designers of OS/2, both on the Microsoft, and IBM side, strove to provide not 
  190. only an easy to use interface, but an easy to program interface.  This ease is 
  191. measured by the robustness of the application programming interfaces (API's) 
  192. that OS/2 supports, as well as the high degree of device independence that is 
  193. provided by the display and printer subsystems. 
  194.  
  195. The OS/2 display and printer subsystems are built around the OS/2 Graphics 
  196. Engine, and a set of executable programs collectively termed "Presentation 
  197. Drivers."  Presentation Drivers (PD's) are a special form of dynamic link 
  198. library (DLL) that provide a predefined interface for the OS/2 Graphics Engine 
  199. (PMGRE) to use in rendering.  The display driver is one form of a presentation 
  200. driver, and the printer/plotter driver is another.  The primary difference 
  201. between the two is that the display driver must provide support for a pointing 
  202. device, but on the whole, the functionality of the two is essentially the same. 
  203.  
  204. In general, Joe and Suzi User interact with OS/2 through the WorkPlace Shell, 
  205. which is an application that makes extensive use of two DLLs, PMWIN.DLL and 
  206. PMGPI.DLL.  These two DLLs are responsible for providing the API interface that 
  207. application programmers use.  In turn, PMWIN and PMGPI, use the PMGRE in a 
  208. manner very similar to the shell's use of PMWIN and PMGPI.  The PMGRE in turn 
  209. uses the PD to produce the physical output that the user sees and interacts 
  210. with, whether this output is on the video display, or a piece of paper.  Figure 
  211. 1 provides an annotated stack model of this simplified view of the OS/2 user 
  212. interface. 
  213.  
  214.        WorkPlace Shell       <--- User interacts with
  215.      PMWIN.DLL  PMGPI.DLL     <--- WorkPlace interacts with
  216.        PMGRE.DLL         <--- PMWIN/PMGPI interact with
  217.      <presentation driver>     <--- produces visual output
  218.       <physical device>      <--- displays visual output
  219.  
  220.                (fig. 1)
  221.  
  222.  
  223. ΓòÉΓòÉΓòÉ 5.1.2. A Brief Overview Of The Printer Subsystem ΓòÉΓòÉΓòÉ
  224.  
  225. The OS/2 Print Subsystem consists of three primary components, the print 
  226. manager, the print spooler, and the presentation driver.  Under OS/2 version 
  227. 2.x the print manager is implemented through WorkPlace Shell printer objects. 
  228. The WPS printer object provides the user the ability to manage print jobs on a 
  229. per printer basis, but is not the focus of this article, so little more will be 
  230. said in regards to it. 
  231.  
  232. OS/2 provides two print spoolers, PMPRINT and PMPLOT, which differ in the fact 
  233. that PMPLOT supplies a reverse vector clipping engine developed by Steve 
  234. Matthews for Micrografx, Incorporated, and reimplemented by Wes Bell, also of 
  235. Micrografx, for IBM, enabling HPGL plotters to produce correct output in what 
  236. is primarily a raster oriented environment.  The purpose of the print spooler 
  237. is to build a meta-data file of an appli- cations request for hardcopy output, 
  238. allowing the application to continue processing without without the need to 
  239. wait for hardcopy output rendering to complete. 
  240.  
  241. The OS/2 print spoolers provide two types of meta-data files, the first is a 
  242. result of opening the printer OD_QUEUED with the PM_Q_STD standard option.  In 
  243. this mode, the spooler will produce an intermediate data file that contains 
  244. boundary information as well as the application supplied arguments to the 
  245. rendering functions.  The second format occurs when the printer is opened 
  246. OD_QUEUED with the PM_Q_RAW option.  This combination will produce an 
  247. intermediate data file that is the raw printer data stream.  However, the 
  248. overall data path through the printer will remain the same. 
  249.  
  250. When used in conjunction with the spooler, the data is passed through the 
  251. presentation driver twice.  In the PM_Q_STD mode, the first pass is used to 
  252. accumulate bounds and do basic error checking on the appli- ation supplied 
  253. arguments, while the second pass, initiated by the spooler, will generate the 
  254. hardcopy output.  In the PM_Q_RAW format, the meta-data file created contains 
  255. raw printer data stream, and will pass through the presentation driver 
  256. unmodified on the second pass. 
  257.  
  258.  
  259. ΓòÉΓòÉΓòÉ 5.1.3. The Presentation Driver ΓòÉΓòÉΓòÉ
  260.  
  261. The OS/2 presentation driver is built around the concept of the dispatch table. 
  262. The dispatch table belongs to the OS/2 graphics engine, and is passed to the 
  263. driver at a specified point during the enable processing, so that the 
  264. presentaion driver can override the default processing of certain key rendering 
  265. routines.  The enable processing for a presentation driver is handled through a 
  266. procedure that is aliased to or called OS2_PM_DRV_ENABLE.  The following snipet 
  267. from a presentation driver module definition file's export section aliases a 
  268. procedure "Enable" to OS2_PM_DRV_ENABLE, and places it at ordinal 200 in the 
  269. driver. Placing the OS2_PM_DRV_ENABLE procedure at ordinal 200 is required. 
  270. OS2_PM_DRV_ENABLE    = Enable       @200 
  271.  
  272. The prototype for the Enable procedure, for the CSet/2(tm) compiler package, is 
  273. as follows.   ULONG _System Enable(ULONG SubFunc,ULONG Param1,ULONG Param2); 
  274.  
  275. The returned value from the enable procedure varies from subfunction to 
  276. subfunction. 
  277.  
  278. The following table lists the subfunctions. 
  279.  
  280.             Name                   Subfunction     Parameters Used
  281.     ---------------------------------------------------------------
  282.       Fill_Logical_Device              (1)             Param2
  283.       Fill_Physical_Device             (2)             Param1
  284.       Disable_Physical_Device          (4)             Param1
  285.       Enable_DC_Begin                  (5)             Param1
  286.       Disable_DC_Complete              (6)             Param1
  287.       Save_DC_State                    (7)             Param1
  288.       Restore_DC_State                 (8)              both
  289.       Reset_DC_State                   (9)              both
  290.       Enable_DC_Complete               (10)             both
  291.       Disable_DC_Begin                 (11)             both
  292.  
  293. The handling of each of these subfunctions is covered in some detail in the 
  294. OS/2 Toolkit white books ( specifically the I/O Subsystem Presentation Driver 
  295. reference), available from IBM, therefore only a brief summary of each will be 
  296. provided here. 
  297.  
  298.   Fill_Logical_Device
  299.   ---------------------
  300.    This ENABLE subfunction is called when the driver is loaded.  Its
  301.    purpose is to initialize the logical device block, which includes
  302.    setting some bits telling the engine whether the PD needs a physical
  303.    device block (PDEV) per DC and to hook the PMGRE entry points that
  304.    the PD wishes/needs to support.
  305.  
  306.  
  307.   Fill_Physical_Device
  308.  ----------------------
  309.    This ENABLE subfunction is called each time a DevOpenDC is requested
  310.    by the application.  The purpose of this function is to create and
  311.    allocate a physical device block, which should contain any information
  312.    needed by the PD to perform I/O to the specified device.  Typically
  313.    these are things such as a Presentation Manager Anchor Block (HAB),
  314.    a "private" memory heap, and a copy of the DEVOPENDATA, passed in
  315.    to Enable() for this subfunction via Param1.
  316.  
  317.  
  318.   Disable_Physical_Device
  319.  -------------------------
  320.    This ENABLE subfunction is called when a physical device block
  321.    is to be deleted.  The major purpose of this function is to release
  322.    any memory allocated in Fill_Physical_Device, and to close the
  323.    pathway to the device.
  324.  
  325.  
  326.   Enable_DC_Begin
  327.   -----------------
  328.    This ENABLE subfunction is called each time a DC is created.  It is
  329.    also the first time the PD is presented the DC handle and is the time
  330.    to allocate the internal DeviceContext.  We are also presented with
  331.    the corresponding physical device pointer returned from the
  332.    Fill_Physical_Device subfunction.
  333.  
  334.  
  335.   Disable_DC_Complete
  336.   ---------------------
  337.    This ENABLE subfunction is called when a DC has completed the disable
  338.    process.  The purpose of this function is to release any memory
  339.    allocated for the PD's internal device context, often referred
  340.    to as the "cookie."
  341.  
  342.  
  343.   Save_DC_State
  344.   ---------------
  345.    This Enable subfunction is called when the engine wishes a device
  346.    driver to save all DC information.  This may be called multiple times
  347.    and the driver should push the DDC in LIFO order.
  348.  
  349.   Restore_DC_State
  350.   ------------------
  351.    This ENABLE subfunction is called when the engine wishes the PD to
  352.    POP a saved DC, or POP to a specified DC.  The following table shows
  353.    the action to be taken based on the contents of Param2.
  354.  
  355.       Param2   Action
  356.       --------   -------------------------------------------------
  357.        = 0     then error.
  358.        > 0     it specifies which state to restore, and the
  359.             others are lost, (if Number is 2, then the second
  360.             DC is restored, the first DC is saved, and all
  361.             others are lost.
  362.        < 0     it specifies how many states to pop back.  If the
  363.             value is -1, then pop back to the first state
  364.  
  365.                  (fig. 3)
  366.  
  367.   Reset_DC_State
  368.   ----------------
  369.    This Enable subfunction is called to reset a the current DC, returning
  370.    it to is initialization state.
  371.  
  372.  
  373.   Enable_DC_Complete
  374.   --------------------
  375.    This call informs the PD that the DC has been created by the Engine.
  376.    Also it is the first time the PD receives the magic cookie returned
  377.    from the Enable_DC_Begin subfunction.  At this time, the PD should
  378.    open the spooler if the current DC was openned OD_QUEUED.  Also any
  379.    metafiles or journaling is started here.
  380.  
  381.    Journaling is used primarily by raster devices that produce output
  382.    through "banding." Even a low resolution printer, with 80 by 120
  383.    dots per inch would require a substantial amount of memory to
  384.    represent an entire page of output in memory.  By journaling, or
  385.    recording the calls into the driver, the driver can break the
  386.    physical page into smaller sections, replaying the journal file
  387.    for each section.  Journaling was very useful in the 1.X releases
  388.    of OS/2 to handle segmentation induced limitations.  However, in
  389.    Version 2.0+, it can be used to prevent a presentation driver from
  390.    consuming a large amount of physical memory, which may lead to
  391.    thrashing.
  392.  
  393.  
  394.   Disable_DC_Begin
  395.   ------------------
  396.    This ENABLE subfunction is called before a the graphics engine
  397.    begins disable processing.  This allows the PD to do any final
  398.    clean up of resources prior to the full disable.  This is the
  399.    point at which the PD ceases journaling, closes the spooler, etc.
  400.  
  401.  
  402. ΓòÉΓòÉΓòÉ 5.1.4. Producing Hardcopy Output ΓòÉΓòÉΓòÉ
  403.  
  404. Once all ENABLE processing has been completed, the presentation driver is ready 
  405. to begin producing output.  In the handling of the Fill_Logical_Device Enable() 
  406. subfunction, the PD hooks out a copy of the the PMGRE dispatch table.  The 
  407. heart of the OS/2 graphics engine is a dispatch routine that is used to access 
  408. presentation drivers and internal PMGRE entry points via vectors. The dispatch 
  409. table is an array of 32bit entries, each representing the addresses of entry 
  410. points into the graphics engine.  Logically, the presentation driver perform 
  411. one of four actions on a given entry in the dispatch table. 
  412.  
  413.  1. Ignore it. 
  414.  
  415.  2. Copy it. 
  416.  
  417.  3. Hook it (replace it.) 
  418.  
  419.  4. Swap it (save and replace it.) 
  420.  The following macros are taken from my presentation driver template source 
  421. code, and provide implementations of options two through four. 
  422.  
  423.    #define COPY(Fun)                                                \
  424.        da##Fun = (PFNL)*(pDispatchTable + (NGre##Fun & 0xffL));
  425.  
  426.    #define HOOK(Fun)                                                 \
  427.        *(pDispatchTable + (NGre##Fun & 0xffL)) = (ULONG)Fun;
  428.  
  429.    #define SWAP(Fun)                                                 \
  430.        da##Fun = (PFNL)*(pDispatchTable + (NGre##Fun & 0xffL));      \
  431.        *(pDispatchTable + (NGre##Fun & 0xffL)) = (ULONG)Fun;
  432.  
  433. Note that the above macros assume that the presentation driver source code 
  434. provides local storage for SWAP'd and COPY'd vectors in local address space. 
  435. In my presentation driver template source code, this storage is provided by 
  436. variables with the prefix "da" . 
  437.  
  438. The following are functions which a driver does NOT need to hook, but should 
  439. save the PMGRE vector.  By calling the vector directly, the overhead of the 
  440. PMGRE dispatch mechanism is avoided.  These entry points are COPY'd . 
  441.  
  442.    COPY( Convert );                    COPY( SelectClipPath );
  443.    COPY( ConvertWithMatrix);
  444.  
  445. The following entry points should be HOOK'd by the PD and processed completely. 
  446. None of these involve PMGRE call-back, ie, your presentation driver must do all 
  447. the work. 
  448.  
  449.   HOOK( AccumulateBounds );      HOOK( ImageData );
  450.   HOOK( Bitblt );           HOOK( LockDevice );
  451.   HOOK( CreateLogColorTable );     HOOK( PolyScanline );
  452.   HOOK( DeviceCreateBitmap );     HOOK( PolyShortLine );
  453.   HOOK( DeviceDeleteBitmap );     HOOK( QueryColorData );
  454.   HOOK( DeviceGetAttributes );     HOOK( QueryColorIndex );
  455.   HOOK( DeviceQueryFonts );      HOOK( QueryDeviceBitmaps );
  456.   HOOK( DeviceSelectBitmap );     HOOK( QueryDeviceCaps );
  457.   HOOK( DeviceSetAttributes );     HOOK( QueryHardcopyCaps );
  458.   HOOK( DeviceSetDCOrigin );      HOOK( QueryLogColorTable );
  459.   HOOK( DeviceSetGlobalAttribute );  HOOK( QueryNearestColor );
  460.   HOOK( DrawLinesInPath );       HOOK( QueryRealColors );
  461.   HOOK( ErasePS );           HOOK( QueryRGBColor );
  462.   HOOK( Escape );           HOOK( RealizeColorTable );
  463.   HOOK( GetBitmapBits );        HOOK( RealizeFont );
  464.   HOOK( GetBoundsData );        HOOK( ResetBounds );
  465.   HOOK( GetCodePage );         HOOK( SetBitmapBits );
  466.   HOOK( GetCurrentPosition );     HOOK( SetCodePage );
  467.   HOOK( GetDCOrigin );         HOOK( SetLineOrigin );
  468.   HOOK( GetLineOrigin );        HOOK( SetPel );
  469.   HOOK( GetPairKerningTable );     HOOK( SetStyleRatio );
  470.   HOOK( GetPel );           HOOK( UnlockDevice );
  471.   HOOK( GetStyleRatio );        HOOK( UnrealizeColorTable );
  472.  
  473. The following entry PMGRE entry points should also be supported by the 
  474. presentation driver, but may also be safely passed back to the PMGRE supplied 
  475. entry points if the processing involves complex clipping, non-device fonts, 
  476. etc. These entry points may be safely SWAP'd by a printer presentation driver. 
  477.  
  478.   SWAP( Arc );             SWAP( FullArcBoundary );
  479.   SWAP( BeginArea );          SWAP( FullArcInterior );
  480.   SWAP( BoxBoth );           SWAP( NotifyClipChange );
  481.   SWAP( BoxBoundary );         SWAP( NotifyTransformChange );
  482.   SWAP( BoxInterior );         SWAP( PartialArc );
  483.   SWAP( CharString );         SWAP( PolyLine );
  484.   SWAP( CharStringPos );        SWAP( PolyMarker );
  485.   SWAP( DeviceQueryFontAttributes );  SWAP( QueryCharPositions );
  486.   SWAP( EndArea );           SWAP( QueryTextBox );
  487.   SWAP( FillPath );          SWAP( QueryWidthTable );
  488.   SWAP( FullArcBoth );         SWAP( SetArcParameters );
  489.   SWAP( SetCurrentPosition );
  490.  
  491. The internals of each of the PD supplied entry points is dependent on the 
  492. physical device type (printer or display, raster or vector), the algorithms and 
  493. data structures chosen, and of course the developer. It is therefore not within 
  494. the scope of this article to discuss them further.  However, each of the 
  495. external entry points to the presentation driver, and for that matter, the 
  496. PMGRE have a common parameter, that is worth discussing.  The last parameter to 
  497. all entry points is a 32 bit unsigned value which contains the entry point id 
  498. in the low 16 bits and a set of flags in the high order 16 bits.  These bits 
  499. should be checked at the beginning of each entry point procedure, as they 
  500. contain additional control information governing exactly what the PD should do. 
  501. For example, on the first pass through the driver, the COM_DRAW bit will be 
  502. clear, while the COM_BOUND bit will be set.  In this case, the PD need only 
  503. accumulate bounds for the operation, and need not produce any physical output. 
  504. Other bits of interest are used to indicate whether or not the driver is 
  505. currently within a path or area bracket. 
  506.  
  507.  
  508. ΓòÉΓòÉΓòÉ 5.1.5. A Few Final Notes ΓòÉΓòÉΓòÉ
  509.  
  510. For a display PD, there are several additional entry points, related to the 
  511. support of mouse and text cursors.  Under V2.0+ of OS/2, the display driver 
  512. also has the additional overhead of the Virtual Device Driver necessary to 
  513. support the multiple virtual DOS machines (MVDMs) and WINOS2.  Overall, display 
  514. drivers are more difficult to write than printer drivers due to performance 
  515. considerations.  If you are interested in creating a display Presentation 
  516. Driver, I would highly recommend contacting IBM through the Developers 
  517. Assistance Program (the local IBM branch office should be able to help.) 
  518.  
  519. OS/2 Presentation Drivers are not overly complex, neither are they simple.  To 
  520. successfully create a PD requires careful thought and design as well as a 
  521. strong knowledge of computer graphics in general.  The use of modular and 
  522. structured programming techniques, along with object oriented concepts (one 
  523. need not use an object language to write object oriented code) will make the 
  524. development cycle far less frustating, and much more rewarding.  Remember that 
  525. a little extra effort in the design phase can save a great deal of recoding in 
  526. the testing phase. 
  527.  
  528.  
  529. ΓòÉΓòÉΓòÉ 5.2. Road Map for the WorkPlace Shell ΓòÉΓòÉΓòÉ
  530.  
  531.                                     Road Map
  532.                                      to the
  533.                                 Work Place Shell
  534.  
  535.                                  David Campbell
  536.                           (campbell@campbell.saic.com)
  537.  
  538.  
  539. ΓòÉΓòÉΓòÉ 5.2.1. The Work Place Shell ΓòÉΓòÉΓòÉ
  540.  
  541. The Work Place Shell (WPS) is the new user interface introduced with OS/2 2.0. 
  542. This article will begin a series of articles dedicated to explaining the 
  543. operation and use of the WPS.  In preparing this initial article, I was 
  544. overwhelmed by the amount of material I would have liked to include.  There are 
  545. so many aspects of the WPS to discuss. For example, Installation and 
  546. Maintenance, Programming new WPS Classes, and future enhancements. 
  547.  
  548. Since the amount of material is so large, I will divide the topic into 2 or 3 
  549. articles.  The first article will discuss Installation and Maintenance.  The 
  550. second article will discuss programming issues facing developers who wish to 
  551. write classes for the WPS.  The third article will discuss future enhancements 
  552. to the WPS. With the first article I will attempt to cover the following 
  553. topics: 
  554.  
  555. o Evolution of the User Interface 
  556. o Configuration and Maintenance of the WPS 
  557. o Suggestions for Implementing the WPS 
  558.  
  559.  
  560. ΓòÉΓòÉΓòÉ 5.2.2. Evolution of the User Interface ΓòÉΓòÉΓòÉ
  561.  
  562. To better understand the WPS, it is necessary to be aware of the evolution of 
  563. the user interface.  The user interface has a very simple purpose, and that is 
  564. to translate human initiated actions into computer operations and communicate 
  565. these operations to the operating system.  Traditionally their have been two 
  566. different types of user interfaces. These include: 
  567.  
  568.  1. Character User Interface (CUI) 
  569.  2. Graphical User Interface (GUI) 
  570.  
  571. Early user interfaces were CUI.  That is they could only display the characters 
  572. defined in the ASCII set.  Examples of this type of interface are the command 
  573. line interfaces provided with DOS 3.3 and early implementations of UNIX and 
  574. VMS.  This was limiting, but it was the only choice primarily because of 2 
  575. hardware constraints.  Early CPUs did not have the processing power to manage a 
  576. GUI.  Also, the video controllers and monitors were unable to display the high 
  577. resolution necessary to implement a GUI. 
  578.  
  579. The CUI evolved into the GUI.  The GUI provided vastly superior capabilities. 
  580. It was now possible to display on the screen different fonts, images, and other 
  581. graphical data.  This type of interface required more CPU processing power, but 
  582. the power of CPUs was increasing and the cost of CPUs was decreasing, so it was 
  583. an acceptable penalty.  With most current GUIs, emphasis is still on the 
  584. 'application'.  That is to say, the user interacts with applications.  These 
  585. applications in turn interact with the data that the user requests. 
  586.  
  587. The philosophy of this type of system is somewhat backward.  The user should 
  588. interface directly with the data, and the application should be evoked 
  589. implicitly to act upon the data. This approach to user-data interaction lead to 
  590. the development of object oriented user interfaces. The WPS is IBM's attempt at 
  591. an object oriented user interface (OOUI). With the graphical interface, it 
  592. became possible to represent 'objects' using icons.  IBM has stated that that 
  593. the current WPS is the template of future OOUIs.  These future OOUIs will be 
  594. grafted onto AIX and future operating systems.  Therefore, the importance of 
  595. becoming proficient at using this type of interface is obvious. 
  596.  
  597. To summarize: 
  598.  
  599. o To implement an OOUI effectively, the video subsystem must be operating in 
  600.   graphical mode.  This is necessary to display detailed images which represent 
  601.   objects. 
  602.  
  603. o An OOUI places emphasis on the data or object, whereas current CUIs and GUIs 
  604.   place emphasis on the application. 
  605.  
  606.  
  607. ΓòÉΓòÉΓòÉ 5.2.3. Configuration and Maintenance of the WPS ΓòÉΓòÉΓòÉ
  608.  
  609. The user interface used by OS/2 is loaded by the base operating system at 
  610. system startup.  OS/2 can be configured to load any type of user interface.  By 
  611. default, the OS/2 installation procedure specifies the WPS as the user 
  612. interface.  In the config.sys file, there is a line which reads: 
  613.  
  614. PROTSHELL=c:\os2\pmshell.exe 
  615.  
  616. This line specifies the protected mode user interface.  If you want to load 
  617. another user interface, this is where you would place the new execution file. 
  618.  
  619. Interestingly, another environment variable affects the WPS.  This environment 
  620. variable is 'RUNWORKPLACE'.  This environment variable is also used to specify 
  621. the shell. 
  622.  
  623. The WPS is composed of objects.  An object is an instance of a class.  Each 
  624. object is represented by an icon.  Each class has its own methods, data, and 
  625. class icon.  The context menu of each class is different.  The settings 
  626. notebook of each class is different.  Interacting with objects has several 
  627. advantages.  The operating system and user interface only have to be familiar 
  628. with the classes.  Each external device is represented as an object.  For 
  629. example, the keyboard, the mouse, the storage devices, and the printers.  Each 
  630. file and directory on storage devices are also represented by objects. 
  631. Finally, each process and print job is represented by an object.  In this way, 
  632. the operating system has a common interface to everything it interacts with, 
  633. because everything it interacts with is an object.  The WPS is written using 
  634. the System Object Model (SOM).  SOM is written to help developers create 
  635. classes more quickly, and from different languages.  SOM forms a class 
  636. hierarchy which is shown below. 
  637.  
  638.  SOMObject
  639.  Γö£ΓöÇ SOMClass
  640.  Γö£ΓöÇ SOMClassMgr
  641.  ΓööΓöÇ WPObject
  642.     Γö£ΓöÇ WPAbstract
  643.     Γöé  Γö£ΓöÇ WPPrinter
  644.     Γöé  Γö£ΓöÇ WPProgram
  645.     Γöé  ΓööΓöÇ WPShadow
  646.     Γö£ΓöÇ WPFileSystem
  647.     Γöé  Γö£ΓöÇ WPDataFile
  648.     Γöé  Γöé  ΓööΓöÇ WPProgramFile
  649.     Γöé  ΓööΓöÇ WPFolder
  650.     Γöé     Γö£ΓöÇ WPDesktop
  651.     Γöé     Γö£ΓöÇ WPDrives
  652.     Γöé     ΓööΓöÇ WPStartup
  653.     ΓööΓöÇ WPTransient
  654.        Γö£ΓöÇ WPJob
  655.        ΓööΓöÇ WPPort
  656.  
  657. Note:  Please note that the above class hierarchy is not complete.  I have only 
  658.        included the most significant classes.  Also note the class 
  659.        WPProgramFile. This class is actually derived from WPDataFile NOT 
  660.        WPFileSystem as most of the IBM documentation states. 
  661.  
  662. Due to the length of this article I will only discuss the classes which begin 
  663. with WPObject.  WPObject is the parent class of the 3 base classes:WPAbstract, 
  664. WPFileSystem, and WPTransient.  These 3 classes represent the foundation 
  665. classes.  Each base class is differentiated from the other base classes in the 
  666. way in which the class data is maintained.  Classes derived from WPAbstract 
  667. classes store their instance data in the os2.ini file.  Classes derived from 
  668. WPFileSystem classes store their instance data as a file or directory on 
  669. permanent storage.  Classes derived from WPTransient classes do not store their 
  670. instance data, therefore all WPTransient instance data is not maintained if the 
  671. computer is restarted.  This differentiation in storage types classifies 
  672. WPAbstract and WPFileSystem as persistent, and WPTransient as being 
  673. non-persistent. 
  674.  
  675. Each of these classes has some very common implementations.  For example, the 
  676. typical user creates a folder which contains programs and files. This 
  677. implementation utilizes 3 WPS classes, WPProgram, WPDataFile and WPFolder.  The 
  678. folder used is an instance of WPFolder.  An instance of WPFolder represents a 
  679. directory on a diskette or hardrive.  A folder is used as a container for other 
  680. objects.  Each file in the folder is an instance of WPDataFile.  An instance of 
  681. WPDataFile represents a physical file which exists either on a diskette or a 
  682. hardrive. Each of the programs within the folder is an instance of WPProgram. 
  683. Notice this is not the same class as WPProgramFile.  Notice also that you do 
  684. NOT want to copy the execution file into the folder.  An instance of WPProgram 
  685. is a reference to an execution file.  This reference is used to specify the 
  686. name of the execution file to execute, any parameters, the startup directory, 
  687. and other information.  An instance of this class typically uses the ICON 
  688. resource within the execution file. 
  689.  
  690. To compare and contrast these 3 classes, we enumerate the settings notebook of 
  691. each class.  Notice the differences in the settings notebook for each class. 
  692.  
  693. An instance of WPProgram has 5 pages in its settings notebook. 
  694.  
  695.  1. Program 
  696.  2. Session 
  697.  3. Association 
  698.  4. Window 
  699.  5. General 
  700.  
  701. An instance of WPDataFile has 4 pages in its settings notebook. 
  702.  
  703.  1. Type 
  704.  2. Menu 
  705.  3. File 
  706.  4. General 
  707.  
  708. An instance of WPFolder has 8 pages in its settings notebook. 
  709.  
  710.  1. View 
  711.  2. Include 
  712.  3. Sort 
  713.  4. Background 
  714.  5. Menu 
  715.  6. File 
  716.  7. Window 
  717.  8. General 
  718.  
  719. Maintaining the WPS is critical to satisfactory use of the user interface. Due 
  720. to the power and flexibility of the WPS, maintenance can be a somewhat 
  721. difficult task.  Fortunately, their are ways to effectively maintain the WPS to 
  722. provide outstanding performance and usability.  The number 1 thing is to keep 
  723. your os2.ini and os2sys.ini files backed up.  This can be done using 'RUN' 
  724. commands in the config.sys file.  You can use a 'RUN=C:\OS2\XCOPY OS2.INI 
  725. C:\TEMP\OS2.INI' this will copy the os2.ini file before the WPS starts.  Once 
  726. the WPS starts, it opens the os2.ini file.  The os2.ini file remains open until 
  727. the system is shutdown.  Therefore, it is impossible to make copies of the 
  728. os2.ini file while the WPS is executing. 
  729.  
  730. Always properly shutdown the system.  The WPS maintains several open files. 
  731. Unexpectedly turning the computer off can have disastrous effects on the system 
  732. configuration files. 
  733.  
  734. The next item of maintenance is concerned with maintaining the active class 
  735. list, file types, file associations, and object handles.  I have written 
  736. several utilities in REXX to perform these functions.  Information on these 
  737. utilities is presented in the next section. 
  738.  
  739.  
  740. ΓòÉΓòÉΓòÉ 5.2.4. Suggestions for Implementing the WPS ΓòÉΓòÉΓòÉ
  741.  
  742. Learning to effectively use the WPS can be difficult.  Upon initial 
  743. introduction, the casual user tends to configure the WPS similar to the 
  744. Microsoft Windows environment.  For example, a software developer starts an 
  745. editor to edit a source file.  The user then saves the file and exits the 
  746. editor.  He then evokes the compiler to convert the source code to an 
  747. executable file.  He then runs the execution file. 
  748.  
  749. Here is an alternative.  Create a folder on your desktop titled source. Within 
  750. this folder create several data files representing your source code. For each 
  751. of the data files, set the TYPE to either C code or REXX or whatever.  Then 
  752. create a program object (WPProgram) for the compiler.  On the associations page 
  753. of the program object, select C code or REXX.  Now when you select the open 
  754. menu of the data file, you should see an editor and a compiler.  Now, to open 
  755. the file to be edited, select the editor from the open menu.  To open the file 
  756. to be compiled, select the compiler from the open menu.  In this way, you are 
  757. interacting more with the data than with the application.  For example, the 
  758. data file can have 2 actions performed on it, edit and compile.  We have 
  759. created an open menu which has these options! 
  760.  
  761. I have created a set of REXX utilities for managing some of the aspects of the 
  762. WPS.  These utilities allow you to add a type, delete a type, enumerate all of 
  763. the existing types, add an association, delete an association, enumerate all of 
  764. the existing associations, get EA information, and set EA information.  Several 
  765. other utilities are needed, but I have not had the time to develop them.  All 
  766. of these utilities can be obtained via anonymous FTP from my machine: 
  767.  
  768. campbell.saic.com      139.121.81.146 
  769.  
  770. Please feel free to contact me if you have questions concerning this article or 
  771. the WPS.  I believe the WPS to be one of the most significant parts of OS/2 
  772. 2.x.  I also believe that without better understanding and knowledge of the 
  773. WPS, OS/2 2.x's future is somewhat in doubt.  It would be a shame for such a 
  774. wonderful system to go unnoticed.  Let's not let that happen! 
  775.  
  776.  
  777. ΓòÉΓòÉΓòÉ 5.3. Beginning Client/Server Programming:Named Pipes. ΓòÉΓòÉΓòÉ
  778.  
  779.                              Beginning Client/Server
  780.                                   Programming:
  781.                                    Named Pipes
  782.  
  783.                                    Steve Lacy
  784.                               (sl31@andrew.cmu.edu)
  785.  
  786.  
  787. ΓòÉΓòÉΓòÉ 5.3.1. Beginning Client/Server Programming: Named Pipes ΓòÉΓòÉΓòÉ
  788.  
  789.  This article is an introduction to client server applications, and thier 
  790. interface in OS/2. Throughout this article, click on "Forward" to read the next 
  791. paragraph.  I've assumed that you've already browsed through the supplied 
  792. source files, "client.c" and "server.c" just so you can see generally whats 
  793. going on in this program, even though you might not know what the actual 
  794. function calls do, you can look at the names (like DosCreateNPipe) and see just 
  795. what they do, in this case, creating a named pipe. 
  796.  
  797.  
  798. ΓòÉΓòÉΓòÉ 5.3.2. Introduction ΓòÉΓòÉΓòÉ
  799.  
  800. One of the current hip words used when talking about OS/2 is "client/server" 
  801. Well, as a programmer, I know I've wondered myself, "what exactly is a 
  802. Client/Server program?"  Well, from my experience, its not a black and white 
  803. definiton, since some people seem to define one style to be client/server 
  804. whereas other poeple wouldn't consider that particular example to be a 
  805. client/server application. 
  806.  
  807. One thing to keep in mind is that the design of client/server applications is 
  808. usually more work than the programming of them. Designing a multithreaded 
  809. program which uses named pipes and semaphores elegantly is quite a task, we'll 
  810. be getting into those topics in later articles. 
  811.  
  812. For our little example, we're going to be developing a "reverse this string" 
  813. client server application.  Basically, we'll have a server that accepts 
  814. connections on a named pipe, reads in a string, and spits that string back out 
  815. onto the pipe, reversed.  Note that in our example we're not using a 
  816. multithreaded server.  This means that if two clients are trying to connect to 
  817. the same pipe at the same time, that only one of them will get through, and 
  818. that the other will have to wait for the other to finish, until it can 
  819. continue. 
  820.  
  821. One thing is known for sure, that client server applications use the following 
  822. OS/2 kernel functions: Named Pipes, Threads/Processes, Semaphores, and Shared 
  823. Memory.  Here's a brief description of four mechanisms 
  824.  
  825. In this article, I don't attempt to define client/server applications, but I do 
  826. show you the basics as to what does define a "typical" client/server program, 
  827. if such a beast does exist. 
  828.  
  829.  
  830. ΓòÉΓòÉΓòÉ 5.3.2.1. Named Pipes ΓòÉΓòÉΓòÉ
  831.  
  832. A named pipe is a mechanism for controlling inter-process (or inter-thread) 
  833. communication under OS/2 2.0.  Named pipes share a lot of the characteristics 
  834. of UNIX sockets, but in my opinion, their programming interface is a lot more 
  835. user friendly, and it doesn't have as much programming overhead as UNIX sockets 
  836. do. 
  837.  
  838. In this article, we're going to dulge into the basics of named pipes, and the 
  839. OS/2 functions used to program a named pipe application. 
  840.  
  841.  
  842. ΓòÉΓòÉΓòÉ 5.3.2.2. Threads/Processes ΓòÉΓòÉΓòÉ
  843.  
  844. A standard OS/2 program, something that you would write with the standard 
  845. library of C functions, has what you call a "single thread of execution."  A 
  846. multithreaded program, as you might think, has multiple threads of execution. 
  847. Basically, this means that your program is running in two different places at 
  848. the same time.  Starting a thread has very little overhead, since all the code 
  849. for you program is already in memory.  The overhead in starting a thread has 
  850. been compared to the overhead for calling a standard C function.  Your program, 
  851. or some thread of your program, can start up another thread by issuing a 
  852. DosCreateThread call. 
  853.  
  854. Processes are basically equivalent to "programs" One program can start up 
  855. another program by issuing a DosStartSession call.  Remember that there is some 
  856. significant overhead in loading and starting another program, even if its 
  857. another copy of the program that is currently executing.  When processes are 
  858. started, they always start with the main() function of your code. 
  859.  
  860.  
  861. ΓòÉΓòÉΓòÉ 5.3.2.3. Semaphores ΓòÉΓòÉΓòÉ
  862.  
  863. A semaphore is an inter-process or inter-thread signaling mechanism.  There are 
  864. three types of semaphores in OS/2, you don't particularly need to know about 
  865. them now, so I won't go into the details.  The way semaphores work is that you 
  866. can either post or wait on an open semaphore.  When you're waiting, you stop 
  867. waiting until someone else posts.  The neat thing is that it can be anyone 
  868. whatsoever, in any process, in any thread. 
  869.  
  870.  
  871. ΓòÉΓòÉΓòÉ 5.3.3. Design of the applications ΓòÉΓòÉΓòÉ
  872.  
  873.  This section deals with the design of our application, and gives an 
  874. introduction to the Named pipe OS/2 services that we'll be using in our 
  875. application.  We have to keep in mind that dealing with named pipes is in fact 
  876. a lot like dealing with standard files, that is, files on disk.  For example, 
  877. the client end of our program uses only the DosOpen , DosWrite , DosRead , and 
  878. DosClose system calls, the exact same calls that would be used if we were 
  879. writing to a file.  We distinguish a named pipe from a file by the name that we 
  880. send to the system calls.  The name for pipes must be of the form "\pipe\*" 
  881. Generally, its a good idea to try to pick pipe names that you don't think other 
  882. people will be using too, since this would create problems.  If you're really 
  883. paranoid, you'll do something like include the process number (which is 
  884. essentially unique) in the pipe name, so you know that no one else could 
  885. possibly have the same name as you.  For our example, that would be just a 
  886. little bit of overkill, so we're just going to be using the name 
  887. "\pipe\reverse\npipe" I find it a good idea to choose pipe names so that 
  888. they're of the form "\pipe\application-name\pipe-name" 
  889.  
  890.  
  891. ΓòÉΓòÉΓòÉ 5.3.3.1. Pipe Semantics. ΓòÉΓòÉΓòÉ
  892.  
  893. If we're going to have interprocess communication, we have to design our 
  894. application so that the communication takes place properly, that all the data 
  895. is transmitted, and that there are "clean" open and close operations.  Browsing 
  896. through the documentation, the most obvious looking function calls are: 
  897. DosCreateNPipe , DosConnectNPipe , and DosDisConnectNPipe. So, we have 
  898. functions to create pipes, "connect" to them (whatever that may mean) and to 
  899. disconnect from them.  Thinking of pipes as files, the create routine creates 
  900. the pipe, the connect waits for the client to connect to the pipe that we've 
  901. just connected.  When the entire process has finished, we disconnect from the 
  902. pipe, and we can either end our application, or continue with the connect 
  903. cycle. 
  904.  
  905. ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
  906. ΓöéServer             Client                 Pipe status       Γöé
  907. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  908. Γöé...                ...                    Nonexistant       Γöé
  909. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  910. ΓöéDosCreateNPipe     ...                    Created           Γöé
  911. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  912. ΓöéDosConnectNPipe    ...                    Blocking for      Γöé
  913. Γöé                                          connections       Γöé
  914. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  915. Γöé...                DosOpen                Opened            Γöé
  916. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  917. ΓöéDosRead            DosWrite               Open              Γöé
  918. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  919. ΓöéDosWrite           DosRead                Open              Γöé
  920. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  921. Γöé...                DosClose               Closing           Γöé
  922. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  923. Γöé...                ...                    Closed            Γöé
  924. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  925. ΓöéDosDisConnectNPipe ...                    Dosconnected (sameΓöé
  926. Γöé                                          as created state) Γöé
  927. ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
  928.  
  929.  
  930. ΓòÉΓòÉΓòÉ 5.3.3.2. Server implementation ΓòÉΓòÉΓòÉ
  931.  
  932. So, thats basically an outline of what our programs should do, but we still 
  933. need a few little modifications.  The main one being that we have to make sure 
  934. that the DosDisconnectNPipe happens after the DosClose happens on the client 
  935. side.  The simples way to synchronize this it to add andother DosRead on the 
  936. server side.  What will happen is that the DosRead will fail with an End Of 
  937. File (EOF) error, then we know that the pipe has defineately closed, and we can 
  938. then do our DosDisConnectNPipe. The way that we know that DosRead has returned 
  939. an end of file error is when it returns zero in the ulBytes field, which 
  940. usually contains the number of bytes that we've read from the pipe.  So, 
  941. generally the code for our server looks like this: 
  942.  
  943.     DosCreateNPipe(...);
  944.     while (1) {
  945.         DosConnectNPipe(...);
  946.     /* Read the input data */
  947.         DosRead(...);
  948.     /* This is where we figure out what the output will be */
  949.         DosWrite(...);
  950.     /* Now we're reading for an end of file. */
  951.         DosRead(...);
  952.     /* If we're not at the end of the file that is, number of bytes read
  953.        isn't zero, then something has happened */
  954.         if (ulBytes!=0) error();
  955.         rc=DosDisConnectNPipe(...);
  956.     }
  957. Other than the parameters for the functions, thats the program.  The reason 
  958. that we have a while(1) in our program is that after one client has finished, 
  959. we want the server to reset and be able to accept more connections from other 
  960. clients. 
  961.  
  962.  
  963. ΓòÉΓòÉΓòÉ 5.3.3.3. Client Implementation. ΓòÉΓòÉΓòÉ
  964.  
  965.  The client, as mentioned before, reads and writes to the created pipe -- 
  966. teating it as a file, not as a pipe.  So, we open the file, write to it, read 
  967. from it, and close it.  Here's a brief version of the code: 
  968.  
  969.     for (i=1;i<argc;i++) {
  970.         DosWaitNPipe(...);
  971.         DosOpen(...);
  972.         DosWrite(...);
  973.         DosRead(...);
  974.         DosClose(...);
  975.     }
  976. The only strange bit at this point should be the DosWaitNPipe call.  This is 
  977. OS/2 solution to a simple problem: What to do if someone else is already using 
  978. the pipe?  Well, you have to wait until the pipe frees up, that is, wait until 
  979. the server has issued a DosCreateNPipe call. 
  980.  
  981.  
  982. ΓòÉΓòÉΓòÉ 5.3.4. Conclusion ΓòÉΓòÉΓòÉ
  983.  
  984.  Well, from this you should be able to write a simple client/server program, 
  985. basically by using the code supplied here along with the reference information 
  986. in the next session.  The OS/2 calls, although they may have long names, large 
  987. numbers of arguments, or something else that you might find initially 
  988. discouraging, they're actually quite easy to use. Just keep plugging, and have 
  989. fun!  Next time, we'll make the server actually do something, and we'll 
  990. introduce semaphores. 
  991.  
  992.  
  993. ΓòÉΓòÉΓòÉ 5.3.5. Reference Section ΓòÉΓòÉΓòÉ
  994.  
  995.  This is the reference section for this article.  You should note that pipe 
  996. handles and file handles are interchangable, so the file handlre returned by 
  997. DosOpen can be passed to DosWaitNPipe, which requires a pipe handle as its 
  998. argument. 
  999.  
  1000.  
  1001. ΓòÉΓòÉΓòÉ 5.3.5.1. DosOpen ΓòÉΓòÉΓòÉ
  1002.  
  1003. PSZ pszFileName;
  1004. PHFILE ppshfFileHandle;
  1005. PULONG pActionTaken;
  1006. ULONG ulFileSize,ulFileAttribute,ulOpenFlag,ulOpenMode;
  1007. PEAOP2 pEABuf;
  1008. APIRET rc;
  1009.  
  1010. rc=DosOpen(pszFileName,
  1011.            ppshfFileHandle,
  1012.            ulFileSize,
  1013.            ulFileAttribute,
  1014.            ulOpenFlag,
  1015.            ulOpenMode);
  1016. pszFileName is the name of the file that we're opening. 
  1017. ppshfFileHandle is the place where the opened file's file handle ps placed. 
  1018. pActionTaken is the plce where the action taken value is placed. It is one if 
  1019. the following: 
  1020.  
  1021. FILE_EXISTED
  1022. FILE_CREATED
  1023. FILE_TRUNCATED
  1024. ulFileSize is the initial size of the file, if you're creating one. 
  1025. ulFileAttribute is one or more of the following: 
  1026.  
  1027. FILE_ARCHIVED
  1028. FILE_DIRECTORY
  1029. FILE_SYSTEM
  1030. FILE_HIDDEN
  1031. FILE_READONLY
  1032. FILE_NORMAL
  1033.  
  1034. ulOpenFlag specifies an open action.  It is one of the following: 
  1035.  
  1036. OPEN_ACTION_FAIL_IF_NEW
  1037. OPEN_ACTION_CREATE_IF_NEW
  1038. OPEN_ACTION_FAIL_IF_EXISTS
  1039. OPEN_ACTION_OPEN_IF_EXISTS
  1040. OPEN_ACTION_REPLACE_IF_EXISTS
  1041. ulOpenMode specifies the mode for opening the file.  It is one of the 
  1042. following: 
  1043.  
  1044. OPEN_FLAGS_DASD (direct open flag)
  1045. OPEN_FLAGS_WRITE_THROUGH (if set, accesses don't go through cache)
  1046. OPEN_FLAGS_FAIL_ON_ERROR
  1047. OPEN_FLAGS_NO_CACHE
  1048. OPEN_FLAGS_NO_LOCALITY         (don't know info. about locality)
  1049. OPEN_FLAGS_SEQUENTIAL          (mostly sequential access file)
  1050. OPEN_FLAGS_RANDOM              (mostly random access file)
  1051. OPEN_FLAGS_RANDOMSEQUENTIAL    (random with some locality)
  1052. OPEN_FLAGS_NOINHERIT           (children don't inherit handle access)
  1053. OPEN_SHARE_DENYREADWRITE
  1054. OPEN_SHARE_DENYWRITE
  1055. OPEN_SHARE_DENYREAD
  1056. OPEN_SHARE_DENYNONE
  1057. OPEN_ACCESS_READONLY
  1058. OPEN_ACCESS_WRITEONLY
  1059. OPEN_ACCESS_READWRITE
  1060. pEABuf Is a pointer to Extended Attribute information. 
  1061.  
  1062.  In most cases, you should specify OPEN_ACTION_OPEN_IF_EXISTS for ulOpenFlag 
  1063. and OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE for the ulOpenMode. 
  1064.  
  1065.  
  1066. ΓòÉΓòÉΓòÉ 5.3.5.2. DosClose ΓòÉΓòÉΓòÉ
  1067.  
  1068. HFILE FileHandle;
  1069. APIRET rc;
  1070.  
  1071. rc=DosClose(FileHandle);
  1072.  
  1073. Where FileHandle is the handle for the file that you want to close. 
  1074.  
  1075.  
  1076. ΓòÉΓòÉΓòÉ 5.3.5.3. DosRead ΓòÉΓòÉΓòÉ
  1077.  
  1078. HFILE FileHandle;
  1079. PVOID pBufferArea;
  1080. ULONG ulBufferLength;
  1081. PULONG pBytesRead;
  1082. APIRET rc;
  1083.  
  1084. rc=DosRead(FileHandle,pBufferArea,ulBufferLength,pBytesRead);
  1085. Where FileHandle is the handle of the open file. 
  1086. pBufferArea is the place where the data is to be read into. 
  1087. ulBufferLength is the size of pBufferArea. 
  1088. pBytesRead is the number of bytes read from the file. (on return) 
  1089.  
  1090.  
  1091. ΓòÉΓòÉΓòÉ 5.3.5.4. DosWrite ΓòÉΓòÉΓòÉ
  1092.  
  1093. HFILE FileHandle;
  1094. PVOID pBufferArea;
  1095. ULONG ulBufferLength;
  1096. PULONG pBytesRead;
  1097. APIRET rc;
  1098.  
  1099. rc=DosWrite(FileHandle,pBufferArea,ulBufferLength,pBytesRead);
  1100. Where FileHandle is the handle of the open file. 
  1101. pBufferArea is the place where the data is to be read from. 
  1102. ulBufferLength is the size of pBufferArea. 
  1103. pBytesRead is the number of bytes written to the file. (on return) 
  1104.  
  1105.  
  1106. ΓòÉΓòÉΓòÉ 5.3.5.5. DosCreateNPipe ΓòÉΓòÉΓòÉ
  1107.  
  1108. PSZ pszFileName;
  1109. PHPIPE pphpipePipeHandle;
  1110. ULONG ulOpenMode,ulPipeMode,ulOutBufSize,ulInBufSize,ulTimeOut;
  1111. APIRET rc;
  1112.  
  1113. rc=DosCreateNPipe(pszFileName,
  1114.                   pphpipePipeHandle,
  1115.                   ulOpenMode,
  1116.                   ulPipeMode,
  1117.                   ulOutBufSize,
  1118.                   ulInBufSize,
  1119.                   ulTimeOut);
  1120. Where pszFileName is the name of the pipe to be created. 
  1121. ulOpenMode is one or more of the following: 
  1122.  
  1123. NP_WRITEBEHIND
  1124. NP_NOWRITEBEHIND
  1125. NP_INHERIT
  1126. NP_NOINHERIT
  1127. NP_ACCESS_INBOUND
  1128. NP_ACCESS_OUTBOUND
  1129. NP_ACCESS_DUPLEX
  1130.  
  1131. ulPipeMode is one or more of the following: 
  1132.  
  1133. NP_WAIT
  1134. NP_NOWAIT
  1135. NP_TYPE_BYTE
  1136. NP_TYPE_MESSAGE
  1137. NP_READMODE_BYTE
  1138. NP_READMODE_MESSAGE
  1139. You should also bitwise-or this with the number of instances of the pipe that 
  1140. you would like. In our case, this will always be one. 
  1141. ulOutBufSize is the size of the outgoing buffer. 
  1142. ulInBufSize is the size of the incoming buffer. 
  1143. ulTimeOut is the default timeout value, in microseconds, that reads and writes 
  1144. will use.  Setting this to -1 gives indefinite timeout.  In other words, reads 
  1145. and writes will wait forever. 
  1146.  
  1147.  
  1148. ΓòÉΓòÉΓòÉ 5.3.5.6. DosConnectNPipe ΓòÉΓòÉΓòÉ
  1149.  
  1150. HPIPE PipeHandle;
  1151. APIRET rc;
  1152. rc=DosConnectNPipe(PipeHandle);
  1153. Where PipeHandle is the pipe handle that we want to begin accepting connections 
  1154. on. 
  1155.  
  1156.  
  1157. ΓòÉΓòÉΓòÉ 5.3.5.7. DosDisConnectNPipe ΓòÉΓòÉΓòÉ
  1158.  
  1159. HPIPE PipeHandle;
  1160. APIRET rc;
  1161. rc=DosDisConnectNPipe(PipeHandle);
  1162. Where PipeHandle is the pipe handle that we want to stop accepting connections 
  1163. on. 
  1164.  
  1165.  
  1166. ΓòÉΓòÉΓòÉ 5.3.5.8. DosWaitNPipe ΓòÉΓòÉΓòÉ
  1167.  
  1168. HPIPE PipeHandle;
  1169. APIRET rc;
  1170. rc=DosWaitNPipe(PipeHandle);
  1171. Where PipeHandle is the pipe handle that we want to wait for an available 
  1172. connection on. 
  1173.  
  1174.  
  1175. ΓòÉΓòÉΓòÉ 6. Columns ΓòÉΓòÉΓòÉ
  1176.  
  1177. o Questions and Answers 
  1178.  
  1179. o Introduction to PM 
  1180.  
  1181.  
  1182. ΓòÉΓòÉΓòÉ 6.1. Questions and Answers ΓòÉΓòÉΓòÉ
  1183.  
  1184.                                     Questions
  1185.                                        and
  1186.                                      Answers
  1187.  
  1188.                                   Larry Salomon
  1189.                                (os2man@panix.com)
  1190.  
  1191.  
  1192. ΓòÉΓòÉΓòÉ 6.1.1. Questions and Answers ΓòÉΓòÉΓòÉ
  1193.  
  1194. Welcome to this month's "Questions and Answers"!  Each month, I collect various 
  1195. questions sent to me via email and try to answer each directly; the ones that I 
  1196. feel contribute the most to developers, whether in terms of information or as a 
  1197. nifty trick to tuck into your cap, get published in this column. 
  1198.  
  1199. To submit a question, send mail to my email address - os2man@panix.com - and be 
  1200. sure to grant permission to publish your question (those that forget will not 
  1201. be considered for publication). 
  1202.  
  1203. Unfortunately, no questions were submitted this month so we will discuss some 
  1204. errors in the "Programming Reference" documentation.  The following topics will 
  1205. be covered: 
  1206.  
  1207. o Cascading Menus 
  1208. o Dropping on a Printer 
  1209. o Message Box Help 
  1210.  
  1211.  
  1212. ΓòÉΓòÉΓòÉ 6.1.1.1. Cascading Menus ΓòÉΓòÉΓòÉ
  1213.  
  1214. OS/2 2.0 added a new feature to menus, which is used extensively throughout the 
  1215. Workplace Shell.  These are called "conditional cascading menus" (I will refer 
  1216. to them as CCM's), which look similar to "pull-rights", but have the following 
  1217. differences in appearance and behavior: 
  1218.  
  1219. o CCM's have a pushbutton instead of simply a right arrow, which looks like the 
  1220.   right arrow on a scrollbar. 
  1221.  
  1222. o CCM's have a default selection.  If you only click on the submenu name, the 
  1223.   default action is taken. 
  1224.  
  1225. Unfortunately, there is no documentation discussing how to code this in your PM 
  1226. applications, with the exception of the mentioning of the menu style. 
  1227. Therefore, let us discuss this new style and introduce two new menu messages. 
  1228.  
  1229. MS_CONDITIONALCASCADE 
  1230.  
  1231. Setting this style indicates that the SUBMENU is to be a CCM. Unfortunately, 
  1232. using this in the resource file does not work. Instead, you must explicitly set 
  1233. it in your code like below: 
  1234.  
  1235.    HWND hwndMenu;
  1236.    USHORT usSubmenu;
  1237.    MENUITEM miItem;
  1238.  
  1239.    WinSendMsg(hwndMenu,
  1240.               MM_QUERYITEM,
  1241.               MPFROM2SHORT(usSubmenu,TRUE),
  1242.               MPFROMP(&miItem));
  1243.  
  1244.    ulStyle=WinQueryWindowULong(miItem.hwndSubMenu,QWL_STYLE);
  1245.  
  1246.    ulStyle|=MS_CONDITIONALCASCADE;
  1247.  
  1248.    WinSetWindowULong(miItem.hwndSubMenu,QWL_STYLE,ulStyle);
  1249.  
  1250. hwndMenu was loaded with WinLoadMenu (specifying HWND_OBJECT as the "frame 
  1251. window" in preparation for calling WinPopupMenu.  usSubmenu is the resource id 
  1252. of the SUBMENU to convert to a CCM. 
  1253.  
  1254. MM_SETDEFAULTITEMID and MM_QUERYDEFAULTITEMID 
  1255.  
  1256. These two messages are used to set and query the default item in a CCM. They 
  1257. should be sent to the SUBMENU window handle (miItem.hwndSubMenu), but I'm not 
  1258. sure if the former must  be sent to it (the latter must).  They have the 
  1259. following form: 
  1260.  
  1261. MM_SETDEFAULTITEMID 
  1262.  
  1263.    mpParm1 
  1264.  
  1265.       sDefault (SHORT) 
  1266.  
  1267.          Specifies the default menu item. 
  1268.  
  1269.       bSearchSubmenus (BOOL) 
  1270.  
  1271.          Specifies whether or not submenus should be searched or not. 
  1272.  
  1273.          TRUE      Search submenus 
  1274.          FALSE     Do not search submenus 
  1275.  
  1276.    mpParm2 (BIT32) 
  1277.  
  1278.       Reserved 
  1279.  
  1280.       NULL      Reserved value. 
  1281.  
  1282.    Returns 
  1283.  
  1284.       bSuccess (BOOL) 
  1285.  
  1286.          Success indicator 
  1287.  
  1288.          TRUE      Successful completion 
  1289.          FALSE     Error occurred 
  1290.  
  1291.    Notes 
  1292.  
  1293.    Setting the default item id does not set the menu item's attribute to 
  1294.    MIA_CHECKED.  This responsibility is left to the programmer. 
  1295.  
  1296. MM_QUERYDEFAULTITEMID 
  1297.  
  1298.    mpParm1 (BIT32) 
  1299.  
  1300.       Reserved 
  1301.  
  1302.       NULL      Reserved value. 
  1303.  
  1304.    mpParm2 (BIT32) 
  1305.  
  1306.       Reserved 
  1307.  
  1308.       NULL      Reserved value. 
  1309.  
  1310.    Returns 
  1311.  
  1312.       sDefault (SHORT) 
  1313.  
  1314.          Specifies the default item. 
  1315.  
  1316.          0         There is no default item associated with the submenu. 
  1317.          Other     Menu item id of the default item. 
  1318.  
  1319. Encapsulation 
  1320.  
  1321. All of this can be encapsulized into a single procedure.  The code for this is 
  1322. shown below: 
  1323.  
  1324. BOOL setCascadeDefault(HWND hwndMenu,USHORT usSubmenu,USHORT usDefault)
  1325. //-------------------------------------------------------------------------
  1326. // This function sets the default menuitem for the specified CCM and checks
  1327. // the menuitem.
  1328. //
  1329. // Input:  hwndMenu - specifies the menu window handle.
  1330. //         usSubmenu - specifies the id of the cascade menu.
  1331. //         usDefault - specifies the id of the default menuitem.
  1332. // Returns:  TRUE if successful, FALSE otherwise.
  1333. //-------------------------------------------------------------------------
  1334. {
  1335.    MENUITEM miItem;
  1336.    ULONG ulStyle;
  1337.  
  1338.    WinSendMsg(hwndMenu,
  1339.               MM_QUERYITEM,
  1340.               MPFROM2SHORT(usSubmenu,TRUE),
  1341.               MPFROMP(&miItem));
  1342.    ulStyle=WinQueryWindowULong(miItem.hwndSubMenu,QWL_STYLE);
  1343.    ulStyle|=MS_CONDITIONALCASCADE;
  1344.    WinSetWindowULong(miItem.hwndSubMenu,QWL_STYLE,ulStyle);
  1345.  
  1346.    WinSendMsg(miItem.hwndSubMenu,
  1347.               MM_SETDEFAULTITEMID,
  1348.               MPFROM2SHORT(usDefault,FALSE),
  1349.               0L);
  1350.    WinSendMsg(miItem.hwndSubMenu,
  1351.               MM_SETITEMATTR,
  1352.               MPFROM2SHORT(usDefault,FALSE),
  1353.               MPFROM2SHORT(MIA_CHECKED,MIA_CHECKED));
  1354.  
  1355.    return TRUE;
  1356. }
  1357.  
  1358.  
  1359. ΓòÉΓòÉΓòÉ 6.1.1.2. Dropping on a Printer ΓòÉΓòÉΓòÉ
  1360.  
  1361. In the "Direct Manipulation" chapter of Volume 4 of the "Redbooks", it states 
  1362. that for the rendering mechanism DRM_PRINT the printer sends the source of the 
  1363. drag operation a DM_PRINTOBJECT  message to print the object(s).  That's all 
  1364. well and good, until you try to use the information in the "Programmer's 
  1365. Reference"  to process this message.  Well, direct manipulation is a real drag 
  1366. (pun intended) as it is, but even more so when the documentation is incorrect 
  1367. about a fundamental message used by the system. 
  1368.  
  1369. The documentation states that mpParm1 will point to the DRAGINFO structure 
  1370. corresponding to the drag operation in progress. Bzzz!  Thanks for playing, and 
  1371. Bob will tell you what your consolation prize is.  One can glean what the true 
  1372. value of this parameter is by looking at the name assigned in the documentation 
  1373. - pDragItem. You guessed it; it points to the DRAGITEM structure corresponding 
  1374. to an item that was dropped on the printer object. 
  1375.  
  1376. While this only makes sense, this poses an interesting design decision. If your 
  1377. application has a container and the user drags 10 items to the printer, you'll 
  1378. get 10 different DM_PRINTOBJECT messages. Since we all know that printing 
  1379. should be done in a separate thread, do we start 10 different threads to handle 
  1380. each message?  What if 50 objects were dropped?  100? 
  1381.  
  1382. This can easily get out of hand, and the system can easily run out of resources 
  1383. (or it did when I originally implemented it this way for one of my 
  1384. applications), so an alternative method of accomplishing this will need to be 
  1385. developed; this is left to the programmer, since the solutions are various and 
  1386. are specific to an application. 
  1387.  
  1388. A second, more serious, problem is that of the second parameter, passed in 
  1389. mpParm2.  It points to a PRINTDEST structure, which is defined in <os2def.h> as 
  1390. such: 
  1391.  
  1392.    typedef struct _PRINTDEST {
  1393.       ULONG cb;
  1394.       LONG lType;
  1395.       PSZ pszToken;
  1396.       LONG lCount;
  1397.       PDEVOPENDATA pdopData;
  1398.       ULONG fl;
  1399.       PSZ pszPrinter;
  1400.    } PRINTDEST, *PPRINTDEST;
  1401.  
  1402. This should save you a lot of time, since the DEVOPENSTRUC structure is already 
  1403. initialized and is disguised as a DEVOPENDATA structure.  However, when you try 
  1404. to use the pdosData field in your call to DevOpenDC, you get a 
  1405. PMERR_INV_DRIVER_DATA error.  This is particularly funny, since the printer 
  1406. specified this as the data to use, yet won't accept it if you use it. 
  1407.  
  1408. The only solution is to query the default driver data yourself using 
  1409. DevPostDeviceModes.  But wait!  You don't have the device name for the printer! 
  1410. Well, making the assumption that your machine doesn't have more than one 
  1411. printer attached to the queue you dropped the object on, you can use the 
  1412. SplQueryQueue function to determine the missing information.  Sparing you the 
  1413. boring details, I'll cut-to-the-quick and will simply show you the procedure 
  1414. below: 
  1415.  
  1416. BOOL initPrinter(HAB habAnchor,PPRINTDEST ppdPrinter,PDEVOPENSTRUCT pdosPrinter)
  1417. //-------------------------------------------------------------------------
  1418. // This function will query the default driver data for the printer
  1419. // specified in ppdPrinter.  It is the caller's responsibility to free
  1420. // the data using free().
  1421. //
  1422. // Input:  habAnchor - anchor block of the calling thread.
  1423. //         ppdPrinter - pointer to the PRINTDEST structure passed via
  1424. //                      the DM_PRINTOBJECT message.
  1425. // Output:  pdosPrinter - points to the variable which received the new
  1426. //                        DEVOPENSTRUC structure.
  1427. // Returns:  TRUE if successful, FALSE otherwise.
  1428. //-------------------------------------------------------------------------
  1429. {
  1430.    ULONG ulNeeded;
  1431.    PPRQINFO3 ppiQueue;
  1432.    CHAR achDriver[64];
  1433.    CHAR achDevice[64];
  1434.    PCHAR pchPos;
  1435.  
  1436.    *pdosPrinter=*((PDEVOPENSTRUC)(ppdPrinter->pdopData));
  1437.  
  1438.    SplQueryQueue(NULL,
  1439.                  pdosPrinter->pszLogAddress,
  1440.                  3,
  1441.                  NULL,
  1442.                  0,
  1443.                  &ulNeeded);
  1444.  
  1445.    ppiQueue=malloc(ulNeeded);
  1446.    if (ppiQueue==NULL) {
  1447.       return FALSE;
  1448.    } /* endif */
  1449.  
  1450.    SplQueryQueue(NULL,
  1451.                  pdosPrinter->pszLogAddress,
  1452.                  3,
  1453.                  ppiQueue,
  1454.                  ulNeeded,
  1455.                  &ulNeeded);
  1456.  
  1457.    strcpy(achDriver,ppiQueue->pszDriverName);
  1458.    free(ppiQueue);
  1459.  
  1460.    pchPos=strchr(achDriver,'.');
  1461.    if (pchPos!=NULL) {
  1462.       *pchPos=0;
  1463.       strcpy(achDevice,pchPos+1);
  1464.    } else {
  1465.       achDevice[0]=0;
  1466.    } /* endif */
  1467.  
  1468.    ulNeeded=DevPostDeviceModes(habAnchor,
  1469.                                NULL,
  1470.                                achDriver,
  1471.                                achDevice,
  1472.                                ppdPrinter->pszPrinter,
  1473.                                DPDM_QUERYJOBPROP);
  1474.  
  1475.    pdosPrinter->pdriv=malloc(ulNeeded);
  1476.    if (pdosPrinter->pdriv==NULL) {
  1477.       return FALSE;
  1478.    } /* endif */
  1479.  
  1480.    DevPostDeviceModes(habAnchor,
  1481.                       pdosPrinter->pdriv,
  1482.                       achDriver,
  1483.                       achDevice,
  1484.                       ppdPrinter->pszPrinter,
  1485.                       DPDM_QUERYJOBPROP);
  1486.  
  1487.    if ((ppdPrinter->fl & PD_JOB_PROPERTY)!=0) {
  1488.       DevPostDeviceModes(habAnchor,
  1489.                          pdosPrinter->pdriv,
  1490.                          achDriver,
  1491.                          achDevice,
  1492.                          NULL,
  1493.                          DPDM_POSTJOBPROP);
  1494.    } /* endif */
  1495.  
  1496.    return TRUE;
  1497. }
  1498.  
  1499.  
  1500. ΓòÉΓòÉΓòÉ 6.1.1.3. Message Box Help ΓòÉΓòÉΓòÉ
  1501.  
  1502. Message boxes are an easy way to communicate something to the user from your PM 
  1503. application.  However, one can only say so much in a message box, so PM allows 
  1504. you to add online help for each one.  To correspond a particular message with a 
  1505. help panel, the panel resource id is specified in the 5th parameter and MB_HELP 
  1506. must be specified in the flags. 
  1507.  
  1508. So how do you process the help requests?  The documentation states that a help 
  1509. hook must be used, and that (because the Help Manager also uses a help hook) it 
  1510. should be installed before creating the help instance so that you can receive 
  1511. the help messages.  Unfortunately, doing this won't work because the 
  1512. documentation is incorrect.  If you look up the entry for WinSetHook, you'll 
  1513. read that this installs the hook at the head of the hook chain, so (doing a few 
  1514. abstract calculations in your head) you can see that setting your help hook 
  1515. before creating the help instance results in the Help Manager's hook being 
  1516. positioned before yours.  Calling WinSetHook after WinAssociateHelpInstance 
  1517. will fix this problem and your help hook will start seeing the messages.  Don't 
  1518. forget to return FALSE if the message isn't processed, or the Help Manager's 
  1519. hook won't see them! 
  1520.  
  1521. As if this wasn't enough, the documentation on the help hook is incorrect also. 
  1522. It states that the second parameter (sMode) can have one of the following four 
  1523. values: 
  1524.  
  1525. o HFM_MENU 
  1526. o HFM_MB 
  1527. o HFM_WINDOW 
  1528. o HFM_APPLICATION 
  1529.  
  1530. but none of these constants are defined in the toolkit!  Instead, sMode can 
  1531. have one of the following three values: 
  1532.  
  1533. o HLPM_FRAME 
  1534. o HLPM_WINDOW 
  1535. o HLPM_MENU 
  1536.  
  1537. HLPM_WINDOW and HLPM_MENU seem to correctly behave according to the 
  1538. documentation for HFM_WINDOW and HFM_MENU, respectively.  There is no 
  1539. corresponding constant for HFM_MB though; instead, message box help requests 
  1540. have the mode HLPM_WINDOW with the topic number specifying the help panel id 
  1541. used in the WinMessageBox call. 
  1542.  
  1543.  
  1544. ΓòÉΓòÉΓòÉ 6.2. Introduction to PM ΓòÉΓòÉΓòÉ
  1545.  
  1546.                                  Introduction to
  1547.                                  PM Programming
  1548.  
  1549.                                      Part II
  1550.  
  1551.                                    Gavin Baker
  1552.                           (demogrb@lust.latrobe.edu.au)
  1553.  
  1554.  
  1555. ΓòÉΓòÉΓòÉ 6.2.1. Introduction ΓòÉΓòÉΓòÉ
  1556.  
  1557.   Introduction 
  1558.  
  1559. Welcome to the second installment of our exciting series on PM programming! 
  1560. (Well, I thought I'd better start on a high note ...) 
  1561.  
  1562. In this article, I present a simple program which takes advantage of OS/2 
  1563. threads.  It uses one thread to handle interacting with the user, and another 
  1564. thread to do all the work.  It simply displays the current time in the middle 
  1565. of the window.  It is obviously a trivial prorgam, but nonetheless serves to 
  1566. illustrate one possible use of threads. 
  1567.  
  1568. Basically you can have one program (process) doing more than one thing 
  1569. (threads) at once.  A little like multi-multitasking... Anyway, in terms of the 
  1570. source code, a thread is just a function in your program which gets called in a 
  1571. special way which allows it to go off and run at the same time as the rest of 
  1572. the program.  You can imagine how complicated things could get having to 
  1573. co-ordinate things, so OS/2 also provides IPC (Inter-Process Communication) 
  1574. functions to help you. 
  1575.  
  1576. The program uses other facets of OS/2 not discussed yet in this series 
  1577. (resources, and the Graphics Programming Interface [GPI]) so we will not spend 
  1578. too much time on them since the main focus is on threads.  I have included the 
  1579. RC file which defines the dialog box and the menu, but I will leave off 
  1580. explaining resources to the next article. 
  1581.  
  1582.  
  1583. ΓòÉΓòÉΓòÉ 6.2.2. Scope ΓòÉΓòÉΓòÉ
  1584.  
  1585.   Scope 
  1586.  
  1587. I am assuming that you are a competent C programmer, and have a working 
  1588. knowledge of OS/2 from a user's perspective. The sample code here was produced 
  1589. with Borland C++ for OS/2, but should work with most other compilers. 
  1590.  
  1591.  
  1592. ΓòÉΓòÉΓòÉ 6.2.3. Trademarks etc. ΓòÉΓòÉΓòÉ
  1593.  
  1594.   Trademarks etc. 
  1595.  
  1596. Please note that any trademarks referred to in this article remain the property 
  1597. of their respective companies. 
  1598.  
  1599. #include <std_disclaimer.h>
  1600.  
  1601.  
  1602. ΓòÉΓòÉΓòÉ 6.2.4. Processes & Threads ΓòÉΓòÉΓòÉ
  1603.  
  1604.   Processes & Threads 
  1605.  
  1606. A process in OS/2 terms is just an application or program.  Because OS/2 is 
  1607. multi-tasking, you can run multiple processes (in their own address spaces) at 
  1608. the same time.  A thread is an execution unit within a process.  It becomes 
  1609. clearer in this diagram: 
  1610.  
  1611. Editor's note: Due to time constraints, this diagram was not available in time 
  1612. for publication. It will be made available at a later date. 
  1613.  
  1614. The red shows OS/2 itself.  The green represents our program, and the yellow 
  1615. boxes are the threads within it.  You would typically have one thread to handle 
  1616. the user interface (the main window procedure), one to do all the work, and 
  1617. perhaps one for printing.  Threads can have different priorities, and there are 
  1618. some very powerful functions to enable threads to communicate with each other, 
  1619. to synchronise and co-ordinate events. 
  1620.  
  1621. Threads within a process share the address space, which means the variables and 
  1622. functions within the program have the same scope between two threads.  For 
  1623. example, consider these two threads: 
  1624.  
  1625. void Thread1()                void Thread2()
  1626. {                             {
  1627.   while (1)                     while (1)
  1628.     i++;                          i--;
  1629. }                             }
  1630.  
  1631. Get the feeling we may not be getting anywhere...?  These two threads are 
  1632. playing a "tug-of-war" with the poor old (overused) variable i.  The first 
  1633. thread will be forever incrementing i, and the second thread will forever be 
  1634. undoing all that hard work. The variable i will stay around the same value, but 
  1635. won't be exactly zero due to the dynamic prioritising of threads that OS/2 
  1636. performs - if a Thread1 gets a tiny bit more CPU time, i will go up a little. 
  1637.  
  1638. Threads allow your programs to be much more efficient, and responsive to the 
  1639. user.  Here I will show you how. 
  1640.  
  1641.  
  1642. ΓòÉΓòÉΓòÉ 6.2.5. The Code ΓòÉΓòÉΓòÉ
  1643.  
  1644.   The Code 
  1645.  
  1646. Right - now we'll go straight to the code.  The general idea is to have the 
  1647. main program handle the user interactions, and during initialization we start 
  1648. the worker thread which carries on its merry way despite what the main thread 
  1649. may be doing. 
  1650.  
  1651. The way we acheive this is by creating the main window itself, and then have 
  1652. the second Worker thread create a dummy window (called an OBJECT WINDOW) which 
  1653. doesn't actually appear on screen, but gives us a window handle and a message 
  1654. queue to work with.  This is not the only way to do this, but it is probably 
  1655. the simplest. The main window sends custom messages to the second dummy window 
  1656. on thread 2 to do all the work. 
  1657.  
  1658. Now here we specify which portions of the include files we need, and also 
  1659. include a few others. 
  1660.  
  1661. #define INCL_WIN
  1662. #define INCL_GPI
  1663. #define INCL_WINDIALOGS
  1664.  
  1665. #include <os2.h>
  1666. #include <process.h>
  1667. #include <string.h>
  1668. #include <time.h>
  1669. #include <stdlib.h>
  1670. #include <stdio.h>
  1671. #include <dos.h>
  1672. #include "step02.h"
  1673.  
  1674. Now we need to pass messages between the two threads, so we define our own by 
  1675. starting at WM_USER and going up.  This ensures we don't accidentally use a 
  1676. system-defined message. 
  1677.  
  1678. #define WM_BEGIN_PAINT  WM_USER+1
  1679. #define WM_END_PAINT    WM_USER+2
  1680. #define WM_ACK          WM_USER+3
  1681.  
  1682. Using global variables is generally considered a no-no, unless it can't be 
  1683. avoided.  It makes things easier to implement and more self-contained if you 
  1684. keep globals to a bare minimum.  We then supply prototypes for our functions. 
  1685.  
  1686. HWND    hwndMain,
  1687.         hwndWorker;
  1688.  
  1689. MRESULT EXPENTRY MainWndProc (HWND, ULONG, MPARAM, MPARAM);
  1690. VOID             WorkerThread ();
  1691. MRESULT EXPENTRY WorkWndProc (HWND, ULONG, MPARAM, MPARAM);
  1692. MRESULT EXPENTRY DlgProc(HWND, ULONG, MPARAM, MPARAM);
  1693. VOID             WorkPaint (HWND, HPS);
  1694.  
  1695. The main function is not very big - all it does is set up some variables, the 
  1696. flags for the window, initialize things for PM, then register and create our 
  1697. window.  After we exit our main message loop (by getting a WM_QUIT) we clean up 
  1698. and exit. 
  1699.  
  1700. int main (void)
  1701. {
  1702.     HAB     hab;
  1703.     HMQ     hmq;
  1704.     HWND    hwndFrame;
  1705.     QMSG    qmsg;
  1706.     ULONG   flFrameFlags =  FCF_TITLEBAR      |     FCF_SYSMENU  |
  1707.                             FCF_SIZEBORDER    |     FCF_MINMAX   |
  1708.                             FCF_SHELLPOSITION |     FCF_TASKLIST |
  1709.                             FCF_MENU          ;
  1710.  
  1711.     randomize();
  1712.  
  1713.     hab = WinInitialize (0);
  1714.     hmq = WinCreateMsgQueue (hab, 0);
  1715.  
  1716.     WinRegisterClass (hab, "STEP2", MainWndProc, CS_SIZEREDRAW, 0);
  1717.  
  1718.     hwndFrame = WinCreateStdWindow (HWND_DESKTOP, WS_VISIBLE,
  1719.                                     &flFrameFlags, "STEP2", NULL,
  1720.                                     0, NULLHANDLE, ID_MAIN, &hwndMain);
  1721.  
  1722.     while (WinGetMsg (hab, &qmsg, 0, 0, 0))
  1723.             WinDispatchMsg (hab, &qmsg);
  1724.  
  1725.     WinDestroyWindow (hwndFrame);
  1726.     WinDestroyMsgQueue (hmq);
  1727.     WinTerminate (hab);
  1728.     return 0;
  1729. }
  1730.  
  1731. Now here is the Window Procedure for the main window itself.  The special bit 
  1732. to notice here is the call to _beginthread when we get created (WM_CREATE). 
  1733. This is where the second thread gets started.  Note we just pass it the name of 
  1734. the function, and that function will start executing from there all by itself. 
  1735. It can operate more or less like any other function, with a few considerations. 
  1736.  
  1737. MRESULT EXPENTRY MainWndProc (HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  1738. {
  1739.     FILEDLG     fild;
  1740.  
  1741.     switch (msg)
  1742.     {
  1743.         case WM_CREATE:
  1744.             if (_beginthread (WorkerThread, 8192, NULL) == -1)
  1745.             {
  1746.                 WinMessageBox (HWND_DESKTOP, hwnd,
  1747.                     "Creation of second thread failed!", "Step 2",
  1748.                     0, MB_OK | MB_CUACRITICAL);
  1749.                 return 0;
  1750.             }
  1751.  
  1752.             return 0;
  1753.  
  1754. Here is the first user message we get.  This message will be sent by the second 
  1755. thread's window procedure to the main one to say it is all setup and ready to 
  1756. go.  We respond by telling it to go ahead and start painting the window. 
  1757.  
  1758.         case WM_ACK:
  1759.             WinPostMsg(hwndWorker, WM_BEGIN_PAINT, 0, 0);
  1760.             return 0;
  1761.  
  1762. When the main window gets resized, we stop the second thread from painting, and 
  1763. then restart it, causing it to update itself with the new size of the window. 
  1764. Note that we use WinSendMsg first, then WinPostMsg.  We send the message first, 
  1765. which calls the target window procedure directly and will not continue until it 
  1766. returns (thus ensuring the message gets processed) and then we Post the message 
  1767. to restart in the second thread's message queue so it can keep going. 
  1768.  
  1769.         case WM_SIZE:
  1770.             WinSendMsg(hwndWorker, WM_END_PAINT, 0, 0);
  1771.  
  1772.             WinPostMsg(hwndWorker, WM_BEGIN_PAINT, 0, 0);
  1773.             return 0;
  1774.  
  1775. We get this message when the user drops a font or colour onto the window.  We 
  1776. Invalidate the entire window and force a repaint so we can display with the new 
  1777. font/colours. 
  1778.  
  1779.         case WM_PRESPARAMCHANGED:
  1780.             WinInvalidateRect(hwndMain,NULL,TRUE);
  1781.             return 0;
  1782.  
  1783. If the main window needs to be painted, we simply make sure that the second 
  1784. thread gets on with it.  Note again the different use of sending and posting 
  1785. the message. 
  1786.  
  1787.         case WM_PAINT:
  1788.             WinSendMsg(hwndWorker, WM_END_PAINT, 0, 0);
  1789.  
  1790.             WinPostMsg(hwndWorker, WM_BEGIN_PAINT, 0, 0);
  1791.             return 0;
  1792.  
  1793. In order to simplify things, PM does some of the painting for us if we like - 
  1794. returning true from this message makes PM erase the entire window with the 
  1795. default colour. 
  1796.  
  1797.         case WM_ERASEBACKGROUND:
  1798.             return (MRESULT) TRUE;
  1799.  
  1800. Now, whenever a user selects something from the menu, we get a WM_COMMAND 
  1801. message.  We can then use the SHORT1FROMMP macro (which means extract a short 
  1802. value from the high word of a message parameter) to get the ID of the menu item 
  1803. selected. The first menu selection we process is the most important one - the 
  1804. About box.  We call WinDlgBox which does a lot of work for us.  It loads the 
  1805. dialog from the resource (check out STEP02.RC), runs the dialog procedure we 
  1806. specify (which works just like a window procedure) and will not return until 
  1807. the dialog is closed.  This is called a modal dialog - it will not allow the 
  1808. user to select anything else until they finish with the dialog.  Contrast this 
  1809. with a modeless dialog, which can be used at the same time as any other 
  1810. windows. These are often used for floating toolboxes, etc. We pass WinDlgBox 
  1811. the parent window (which will be the desktop), the owner window (our main 
  1812. window), a pointer to the dialog procedure which handles the messages, a handle 
  1813. to the module where the resource is located (by specifying NULLHANDLE OS/2 
  1814. looks in the EXE file), and any extra info we want to pass the dialog 
  1815. procedure. 
  1816.  
  1817.         case WM_COMMAND:
  1818.             switch (SHORT1FROMMP(mp1))
  1819.             {
  1820.                 case ID_ABOUT:
  1821.                     WinDlgBox(HWND_DESKTOP,hwnd,(PFNWP)DlgProc,NULLHANDLE,
  1822.                         DLG_ABOUT,NULL);
  1823.                     return 0;
  1824.  
  1825. Now we will use one of OS/2's standard dialogs - the File Open dialog. All we 
  1826. do is set up a structure with the appropriate options, and call WinFileDlg. 
  1827. Once it returns we can examine the FILEDLG struct for the file the user 
  1828. selected.  This example only displays the dialog - it does nothing with what 
  1829. the user selected. 
  1830.  
  1831.                 case ID_FILEOPEN:
  1832.                     memset(&fild, 0, sizeof(FILEDLG));
  1833.                     fild.cbSize=sizeof(FILEDLG);
  1834.                     fild.fl=FDS_OPEN_DIALOG | FDS_CENTER ;
  1835.                     fild.pszIDrive="C:";
  1836.                     WinFileDlg(HWND_DESKTOP,hwnd,&fild);
  1837.                     return 0;
  1838.  
  1839. If the user selects Exit from the File menu, we just send ourselves a WM_CLOSE 
  1840. message, which by default will shut down our application. 
  1841.  
  1842.                 case ID_FILEEXIT:
  1843.                     WinPostMsg(hwnd, WM_CLOSE, 0, 0);
  1844.                     return 0;
  1845.  
  1846. Any other messages we don't need to worry about, so let PM handle them by 
  1847. passing them on to the default PM window procedure. 
  1848.  
  1849.                 default:
  1850.                     return WinDefWindowProc(hwnd,msg,mp1,mp2);
  1851.             }
  1852.  
  1853. Notice how we first destroy the second thread so it can clean up, before we 
  1854. close ourselves. 
  1855.  
  1856.         case WM_DESTROY:
  1857.             WinSendMsg(hwndWorker, WM_DESTROY, mp1, mp2);
  1858.             return 0;
  1859.         }
  1860.     return WinDefWindowProc (hwnd, msg, mp1, mp2);
  1861. }
  1862.  
  1863. Now we get to the interesting bit - our Worker thread.  It looks like a normal 
  1864. function, it just gets called differently.  Although this example does not 
  1865. cover it, you must keep in mind that having multiple threads in the one program 
  1866. requires some forethought. You can't have two threads trying to write to the 
  1867. one data file, for example.  We will explore this problem and how to solve it 
  1868. (using Semaphores) in a later article. 
  1869.  
  1870. VOID WorkerThread ()
  1871. {
  1872.     HAB  hab;
  1873.     HMQ  hmq;
  1874.     HWND hwndObject,
  1875.          hwndParent;
  1876.     QMSG qmsg;
  1877.  
  1878. This should look familiar - it looks very much like the main procedure.  The 
  1879. only difference is that we specify HWND_OBJECT when we create the window.  We 
  1880. need our own message queue so we can talk to the main window.  Notice how we 
  1881. ACKnowledge the main window once we get created, and go into our message loop. 
  1882. All the work is actually done in the second thread's window procedure.  One 
  1883. special thing to note is the call to _endthread at the end. This lets the C 
  1884. runtime library clean up after us, and shuts down the thread properly. 
  1885.  
  1886.     hab = WinInitialize (0);
  1887.  
  1888.     hmq = WinCreateMsgQueue(hab, 0);
  1889.  
  1890.     WinRegisterClass(hab, "STEP2_B", WorkWndProc, 0, 0);
  1891.  
  1892.     hwndWorker = WinCreateWindow( HWND_OBJECT, "STEP2_B", "",
  1893.              0, 0, 0, 0, 0, HWND_OBJECT, HWND_BOTTOM, 0, NULL, NULL );
  1894.  
  1895.     WinSendMsg( hwndMain, WM_ACK, 0, 0 );
  1896.  
  1897.     while( WinGetMsg ( hab, &qmsg, 0, 0, 0 ))
  1898.         WinDispatchMsg ( hab, &qmsg );
  1899.  
  1900.     WinPostMsg( hwndMain, WM_QUIT, 0, 0 );
  1901.  
  1902.     WinDestroyWindow( hwndWorker );
  1903.     WinDestroyMsgQueue( hmq );
  1904.     WinTerminate (hab);
  1905.     _endthread ();
  1906. }
  1907.  
  1908. This is the dialog procedure for the About box.  It is just the same as a 
  1909. window procedure except that for the messages we don't process, we call 
  1910. WinDefDlgProc instead of WinDefWindowProc because certain things have to be 
  1911. handled differently.  If the user presses a button in the dialog, we get a 
  1912. WM_COMMAND much the same as if they selected a menu item.  We know that the OK 
  1913. button is the only button that will do anything so we don't bother checking and 
  1914. just close the dialog by calling WinDismissDlg.  We pass it the handle of the 
  1915. dialog, and a BOOLean value which will be returned to the calling function 
  1916. (back at WinDlgBox) so we can tell if the user pressed OK or Cancel. 
  1917.  
  1918. MRESULT EXPENTRY DlgProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  1919. {
  1920.     switch (msg)
  1921.     {
  1922.         case WM_COMMAND:
  1923.             WinDismissDlg(hwnd,TRUE);
  1924.             return 0;
  1925.         default:
  1926.             return WinDefDlgProc(hwnd,msg,mp1,mp2);
  1927.     }
  1928. }
  1929.  
  1930. This is the window procedure for the "dummy" OBJECT window which the second 
  1931. thread creates.  We have to set up a few things when we get CREATEd.  Firstly, 
  1932. we ask PM for a timer.  We give it a handle to the anchor block (which we get 
  1933. from our handle), the handle itself, an ID for the timer (you can have more 
  1934. than one), and the delay in milliseconds between timer "ticks".  We ask it to 
  1935. send us a WM_TIMER once a second, so we can update our clock.  The next thing 
  1936. we do is get ourselves a Presentation Space (PS), which is like a handle which 
  1937. is used when we want to draw or paint in the window.  We then say that we want 
  1938. the background to be cleared when we draw things.  Otherwise the time we 
  1939. display would overwrite itself and soon become garbled. I will not go into to 
  1940. much detail on the drawing side of things, as the GPI (Graphics Programming 
  1941. Interface) itself could fill a book (and has). 
  1942.  
  1943. MRESULT EXPENTRY WorkWndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  1944. {
  1945.     static BOOL Paint=FALSE;
  1946.     static HPS  hps;
  1947.     SIZEL       sizel;
  1948.  
  1949.     switch (msg)
  1950.     {
  1951.         case WM_CREATE:
  1952.             if (!WinStartTimer(WinQueryAnchorBlock(hwnd), hwnd, 1, 1000))
  1953.                 WinMessageBox(HWND_DESKTOP,hwnd,"Could not start timer!",
  1954.                     "Error",0,MB_CUACRITICAL | MB_OK);
  1955.             hps = WinGetPS(hwndMain);
  1956.             GpiSetBackMix(hps, BM_OVERPAINT);
  1957.             return 0;
  1958.  
  1959. WM_BEGIN_PAINT is our first user message sent to us from the main window.  All 
  1960. we do is set a flag saying it is OK to keep painting.  If we get WM_END_PAINT 
  1961. then we stop painting for the moment (even while we are still getting WM_TIMER 
  1962. messages). 
  1963.  
  1964.         case WM_BEGIN_PAINT:
  1965.             Paint = TRUE;
  1966.             return 0;
  1967.  
  1968.         case WM_END_PAINT:
  1969.             Paint = FALSE;
  1970.             return 0;
  1971. Every second, we will get a WM_TIMER message, and all we do is check if it is 
  1972. Ok to paint, and then do so. 
  1973.  
  1974.         case WM_TIMER:
  1975.             if (Paint)
  1976.                 WorkPaint(hwndMain, hps);
  1977.             return 0;
  1978.  
  1979. If we get closed, make sure we clean up by stopping our timer and releasing the 
  1980. PS we got to draw with.  Cleanup is always very important, and could 
  1981. potentially cause some nasty bugs which may not be obvious. Always consult the 
  1982. PM Reference manual for functions you may use which require resources to be 
  1983. released or destroyed. 
  1984.  
  1985.         case WM_DESTROY:
  1986.             WinStopTimer(WinQueryAnchorBlock(hwnd), hwnd, 0);
  1987.             WinReleasePS(hps);
  1988.             return 0;
  1989.     }
  1990.     return WinDefWindowProc (hwnd, msg, mp1, mp2);
  1991. }
  1992.  
  1993. This function does the painting for us.  We get the size of the main window (a 
  1994. RECTangLe), then calculate its width and height. 
  1995.  
  1996. VOID WorkPaint (HWND hwnd, HPS hps)
  1997. {
  1998.     ULONG   x, y, cx, cy;
  1999.     RECTL   rect;
  2000.     POINTL  ptl;
  2001.     char    s[42];
  2002.     struct time  t;
  2003.  
  2004.     WinQueryWindowRect(hwnd, &rect);
  2005.  
  2006.     cx = rect.xRight - rect.xLeft;
  2007.     cy = rect.yTop - rect.yBottom;
  2008.  
  2009. We check what the current time is, and compose our string. 
  2010.  
  2011.     gettime(&t);
  2012.     sprintf(s,"Current Time: %2.2d:%2.2d%colon.%2.2d",t.ti_hour,t.ti_min,t.ti_sec);
  2013.  
  2014. We want the time to be shown roughly in the middle of the screen, so we figure 
  2015. out where that is.  We then randomly pick a colour and display the string at 
  2016. the specified point. 
  2017.  
  2018.     ptl.x=(cx/3); ptl.y=(cy/2);
  2019.     GpiSetColor(hps, rand() % 16);
  2020.     GpiCharStringAt(hps, &ptl, strlen(s), s);
  2021. }
  2022.  
  2023.  
  2024. ΓòÉΓòÉΓòÉ 6.2.6. Prologue ΓòÉΓòÉΓòÉ
  2025.  
  2026.   Prologue 
  2027.  
  2028. Well, that's it.  It's not a particularly exciting program, and it could 
  2029. probably be more efficient, but it does provide a general skeleton for a 
  2030. multi-threaded PM application.  Study the structure of the program, in blocks. 
  2031. The main function which sets up and creates the window, the main window 
  2032. procedure which handles all the messages and delegates work through user 
  2033. messages to the worker thread.  Then the worker thread which sets up the object 
  2034. window, and its corresponding window procedure which does all the work. 
  2035.  
  2036. Try a few things.  First, drop a different font on the time.  Then try a colour 
  2037. (this will only last until it updates itself, since it changes colour itself). 
  2038. Then bring up the About box and move it so you can see the time still updating 
  2039. itself. 
  2040.  
  2041. Something we have not discussed is thread states.  Basically, a thread can 
  2042. either be running (the currently executing thread of which there can only ever 
  2043. be one), ready to run, or blocked (waiting for something to happen).  This 
  2044. makes for very efficient programming. Imagine a terminal program.  One thread 
  2045. can be handling the user interface, one doing general work, and another 
  2046. monitoring the serial port for input.  It would call DosRead from COM1 for 
  2047. example.  If there is nothing to read, it doesn't have to sit in a loop and 
  2048. poll the port.  OS/2 will block the thread and drop its priority until DosRead 
  2049. returns with something, so it won't get any CPU time until it needs it. 
  2050.  
  2051. The possibilities for threads are endless - background recalculating, 
  2052. background printing, etc. are just some.  See how threads could improve your 
  2053. programs. 
  2054.  
  2055.  
  2056. ΓòÉΓòÉΓòÉ 6.2.7. What Next? ΓòÉΓòÉΓòÉ
  2057.  
  2058.   What Next? 
  2059.  
  2060. There is a lot of ground to cover in this area, and we have only scratched the 
  2061. surface.  But fear not - help is at hand! (You'll just have to wait until next 
  2062. month...) 
  2063.  
  2064. I welcome any feedback on this article (netmail preferred) - any comments or 
  2065. suggestions you may have, questions on this article, or things you would like 
  2066. to see in a future article. I hope you have learned something! 
  2067.  
  2068.  
  2069. ΓòÉΓòÉΓòÉ 6.2.8. Bibliography ΓòÉΓòÉΓòÉ
  2070.  
  2071.   Bibliography 
  2072.  
  2073. The following references were used in the preparation of this article: 
  2074.  
  2075. Γûá  Borland C++ for OS/2 Documentation 
  2076.  
  2077. Γûá  OS/2 Version 2.0 - The Redbooks 
  2078.  
  2079. Γûá  OS/2 Version 2.0 Technical Library 
  2080.  
  2081.  
  2082. ΓòÉΓòÉΓòÉ 7. Future Attractions ΓòÉΓòÉΓòÉ
  2083.  
  2084. Coming up in the future, we have: 
  2085.  
  2086. o Introduction to PM Part 3 
  2087.  
  2088. o Writing Installable File Systems 
  2089.  
  2090. o Getting Started with IPF 
  2091.  
  2092. o And much more! 
  2093.  
  2094.  
  2095. ΓòÉΓòÉΓòÉ 8. Contributors to this issue ΓòÉΓòÉΓòÉ
  2096.  
  2097. o Gavin R Baker 
  2098.  
  2099. o David Campbell 
  2100.  
  2101. o Steve Lacy 
  2102.  
  2103. o Steve Luzynski 
  2104.  
  2105. o Dave Raymer 
  2106.  
  2107. o Larry Salomon 
  2108.  
  2109.  
  2110. ΓòÉΓòÉΓòÉ 8.1. Gavin R Baker ΓòÉΓòÉΓòÉ
  2111.  
  2112.   Gavin R Baker 
  2113.  
  2114. Gavin R Baker is the man behind  ThinkSoft, a consulting firm based in 
  2115. Melbourne Australia which specialises in developing custom software. He has 
  2116. experience in Assembler, Pascal, C, C++, (and a lot of other languages), and 
  2117. has worked with Unix, DOS, Windows, OS/2, VMS and Pick operating systems. He is 
  2118. an active member of Team OS/2. When he isn't programming, he is also a 
  2119. musician, an actor, and wastes lots of time reading Net News. He can be 
  2120. contacted thusly: 
  2121.  
  2122. net: demogrb@lust.latrobe.edu.au
  2123. cis: 100026,270
  2124. bix: gbaker
  2125.  
  2126.  
  2127. ΓòÉΓòÉΓòÉ 8.2. David Campbell ΓòÉΓòÉΓòÉ
  2128.  
  2129. David has a degree in Mechanical Engineering.  Currently he is working as a 
  2130. network designer and software developer.  He is involved in everything from 
  2131. wide area token ring networks to developing an object oriented eMail system 
  2132. which works with IBM's TCP/IP for OS/2. 
  2133.  
  2134. David W. Campbell 
  2135.  
  2136. campbell@campbell.saic.com 
  2137.  
  2138. Work : (615) 481-2131 
  2139.  
  2140. Home : (615) 693-1479 
  2141.  
  2142.  
  2143. ΓòÉΓòÉΓòÉ 8.3. Steve Lacy ΓòÉΓòÉΓòÉ
  2144.  
  2145. Steve Lacy is a third year computer science student at Carnegie Mellon 
  2146. University, and is currently overoccupied with school, and his job as an 
  2147. undergraduate research programmer for the Digital Mapping Lab at CMU.  You can 
  2148. reach Steve via e-mail at sl31@andrew.cmu.edu. He would be glad to hear any and 
  2149. all of your comments about the previous article.  Steve also spends his free 
  2150. time collecting CD's, and unfortunately spends far too much money in the 
  2151. process.  Remember, diversity is knowledge, and personal gratification 
  2152. overrides the need for food in many cases..... 
  2153.  
  2154.  
  2155. ΓòÉΓòÉΓòÉ 8.4. Steve Luzynski ΓòÉΓòÉΓòÉ
  2156.  
  2157. Steve Luzynski is the editor and creator of this magazine. He is currently a 
  2158. Computer Engineering student at Case Western Reserve University in Cleveland, 
  2159. OH, where he spends a lot of time being cold. Steve has yet to release any 
  2160. programs for OS/2 as a direct result of: 1) editing this magazine; and 2) 
  2161. having to waste time going to class when there are programs to write. Steve can 
  2162. by reached via e-mail at 'sal8@po.cwru.edu' or on Compuserve at 72677,2140. 
  2163.  
  2164. The old fashioned kind of mail can currently find Steve at: 
  2165.  
  2166. Steve Luzynski
  2167. 11904 Carlton Road, Apt. 430D
  2168. Cleveland, OH  44106
  2169.  
  2170.  
  2171. ΓòÉΓòÉΓòÉ 8.5. Dave Raymer ΓòÉΓòÉΓòÉ
  2172.  
  2173. Dave lives in Fort Worth, Texas with his wife, two sons, and five dogs. His 
  2174. current employer is Suite Software, a small privately held company which 
  2175. produces an object oriented, multi-platform distributed operating system. He 
  2176. has worked extensively in Windows/DOS, and OS/2; both at the device and 
  2177. application levels. Additionally he has worked in X-Windows/UNIX and NeXTSTEP, 
  2178. as well as in MVS and VMS. 
  2179.  
  2180. Dave can be reached in the following ways.  Please do not hesitate to contact 
  2181. him for any reason.  He is available for Q&A issues (preferably through 
  2182. e-mail.) 
  2183.  
  2184.   e-mail:
  2185.    Internet  --  dave@suite.com
  2186.    Prodigy  --  shph80a
  2187.  
  2188.   voice/direct:
  2189.    Work    --  (214)980-9900
  2190.    Home    --  6528 Levitt Drive
  2191.            Watauga, Texas, 76148
  2192.            (817)656-3813
  2193.  
  2194.  
  2195. ΓòÉΓòÉΓòÉ 8.6. Larry Salomon ΓòÉΓòÉΓòÉ
  2196.  
  2197. Larry Salomon wrote his first Presentation Manager application for OS/2 version 
  2198. 1.1 in 1989.  Since that time, he has written numerous VIO and PM applications, 
  2199. including the Scramble applet included with OS/2 and the I-Brow/Magnify/Screen 
  2200. Capture trio included with the IBM Professional Developers Kit CD-ROM currently 
  2201. being distributed by IBM.  Currently, he works for International Masters 
  2202. Publishers in Stamford, Connecticut and resides in Bellerose, New York with his 
  2203. wife Lisa.