

               OS/2 Programming FAQ v3.4 - June 4, 1995

                        Introduction and Credits

       OS/2 2.x Frequently Asked Questions, Programmer's Edition


                      Version 3.4, June 4, 1995
                     Compiled by Andreas Almroth

                  Pre-3.x versions by Jeff M. Garzik
                   Pre-2.x versions by Barry Jaspan


                Copyright (c) 1995 by Andreas Almroth
                All Rights Reserved.



For changes, suggestions, or additions please mail

andreas@traci.almroth.pp.se

or write to:

Andreas Almroth
Fanrik Stals Gata 153
S-754 39 UPPSALA
SWEDEN



Mention of a product does not constitute an endorsement.  Customers
outside the United States should not necessarily rely on 800 telephone
numbers, page numbers, part numbers, or upgrade policies contained in
this List.  Electronic mail addresses are in Internet form; use
addressing appropriate to your mail system.


This FAQ is freely distributable for noncommercial purposes.  (For
commercial purposes, please contact the author.)  If you redistribute
the FAQ, please include all the original files.


This FAQ is updated quarterly and is distributed through various
computer networks and online services, including the Internet and many BBSes.


Both ASCII text and OS/2 Information Presentation Facility (INF)
versions of the FAQ are provided.  To view the INF version of the List,
go to any OS/2 command line prompt (e.g. double click on "OS/2 Window")
and type
VIEW PROGFAQ.INF



The ASCII text version may be viewed using any text editor, word
processor, or file listing utility.  The text version is intended to
answer any questions you may have before actually obtaining and using
OS/2.  You will find that the INF version provides a much more
attractive interface, with hypertext links, fast indexing, and,
increasingly, illustrations.


If you have not received all three files (README.CMD, PROGFAQ.INF, and
PROGFAQ.TXT), please ask your system operator to make sure he/she is
receiving the correct and complete package.


Many of the answers in this FAQ refer to anonymous ftp site
FTP-OS2.CDROM.COM (192.153.46.2). The name 'cdrom.com' is used as a
shorthand to refer to this site.  It has become, by default, the
Internet storehouse for OS/2 files.  If you cannot get files from this
site (for whatever reason), then check OS/2
Software Sources for a source near you. You may find a more
convenient method of getting files than from cdrom.com.  (Non-US readers
will note ftp.luth.se, is the main overseas FTP site.)


Related Information:

Release Notes 
OS/2 Software Sources 
Obtaining this FAQ / Contacting the Author 


Release Notes



Text which has been revised or updated since the last release will
appear in the same color as this paragraph.


Version 3.4:
Updated some answers and added some answers.



Version 3.3:
Updated some answers and added some answers.


Version 3.2:
Updated some answers and added some answers.
Added a lot of books


Version 3.1:
Added index search facility (but only to some extent, sorry)
As usual, questions and answers have been added or changed.


Version 3.0:
A new maintainer of the OS/2 Programming FAQ: Andreas Almroth
Some answers changed or added.


Version 2.3:

This release does not include very many new sections, but the
information has been reorganized for greater readability and easier
maintenence (for me).  Major changes include title re-wording for
brevity, revision coloring, and updated information.


Related Information:

Introduction and Credits 
OS/2 Software Sources 
Obtaining this FAQ / Contacting the Author 

==============================================================================
Questions Covered in this Release

Languages, Compilers, Debuggers
    What programming languages come with OS/2?
    Programming language availability
    Which of these compilers can be used to generate PM apps?
    Which assemblers can produce OS/2 32-bit code?
    The two GNU C/C++/ObjC packages
    What is REXX?  How do I write and run a REXX program?
    What debuggers are available for OS/2?
     CSD levels for IBM C Set++?
Tools, Toolkits, Accessories
    How do I use PATCH, maybe with my own programs?
    What programming editors are available for OS/2?
    What programming tools/toolkits/accessories are available for OS/2?
    TOUCH-GUI 2.0 for OS/2 2.x
    Prominare Designer for OS/2 2.x
    JAM for OS/2 2.x
    What GNU tools are available and where can I find them?
    Is a socket library available?  How can I use it?
Compiling
    Can I distribute the C-Set++ runtime DLL?
    How do I statically link my C/C++ program with C-Set++?
    How big should my stacksize be?
    How do I determine what C/C++ compilator is compiling my code?
    How do I perform parallel compilation?
    What is an RC of 87 (invalid param) from the API?
    GCC/2 crashes with a trap when I try to compile a program.  Why?
    How do I recompile EPM (easily)?
    How to I get BC++ for DOS/Windows to run?
Documentation, Help
    Where can I get information on OS/2 APIs and programming?
    Where can I get sample code?
    Are there any OS/2 programming classes or seminars?
    What are good ref. books for programming in OS/2 and PM?
    What are good OS/2 programming magazines?
    What are the OS/2 redbooks, and how do I get them?
    How can I view the GNU C documentation?
    Where can I get documentation on the OBJ/LIB/EXE format used by OS/2 2.x?
    Where can I find information on HPFS?
PM Programming
    Printing
        Are there any SIMPLE examples of printing?
        Is there an easy way to get printer output (another opinion)?
        How do I print a bitmap?
        How do I do my own Print Screen?
    Menus
        How do I add a menu to a dialog box?
        How do I make a dynamically changing menu?
        How do I create a conditional cascade menu?
        How do I remove a separator from a menu?
    Container Controls
        How do I stop a container from flashing every time I add a record?
        How do I get my containers to use Mini-icons?
        How do I sort a container control?
        How do I query all records in a container - tree view?
    I can't get different colors in text control windows
    How can I toggle my titlebar on and off?
    How can I get transparent regions in bitmaps?
    How do I create a status bar at the bottom of my window?
    How to have a frame/client and still have a std window?
    How do I use printf() in a PM program?
    I have a SOM DLL. How do I register it?
    How do I save and restore my window size and position?
    How do you make a window float above all other?
    How to ensure the sizing's correct so the dlg "fits" in the notebook. . . ?
    How do I prevent Shutdown from stopping my app?
    When I pass a structure to WinCreateWindow, sometimes it doesn't work!
    How do I use type filtering in 2.0's open dlg?
    When minimizing, my dialog box is overwriting my icon!
    How do I make a multi-column listbox?
    How do I create my own Master Help Index?
    How do I change the font in an MLE?
    Why can't I import files larger than 64KB into my MLE?
    How do I get PM screen size?
    How do I attach Instance data to window created with WinCreateStdWindow?
    How do I get a list of fonts?
    How do I create a folder in C and put my pgm in it?
    How do I do it in REXX?
    How do I use the Font dialog (WinFontDlg)?
    How do I take control of frame sizing?
    How do I use the 16-bit EPM toolkit?
    How do I get error info after using WinGetLastError()?
    Do you have code to save/restore the clipboard?
    How do I know what item was selected in a Combo box?
    How do I get a bitmap into a dialog in a DLL?
    How does programming PM compare to programming X?
    How do I put bitmaps on buttons?
    Can a PM program tell if there's a previous instance of itself running?
    How to avoid crash when using Drag and Drop?
    Are there any PM bindings for GNU Ada?
Miscellaneous Programming
    Explain the SYS_DLL keywords.
    How do I start another session?
    How do I check if a filename is valid?
    Why should I use _beginthread instead of DosCreateThread?
    How do I open a file that is already in use?
    Can we use Vio in v2.0? Where are the docs for it?
    Can I redirect stdin and stdout in a child process?
    How do I use DosMon*() to stuff the kbd buf?
    How do I determine what file system a drive uses?
    How do I get the error message from a DOS API call?
    How do I set an exception handler?
    How can I determine a diskette format and if a disk is in a drive?
    What do all those keywords mean when making a DLL?
    Where can I find serial port sample code?
    How do I disable <Ctrl><Alt><Del>?
    Why doesn't printf() produce output when I expect it to?
    How do I write an OS/2 device driver?
    How do I change the master environment?
    What is the best way to communicate between processes?
    What is the best way to communicate between threads?
    How to I write an IFS?
    How do I interface with OS/2's SCSI support?
    How do I program full-screen graphics?
    How do I program MMPM/2 programs?
    How do I peripheral memory or an I/O port?
Porting
    How do I port my DOS keyboard TSR to OS/2?
    How can I simulate (Unix feature) under OS/2?
    How can I recompile public domain/shareware source code for OS/2?
    How can I port my DOS program to OS/2?
    How can I port my Windows program to OS/2?
Miscellaneous
    Is OS/2 suitable for real time programs?
    What is available for multimedia programming under OS/2?
    What is available for AI/neural net programming under OS/2?
    Special software offers
    Technical Support
    Developer's Assistance Program (DAP)

==============================================================================
Programming Languages
Languages, Compilers, Debuggers


This section covers the programming tools currently available for OS/2.

The following topics are available:

What programming languages come with OS/2?
Programming language availability
Which of these compilers can be used to generate PM apps?
Which assemblers can produce OS/2 32-bit code?
The two GNU C/C++/ObjC packages
What is REXX?  How do I write and run a REXX program?
What debuggers are available for OS/2?
CSD levels for C Set++2?


-------------------------------------------------------------------------------
What programming languages come with OS/2?


The original BASIC and BASICA (for systems with BASIC in ROM, mainly IBM
systems), DOS's QBASIC, and OS/2's REXX comes with OS/2 2.x.


REXX is basically the official command language of OS/2.
REXX


Related Information:

What is REXX?  How do I write and run a REXX program? 

-------------------------------------------------------------------------------
Programming language availability


Virtually all of them: Assembler, C, C++, COBOL, Pascal, Fortran,
Smalltalk, Modula-2, LISP, Forth, Perl, and more.  The OS/2 Tools Guide
on cdrom.com (os2/all/info/tinf34.zip) contains information on these and
more.  Also check the User's Edition for information on IBM's
Development Tools Guide.  It's a free catalog from IBM.


All prices listed are "list price," as listed in Fall/Winter
Programmer's Paradise catalog.  All entries marked "Freely Available"
can be obtained from cdrom.com unless otherwise noted.


Ada

o GNU Ada 2.04 for OS/2; freely available; under development;
cs.nyu.edu in the pub/gnat directory
o Alsys AdaWorld OS/2

Assembler

o IBM Macro Assembler, $288
o GNU assembler v2.3, included in emx 0.9a package
o GNU assembler v1.38, included in GCC/2 v2.5.4 package
o Turbo Assembler v3.21, included in Borland C++ for OS/2 package

BASIC

o CA-REALIZER, $100
o ObjectView PM Basic, $899

C/C++

o Borland C++ for OS/2 v1.00, $495
o CA-C++ for OS/2, $449
o emx/gcc 0.9a (v2.6.3), freely available
o GCC/2 v2.4.5, freely available
o IBM C-Set++ 2.x FirstStep (without optimization) $85 (CD-ROM)
o IBM C-Set++ 2.x , $595
o IBM VisualAge C++ 3.0, price: ???.
o Objective C, $249 (not to be confused with the Objective-C language
  included free with emx/gcc and GCC/2)
o TopSpeed C Pro, $297
o TopSpeed C Standard, $198
o WATCOM C/C++ 16 9.5, $495
o WATCOM C/C++ 32 9.5, $599
o Metaware High C/C++

CSP

o IBM CSP/2, $???

COBOL

o MicroFocus Workbench with COBOL, tools and editor, $1,250.
Call 1 800-872-6265 for more information

Fortran

o GNU Fortran (g77-0.5.14). 
  Freely available (ftp.uni-stuttgart.de:/pub/systems/os2/emx0.9a/contrib)
o f2c [Fortran to C translator], freely available
o IBM Fortran/2 OS/2, $761
o WATCOM FORTRAN 77 16 9.5, $495
o WATCOM FORTRAN 77 32 9.5, $599

LISP

o CLISP, freely available 
  (ma2s2.mathematik.uni-karlsruhe.de:/pub/lisp/clisp/binaries/os2/clisp.zip)
o XLisp, freely available

Pascal

o Borland Pascal v7.0, $495 (requires patch)
o MicroWay, $???
o Power Pascal/2, freely available, req. MASM 6.00
o p2c [Pascal to C translator], freely available
o pasos2, freely available, supports EMX/GCC (ftp.eb.ele.tue.nl)
o SPEED-Pascal/2 for OS/2 GAMMA (March 95), 
  (available at hobbes.nmsu.edu as speed2.zip)
o TopSpeed, $???

PERL

o GNU PERL v4.0pl36, freely available

PL/I

o IBM PL/I for OS/2

Prolog

o  SWI Prolog for OS/2, available at ftp.cdrom.com:os2/2_x/unix/plbin.zip

Modula-2

o TopSpeed, $???
o Stonybrook, $???

Scheme

o XScheme for OS/2, freely available
o MIT Scheme for OS/2, freely available (swiss-ftp.ai.mit.edu:/pub/scheme-os2/)

SmallTalk

o SmallTalk/V PM, $995
o Parc Place Smalltalk, $???

-------------------------------------------------------------------------------
The two GNU C/C++/ObjC packages


The two versions of GNU C that are available were ported to OS/2 with
different goals and philosophies in mind and therefore have different
characteristics.  However, both systems include a fairly complete C
library and can be used to compile useful programs, although their
support of Unix-specific semantics differs.  Furthermore, both systems
are being actively developed and are constantly improving.


The goal of GCC/2 is to create a pure, freely redistributable OS/2 2.x
development environment.  It is based on GNU C 2.5.4, supports C, C++,
and Objective C, and can create PM programs.  It produces "native" 32
bit .OBJ files that are linked with OS/2's LINK386.EXE, and can be
linked together with .OBJ files produced by IBM C-Set++ and other
compatible compilers.  The mailing list os2gcc@netcom.com exists for
discussion of this port; send mail to os2gcc-request@netcom.com for
subscription information.


emx/gcc 0.9a, based on the GNU compiler 2.x.x, supports C, C++,and Objective C 
and can create PM programs.  emx's goal is to make porting Unix programs
easier by emulating Unix semantics as closely as possible. It produces
programs that can run under OS/2 using EMX.DLL, it produces programs
that can run under OS/2 without any DLL requirements (linked with OS/2's
LINK386), and it can produce MS-DOS 32-bit executables (emx.exe DOS
extender required for MS-DOS executables).  emx/gcc uses standard Unix
development tools like ld and nm, and fully supports Unix-isms like
select() and fork().  A version of gdb exists that can debug emx/gcc
programs. An emx-related mailing list exists; send mail to
LISTSERV@eb.ele.tue.nl with a message body of "help" for subscription
information.

-------------------------------------------------------------------------------
Which of these compilers can be used to generate PM apps?


Basically, all the compilers except the following can generate PM
applications:

o CLISP
o XLisp
o PERL
o XScheme


-------------------------------------------------------------------------------
What is REXX?  How do I write and run a REXX program?


REXX is the IBM SAA (Systems Application Architecture) standard,
user-friendly programming language.  It is available for IBM
mainframes, Unix, the Amiga, DOS (Quercus's Personal REXX),
Windows, and other platforms.  It has been a part of standard
OS/2 since Version 1.3.  Programs written in REXX that do not use
system-specific libraries are fully portable.

OS/2 2.x comes with an online REXX reference, and printed REXX
documentation is available (Mike Cowlishaw's REXX book, IBM's twin
guides).  The Usenet group comp.lang.rexx discusses REXX programming.

It should be stressed, however, that OS/2's online REXX reference is a
reference, not really a tutor.	Paul Prescod
(papresco@undergrad.math.uwaterloo.ca) is working on a free REXX tutor,
which will be available on cdrom.com.

Watcom also makes VX REXX and Hockware makes VisPro/REXX.  Both of these
products allow quick and easy development of REXX PM programs.

-------------------------------------------------------------------------------
What debuggers are available for OS/2?

IPMD, a PM-based debugger, ships with C-Set++.  It is capable of
source- and assembly-level debugging multithreaded 16 bit and 32 bit
OS/2 applications

emx/gcc comes with gdb 4.11, the GNU debugger.

Borland C++/2 comes with a PM-based debugger called Turbo Debugger GX
which has the same basic functionality as IPMD.

There are also several commercial debuggers on the market.  WATCOM C and
FORTRAN come with WVIDEO, a full-screen source or assembly level
debugger that handles multithreaded 16 and 32 bit OS/2 programs.
Multiscope (no longer available for 32-bit OS/2) and Periscope are
others.  CodeView is still applicable for 16-bit OS/2 apps, but it is
not recommended for 32-bit apps.

-------------------------------------------------------------------------------
Which assemblers can produce OS/2 32-bit code?

o Masm 5.2 and 6.0B can produce 32 bit, 2.x compatible code.
o Masm 6.1 no longer supports OS/2.
o Tasm 1.0 (OS/2) generates 2.x code, when proper options are given.
o Tasm 3.2.1 (DOS) can produce OS/2 compatible .OBJ files with the /oi option.
o Turbo Assembler 4.1 for OS/2 (in Borland C++ for OS/2)
o GAS (GNU Assembler) 1.38.1
o GAS (GNU Assembler) 2.3
o IBM has an assembler for 2.x, but they won't release it to the public.

-------------------------------------------------------------------------------
CSD levels for IBM C Set++?


Following CSD's are current

o CTC0011 - IBM C/C++ Tools (Compiler) 2.0 and 2.01
o CTU0003 - IBM C/C++ Tools (Utilities) 2.0 and 2.01
o CTL0009 - IBM C/C++ Tools (Class Libraries) 2.0
o CTM0009 - IBM C/C++ Tools (Class Libraries) 2.01

Be careful with CTL0009 and CTM0009. Do NOT install wrong CSD. Check with
the OS/2 command SYSLEVEL the version level of the class libraries.
IBM C/C++ packages bought later than November 1993 should be 2.01 or higher.



===============================================================================
Utilites, Tools
Tools, Toolkits, Accessories

This section covers tools, toolkits, and accessories available to OS/2
programmers.

-------------------------------------------------------------------------------
How do I use PATCH, maybe with my own programs?


The file format is actually quite simple - you just need a text file
which consists of lines each starting with one of the following
commands:

    FILE <filename>         e.g. FILE test.exe

specifies the file to which the following instructions are to be
applied. A patch file may contain multiple FILE directives.

    VER <offset> <data>     e.g. VER 00001234 abcdef

checks if the specified data is present at the given file offset [all
values in hex, data can be just a string of digits with no blanks in
between; up to 16 bytes are allowed in one statement]. If the check
fails, the patch process is aborted resp. the program skips to the next
FILE statement.

    CHA <offset> <data>     e.g. CHA 00001234 012345

changes the data at the given offset - syntax is same as in VER. It is a
good idea to do all VERification before starting the first CHA.

Blank lines and data lines starting with a ";" are ignored.

As you can see from this, there seems to be no way to _insert_ or _delete_
bytes in the file. To make patches applicable to multiple
versions of the same file, you can specifiy more than one
FILE directive for the same filename - only a patch with
all VER commands matching is executed.

Credit:  Marcus Groeber

-------------------------------------------------------------------------------
What programming editors are available for OS/2?


OS/2 comes with the Enhanced Editor (EPM).

GNU Emacs
GNU Emacs 19.27 is available.It supports PM windows, VIO windows and
full-screen sessions. Emacs is available on cdrom.com in os2/2_x/unix/gnu/emacs.
(If you want to recompile emacs, you will need the full emx distribution; see
question 1.2.) Also, you must have HPFS installed to use GNU
emacs. You can read, write, and edit files that exist on a DOS
partition, but you must have GNU emacs installed on an HPFS partition.

Several public-domain vi clones are available, including elvis, Stevie
and levee. The MKS Toolkit also includes vi.

Many other text editors are available.

o Epsilon, by Luguru, (412) 421-5678.  DOS upgrade to OS/2 is
  $90.  Character based editor.

o QEdit 3.0, by SemWare, (404) 641-9002.  Character based editor.
  Supports long filenames and extended attributes. Semware can be reached
  by EMailsupport@semware.atl.ga.us or sales@semware.atl.ga.us.

o Brief, KEDIT, others? [Vendors, phone numbers, prices?]

o EHP, a text mode editor, was recently ported by Axel Uhl to PM, and
  is available on cdrom.com.

o THE (The Hessling Editor), is a free text-mode editor, based on
  IBM's VM/CMS XEDIT and Mansfield Software's KEDIT.  THE has full REXX
  macro capabilities. Versions also exist for DOS and most Unices.

o BOXER 6.0 for OS/2. Shareware and commercial versions available. Some
  features; multi-level undo, multiple windows, syntax highlighting.
  Commercial version priced at $89. Boxer Software can be contacted at;
  phone2126@compuserve.com

o Slick for DOS/Windows, by MicroEdge.

o Visual Slickedit for OS/2, by MicroEdge. Price: $295 (US).

o Preditor/2 V2.1, by Compuware Corp.. Introductory price: $149 /US).
  Demo version available at ftp.cdrom.com.

o ZED for OS/2. PM-based editor with lots of features. Shareware. Available
  at hobbes.nmsu.edu:/os2/editors or ftp-os2.cdrom.com:/os2/incoming.

-------------------------------------------------------------------------------
What programming tools/toolkits/accessories are available for OS/2?


C-Set++ includes the following tools (excluding the compiler & debugger
themselves):

o EXTRA - Execution trace analyzer
o A Class browser

The IBM Programmer's Toolkit, included in Workset/2 and also in C-Set++,
includes many tools:

o A dialog editor
o A SOM compiler
o A font editor
o An icon editor
o An IPF compiler
o Message compile & bind tools
o Resource compile & bind tools
o KwikInf
o . . . and a couple others

Borland C++ for OS/2 also includes a number of utilities, such as the
Resource Workshop.

The MKS Toolkit, available from MKS ($299 USD, 800-265-2797 or
inquiry@mks.com), has over 160 Unix tools, including Korn shell, tar,
vi, awk, grep, tail, cpio, and so forth.  It also contains a Lex and
Yacc capable of generating C, C++, and Turbo Pascal code.

There is a product called ARGO/UX which provides a BSD environment for
OS/2.  [details?]

SOFTPRO GmbH has announced its 32bit C++ class library
TOUCH-GUI 2.0 for OS/2 2.x.

-------------------------------------------------------------------------------
TOUCH-GUI 2.0 for OS/2 2.x


SOFTPRO GmbH has announced its 32bit C++ class library TOUCH-GUI 2.0 for OS/2
2.x. The product contains more than 130 classes which support windows, menu
bars, multi-threading, controls, dialog boxes, graphics, printer control, MDI,
DDE, Drag and Drop, loadable resources from DLL's, and management of profiles
(.INI files). Other features include auxiliary classes for data manipulation
and storage management. Additionally, TOUCH-GUI 2.0 contains high-level classes
like toolbars, formatted entry fields, tables, and complete NLS.

Supported C++ compilers are: Borland, GNU (emx), IBM, and WATCOM

The product includes online (.INF) and printed documentation, a demo
program, and samples.

TOUCH-GUI 2.0 costs DM 1.720,00 excl. VAT (ca. $1075), the runtime DLL's may
be distributed royalty free. Special project licenses are available, contact
the supplier.

Contact:
SOFTPRO GmbH, Stadtgrabenstr. 21, 71032 Boeblingen, Germany
Tel.: +49 7031 6606-0
Fax: +49 7031 6606-66
Mr. Frank Fuchs (extension -50)
Internet email: ffu@softpro.de
IBMMAIL (IEA): DEJP9SK9

-------------------------------------------------------------------------------
Prominare Designer for OS/2


Prominare Inc, of Canada have launched a development tool for creating
GUI's for OS/2. The tool can generate code for multiple class libraries
and multiple compilers. 

Supported Platforms are OS/2, Windows 3.1 and Windows NT.

The tool supports all PM controls, MMOS/2 and Pen for OS/2.

Suggested retail price: $699 (US).

A complete list of qualified resellers can found through
Prominare's WWW server at http://www.prominare.com/prominare.

-------------------------------------------------------------------------------
JAM for OS/2


JYACC, Inc. have released JAM (JYACC Application Manager), their tool
for building 32-bit client/server GUI applications. 

Over 100+ platforms are supported, including OS/2, MS-Windows, DOS, Motif,
VAX/VMS, Macintosh and many UNIX-implementations.

Suggested retail price: $2000 (US).

The toolkits includes; Database drivers for JDB (JYACC database) and one
of the drivers for Oracle, Sybase, Informix or DB2 (customers choice),
and complete documentation.

-------------------------------------------------------------------------------
What GNU tools are available and where can I find them?


Nearly all the GNU utilities have been ported to OS/2 2.x - and nearly
all of those ports are located on cdrom.com in os2/2_x/unix/gnu.
Other, more involved (or independent) ports of GNU software is scattered
about cdrom.com, including a PM version of GhostView and GhostScript.

-------------------------------------------------------------------------------
Is a socket library available?  How can I use it?


emx 0.9a supports sockets without the Programmer's Toolkit.  The
socket support of emx is `seamless': fdopen(), for instance, can be
used on sockets.

IBM's TCP/IP 2.0 ($200) includes an optional
Programmer's Toolkit ($500, part #02G6973).  It includes a socket
library, and support for Sun RPC, NCS RPC, and a limited Kerberos
capability.  It requires IBM C-Set++ or another compiler that
understands 16-bit code.

For REXX programs there is a socket library called REXXSOCK available
from cdrom.com as os2/ibm/ews/rxsock.zip.  It can be used to write
TCP/IP applications such as Gopher, IRC, etc.

FTP Software, Inc., has an OS/2 version of its TCP/IP product.  They
can be reached at (617) 246-0900 or info@ftp.com.

If you have the IBM TCP/IP 2.0 base package and IBM C-Set++, you can
use the TCPIPDLL.DLL directly.

===============================================================================
Compiling


This section covers compiling OS/2 programs and accessories.  The
following topics are available:

Can I distribute the C-Set++ runtime DLL?
How do I statically link my C/C++ programs with C-Set++?
How big should my stacksize be?
How do I perform parallel compilation?
What is an RC of 87 (invalid param) from the API?
GCC/2 crashes with trap when I try to compile a program. Why?
How do I recompile EPM (easily)?
How to I get BC++ for DOS/Windows to run?

-------------------------------------------------------------------------------
Can I distribute the C-Set++ runtime DLL?


Yes, you can! (But it is not as simple as that really...)

You can not distribute the runtime DLL that comes with C-Set++ in its
native form, you must rebuild the DLLs you want to distribute and delete 
functions in the .DEF files, so that your DLL files only contains functions
your program need. Read more about this topic in C/C++ Tools - 
Programming Guide pages 195-222, that comes with C-Set++ (2.x).

-------------------------------------------------------------------------------
How do I statically link my C/C++ program with C-Set++?


There are several flags you must use to get static linking work.

Add the flag /Tdp when compiling C/C++ sources. This flag ensures that
any template functions are resolved correctly.

When linking the program, make sure that the /B flag looks like this:
 /B"/pmtype:pm /NOI /NOE /NOD".

/NOD    Tells the linker to disregard default libraries

/NOE    Tells the linker to ignore extended library information
          found in the object files.

/NOI     Maintains the case sensitivity for identifier names.


Linking a typical PM program might look like this:

myapp.exe: $(OBJS) myapp.res
icc $(CFLAGS) /Gn+ /Ge+ /Gd- /Gm+ /Fe myapp.exe $(OBJS) myapp.def \
/B"/pmtype:pm /NOI /NOE /NOD" DDE4MBS.LIB DDE4MUIB.LIB \
DDE4MUIC.LIB DDE4MUID.LIB DDE4CC.LIB OS2386.LIB
rc myapp.res myapp.exe

Credit: Lyle Sutton

-------------------------------------------------------------------------------
How do I determine what C/C++ compilator is compiling my code?


If your source code is written for several compilers, one need to know what
uniqe defines these compilers use. 


IBM C/C++              __IBMCPP__
Borland C++ for OS/2   __BORLANDC__
EMX/GCC                __EMX__
Watcom C/C++           __WATCOMC__


-------------------------------------------------------------------------------
How big should my stacksize be?


o For non-PM programs: At least 8k for every thread.
o It is critical to avoid stack sizes where byte 2 has a value of 2 or
4, e.g.:

o 0x00020000 (128k)
o 0x00040000 (256k)
o 0x33023678
o 0x11041111

Otherwise, when executing under OS/2 2.0GA there may be various and
always differing runtime error behaviors.  (This is documented in
OMF.INF, an online reference from IBM concerning OBJ/EXE file formats
for 16 and 32 bits.)


-------------------------------------------------------------------------------
How do I perform parallel compilation?


You can accomplish parallel compilation in one of two ways. The
first involves GNU Make, the second involves DMAKE.

GNU Make:

To perform parallel compilation with GNU Make simply do two
things:

o Make sure your make file does not execute the make utility (again)
  from inside the make file.
o On the GNU Make command line, specify '--jobs x', where 'x' is the
  maximum number of concurrent jobs you will allow GNU Make to execute.
  If you do not wish to have a maximum, then you may specify simply
  '--jobs' and GNU Make will execute as many jobs as possible at one time.


DMAKE:

The ini file comes in the dmake archive in a few
different flavours.  Take the file with the extension .msc
and rename it to dmake.ini.  Set an environment variable in
config.sys

SET MAKESTARTUP=d:\COMPILER\DMAKE.INI

====== Makefile

CC=icc
COPTS=/Sm /Gt+ /Ti+ /O- /Gs+
CONLY=-c

LIBS=os2

INCS=psfax2.h

MAXPROCESS=2

all: psfax2.exe sendfax.exe

modem.obj:  modem.c $(INCS)
   $(CC) $(CONLY) $(COPTS) modem.c

psfax2.obj: psfax2.c $(INCS)
   $(CC) $(CONLY) $(COPTS) psfax2.c

psfax2.exe: psfax2.obj modem.obj
   link386 /ST:32768 /CO psfax2+modem;

sendfax.obj: sendfax.c $(INCS)
   $(CC) $(CONLY) $(COPTS) sendfax.c

sendfax.exe: sendfax.obj modem.obj
   link386 /ST:32768 /CO sendfax+modem;

====== End of makefile

Couple of gotchas.  There is a bug in the MAXPROCESS handling of the
copy I use.  Set it to 1, and you get a typical make, ie one at a time.
Set it to 2, and the thing launches 3 processes. Ie with more than 1,
you actually get n+1 processes running.

Another caveat

xxx.yy: aaa.bbb
    command1
    command2
    command3

The above structure does not work.  The reason is that all 3 commands
will be spawned together in parallel sessions.  To serialize them you
must make up dependancies in the makefile.  I ran into this becuase I
have one makefile that completes every link stage by copying the result
onto a network drive.  I had to take all the links that previously had 2
commands, and break them into 2 blocks with the second being a
dependancy of the remote file on the local file, and the copy command.

One more word of warning, dmake is not to pleased with those convoluted
things that workframe spits out in the name of makefiles.  You may have
to do a significant amount of makefile editing, depending on how you
makefile look now.

Provided that 3 simultaneous copies of your compiler doesn't push your
machine into excessive swapping, the improvements in build speed are
amazing.  Before dmake I'd never seen a build actually top the cpu meter
here, the process was always waiting for disk as it loaded
compilers/headers etc.	Now the compiler sits in ram and just gets
re-used, and the headers are coming directly from cache all the time.
Build time cut in half, and my cpu guage is pegged at 100% when a build
is running.

-------------------------------------------------------------------------------
What is an RC of 87 (invalid param) from the API?


You are (most likely) not including the prototype.  Use the
following flags to get a good balance of warning messages:

        /Kbperc

For C Set++, I use these flags as the approximate equivalent

        /Wall /Wext- /Wgnr- /Wgot- /Wpor- /Wppt- /Wtrd /Wuni- /Wvft-

I have these set as part of my ICC string in CONFIG.SYS, so I
don't have to add them to each compile.  I have found that these
settings give me a good combination of thorough warning messages
without too many warnings.

-------------------------------------------------------------------------------
GCC/2 crashes with a trap when I try to compile a program.  Why?


Because you didn't read the README or INSTALL files, probably.  There
are three general reasons GCC/2 will crash:

o You did not set up the environment variables in CONFIG.SYS
  properly.  Read doc/INSTALL for instructions.
o Some program that gcc expects to be in the PATH is not;
  unfortunately, gcc crashes instead of just printing an error
  message.  You may have forgotten to install something, or your PATH
  may be wrong; see above.  Giving gcc the -v option will cause
  it to print each command line as it executes it; this will
  tell you which program is missing.
o You are trying to get gcc to link your program for you.
  It cannot because ld does not exist, and so it crashes (see
  item 2).  You must specify -c, -E, or -S on every invokation
  of gcc, and then use LINK386.EXE to create an executable.  See
  the sample makefiles for an example of how to do this.

[Colin Jensen, the current maintainer of GCC/2, also adds the
following. . . ]

Not true as of gcc/2 2.3.3.  Gcc will invoke a small stub program
called ld.exe that in turn will invoke link386 for you.  If the command
line is too long for the ever-pathetic link386 to handle, ld will properly
create a response file.

It is easier to use gcc/2 to call link386 than to do it
yourself since gcc/2 will also arrange to call a subprogram called
"collect" that is required before linking a C++ program.

Also, gcc/2 is just plain easier to use than link386, for example:

         gcc -o foobar.exe mydef.def foobar.cpp mylib.lib

will compile foobar.cpp, link with mylib.lib, and pass the linker
definition file mydef.def to link386 in the correct order.  Link386
demands that you know which types of files can be put between which
sets of commas on the command line.

-------------------------------------------------------------------------------
How do I recompile EPM (easily)?


I have a small CMD file to automatically compile all needed macros.  It
is from Larry Margolis (IBM), one of the authros of EPM.

You need to have at aleast minimum knowledge of EPM.  The online manual
describes most things.	Concerning the E macros (.E) the best thing to
do is, to look in the online documentation for general questions
(like: What does 'getpminfo' do?) and then look into the supplied
standard E macros for detailed reference on hwo to use the EPM functions
exactly.

Online documentation files are: EPMTECH.INF and EPMUSERS.INF

Here is MAKEEPM.CMD:

@echo off
set EPMPATH=h:\e;h:\e\emacros;h:\e\ebookie;h:\e\lamacros;h:\e\mye;
rem Command file to call ETPM and beep in case of error.  Can be given
rem a list of macro files to compile; the default is to compiler all of
rem the distributed .ex files.			by Larry Margolis
setlocal
@if .%1 == . goto all
:loop
etpm %1 /v || echo 'Error in %1!'
@shift
@if .%1 == . goto end
@goto loop
:all
etpm EPM /v      || echo 'Error in EPM!'
etpm EPMLIST /v  || echo 'Error in EPMLIST!'
etpm LAMEXTRA /v || echo 'Error in LAMEXTRA!'
etpm MAILLIST /v || echo 'Error in MAILLIST!'
etpm EXTRA /v    || echo 'Error in EXTRA!'
etpm BKEYS /v    || echo 'Error in BKEYS!'
etpm E3EMUL /v   || echo 'Error in E3EMUL!'
etpm BOX /v      || echo 'Error in BOX!'
etpm DRAW /v     || echo 'Error in DRAW!'
etpm GET /v      || echo 'Error in GET!'
etpm MATHLIB /v  || echo 'Error in MATHLIB!'
etpm PUT /v      || echo 'Error in PUT!'
etpm HELP /v     || echo 'Error in HELP!'
etpm EPMLEX /v   || echo 'Error in EPMLEX!'
etpm MAKETAGS /v || echo 'Error in MAKETAGS!'
:end
endlocal


-------------------------------------------------------------------------------
How to I get BC++ for DOS/Windows to run?


Open up the Settings folder for the executable, and under "DOS Settings"
set DPMI_DOS_API to ENABLED.



===============================================================================
Documentation, Help

This section covers documentation and help available for OS/2
programming.

-------------------------------------------------------------------------------
Where can I get information on OS/2 APIs and programming?


The IBM Programmer's Toolkit, included in Workset/2, includes a
complete on-line syscall reference.

You can FTP the documentation for the 16-bit calls (IBM does not supply
them), such as Vio*(), Kbd*(), Mou*(), from cdrom.com as
os2/2_x/program/inf16bit.zip.

You can order the seventeen volume IBM OS/2 Technical Library
(possibly at a discount, see question 1.2) and/or order various
volumes individually.

Most of these volumes are now published by Que books also, and can
be found in many bookstores.

All this information is also on the OS/2 Online Library Omnibus Edition:
OS/2 Collection CD-ROM (SK2T-2176), and the Professional Developer's
Kit CD-ROMs have this information as well.


-------------------------------------------------------------------------------
Where can I get sample code?


There is a lot of sample code included in the IBM Toolkit, and four
sample programs (with source) included with C-Set++.  GNU C/C++, from
cdrom.com, includes a sample C, C++, and C++ PM program.  The OS/2
Redbooks also have some sample code.  cdrom.com also has little bits of
OS/2 and PM source code lying around in the os2/2_x/program
directory.

-------------------------------------------------------------------------------
Are there any OS/2 programming classes or seminars?


Yes.  Call IBM at either 1-800-3-IBM-OS2 in U.S. (or the PS/2 Help
Center).  Local IBM branches frequently hold OS/2 classes and
seminars.

See os2/2_x/info/ivleague.txt on cdrom.com or the User's Edition for a
list of third-party support organizations.

Make sure to check your local software companies.  Many of them offer
OS/2 specific classes also.

-------------------------------------------------------------------------------
What are good ref. books for programming in OS/2 and PM?
Reference Books


Van Nostrand Reinhold publishes a number of books on OS/2 2.x.  Those
that relate to programming are:

INTEGRATING APPLICATIONS WITH OS/2 2.0
By William H. Zack
0-422-01234-9

CLIENT/SERVER PROGRAMMING WITH OS/2 2.0, 2nd Edition
By Robert Orfali and Daniel Harkey, IBM Corporation
0-422-01219-5

WRITING OS/2 2.0 DEVICE DRIVERS IN C
By Steven J. Mastrianni; Foreword by John Soyring, IBM Corporation
0-442-01141-5

OS/2 2.0 PRESENTATION MANAGER GPI: A Programming Guide to Text, Graphics, 
And Printing
By Graham C.E. Winn, IBM Corporation
0-442-00739-6

THE COBOL PRESENTATION MANAGER PROGRAMMING GUIDE
By David M. Dill, Consultant
0-442-01293-4

LEARNING TO PROGRAM OS/2 2.0 PRESENTATION MANAGER BY EXAMPLE: Putting the 
Pieces Together
By Stephen Knight, IBM Corportaion
0-442-01292-6

THE ART OF OS/2 2.1 C PROGRAMMING
By Panov, Salomon, and Panov
620p, $39.95 US (suggested) includes disk with samples
ISBN 0-89435-446-9

OS/2 PRESENTATION MANAGER PROGRAMMING
By Charles Petzold
Ziff-Davis Press
ISBN 1-56276-123-4

DESIGN OF OS/2, SECOND EDITION
By Kogan & Deitel
Addison-Wesley
0-201-52886-X

ULTIMATE OS/2 PROGRAMMER's MANUAL
Mueller
McGraw-Hill Professional Computing 
0-07-043972-9

EFFECTIVE MULTITHREADING IN OS/2
By Dorfman & Neuberger, McGraw-Hill Professional Computing
0-07-017841-0,

OS/2 REMOTE COMMUNICATIONS: Async to sync
By Stonecipher
Van Nostrand Reinhold
0-0442-01814-2,

OS/2 V2 C++ CLASS LIBRARY: Power GUI Programming using C Set++
By Kevin Leong, William Law, Robert Love, Hiroshi Tsuji and Bruce Olson
Van Nostrand Reinhold
$36.95
0-442-01795-2

OBJECTS FOR OS/2 2.1
By S. Danforth, P Koenen and Tate et al
Van Nostrand Reinhold
ISBN 0-442-01738-3

OS/2 AND NETWARE PROGRAMMING: Netware Client API
By Gauthier
Van Nostrand Reinhold
ISBN 0-442-01815-0

OS/2 PROGRAMMING: Your Fast-Track Guide to OS/2
By Herbert Schildt & Robert Goosey
306p, $29.95
ISBN 0-07-881910-5

ADVANCED ASSEMBLY LANGUAGE
Steven Holzner and Peter Norton Computing Inc.
Brady Books, A division of Simon & Schuster, New York, NY, USA
1991  ISBN 0-13-633014-6

OBJECT-ORIENTED PROGRAMMING WITH SOM AND DSOM
Christina Lau
Van Nostrand Reinhold
ISBN 0-442-01948-3

REAL WORLD PROGRAMMING FOR OS/2 2.1
By Blain, Delimon and English
SAMS
ISBN 0-673-30300-0

RUNNING WINDOWS APPLICATIONS IN OS/2
Ayo Anise, Teresa Beck and Jean Shortley

DYNAMIC DATA EXCHANGE FOR OS/2 PROGGRAMMERS
By Glenn T. Puchtel
Disk included.

LOTUS NOTES RELEASE 3 IN THE OS/2 ENVIROMENT
By Tony Walsh


Books announced to be released Dec '94 and early '95.

THE OS/2 SURVIVAL KIT
By Proffit
Addison-Wesley

STEPPING UP TO OS/2 WARP
Abacus

THE OS/2 WARP BIBLE
Abacus

THE PHOTO CD BOOK (with OS/2 Warp applications disk)
Abacus

OS/2 FOR DUMMIES (2nd Edition)
By Rathbone
IDG Books
ISBN 1-56884-205-8

YOUR OS/2 CONSULTANT (Warp Edition)
By Tyson
Sams

OS/2 Unleashed (Warp Edition)
Moskowitz & Kerr
Sams

MASTERING OS/2 WARP
By Dyson
Sybex

USING OS/2 WARP
By Clifford et al
QUE

TEACH YOURSELF OS/2 PROGRAMMING
Sams

Quick Reference Library for OS/2 Functions: Vol 3 - Workplace Shell
By Scholin
VNR
ISBN 0-442-01899-1

Developing Multimedia Applications Under OS/2
By Lopez
VNR
ISBN 0-442-01929-7

OS/2 Warp Advanced Graphics GPI
By Knight and Ryan
John Wiley & Sons

OS/2 Remote CommunicationsT
Stonecipher
VNR
ISBN 0-442-01814-2

Quick Reference Library for OS/2 Functions: Vol 4 - GPI Functions
By Scholin
VNR
ISBN 0-442-01900-9

Quick Reference Library for OS/2 Functions: Vol 5 - DOS Functions
By Scholin
VNR
ISBN 0-442-01901-7

OS/2 Warp Presentation Manager Programming for Power Programmers
By Stern and Morrow
Wiley-QED
ISBN 0-471-05839-4

OS/2 Warp Control Program API
By Stock
Wiley-QED
ISBN 0-471-03887-3

Secrets of the OS/2 Masters
By Sullivan
VNR
ISBN 0-442-01991-2

OS/2 Presentation Manager
By Drapkin
VNR 
ISBN 0-442-01989-0



REXX Reference Summary Handbook
Dick Goran
ISBN 0-9639854-2-6

OS/2 Warp for Dummies
ISBN 1-56884-205-8, $19.95 (US)
Andy Rathbone
IDG Books

Programming the OS/2 Warp Version 3 GPI
ISBN 0-471-10718-2, $39.95 (US)
Stephen Knight and Jeffrey Ryan
John Wiley & Sons

Books mentioned below was scheduled to be released April'95, maybe they are
out in the bookstores now.

The REXX Cookbook
ISBN 0-9632773-4-0, $27.95 (US)
Optional companion disk (ISBN # 0-9632773-5-9, $14.95 (US) )
Merrill Callaway
Whitestone

Quick Reference Library for OS/2 Functions
Scholin
VNR
ISBN 0-442-01902-5

Making OS/2 Work for You Using OS/2 Warp
Azzarito & Green
ISBN 0-471-06083-6, 
Wiley-QED, 

The Art of OS/2 Warp C Programming
Panov et al
ISBN 0-471-08633-9
Wiley-QED, 

OS/2 Warp Presentation Manager API
Stock
ISBN 0-471-03873-3
Wiley-QED


IBM has a .INF file where all IBM OS/2 related books are listed.

-------------------------------------------------------------------------------
What are good OS/2 programming magazines?


OS/2 Developer

For new orders and customer service, call (800) WANT-OS2
(800-926-8672) or (708) 647-5960.  IBM employees and branch
office customers can subscribe to OS/2 Developer through IBM
Mechanicsburg's Systems Library Services (SLSS) using OS/2
Developer's order number G362-0001.  POSTMASTER: Please send
address changes to OS/2 Developer, P.O. Box 1079, Skokie, IL,
60076-8079.


Electronic Developers' Magazine/2

Excellent electronic magazine for OS/2 developers.  Available somewhat
monthly from ftp-os2.cdrom.com in 2_x/program/newsltr/edmi*.zip.


PC Magazine

While this is not by any means a dedicated OS/2 programming magazine, it
includes several gems from Charles Petzold, most notably a nice series
on font handling.

-------------------------------------------------------------------------------
What are the OS/2 redbooks, and how do I get them?


(see end note for FTP information)

IBM publishes so-called "redbooks" on many products, including OS/2
2.x.  They seem to be a combination of power-user's guides and design
information that may be of use to both users and programmers [I
haven't received my copies yet, so I cannot say for sure.]  These are
usually intended only for special IBM customers and contain
documentation that is generally unavailable anywhere else.  You can
order these books directly from IBM (see below).  All of the following
names are implicitly preceeded by "OS/2 Version 2.0."

Volume 1: Control Program: GG24-3730, $4.15
Technical information on Memory Management, Task Mangement,
Support, Installation Considerations, Hardware Considerations, Boot
Manager, National Language Considerations, Intel 386 architecture,
Channel Architecture and SCSI.	Documents config.sys.

Volume 2: DOS and Windows Environment, GG24-3731, $6.20
MVDM,8086 Emulation,MVDM DOS emulation, Device Drivers, Memory
Extender Support, Installing and Migrating Applications, Windows
Applications, DPMI, Running DOS apps,DOS settings, VMB.

Volume 3:  PM and Workplace Shell GG24-3732, $3.65
Available now, but I don't know precisely what's in it.

Volume 4: Application Development GG24-3774, $5.25
Technical programming info (includes list in C) Overview,
Object-Oriented Apps, PM application model, flat memory model,
building PM app, Workplace AOAShell and System Object Model, Direct
Manipulation (Drag/drop), PM resources, Multitasking
Considerations, SAA CUA considerations, App Migration, Mixing 16
and 32 bit application modules, compiling and link editing (SOM),
Adding Online Help, Problem Determination, Managing Development,
Naming conventions.

Volume 5: Print Subsystem, GG24-3775, $5.20
Information on programming the print subsystem.


The special part number GBOF-2254 is for all currently available
volumes (presently 1-5).


ORDERING INFORMATION:

The OS/2 2.x Redbooks are available from IBM's TeleServices customer
support number.  The number is 1-800-7654-IBM (1-800-765-4426).  You
can pay by credit card or mail in a check after calling.  The order
will take about 2 weeks but can be sped up by paying for faster
shipping.

You can also order the redbooks from your local IBM Branch Office
library.  Some possibly useful phone numbers are included here.

        IBM Central Library, Los Angeles CA: (213) 621-6710
                P.O. Box 60737
                Los Angeles, CA 90060
        Canada: (800) 465-1234, ext 4205 ($33.52)
        UK: (0256) 478166, (#36.51, credit cards accepted)
        Australia (Victoria): 698-1234 ($46.80 A)
                IBM Australia
                The Library
                211 Sturt Street
                South Melbourne, 3205
                Att: Kate Seeley
        Denmark: 33 32 40 55 (dkk 310)


The redbooks are also FTP'able from cdrom.com as the
os2/ibm/misc/redbk*.zip.  The source code that accompanies the first
three redbooks can FTP'able from cdrom.com as os2/2_x/program/rb37*.zip.

Various OS/2 technical programming books can be found on the 
Developers Connection CDROM.

The entire OS/2 Library can be found on the "OS/2 Online Book Collection 
CDROM", Order Number S53G-2166-00.

-------------------------------------------------------------------------------
How can I view the GNU C documentation?


GNU C/C++ comes with documentation from the Free Software Foundation
in texinfo (.texi) format.  This documentation is about gcc in
general, and has no OS/2-specific information.

All utilities needed to compile/view/tex the texinfo files are readily
available for OS/2.  The GNU texinfo package, available on cdrom.com in
os2/all/unix/gnu/gnuinfo.zip, includes makeinfo.exe for compiling
texinfo, info.exe for viewing them, and texinfo.tex and texindex.exe
for TeXing them.

An ASCII text version of the gcc documentation is also available on
cdrom.com, in the file os2/2_x/unix/gnu/gcc2_233/gcc_inf.zip.

An INF hypertext version of the gcc (and related programs) documentation
was uploaded recently to cdrom.com.

emx/gcc includes its own hypertext style reader and texinfo files for
all the GNU tools that come with it.

GCC/2 includes the hypertext manuals for gcc, gas, and gnumake
as well as a PM reader for the manuals.

GNU Emacs for OS/2 reads GNU info files in their native format, with no
conversion necessary on the user's part.

-------------------------------------------------------------------------------
Where can I get documentation on the OBJ/LIB/EXE format used by OS/2 2.x?


o The .EXE format was described briefly in PC Magazine, Vol 11 No. 12
  (June 30, 1992?); it was also described in a 1988 issue.
o The glib source code contains a full description of the 32 bit OBJ and LIB
  formats.  It is found at cdrom.com:/os2/2_x/unix/gnu/gcc2_233/glibs115.zip.
o It is available in text form from cdrom.com as
  os2/2_x/program/lxexe.doc.
o OMF.INF from IBM (the IBM BBS) comes highly recommended.


-------------------------------------------------------------------------------
Where can I find information on HPFS?


There is a Microsoft Systems Journal article about it (64k ZIPped, so it
has a lot of information) in ftp-os2.cdrom.com in all/info.hpfs.zip.
PM Programming



===============================================================================
This section covers Presentation Manager programming.

-------------------------------------------------------------------------------
Printing

This section covers Presentation Manager printing.

-------------------------------------------------------------------------------
Are there any SIMPLE examples of printing?


Here is some sample code, but also check out PRINTQ12.ZIP on cdrom.com
in 2_x/program. PRINTQ12.ZIP contains a DLL that encapselates alot of
the messy details.

Here is the working code for allowing the user to change the printer
setup. I've also included the code that I use to start and end printing,
so you can see how it all works.

If you have any questions or comments, feel free to write.

PrinterSetup() is the routine that gets the printer setup information
from the user.

GetPrinterHPS() is the routine that gets the DC and PS for the printer,
and starts the document.

EndPrint() is the routine that ends the document, and closes the DC.

First, here's a little sample of how to use PrinterSetup(),
GetPrinterHPS(), and EndPrint():

{
   HPS          hPrinterPS;
   HDC          hPrinterDC;
   DEVOPENSTRUC dops;

   . . . 

   dops->pszLogAddress = NULL;  // in case PrinterSetup fails, this will tell
                                // GetPrinterHPS() to use default printer info

   PrinterSetup(&dops);

   hPrinterPS = GetPrinterHPS(&dops, &hPrinterDC,
                              "Document Name", 0L, 0L);
   if (hPrinterPS != GPI_ERROR) {
      // do your printing here

      EndPrint(hPrinterPS, hPrinterDC, "Document Name");
   }

   . . . 
}


/*
******************************************************************************
**  FUNCTION: PrinterSetup
**  PURPOSE :   This function allows the user to change printer settings.
**
**  PARAMS  :   lpdos - the printer info
**  RETURN  :   BOOL   (TRUE = success)
**
**  DATE    :   11-Dec-1992
**  AUTHOR  :   Carl Samos
******************************************************************************
**  Modified: Date:     Description of changes
**  CNS     :   11-Dec-1992 Initial version
******************************************************************************
*/
BOOL PrinterSetup(DEVOPENSTRUC FAR *lpdos)
{
   PDRIVDATA     pDriveData;      /*  The drive data  */
   unsigned long ulSize;          /*  The buffer size required  */
   char          szPrinter[32];   /*  The printer name  */
   PSZ           pszDriver;       /*  The driver name  */
   PSZ           pszLogicalPort;  /*  The logical port  */
   char          szDeviceName[32];/*  The printer's name */


   // get the printer driver, name and logical port
   // GetPrinterInformation allocates space for pszDriver and pszLogicalPort
   if (!GetPrinterInformation(szPrinter, &pszDriver,
                              szDeviceName,&pszLogicalPort))
      return(FALSE);
   
   
   // get the size needed for the DRIVDATA struct
   ulSize = DevPostDeviceModes(habMain, NULL, pszDriver, NULL, szPrinter, 0L);
   
   
   if (ulSize != DPDM_NONE && ulSize != DPDM_ERROR) {
      pDriveData = (PDRIVDATA) _fmalloc(ulSize);
      pDriveData->cb = ulSize;
  
      // bring up the dialog boxes, and fill the DRIVDATA struct
      ulSize = DevPostDeviceModes(habMain, pDriveData, pszDriver, NULL,
                                  szPrinter, 0L);
   
      if (ulSize == DEV_OK) {
          // if there is a printer name, copy it into the DRIVDATA
          if (szDeviceName[0] != '\0')
             strcpy(pDriveData->szDeviceName, szDeviceName);
   
          // remove the old information
          if (lpdos->pszLogAddress != NULL) {
             _ffree(lpdos->pszLogAddress);
          }
          if (lpdos->pszDriverName != NULL) {
             _ffree(lpdos->pszDriverName);
          }
          if (lpdos->pdriv != NULL)
             _ffree(lpdos->pdriv);
   
          /*  Setup the DEVOPENSTRUC  */
          lpdos->pszLogAddress = pszLogicalPort;
          lpdos->pszDriverName = pszDriver;
          lpdos->pszDataType   = NULL;
          lpdos->pdriv         = pDriveData;
      }
      else {
          _ffree(pszDriver);
          _ffree(pszLogicalPort);
          _ffree(pDriveData);
      }
   }
   else {
      _ffree(pszDriver);
      _ffree(pszLogicalPort);
   }
   
   return(TRUE);
}


/*
******************************************************************************
**  FUNCTION:   GetPrinterInformation
**  PURPOSE :   This function gets the current printer information from the
**                    os2.ini file.
**
**  PARAMS  :   PSZ pszPrinter
**              PSZ pszDriver
**              PSZ pszDeviceName
**              PSZ pszLogicalPort
**
**  RETURN  :   void
**
**  DATE    :   11-Dec-1992
**  AUTHOR  :   Carl Samos
******************************************************************************
**  Modified: Date:     Description of changes
**  CNS     :   11-Dec-1992 Initial version
******************************************************************************
*/
BOOL GetPrinterInformation(PSZ pszPrinter, PSZ FAR *lpszDriver,
                           PSZ pszDeviceName, PSZ FAR *lpszLogicalPort)
{
   int  cb;
   char szDetails[256];
   PSZ  pszBegin;
   PSZ  pszTemp;
   char szPort[64];
   char szDriver[64];
   char szLogPort[64];
   
   /*  Get the printer name  */
   cb = WinQueryProfileString(habMain, "PM_SPOOLER",
                              "PRINTER", "", pszPrinter,32);
   pszPrinter[cb-2] = 0;
   
   
   /*  Get the other details  */
   WinQueryProfileString(habMain, "PM_SPOOLER_PRINTER", pszPrinter, "",
                         szDetails, 256);
   
   // the profile string has the following format:
   // PORT;DRIVER;LOGICAL PORT;NETWORK INFO;
   // fields can have more than one entry, separated by a comma
   // the printer's name will follow the driver, separated by a period.
   
   pszBegin = szDetails;
   
   // get the printer port
   pszTemp = strchr(pszBegin, ';');
   if (pszTemp != NULL) {
      *pszTemp = '\0';
      strcpy(szPort, pszBegin);
      *pszTemp = ';';
      pszBegin = pszTemp + 1;
   
      // check for a comma in the string
      pszTemp = strchr(szPort, ',');
      if (pszTemp != NULL) *pszTemp = '\0';
   }
   else {
      return(FALSE);
   }
   
   // now get the driver and printer name
   pszTemp = strchr(pszBegin, ';');
   if (pszTemp != NULL) {
      *pszTemp = '\0';
      strcpy(szDriver, pszBegin);
      *pszTemp = ';';
      pszBegin = pszTemp + 1;
   
      // check for a period (printer name follows it)
      pszTemp = strchr(szDriver, '.');
      if (pszTemp != NULL) {
         strcpy(pszDeviceName, pszTemp+1);
         *pszTemp = '\0';
      }
      else {
         pszDeviceName[0] = '\0';
      }
   
      // check for a comma in the string
      pszTemp = strchr(szDriver, ',');
      if (pszTemp != NULL)
        *pszTemp = '\0';
   }
   else {
      return(FALSE);
   }
   
   
   // now get the logical port
   pszTemp = strchr(pszBegin, ';');
   if (pszTemp != NULL) {
      *pszTemp = '\0';
      strcpy(szLogPort, pszBegin);
      *pszTemp = ';';
      pszBegin = pszTemp + 1;
   
      // check for a comma in the string
      pszTemp = strchr(szLogPort, ',');
      if (pszTemp != NULL)
        *pszTemp = '\0';
   }
   else {
      return(FALSE);
   }
   
   *lpszDriver = _fmalloc(sizeof(char) * (strlen(szDriver)+1));
   strcpy(*lpszDriver, szDriver);
   
   *lpszLogicalPort = _fmalloc(sizeof(char) * (strlen(szLogPort)+1));
   strcpy(*lpszLogicalPort, szLogPort);

   return(TRUE);
}


/*
******************************************************************************
**  FUNCTION: GetPrinterHPS
**  PURPOSE :   Gets the presentation space for a printer, and starts the doc
**  PARAMS  :   lpdos
**          :   lphDC           - the printer's hdc
**          :   pszDocName      - the name of the document
**          :   lWidth          - the width of the document
**          :   lHeight         - the height of the document
**  RETURN  : hPS
**  DATE    :   11-Dec-1992
**  AUTHOR  :   Carl Samos
******************************************************************************
**  Modified: Date:     Description of changes
**  CNS     :   11-Dec-1992 Initial version
******************************************************************************
*/
HPS GetPrinterHPS(DEVOPENSTRUC FAR *lpdos, HDC FAR* lphDC, PSZ pszDocName,
		  LONG lWidth, LONG lHeight)
{
   LONG  lReturn;
   SIZEL sizl;
   HPS   hPS;
   
   if (lpdops->pszLogAddress == NULL) {  // get the default settings
      char szPrinter[32];   /*  The printer name	*/
      PSZ  pszDriver;       /*  The driver name  */
      PSZ  pszLogicalPort;  /*  The logical port	*/
      char szDeviceName[32];/*  The printer's name */
   
      // GetPrinterInformation allocates space for pszDriver and pszLogicalPort
      if (!GetPrinterInformation(szPrinter, &pszDriver, szDeviceName,
                                 &pszLogicalPort))
      return(GPI_ERROR);
   
      lpdops->pszLogAddress = pszLogicalPort;
      lpdops->pszDriverName = pszDriver;
      lpdops->pszDataType   = NULL;
      lpdops->pdriv         = NULL;
   }
   
   // open the printer DC
   *lphDC = DevOpenDC (habMain, OD_QUEUED, "*", 4L, lpdops, (HDC) NULL);
   if (*lphDC == DEV_ERROR) {
      return(GPI_ERROR);
   }
   
   // start the document
   lReturn = DevEscape(*lphDC, DEVESC_STARTDOC, strlen(pszDocName), 
                       pszDocName,NULL, NULL);

   // get the PS for the printer
   if (lReturn == DEV_OK) {
       sizl.cx = lWidth;
       sizl.cy = lHeight;
   
       if (lWidth == 0) {
          hPS =  GpiCreatePS (habMain, *lphDC, &sizl,
                              PU_LOENGLISH | GPIF_DEFAULT | 
                              GPIT_NORMAL | GPIA_ASSOC);
          return(hPS);
       }
       else {
          hPS = GpiCreatePS (habMain, *lphDC, &sizl,
                              PU_ARBITRARY | PU_LOENGLISH | 
                              GPIF_DEFAULT | GPIT_NORMAL |
                              GPIA_ASSOC);
          return(hPS);
      }
   }
   
   return(GPI_ERROR);
}


/*
******************************************************************************
**  FUNCTION: EndPrint
**  PURPOSE : To close the hdc and end the document
**  PARAMS  : hpsPrinter - the printer's presentation space
**     : hdcPrinter - the printer's device context
**     : pszDocName - the name of the document
**  RETURN  : nothing
**  DATE    :   11-Dec-1992
**  AUTHOR  :   Carl Samos
******************************************************************************
**  Modified: Date:     Description of changes
**  CNS     :   11-Dec-1992 Initial version
******************************************************************************
*/
void EndPrint(HPS hpsPrinter, HDC hdcPrinter, PSZ pszDocName)
{
DevEscape(hdcPrinter, DEVESC_ENDDOC, strlen(pszDocName),
pszDocName, NULL,NULL);
GpiAssociate(hpsPrinter, (HDC) NULL);
DevCloseDC(hdcPrinter);
GpiDestroyPS(hpsPrinter);
}

Credit:  Carl Samos

-------------------------------------------------------------------------------
Is there an easy way to get printer output (another opinion)?


A reader who desires to remain anonymous writes:

Generally:  My understanding was that OS/2 would handle printing
for me.  That is to say that I wouldn't have to create separate printer
drivers for every printer under the sun (or any for that matter).  Since
I am creating an image on the screen that is device independent (well,
mostly anyway), is there an easy way to get printer output?

PM achieves a level of device independence by defining a logical output
space.  This logical output space is then bound to a physical output
space, which creates a mapping of logical characteristics to their
physical counterparts.  The logical and physical output spaces are
referred to as the presentation space and the device context (HPS and
HDC) and are bound to one another by using either the GpiAssociate
function or by specifying GPIA_ASSOC to the GpiCreatePS function.

The easiest way to accomplish what you desire is to organize your
drawing code into one or more functions with a single entrypoint that
accepts an HPS as a parameter.  Then, when you want to draw to the
screen, you can call WinGetPS/WinBeginPaint to get an HPS and call the
function. When you want hardcopy, you call DevOpenDC to get an HDC and
GpiCreatePS to get an HPS and call the function.

Note that to get hardcopy, you need to perform some additional setup to
get things to work properly.  The two most important things are that you
initialize the DEVOPENSTRUC structure properly before calling DevOpenDC
and that you send the following escape codes (via DevEscape) at the
following times:

  hdcPrn=DevOpenDC(. . . );
  hpsPrn=GpiCreatePS(. . . );

  DevEscape(. . . ,DEVESC_STARTDOC,. . . );

  if (!doDraw(hpsPrn)) {
     DevEscape(. . . ,DEVESC_ABORTDOC,. . . );
  } /* endif */

  DevEscape(. . . ,DEVESC_ENDDOC,. . . );

  GpiDestroyPS(hpsPrn);
  DevCloseDC(hdcPrn);

I'm not sure because I can't seem to find my copy anywhere, but I belive
that the book by Graham Winn (entitled something to the effect of
"Building applications using the OS/2 Presentation Manager") dedicates a
chapter to the nuances of printing.

(Quoted almost directly from EDMI/2 Edition 1)


-------------------------------------------------------------------------------
How do I print a bitmap?


Here's my code for sending a bitmap to the printer I'm leaving out the
part where I maintain an aspect ratio between the screen and the printer
page.

    SIZEL sizl = {0,0};
    // Get DC/PS for printer
    hdcPrinter = GetPrinterDC( hab, PM_Q_STD );
    hpsPrinter = GpiCreatePS( hab, hdcPrinter, &sizl,
                              PU_PELS | GPIF_DEFAULT |
                              GPIT_NORMAL | GPIA_ASSOC);

    // Get DC/PS for memory DC compatible with printer
    hdcPrtMem = DevOpenDC( hab, OD_MEMORY, "*", 0, NULL, hdcPrinter );
    hpsPrtMem = GpiCreatePS( hab, hdcPrtMem, &sizl,
                             PU_PELS | GPIF_DEFAULT |
                             GPIT_MICRO | GPIA_ASSOC );

    // Get DC/PS for memory DC compatible with display
    hdcMemory = DevOpenDC( hab, OD_MEMORY, "*", 0, NULL, NULLHANDLE );
    hpsMemory = GpiCreatePS( hab, hdcMemory, &sizl,
                             PU_PELS | GPIF_DEFAULT |
                             GPIT_MICRO | GPIA_ASSOC );

    // Get PS for a window to be printed
    hpsToPrint = WinGetPS( hwndToPrint );

    // Set up memory BitBlt
    BITMAPINFOHEADER2 bmih;
    LONG alBitmapStuff[ 2 ];
    WinQueryWindowPos( hwndToPrint, &swp );
    bmih.cbFix = sizeof( BITMAPINFOHEADER2 );
    bmih.cx = swp.cx;
    bmih.cy = swp.cy;
    GpiQueryDeviceBitmapFormats( hpsToPrint, 2L, alBitmapStuff );
    bmih.cPlanes = (USHORT) alBitmapStuff[ 0 ];
    bmih.cBitCount = (USHORT) alBitmapStuff[ 1 ];

    // . . . . Set up aptl[0],[1],[2],[3] for the memory BitBlt

    // Do BitBlt from Display memory PS to Printer memory PS
    hbmToPrint = GpiCreateBitmap( hpsMemory, &bmih, 0, NULL, NULL );
    GpiSetBitmap( hpsMemory, hbmToPrint );
    GpiBitBlt( hpsMemory, hpsToPrint, 3L, aptl, ROP_SRCCOPY, BBO_IGNORE );
    GpiSetBitmap( hpsMemory, NULLHANDLE );

    GpiSetBitmap( hpsPrtMem, hbmToPrint );

    // . . . . Set up aptl[0],[1],[2],[3] for the printer BitBlt

    // BitBlt to printer PS from Printer memory PS
    DevEscape( hdcPrinter, DEVESC_STARTDOC,strlen( szDocName ), szDocName,
               &lBytes, NULL );
    GpiBitBlt( hpsPrinter, hpsPrtMem, 4L, aptl, ROP_SRCCOPY,BBO_IGNORE );
    DevEscape( hdcPrinter, DEVESC_ENDDOC, 0, NULL, &lBytes, NULL );
    GpiSetBitmap( hpsPrtMem, NULLHANDLE );
    GpiDeleteBitmap( hbmToPrint );

    // Release all hdc's and hps's



Credit:  Rick Fishman

-------------------------------------------------------------------------------
How do I do my own Print Screen?


The following code segments are drawn from a much larger system
that I manage at work.  The intent is to show how to provide
a graphical print screen capability in a PM program.  We install
a hook to watch for the print screen key and then take a bit map
snapshot of the screen.  This bit map is then sent to the printer.
Forgive me if I don't go into all the details about the non-related
data structures -- it's late and my mind is a bit foggy.

Feel free to use anything here.  Please add some kind of acknowledgement,
if you use them as is, like:

    "Print routines (c) 1990, 1991 Applied Signal Technology, Inc."

Comment, questions, ridicule should be directed to:

    Jeff Hitchcock
    CompuServe 71601,260

    or U.S. Mail to

    Applied Signal Technology, Inc.
    470 Spring Park Place, Suite 700
    Herndon, VA 22070

    or phone to

    703/478-5619



/*****************************************************************************/


1.  During the WM_CREATE message processing, add the following:

   // Set the print screen hook

   WinSetHook (hab, HMQ_CURRENT, HK_INPUT, (PFN) PrintScreenHook,
               (HMODULE) NULL);



/*****************************************************************************/


2.  Somewhere, have this function:

BOOL CALLBACK PrintScreenHook (HAB hab, PQMSG pQmsg, USHORT fs)
{
   if ( pQmsg->msg == WM_CHAR )
      if ( ( SHORT1FROMMP(pQmsg->mp1) & KC_KEYUP) &&
           ( SHORT1FROMMP(pQmsg->mp1) & KC_VIRTUALKEY ) &&
           ( SHORT2FROMMP(pQmsg->mp2) == VK_PRINTSCRN) )
             PrintScreen (hab);
   return FALSE;
}



/*****************************************************************************/


3.  Here's the "driver:"

VOID EXPENTRY PrintScreen (HAB hab)
{
    HBITMAP	 hbm;

    hbm = ScreenToBitmap (hab);
    PrintBitMap(hbm);
}



/*****************************************************************************/


4.  Here's a general print-related structure we use.  We often have many
print threads running concurrently, but we only allow one to "run" at a
time.  We use a semaphore to show availability of the printer (so to
speak), and only one thread at a time gets it.	If we didn't do this,
and more than a few print threads are running (especially graphical
prints), even a 486/33 with 16 MB of RAM begins to C-R-A-W-L.  So, for
what it's worth, these are the structures that we use:

/**************************************************************
*                                                             *
*   PRINTTHREADPARAMS structure                               *
*                                                             *
*   Parameters that are used to manage separate print threads *
*                                                             *
*   Item             Contents/Use                             *
*   --------------   ---------------------------------------- *
*                                                             *
*   sJobNumber       Print job number, used for cancelling    *
*   aiThreadStack    Thread's stack                           *
*   hwndNotify       Window to which notif. msgs are sent     *
*   tidPrint         System task id                           *
*   hssmPrinter. . .    Semaphore for printer available          *
*   fSemSet          TRUE if semaphore was made and cleared   *
*   szSummary        Print summary (e.g., fax printout)       *
*   fStart           Can't start until TRUE (default FALSE)   *
*   fContinue        Quit if FALSE (default is TRUE)          *
*   fHold            Hold if TRUE (default is FALSE)          *
*   sStartingPage    For multipage, start here                *
*   sEndingPage      For multipage, end here                  *
*   usParam          Module-dependent USHORT                  *
*   ulParam          Module-dependent ULONG                   *
*   pPrintData       PVOID to the print data                  *
*                                                             *
*   PAGESETUP structure                                       *
*                                                             *
*   Parameters used to describe the appearance                *
*                                                             *
*   Item             Contents/Use                             *
*   --------------   ---------------------------------------- *
*                                                             *
*   szFont           The name of the font to use              *
*   sLinesPerPage    Used to scale font                       *
*   sCharsPerLine    Used to scale font                       *
*   sLeft            Used to position on page, in chars       *
*   sRight           Used to position on page, in char        *
*   sTop             Used to position on page, in lines       *
*   sBottom          Used to position on page, in lines       *
*   szHeader         Text to place on top of each page        *
*   fIncludeSummary  If TRUE, include SRI summary on page 1   *
*   fHeaderEveryPage TRUE for every page, false for pg 1      *
*   fHeaderUnderline TRUE for underline                       *
*   szFooter         Text to place at bottom of each page     *
*   fFooterEveryPage TRUE for every page, false for pg 1      *
*   fOverlineFooter  TRUE for overline                        *
*                                                             *
*   HEADER AND FOOTER OPTIONS:                                *
*                                                             *
*   Special Flags that should be supported in each module:    *
*                                                             *
*           &l  Left justify                                  *
*           &c  Center                                        *
*           &r  Right justify                                 *
*           &d  Date                                          *
*           &t  Time                                          *
*           &p  Page number                                   *
*                                                            *
**************************************************************/

typedef struct
    {
    CHAR       szFont[FACESIZE] ;
    SHORT      sLinesPerPage ;
    SHORT      sCharsPerLine ;
    SHORT      sLeft ;
    SHORT      sRight ;
    SHORT      sTop ;
    SHORT      sBottom ;
    BOOL       fIncludeSummary ;
    CHAR       szHeader[HEADERFOOTERLENGTH] ;
    BOOL       fHeaderEveryPage ;
    BOOL       fUnderlineHeader ;
    CHAR       szFooter[HEADERFOOTERLENGTH] ;
    BOOL       fFooterEveryPage ;
    BOOL       fOverlineFooter ;
    }
    PAGESETUP ;

typedef PAGESETUP FAR *PPAGESETUP ;

typedef struct
    {
    SHORT        sJobNumber ;
    int          aiThreadStack[STACKSIZE / sizeof (int)] ;
    HWND         hwndNotify ;
    HSYSSEM      hssmPrinterAvailable ;
    BOOL         fSemSet ;
    CHAR         szSummary[HEADERFOOTERLENGTH] ;
    BOOL         fStart ;
    BOOL         fRunning ;
    BOOL         fContinue ;
    BOOL         fHold ;
    SHORT        sStartingPage ;
    SHORT        sEndingPage ;
    PAGESETUP    page ;
    USHORT       usParam ;
    ULONG        ulParam ;
    VOID huge    *pPrintData ;
    }
    PRINTTHREADPARAMS ;

typedef PRINTTHREADPARAMS FAR *PPRINTTHREADPARAMS ;



/*****************************************************************************/


5.  This function saves the screen display to a bitmap.

HBITMAP ScreenToBitmap (HAB hab)
{
     BITMAPINFOHEADER bmp ;
     HBITMAP          hbm ;
     HDC              hdcMemory ;
     HPS              hpsScreen, hpsMemory ;
     LONG             alBitmapFormats [2] ;
     POINTL           aptl[3] ;
     SIZEL            sizl ;
     SHORT            cxScreen;
     SHORT            cyScreen;
     BOOL             fMonochrome = FALSE;
                      // Create memory DC and PS

     cxScreen = (SHORT) WinQuerySysValue (HWND_DESKTOP, SV_CXSCREEN);
     cyScreen = (SHORT) WinQuerySysValue (HWND_DESKTOP, SV_CYSCREEN);

     hdcMemory = DevOpenDC (hab, OD_MEMORY, "*", 0L, NULL, NULL) ;

     sizl.cx = sizl.cy = 0 ;
     hpsMemory = GpiCreatePS (hab, hdcMemory, &sizl,
                              PU_PELS | GPIF_DEFAULT |
                              GPIT_MICRO | GPIA_ASSOC) ;

                              // Create bitmap for destination

     bmp.cbFix = sizeof bmp ;

     if (fMonochrome)
     {
        bmp.cPlanes = 1 ;
        bmp.cBitCount = 1 ;
     }
     else
     {
        GpiQueryDeviceBitmapFormats (hpsMemory, 2L, alBitmapFormats) ;

        bmp.cPlanes = (USHORT) alBitmapFormats[0] ;
        bmp.cBitCount = (USHORT) alBitmapFormats[1] ;
     }

     bmp.cx = cxScreen ;
     bmp.cy = cyScreen ;

     hbm = GpiCreateBitmap (hpsMemory, &bmp, 0L, NULL, NULL) ;

                              // Copy from screen to bitmap

     if (hbm != NULL)
     {
        GpiSetBitmap (hpsMemory, hbm) ;
        hpsScreen = WinGetScreenPS (HWND_DESKTOP) ;

        aptl[0].x = 0 ;
        aptl[0].y = 0 ;
        aptl[1].x = cxScreen ;
        aptl[1].y = cyScreen ;
        aptl[2].x = 0 ;
        aptl[2].y = 0 ;

        WinLockVisRegions (HWND_DESKTOP, TRUE) ;

        GpiBitBlt (hpsMemory, hpsScreen, 3L, aptl,
                   fMonochrome ? ROP_NOTSRCCOPY : ROP_SRCCOPY, BBO_IGNORE) ;

        WinLockVisRegions (HWND_DESKTOP, FALSE) ;

        WinReleasePS (hpsScreen) ;
        GpiDestroyPS (hpsMemory) ;
        DevCloseDC (hdcMemory) ;
     }

     return hbm ;
}



/*****************************************************************************


6.  The "core" function:


This function prints a bitmap to the printer.  The bitmap is scaled according
to the size of the printer.  No distortion is allowed of the bitmap image.

o Returns False : if an error occurrs
o Returns True : no Error occurred



Known bug(s):


Areas on the screen that have a black foreground and a gray background
are completely black when printed.  For example, when a window does
not have the focus, it's title bar becomes black lettering on a gray
background.  When this window is printed, the entire title bar is black and
no title can be read.  This is using the Hewlett Packard LaserJet Series II
printer.


According to MicroSoft online help this is a known bug with the
printer device driver.	To fix the bug you must go to the control
panel and change the colors of the inactive window.



************************************************************************/
SHORT  sBitmapToPrinter(PPRINTTHREADPARAMS pptp,
HPS   hpsPrinter,
HDC   hdcPrinter,
HAB   habPrinter,
SIZEL *psizlPage,
SIZEL *psizlChar)
{
    HDC    hdcPrinterMemory;
    HPS    hpsPrinterMemory;
    POINTL ptl;
    SHORT  sPage = 1;
    RECTL  rcl;     // Coordinates of region

    long  lCapsHRes;
    long  lCapsVRes;
    float fYAspectRatio;
    float fXAspectRatio;
    SIZEL sizl;

    HBITMAP hbm;
    POINTL  aptl [4] ;
    SHORT   cxScreen;
    SHORT   cyScreen;
    float   fltScale;

    // Skip down top margin, . . . 

    ptl.x = pptp->page.sLeft * psizlChar->cx ;
    ptl.y = psizlPage->cy - (pptp->page.sTop * psizlChar->cy) ;

    // Print header, if requested

    if (pptp->page.szHeader[0] != '\0')
    {
       PrintHeaderFooter (hpsPrinter, &ptl, pptp,
                          psizlPage, psizlChar,
                          pptp->page.szHeader,
                          sPage, PRINT_HEADER) ;
    }

    hbm = pptp->pPrintData;

    // Find the aspect ratio of the printer

    DevQueryCaps(hdcPrinter,CAPS_HORIZONTAL_RESOLUTION,1L,&lCapsHRes);
    DevQueryCaps(hdcPrinter,CAPS_VERTICAL_RESOLUTION,1L,&lCapsVRes);

    if ( (lCapsVRes == 0) || (lCapsHRes == 0) ) {
       fXAspectRatio = (float) 1;
       fYAspectRatio = (float) 1;
    }
    else {
       fXAspectRatio = (float) ((float) lCapsVRes / (float) lCapsHRes);
       fYAspectRatio = (float) ((float) lCapsHRes / (float) lCapsVRes);
    }

    // determine coordinates to print on printer

    rcl.xLeft =  pptp->page.sLeft * psizlChar->cx; // Printer left

    rcl.xRight = psizlPage->cx -
                 (pptp->page.sRight * psizlChar->cx); // Printer right

    rcl.yBottom = (pptp->page.sBottom + 1) * psizlChar->cy; // Printer bottom

    rcl.yTop = psizlPage->cy -
              ( (pptp->page.sTop + 1) * psizlChar->cy);     // Printer top

    cxScreen = (SHORT) WinQuerySysValue (HWND_DESKTOP, SV_CXSCREEN);
    cyScreen = (SHORT) WinQuerySysValue (HWND_DESKTOP, SV_CYSCREEN);

    ScaleToWindowSize ((SHORT) (rcl.xRight - rcl.xLeft),   // sXtarget
                       (SHORT) (rcl.yTop - rcl.yBottom),   // sYTarget
                       cxScreen,                           // sXSource
                       cyScreen,                           // sYSource
                       &fltScale) ;

    // Create a memory device context
    // Memory device contexts are used to contain bitmaps

    hdcPrinterMemory = DevOpenDC (habPrinter, OD_MEMORY, "*", 0L,
                                  NULL, hdcPrinter);

    if ( hdcPrinterMemory == DEV_ERROR ) return FALSE;

    sizl.cx = 0;
    sizl.cy = 0;

    // Create a presentation space and associate it the memory device context

    hpsPrinterMemory = GpiCreatePS (habPrinter, hdcPrinterMemory, &sizl,
                                    PU_PELS | GPIF_DEFAULT |
                                    GPIT_NORMAL | GPIA_ASSOC);

    if( ! hpsPrinterMemory) {
       DevCloseDC (hdcPrinterMemory);
       return FALSE;
    }

    GpiSetBitmap(hpsPrinterMemory,hbm);

    aptl [0].x = rcl.xRight - (long) ((float) cxScreen * fltScale);
    aptl [0].y = rcl.yTop - (long) ((float) cyScreen * fltScale *
                 fYAspectRatio);
    aptl [1].x = rcl.xRight;
    aptl [1].y = rcl.yTop;
    aptl [2].x = 0;
    aptl [2].y = 0;
    aptl [3].x = cxScreen;
    aptl [3].y = cyScreen;

    GpiBitBlt(hpsPrinter,hpsPrinterMemory,4L,aptl,ROP_SRCCOPY,BBO_IGNORE);

    GpiAssociate   (hpsPrinterMemory, NULL) ;
    GpiDestroyPS   (hpsPrinterMemory);
    DevCloseDC     (hdcPrinterMemory);

    // If a footer is defined, . . . 

    if (pptp->page.szFooter[0] != '\0')
    {
       // . . .  compute its position . . . 

       ptl.x = pptp->page.sLeft * psizlChar->cx ;
       ptl.y = pptp->page.sBottom * psizlChar->cy ;

       // . . .  and print it.

       PrintHeaderFooter (hpsPrinter, &ptl, pptp,
                          psizlPage, psizlChar,
                          pptp->page.szFooter,
                         sPage, PRINT_FOOTER) ;
   }
   return( TRUE);
}


-------------------------------------------------------------------------------
Menus

This section covers Presentation Manager menus.

-------------------------------------------------------------------------------
How do I add a menu to a dialog box?


Do a WinLoadMenu and then WM_UPDATEFRAME.

-------------------------------------------------------------------------------
How do I make a dynamically changing menu?


Create the menu with all the items that it will ever contain, then
dynamically remove and insert the items as required. After loading menu,
Query and maintain a copy of the menuitem(s) that will be removed.

 // Obtain and keep a copy of the convert submenuitem
  if (!WinSendMsg (pwd->hwndAB, MM_QUERYITEM,
      MPFROM2SHORT (IDM_KanCnvMnu, TRUE), MPFROMP (&pwd->miCnvMnu))) {

  // And the convert submenu text
  if (!WinSendMsg (pwd->hwndAB, MM_QUERYITEMTEXT,
    MPFROM2SHORT (IDM_KanCnvMnu, sizeof (pwd->szCnvMnuTxt)),
                  MPFROMP (&pwd->szCnvMnuTxt)) ){

When menu is to updated, insert/remove as necessary

    // This is layout, is the submenu already in place?
    if (!pwd->fCnvMenu) {

      // The submenu is not installed, so insert the submenu
      WinSendMsg (pwd->hwndAction, MM_INSERTITEM,
                  MPFROMP (&pwd->miCnvMnu), MPFROMP (pwd->szCnvMnuTxt));

      // And remove the convert menuitem
      WinSendMsg (pwd->hwndAB, MM_REMOVEITEM,
                  MPFROM2SHORT (IDM_KanCnv, TRUE), 0L);

      // Set the submenu flag
      pwd->fCnvMenu = TRUE;
    }

Credit:  Matthew S. Osborn

-------------------------------------------------------------------------------
How do I create a conditional cascade menu?


The following works for me to set a submenu as a conditional-cascade
menu, then set it's default id (hwndMenu is the hwnd of the top-level
menu):

   MENUITEM mi;

   WinSendMsg( hwndMenu, MM_QUERYITEM,
               MPFROM2SHORT( idSubMenu, TRUE ), &mi );

   // Set the MS_CONDITIONALCASCADE bit for the submenu.

   WinSetWindowBits( mi.hwndSubMenu, QWL_STYLE, MS_CONDITIONALCASCADE,
                     MS_CONDITIONALCASCADE );

   // Set cascade menu default

   WinSendMsg( mi.hwndSubMenu, MM_SETDEFAULTITEMID,
               MPFROMSHORT( idDefaultItem ), NULL );



Then I do this to query the default item:

 MENUITEM mi;

 WinSendMsg( hwndMenu, MM_QUERYITEM, MPFROM2SHORT( idSubMenu, TRUE ),
             &mi );

 id = (USHORT) WinSendMsg( mi.hwndSubMenu,
                           MM_QUERYDEFAULTITEMID, NULL, NULL );



Credit:  Rick Fishman

-------------------------------------------------------------------------------
How do I remove a separator from a menu?


Here are two methods of doing that.  The first is from the PMHINTS file,
and the second is from Gpf.  PMHINTS takes the approach of removing
SC_CLOSE and the nearby separator.  The Gpf solution takes the approach
of deleting everything that it doesn't explicitly want.  I've extended
it to, among other things, conditionally delete the "Window List" menu
item as well.

The deletion problems get messier in application menus when there are
multiple separaters in different pull-downs.  That is when assigning the
separators an id really pays off.

Both examples are 16-bit OS/2 1.x code.

PMHINTS:

 VOID  DelClose(HWND hwnd)
 {
   HWND        hSysMenu,
               hSysSubMenu;
   MENUITEM    SysMenu;
   SHORT       idItem,
               idSep,
               idSysMenu;

   hSysMenu = WinWindowFromID(WinQueryWindow(hwnd, QW_PARENT, FALSE),
                              FID_SYSMENU);

   idSysMenu = SHORT1FROMMR(WinSendMsg(hSysMenu, MM_ITEMIDFROMPOSITION,
                            NULL, NULL));

   WinSendMsg(hSysMenu, MM_QUERYITEM, MPFROM2SHORT(idSysMenu, FALSE),
              MPFROMP(&SysMenu));

   hSysSubMenu = SysMenu.hwndSubMenu;

   idItem = SHORT1FROMMR(WinSendMsg(hSysSubMenu, MM_ITEMPOSITIONFROMID,
                         MPFROM2SHORT(SC_CLOSE, FALSE), NULL));
   if (idItem != MIT_ERROR)
   {
     idSep = idItem + 1;  // Get separator ID

     idSep  = SHORT1FROMMR(WinSendMsg(hSysSubMenu, MM_ITEMIDFROMPOSITION,
                           MPFROMSHORT(idSep), NULL));

     WinSendMsg(hSysMenu, MM_DELETEITEM, MPFROM2SHORT(SC_CLOSE, TRUE),
                MPFROMSHORT(NULL));

     WinSendMsg(hSysSubMenu, MM_DELETEITEM, MPFROM2SHORT(idSep, FALSE), NULL);
   }
 }

Derived from Gpf, adapted for a client, with some of my changes
expurgated (so it won't compile as is):

 /*****
  *
  *  UtilDlgSysMenu
  *
  *      Remove unavailable items from system menu of dialog box.
  *
  *  History:
  *      8/31/92  gts  Adapted from Gpf's GpfSetDialogBoxSysMenu
  *               with slight modifications.
  *
  *****/

 void _export UtilDlgSysMenu (	      /* Remove unwanted system menu items */
 HWND hwndFrame)                      /* I - Handle to dialog window */
 {
    HWND     hwndSubMenu;             /* sys menu pull-down handle  */
    MENUITEM miTemp;                  /* menu item template         */
    SHORT    sItemId;                 /* system menu item ID        */
    SHORT    sItemIndex;              /* system menu item index     */
    MRESULT  mresult;

    /********************************************************************/
    /* Get the handle of the system menu pull-down.			*/
    /********************************************************************/
    hwndSubMenu = WinWindowFromID( hwndFrame, FID_SYSMENU );
    WinSendMsg( hwndSubMenu,
                MM_QUERYITEM,
                MPFROM2SHORT( SC_SYSMENU, FALSE ),
                MPFROMP( (PSZ)&miTemp ) );
    hwndSubMenu = miTemp.hwndSubMenu;

    /********************************************************************/
    /* Remove all items from the system menu pull-down that are no	*/
    /* longer wanted.							*/
    /********************************************************************/
    mresult = WinSendMsg( hwndSubMenu,
                         MM_QUERYITEMCOUNT,
                         MPFROMSHORT(0),
                         MPFROMSHORT(0) );
    sItemIndex = SHORT1FROMMR(mresult);
    for (sItemId = 0; sItemIndex != -1; sItemIndex--)
    {
       mresult = WinSendMsg( hwndSubMenu,
                             MM_ITEMIDFROMPOSITION,
                             MPFROMSHORT(sItemIndex),
                             MPFROMSHORT(0) );
       sItemId = SHORT1FROMMR(mresult);
       if ( sItemId != MIT_ERROR
             &&sItemId != SC_MOVE
             &&sItemId != SC_CLOSE
             &&(sItemId != SC_TASKMANAGER
             || (flFlags & MAXONLY)) )   // <- application controls
       {
          WinSendMsg( hwndSubMenu,
                      MM_DELETEITEM,
                      MPFROM2SHORT(sItemId,FALSE),
                      MPFROMSHORT(0) );
       }
    }
 }



Credit:  Guy Scharf

-------------------------------------------------------------------------------
Container Controls


This section covers Presentation Manager container controls.

-------------------------------------------------------------------------------
How do I stop a container from flashing every time I add a record?


Disable/Enable the container window before and after the insertion/deletion.

How do I get my containers to use Mini-icons?

Here is some sample code that displays mini icons.  First, supply a missing
#define:

 #ifndef CCS_MINIICONS
     #define CCS_MINIICONS 0x0800
 #endif // not CCS_MINIICONS

Use CCS_MINIICONS as part of your container style (when you create the
container).  Now, fill in the RECORDCORE structure pointed to by
'pRecordCore'; 'pUserRecord' also points to 'pRecordCore', which is part
of a "user data" structure:

 // Fill in fields of container record.
 #ifdef USE_MINIRECORDCORE
     pRecordCore->cb = sizeof (MINIRECORDCORE) ;
 #else
     pRecordCore->cb = sizeof (RECORDCORE) ;
 #endif // USE_MINIRECORDCORE
     pRecordCore->flRecordAttr = 0 ; // CRA_DROPONABLE ;
     // pRecordCore->ptlIcon is left to zeros.
     pRecordCore->pszIcon = pUserRecord->szName ;
     if (*DDirRecord.szIconName)
        LoadIcon (pUserRecord) ;
     // pRecordCore->hptrMiniIcon is left to zeros.
     // pRecordCore->hbmBitmap is left to zeros.
     // pRecordCore->hbmMiniBitmap is left to zeros.
     // pRecordCore->pTreeItemDesc is left to zeros.

 #ifndef USE_MINIRECORDCORE
     pRecordCore->pszText = pUserRecord->szName ;
     pRecordCore->pszName = pUserRecord->szName ;
     pRecordCore->pszTree = pUserRecord->szName ;
 #endif // not USE_MINIRECORDCORE



Now, if your user asks for mini-icons (via a popup menu, presumably),
you send a CM_SETCNRINFO message to your container, after ORing in the
CV_MINI style to CnrInfo.flWindowAttr, or removing it to restore
full-size icons.  Hope this helps.


Credit:  Wayne Kovsky

-------------------------------------------------------------------------------
How do I sort a container control?

/*
 * Note that PSESSION is my own instance data structure.
 * I use it here to retrieve the type of field comparison I should do.
 */

 static SHORT APIENTRY Compare (PMINIRECORDCORE pmrc1,
                                PMINIRECORDCORE pmrc2,
                                PVOID pvStorage)
 {
    PSESSION pSession = (PSESSION) pvStorage ;
    PFIELDINFO pFieldInfo = pSession->pFieldInfoSort ;
    PPVOID pField1 = (PPVOID) ((PBYTE) pmrc1 +
       pFieldInfo->offStruct) ;
    PPVOID pField2 = (PPVOID) ((PBYTE) pmrc2 +
       pFieldInfo->offStruct) ;
    SHORT sResult = 0 ;

    if (pFieldInfo->flData & CFA_STRING) {
       sResult = strcmp ((PCHAR) *pField1, (PCHAR) *pField2) ;
    }
    else if (pFieldInfo->flData & CFA_ULONG) {
       if ((ULONG) *pField1 < (ULONG) *pField2)
          sResult = -1 ;
       else if ((ULONG) *pField1 > (ULONG) *pField2)
          sResult = 1 ;
       else
          sResult = 0 ;
    }
    else if (pFieldInfo->flData & CFA_DATE) {
       sResult = CompareDate ((PCDATE) (PVOID) pField1,
                              (PCDATE) (PVOID) pField2) ;
    }
    else if (pFieldInfo->flData & CFA_TIME) {
       sResult = CompareTime ((PCTIME) (PVOID) pField1,
                              (PCTIME) (PVOID) pField2) ;
    }

    // Any other data type gets treated as equal;
    // no sorting can be done. Now handle reverse sequence.
    if (pSession->fSortReverse && sResult)
       sResult = -sResult ;
    return sResult ;
 }

Here is how I invoke the sort:

 // Send container a sort message.
 ulRC = LONGFROMMR (WinSendMsg (
                    pSession->hwndContainer,
                    CM_SORTRECORD,
                    MPFROMP (Compare), MPFROMP (pSession))) ;
 if (!ulRC) {
    // Report error.
 }



Credit:  Wayne Kovsky

-------------------------------------------------------------------------------
How do I query all records in a container - tree view?


The following code works for querying all records in a tree structure but

 USHORT usParentCmd = CMA_FIRST, usChildCmd;
 PUSERREC purParent = NULL, purChild;

 for( ; ; )
 {
    purParent = (PUSERREC) WinSendMsg( hwndCnr, CM_QUERYRECORD,
                                       MPFROMP( purParent ),
                                       MPFROM2SHORT( usParentCmd, CMA_ITEMORDER ) );
    if( !purParent )
       break;

    DoWhatever( purParent );

    usChildCmd = CMA_FIRSTCHILD;
    purChild = NULL;
    for( ; ; )
    {
       purChild = (PUSERREC) WinSendMsg( hwndCnr, CM_QUERYRECORD,
                                         MPFROMP( purChild ),
                                         MPFROM2SHORT( usChildCmd, CMA_ITEMORDER ));
       if( !purChild )
          break;

       DoWhatever( purChild );
       usChildCmd = CMA_NEXT;
    }

    usCmdParent = CMA_NEXT;
 }



Credit:  Rick Fishman

-------------------------------------------------------------------------------
I can't get different colors in text control windows


I've finally got static text control windows (WS_STATIC, SS_TEXT)
working with a different color pres. parameter set ! Thanks, Rick, Dan
and Wayne. Code as follows:

 RGB2 rgb2 ;   // RGB2 structure
 HWND hwnd ;   // window handle

 // Set RGB values for a SYSCLR_BACKGROUND (light gray) color
 rgb2.bred = 204 ;     // Found these in 'WinSetSysColors' API
 rgb2.bgreen = 204 ;
 rgb2.bblue = 204 ;
 rgb2.fcOptions = 0 ;
 // Set background color
 WinSetPresParam (hwnd, PP_BACKGROUNDCOLOR, (ULONG) sizeof (RGB2),
                  &rgb2) ;
 // Set RGB values for black
 rgb2.bred = 0 ;
 rgb2.bgreen = 0 ;
 rgb2.bblue = 0 ;
 rgb2.fcOptions = 0 ;
 // Set text foreground color
 WinSetPresParam (hwnd, PP_FOREGROUNDCOLOR, (ULONG) sizeof (RGB2),
                  &rgb2) ;
 // Set text border color (important for outline fonts)
 WinSetPresParam (hwnd, PP_BORDERCOLOR, (ULONG) sizeof (RGB2), &rgb2) ;


Three big caveats here:

o The OS/2 internal code for static text control windows is
*definitely* using RGB colors, not index colors when it draws the
text string. Thus, the PP_*INDEX presentation parameter values will
*not* work.
o You *must* use a set of colors that are already loaded in the
color table. If the RGB color is not found, the background will
be dithered affecting the text appearance (washed out). If you
are not sure the RGB color is loaded do a GpiQueryNearestColor
to get the nearest color.
o You *must* use the RGB2 structure and *not* the RGB structure.
This is *NOT* documented, but it appears in general that all
OS/2 2.0 APIs should use RGB2 *instead* of RGB.



Credit:  Bill Kenning

-------------------------------------------------------------------------------
How can I toggle my titlebar on and off?


Basically, move frame window controls from being children of the frame
to being children of the background HWND_OBJECT.  You can then reverse
the process to bring them back into view.  Following function implements
this code.

/* --------------------------------------------------------------
-- Function:  ToggleFrame
--
-- Description:  Toggles frame control visible/invisible
--
-- Notes: Code stolen from someone who stole it from an MS sample
----------------------------------------------------------------- */
void ToggleFrame(HWND hwndFrame) {

 if (! Hidden) {     /* hide contorls */
    hwndTitle = WinWindowFromID( hwndFrame, FID_TITLEBAR );
    hwndSys   = WinWindowFromID( hwndFrame, FID_SYSMENU );
        . . .  repeat for FID_MINMAX, etc . . . 
    WinSetParent(hwndTitle, HWND_OBJECT, FALSE );
    WinSetParent(hwndSys,   HWND_OBJECT, FALSE );
        . . .  repeat for FID_MINMAX, etc . . . 
 } 
 else { /* restore controls */
    WinSetParent( hwndTitle, hwndFrame, FALSE );
    WinSetParent( hwndSys, hwndFrame, FALSE );
        . . .  basically reverse of above . . . 
 }
 WinSendMsg( hwndFrame, WM_UPDATEFRAME,
      (MPARAM)(FCF_TITLEBAR | FCF_SYSMENU | . . . ), NULL);
 SizeTheWindow( hwndFrame );
 Hidden = ! Hidden;
 return;
}



Credit:  Mike Thompson

-------------------------------------------------------------------------------
How can I get transparent regions in bitmaps?


Currently, you can get the same effect with PM if you use the method
used with icons:

o Use a monochrome mask to prep the destination area. The mask would
define which areas would be transparent and which would show the bitmap.
The bits would be defined as 1=transparent,0=bitmap. You would blit the
mask to the destination using ROP_SRCAND. This would blacken out the
area that would display the non-transparent bits of the bitmap.
o Now blit the bitmap to the destination using ROP_SRCPAINT. Note that
the "transparent" areas of the bitmap must have the color black (i.e.
bits=0). This ORs the bitmap onto the prep area. Viola - "transparent"
bitmap.



Credit:  John Webb

-------------------------------------------------------------------------------
How do I create a status bar at the bottom of my window?


You need to intercept several frame messages:


WM_CALCFRAMERECT to calculate the new location of the client.  You should
send it to the frame superclass then modify the result.  This message is
invoked during frame formatting and whenever WinCalcFrameRect is called
against your frame window handle.


WM_FRAMECTLCOUNT to tell the frame superclass the number of frame controls
you expect to format.  If you're adding a status line as a child of the
frame (below the client, I suspect), you would add 1 to the result returned
by your frame superclass.


WM_FORMATFRAME is where you actually position/size the frame controls.
The message gives you an array of SWP's.  Call your frame superclass
and modify the result (in your case, I would expect only FID_CLIENT and
your status line).


Sample follows. . . 

    /*
     * FYI, WinDefFrameProc is just a macro I defined to
     * call my superclass frame window procedure, ie,
     *    (*vpfnFrameWndProc) (h,m,p1,p2).
     *
     * This example splits the client area space 1/3
     * and 2/3 horizontally with the old client area
     * and a new sibling.
     */

    case WM_CALCFRAMERECT:
      mr = WinDefFrameProc(hwnd, msg, mp1, mp2);

      /*
       * Calculate the position of the client rectangle
       * Otherwise,  we'll see a lot of redraw when we move the
       * client during WM_FORMATFRAME.
       */

      if (mr && mp2)
      {
         prectl = (PRECTL) mp1;
         prectl->xLeft += ((prectl->xRight - prectl->xLeft) / 3);
      }
      break;

    case WM_FORMATFRAME:
      sCount = (SHORT) WinDefFrameProc(hwnd, msg, mp1, mp2);

      /*
       * Reformat the frame to move the client
       * over and make room for the his/her sibling.
       */

      pswp = (PSWP) mp1;
      pswpClient = pswp + sCount - 1;
      pswpNew = pswpClient + 1;

      *pswpNew = *pswpClient;
      swpClient = *pswpClient;

      pswpNew->hwnd = WinWindowFromID(hwnd, ID_SIBLING);
      pswpNew->cx = pswpClient->cx / 3;

      pswpClient->x = pswpNew->x + pswpNew->cx - 1;
      pswpClient->cx = swpClient.cx - pswpNew->cx + 1;

      sCount++;
      mr = MRFROMSHORT(sCount);
      break;

    case WM_QUERYFRAMECTLCOUNT:
      sCount = (SHORT) WinDefFrameProc(hwnd, msg, mp1, mp2);
      sCount++;
      mr = MRFROMSHORT(sCount);
      break;



Credit:  Dan Kehn

-------------------------------------------------------------------------------
How to have a frame/client and still have a std window?

 FRAMECDATA fcdata;

 fcdata.cb	      = sizeof( FRAMECDATA );
 fcdata.flCreateFlags = FCF_TASKLIST | FCF_MENU, etc.;
 fcdata.hmodResources = 0; // or the hmod of the DLL containing the resources
 fcdata.idResources   = ID_RESOURCES;  // ID of the resources, as usual

 hwndFrame = WinCreateWindow( HWND_DESKTOP, WC_FRAME, NULL, 0, 0, 0, 0, 0,
                              NULLHANDLE, HWND_TOP, ID_RESOURCES,
                              &fcdata, NULL);

 hwndClient = WinCreateWindow( hwndFrame, szClientClass, NULL, 0, 0, 0, 0, 0,
                               NULLHANDLE, HWND_TOP, FID_CLIENT, NULL, NULL );

 WinSetWindowPos( hwndFrame, HWND_TOP, x, y, cx, cy,
                  SWP_ZORDER | SWP_SIZE | SWP_MOVE |
                  SWP_SHOW | SWP_ACTIVATE );



If you want to then add new controls, like the system menu, you would do
this:

 fcdata.flCreateFlags = FCF_SYSMENU;
 WinCreateFrameControls( hwndFrame, &fcdata, NULL );
 WinSendMsg( hwndFrame, WM_UPDATEFRAME,
             MPFROMLONG( FCF_SYSMENU ), NULL );


The same thing applies to all the other controls like FCF_SIZEBORDER,
FCF_TITLEBAR, FCF_HORZSCROLL, FCF_MINMAX, etc. You could also OR more
than one together if you wanted to add more than one frame control in
the same shot. On the titlebar, you need to also send this
message:

 WinSendMsg( WinWindowFromID( hwndFrame, FID_TITLEBAR ),
             TBM_SETHILITE, MPFROMSHORT( TRUE ), NULL );


If you want to delete frame controls, you would do this (assuming system
menu):

 WinDestroyWindow( WinWindowFromID( hwndFrame, FID_SYSMENU ) );
 WinSendMsg( hwndFrame, WM_UPDATEFRAME,
             MPFROMLONG( FCF_SYSMENU ), NULL );


Unfortunately this doesn't fit completely well with OOP, since the
controls really are not themselves objects independent of the frame
window. One of the problems here is that in order to make them
independent objects, you need to know the internals of the frame window
proc. For instance, you would think that the MIN and MAX are two
WC_BUTTON controls, but they are really one menu with two bitmap
menuitems (at least in 1.x they were). So if you were to do a
WinCreateWindow for either, you'd have to know where to get the bitmaps,
and hope that doesn't change.

Similarly you'd have to be able to construct the system menu after
creating a WC_MENU window. This isn't a tough feat, but if a later
version of OS/2 adds a new menu item to the system menu, you'd have to
become aware of it.

The titlebar and the scrollbars aren't a problem since they have their
own public window classes - WC_TITLEBAR and WC_SCROLLBAR respectively.
You can, for instance, do a WinCreateWindow( . . . , WC_SCROLLBAR,. . . ,
FID_HORZSCROLL, . . ), then send the frame a WM_UPDATEFRAME message for
FCF_HORZSCROLL and this would work. But there is no WC_SIZEBORDER so you
couldn't use this method to add the sizing border later. So for the
sizing border you need to use the method I first posted above.

I spoke too soon about the sizing border. If you want to add or remove
it from a frame window, you need to add or remove the FS_SIZEBORDER
style from the frame, then send the frame an UPDATEFRAME message for
FCF_SIZEBORDER.

To change the style, here is a technique that John Webb just turned me
on to. To add the style:

 WinSetWindowBits( hwndFrame, QWL_STYLE, FS_SIZEBORDER, FS_SIZEBORDER );


To remove it:

 WinSetWindowBits( hwndFrame QWL_STYLE, 0, FS_SIZEBORDER );

Also, if you want to add or remove just one of the MIN or the MAX, you
basically need to get the window handle of the MINMAX menu, then do a
MM_REMOVEITEM for the one you want to remove. I did this in 1.x but
haven't yet in 2.0.


Credit:  Rick Fishman

-------------------------------------------------------------------------------
How do I use printf() in a PM program?


Use PMPRINTF, check your local BBS's, CIS, BIX or cdrom.com
or redirect stdout and stderr to files.

    // Redirect 'stderr'.
    (void) freopen ("stderr.txt", "w", stderr) ;

    // Redirect 'stdout'.
    (void) freopen ("stdout.txt", "w", stdout) ;


-------------------------------------------------------------------------------
I have a SOM DLL. How do I register it?


Here's an example I use for registering.  It checks if the DLL is valid before
continuing:

#define INCL_WPCLASS
#define INCL_WIN
#define INCL_DOS
#include <os2.h>
#include <string.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
   HMQ hmq;
   HAB hab;
   CHAR szText[256];
   USHORT usResponse;
   CHAR szLoadError[128];
   HMODULE hmod;
   APIRET rc;

   if (argc != 3)
     return 0;

   hab = WinInitialize(0);
   hmq = WinCreateMsgQueue(hab, 0);

   WinDeregisterObjectClass(argv[1]);

   sprintf(szText, "Register %s DLL '%s'?", argv[1], argv[2]);
   usResponse = WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, szText,
      "Register WPS Class", 0, MB_YESNO | MB_MOVEABLE | MB_ICONQUESTION);

   if (usResponse != MBID_YES)
     return 0;

   rc = DosLoadModule(szLoadError, sizeof(szLoadError), argv[2], &hmod);

   if (rc != 0)
   {
     sprintf(szText, "Return code = %u, error module = '%s'.",
             rc, szLoadError);
     WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, szText,
                   "DosLoadModule Failed", 0,
                   MB_ENTER | MB_MOVEABLE | MB_ERROR);

     return 0;
  }


  if (WinRegisterObjectClass(argv[1], argv[2]))
  {
    if (WinCreateObject(argv[1], argv[1], " ",
        "<WP_DESKTOP>", CO_REPLACEIFEXISTS))
      WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, "Created.",argv[1],
                    0, MB_ENTER | MB_MOVEABLE | MB_INFORMATION);
    else
    {
      DosFreeModule(hmod);
      WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, "Failed.",argv[1],
                    0,MB_ENTER | MB_MOVEABLE | MB_ERROR);
    }
  }
  else
  {
    DosFreeModule(hmod);
    WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, "Registration failed.",
                  argv[1], 0,MB_ENTER | MB_MOVEABLE | MB_ERROR);
  }

  return 0; }


FYI, when WPS registers your DLL with SOM, SOM checks if your DLL has a
module entry point called SOMInitModule.  If it does, it is called (most
use SOMInitModule for registering DLLs that have more than one class).
Otherwise, it checks for YourClassNewClass, and tries to call it (where
'YourClass' is the parameter you specified on the
WinRegisterObjectClass). SOM generates 'YourClassNewClass' in the .IH
file; if you're still stuck, verify it is being called and that it
doesn't fail (eg, because of version number check).


I suggest registering with a full DLL filespec, eg,
C:\MYSTUFF\MYCAR.DLL. Saves on the length of the LIBPATH.


Credit:  Dan Kehn

-------------------------------------------------------------------------------
How do I save and restore my window size and position?


Here is code I use to restore a saved window position, which includes
checks to make sure the user hasn't saved a window position at one
screen resolution and then restored it at a different screen resolution
(which might cause the window's frame controls to be completely off the
display -- we've all seen that, right???):

 BOOL SetWindowPosition (const HAB hab, const HWND hwndFrame,
                         const SWP swpConfig)
 {
     SWP swp ;
     APIRET ulRC ;

     // Initialize the window positioning flags.
     swp.fl = SWP_ACTIVATE | SWP_MOVE | SWP_SIZE | SWP_SHOW ;

     /* Check for saved user preferences for screen width/height in
      * config file.  Did user want to start maximized?
      */
     if (swpConfig.fl & SWP_MAXIMIZE)
     {
         // Get maximized frame window position and size.
         ulRC = WinGetMaxPosition (hwndFrame, &swp) ;
         if (!ulRC)
         {
             // Report error, and then . . . 
             return TRUE ;
         }
         swp.fl |= SWP_MAXIMIZE ;
     }

     // Did user want to start minimized?
     else if (swpConfig.fl & SWP_MINIMIZE)
     {
         // Get minimized frame window position and size.
         ulRC = WinGetMinPosition (hwndFrame, &swp, (PPOINTL) NULL) ;
         if (!ulRC)
         {
             // Report error, and then . . . 
             return TRUE ;
         }
         swp.fl |= SWP_MINIMIZE ;
     }

     // Did user have a saved starting position and size?
     else if (swpConfig.cy || swpConfig.cx || swpConfig.x || swpConfig.y)
     {
         LONG cxClientMax ;
         LONG cyClientMax ;
         LONG cyTitleBar ;
         LONG cxSizeBorder ;
         LONG cySizeBorder ;

         // Get maximum client window size.
         cxClientMax = WinQuerySysValue (HWND_DESKTOP, SV_CXFULLSCREEN) ;
         cyClientMax = WinQuerySysValue (HWND_DESKTOP, SV_CYFULLSCREEN) ;
         cyTitleBar = WinQuerySysValue (HWND_DESKTOP, SV_CYTITLEBAR) ;
         cxSizeBorder = WinQuerySysValue (HWND_DESKTOP, SV_CXSIZEBORDER) ;
         cySizeBorder = WinQuerySysValue (HWND_DESKTOP, SV_CYSIZEBORDER) ;

         // Maximum client window size excludes title bar.
         cyClientMax += cyTitleBar ;

         // Make sure x origin is within display boundaries.
         swp.x = swpConfig.x ;
         if (swp.x < -cxSizeBorder)
             swp.x = 0 ;

         // Make sure window isn't too wide, or negative value.
         swp.cx = swpConfig.cx ;
         if (swp.cx >= cxClientMax || swp.cx < 0)
         {
             swp.cx = cxClientMax ;
             swp.x = 0 ;
         }

          if ((swp.x + swp.cx) > (cxClientMax + cxSizeBorder))
             swp.x = cxClientMax + cxSizeBorder - swp.cx ;

         // Make sure y origin is within display boundaries.
         swp.y = swpConfig.y ;
         if (swp.y < -cySizeBorder)
             swp.y = 0 ;

         // Make sure window isn't too high, or negative value.
         swp.cy = swpConfig.cy ;
         if (swp.cy > cyClientMax || swp.cy < 0)
         {
             swp.cy = cyClientMax ;
             swp.y = 0 ;
         }

         if ((swp.y + swp.cy) > (cyClientMax + cySizeBorder))
             swp.y = cyClientMax + cySizeBorder - swp.cy ;
     }

     // No saved position -- move window to FCF_SHELLPOSITION location.
     else
     {
         // Get default window size and position.
         ulRC = WinQueryTaskSizePos (hab, 0, &swp) ;
         if (ulRC)
         {
             // Report error, and then . . . 
             return TRUE ;
         }
         swp.fl = SWP_ACTIVATE | SWP_MOVE | SWP_SIZE | SWP_SHOW ;
     }

     // Position and size this frame window.
     ulRC = WinSetWindowPos (hwndFrame, HWND_TOP,
       swp.x, swp.y, swp.cx, swp.cy, swp.fl) ;
     if (!ulRC)
     {
         // Report error, and then . . . 
         return TRUE ;
     }

     return FALSE ;
 }

Credit:  Wayne Kovsky

-------------------------------------------------------------------------------
How do you make a window float above all others?


Here's a sample program showing floating windows (derived from
dvipm).  The advantages over the WM_TIMER approach [which was presented
in v2.2 -ed] are:

o there's no delay
o repainting is minimized
o avoids timing problems
o the floating windows are not unconditionally moved to the very top
  (that is, when moving the base window B (with floating window F) behind
  a window W, the order of the windows is this: BFW, not BWF!)


The excessive repainting caused by other solutions was quite annoying
with dvipm as recomputing the dvipm status window is slow.


Credit: Eberhard Mattes

/* floatwin.c */

/* This program shows how to implement floating windows.  The source
   code works with both 16-bit and 32-bit C compilers.

   If the Z-order of the base window is changed, the floating window
   will be moved on top of the base window.

   Generalizing this approach to many windows floating on many windows
   (floating on many windows ...) is left as exercise. */

#define INCL_WIN
#define INCL_GPI
#include <os2.h>

/* Syntactic sugar for supporting both 16-bit and 32-bit compilers. */

#ifdef __32BIT__
#define MSG ULONG
#define FLAGS fl
#else
#define MSG USHORT
#define FLAGS fs
#endif
#ifndef NULLHANDLE
#define NULLHANDLE NULL
#endif

/* The original frame window procedure. */
static PFNWP pfOldBaseFrameProc = NULL;

/* The handle of the floating window. */
static HWND hwndFloat = NULLHANDLE;

/* This frame window procedure is used for subclassing base windows.
   When changing the Z-order of the frame window, the floating window
   is moved instead of the base window and the hwndInsertBehind field
   is modified to move the base window behind the floating window. */

MRESULT EXPENTRY
BaseFrameProc (HWND hwnd, MSG msg, MPARAM mp1, MPARAM mp2)
{
  PSWP pswp;

  switch (msg)
    {
    case WM_ADJUSTWINDOWPOS      pswp = PVOIDFROMMP (mp1);
      if ((pswp->FLAGS & SWP_ZORDER) && hwndFloat != NULLHANDLE
           && WinIsWindowVisible (hwndFloat))
        {
          WinSetWindowPos (hwndFloat, pswp->hwndInsertBehind,
                           0, 0, 0, 0, SWP_ZORDER);
          /* This is the trick! */
          pswp->hwndInsertBehind = hwndFloat;
        }
      break;
    }
  return pfOldBaseFrameProc (hwnd, msg, mp1, mp2);
}


/* Common client window procedure for base windows and floating
   windows.  Display TXT and use CLR for filling the background. */

MRESULT CommonClientWndProc (HWND hwnd, MSG msg, MPARAM mp1, MPARAM mp2,
                             PCH txt, COLOR clr)
{
  HPS hps;
  RECTL rcl;

  switch (msg)
    {
    case WM_PAINT      hps = WinBeginPaint (hwnd, 0L, 0L);
      WinQueryWindowRect (hwnd, &rcl);
      GpiSetColor (hps, CLR_DARKCYAN);
      GpiSetBackColor (hps, clr);
      WinDrawText (hps, -1, txt, &rcl, 0, 0,
                   DT_TEXTATTRS | DT_CENTER | DT_VCENTER | DT_ERASERECT);
      WinEndPaint (hps);
      return 0;
    }
  return WinDefWindowProc (hwnd, msg, mp1, mp2);
}


/* Client window procedure for floating windows. */

MRESULT EXPENTRY FloatClientWndProc (HWND hwnd, MSG msg, MPARAM mp1, MPARAM mp2)
{
  return CommonClientWndProc (hwnd, msg, mp1, mp2, "Floating Window", CLR_RED);
}

/* Client window procedure for base windows. */

MRESULT EXPENTRY BaseClientWndProc (HWND hwnd, MSG msg, MPARAM mp1, MPARAM mp2)
{
  return CommonClientWndProc (hwnd, msg, mp1, mp2, "Base Window", CLR_YELLOW);
}


/* Start here. */

int main (void)
{
  static char szBaseClientClass[] = "floatwin.base";
  static char szFloatClientClass[] = "floatwin.float";
  ULONG flFrameFlags;
  HAB hab;
  HMQ hmq;
  QMSG qmsg;
  HWND hwndBase;

  /* Initialize Presentation Manager. */

  hab = WinInitialize (0);
  hmq = WinCreateMsgQueue (hab, 0);

  /* Create client window classes. */

  WinRegisterClass (hab, szBaseClientClass, BaseClientWndProc,
                    CS_SIZEREDRAW, 0);
  WinRegisterClass (hab, szFloatClientClass, FloatClientWndProc,
                    CS_SIZEREDRAW, 0);

  /* Create the base window and the floating window.  Note     windows are initially invisible. */

  flFrameFlags = (FCF_TITLEBAR      | FCF_SYSMENU |
                  FCF_SIZEBORDER    | FCF_MINMAX  |
                  FCF_TASKLIST);

  /* Create and subclass the base window. */

  hwndBase = WinCreateStdWindow (HWND_DESKTOP, 0,
                                 &flFrameFlags, szBaseClientClass,
                                 "floatwin - Base Window",
                                 0L, 0, 1, NULL);
  pfOldBaseFrameProc = WinSubclassWindow (hwndBase, BaseFrameProc);

  /* Create the floating window. */

  hwndFloat = WinCreateStdWindow (HWND_DESKTOP, 0,
                                  &flFrameFlags, szFloatClientClass,
                                  "floatwin - Floating Window",
                                  0L, 0, 1, NULL);

  /* Set the position, size and Z-order of the windows and make them
     visible.  It's important to use SWP_ZORDER for the base
     window. */

  WinSetWindowPos (hwndFloat, HWND_TOP, 10, 10, 300, 80,
                   SWP_SHOW | SWP_MOVE | SWP_SIZE | SWP_ZORDER);
  WinSetWindowPos (hwndBase, HWND_TOP, 100, 50, 300, 80,
                   SWP_SHOW | SWP_MOVE | SWP_SIZE | SWP_ZORDER | SWP_ACTIVATE);

  /* The message loop. */

  while (WinGetMsg (hab, &qmsg, 0L, 0, 0))
    WinDispatchMsg (hab, &qmsg);

  /* Clean up. */

  WinDestroyWindow (hwndBase);
  WinDestroyWindow (hwndFloat);
  WinDestroyMsgQueue (hmq);
  WinTerminate (hab);

  return 0;
}


-------------------------------------------------------------------------------
How to ensure the sizing's correct so the dlg "fits" in the notebook. . . ?


Override wpclsQuerySettingsPageSize (or some such verbage, I don't
have docs at home).  I don't know if this API made it into the on-line
docs, but it is definitely public (see \TOOLKT20\C\OS2H\WPOBJECT.H).
There you are given the opportunity to adjust the size up to accomodate
your dialog, if necessary.

The PAGEINFO structure is used by the Settings page code to delay the
loading of the dialog required for a page until the user turns to it for
the first time.  It has the resource ID, module handle (which is
incorrectly named in the structure, 'resid' or some nonsense, grr-r),
dialog procedure, and pointer of your choice to receive on WM_INITDLG
(most choose the object pointer, somSelf). When the user selects the
notebook tab, WPS calls WinLoadDlg based on the parameters in PAGEINFO.

-------------------------------------------------------------------------------
How do I prevent Shutdown from stopping my app?


In our application, the WM_CLOSE message processor determines the state of
the application, issues all the "Are you sure?" questions, etc. If the close
is to be continued, a WM_QUIT message is posted and a value of FALSE is
returned. Otherwise a value of TRUE is returned.

The window receiving the WMU_EndTask message handles it by posting a WM_CLOSE
message to itself, and letting the WM_CLOSE processing handle it. The only
reason it is not translated to a WM_CLOSE within the message loop is allow
future use. This message requires no special handling.

The window receiving the WMU_ShutDown message handles it by sending (not
posting) a WM_CLOSE message to itself. If the WM_CLOSE message returns TRUE,
then a WinCancelShutdown (hmq, FALSE) call is issued to cancel this instance
of the shutdown.

If you issue a WinCancelShutdown (hmq, TRUE), a WM_QUIT message will never be
sent to your message queue, so will not have an opportunity to stop the
shutdown.  This is intended for secondary message queues that do not have a
message loop.

  while (1) {

    HWND hwndClient;

    // Get next message from queue
    if (!WinGetMsg (hab, &qmsg, NULLHANDLE, 0, 0)) {

      // The WM_QUIT message has three sources:
      //   1. The task manager 'End task' pulldown
      //   2. System shutdown from desktop manager
      //   3. Posted by the application

      // This is a WM_QUIT message.  Check the window handle, if the
      // handle matches mp2, it is from switch list close
      if (qmsg.hwnd == HWNDFROMMP (qmsg.mp2)) {

        // This is from close on the task list, convert to our message
        qmsg.msg = WMU_EndTask;

        // Get the intended client's window handle
        if (!(hwndClient = WinWindowFromID (qmsg.hwnd, FID_CLIENT))) {

          // Failed to find client. No idea who this belongs to,
          // give it to default window for processing
          hwndClient = hwndDefault
        }

        // Otherwise, readdress the message to the appropriate client
        qmsg.hwnd = hwndClient;

        // We can use mp1 and mp2 for anything we want.
        // Currently, just clear both
        qmsg.mp1  = qmsg.mp2 = 0L;

      } else if (qmsg.hwnd == NULLHANDLE) {

        // This message is from shutdown,
        // Address it to default window for processing
        qmsg.hwnd = hwndDefault;

        // And set the message to our shutdown message,
        qmsg.msg = WMU_SysShutdown;

      } else {

        // If here, we posted the WM_QUIT message, so break out of
        // the message loop
        break;
      }
    }

    // This is not a WM_QUIT message, intercept all other
    // messages intended for the NULL window
    if (qmsg.hwnd == NULLHANDLE) {

      // Pass all NULL window messages to the NULL window handler
      NullMsg (qmsg.hwnd, qmsg.msg, qmsg.mp1, qmsg.mp2);

    } else {

      // This is not a WM_QUIT message, nor is it intended for the NULL
      // window.  Pass this message to the message filter hook for
      // processing.
      if (!WinCallMsgFilter (hab, (PQMSG) &qmsg, 0)) {

        // Filter hook has not cancelled the message, so dispatch
        // the message to the proper window
        WinDispatchMsg (hab, &qmsg);
      }
    }
  }



Credit:  Matt Osborn

-------------------------------------------------------------------------------
When I pass a structure to WinCreateWindow, sometimes it doesn't work!


Put the size of the structure as the first word.

-------------------------------------------------------------------------------
How do I use type filtering in 2.0's open dlg?


It's broken in 2.0GA, fixed in 2.0's service pak.

-------------------------------------------------------------------------------
When minimizing, my dialog box is overwriting my icon!


In the WM_MINMAXFRAME message hide the offending control.

    case WM_MINMAXFRAME:
      {
        PSWP pswp; /* pos change structure */

        /* hide list box when minimized so it doesn't overwrite icon */
        pswp = PVOIDFROMMP( mp1 );
        WinShowWindow(
          WinWindowFromID( hwnd, IDD_FILES ),
          !(pswp->fs & SWP_MINIMIZE)
        );
      }
      break;


-------------------------------------------------------------------------------
How do I make a multi-column listbox?


Use the container in details view.  If you just have to use a list
box:

Here is an extract from my dialog box procedure that produces a two-column
list box.

In this example there is only one list box, so I don't have to worry
about which control is involved.  In this example, a blank is used to
separate the first and second column.  You could use tabs or any other
sort of separator character.  You could also draw anything you wanted in
the list box item, including bit maps, colors, other fonts, etc.

This is not a complete program, of course, but does show the details of
handling a multi-column list box.

/******************** Dialog Box Procedure ******************************/

MRESULT EXPENTRY SelectDlgProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2)
{
    HPS     hPS;                 /* Handle to the presentation space */
    FONTMETRICS FontMetrics;     /* Metrics of default font */
    CHAR    pszItemText[MAX_ITEM_TEXT_LENGTH];
    CHAR    *s;
    OWNERITEM FAR *poi;          /* Pointer to owner item structure */
    RECTL   rcl;                 /* Rectangle for writing */
    COLOR   clrForeGround;
    COLOR   clrBackGround;

    switch (msg)
        {
        case WM_INITDLG:   /* Initialize the list box */

            FillCfgListBox (hwnd);     /* Fill the list box */

            return (FALSE);


        case WM_MEASUREITEM:     /* Measure text height */

            hPS = WinGetPS (hwnd);     /* Get handle to presentation space */
            GpiQueryFontMetrics (hPS, (LONG) sizeof (FONTMETRICS),
                                 &FontMetrics);
            WinReleasePS (hPS);        /* Release the presentation space */

            return (FontMetrics.lMaxBaselineExt);


        case WM_DRAWITEM:      /* Draw a list box entry */

            poi = mp2;               /* Get address of owner item */

            if (poi->fsState == TRUE)  /* Is this cell to be highlighted? */
            {                          /* Yes, use highlight attributes */
                clrForeGround = SYSCLR_HILITEFOREGROUND;
                clrBackGround = SYSCLR_HILITEBACKGROUND;
            }
            else                       /* No, use normal attributes */
            {
                clrForeGround = CLR_NEUTRAL;
                clrBackGround = CLR_BACKGROUND;
            }

            WinSendMsg (poi->hwnd,     /* Get item text */
                        LM_QUERYITEMTEXT,
                        (MPARAM) MAKEULONG (poi->idItem,
                        MAX_ITEM_TEXT_LENGTH),
                        (MPARAM) pszItemText);

            rcl.xLeft	= poi->rclItem.xLeft;  /* Set co-ordinates */
            rcl.xRight	= poi->rclItem.xRight; /* of rectangle */
            rcl.yTop	= poi->rclItem.yTop;
            rcl.yBottom = poi->rclItem.yBottom;

            s = strchr (pszItemText, ' ');  /* Find first blank */
            if (s)
              *s = '\0';               /* Terminate first column here */

            WinDrawText (poi->hps,     /* Draw the first column */
                         -1,           /* Null-terminated string */
                         pszItemText,  /* File name is here */
                         &rcl,     /* Rectangle to draw in */
                         clrForeGround,/* Foreground color */
                         clrBackGround,/* Background color */
                         DT_LEFT | DT_VCENTER | DT_ERASERECT);

            if (s)		       /* If there is a second column */
            {
              rcl.xLeft = 100;         /* It starts out here */
                                       /* Spacing calculations could be */
                                       /* much cleverer than this very */
                                       /* crude use of an absolute position */
                                       /* (which is not transportable */
                                       /* to different display types, as */
                                       /* between 8514 and VGA) */
                s++;                   /* Point to beginning of text */
                WinDrawText (poi->hps, /* Draw the second column */
                             -1,       /* Also a null-terminated string */
                             s,        /* File Description */
                             &rcl, /* Rectangle to draw in */
                             clrForeGround,  /* Colors are same as */
                             clrBackGround,  /* before */
                             DT_LEFT | DT_VCENTER);
            }

                /* If fsState || fsStateOld and return TRUE, then */
                /* control will invert the rectangle -- not what */
                /* we want.  Tell control not do do anything like that! */

            poi->fsState = poi->fsStateOld = FALSE;

            return (TRUE); /* Say we did it */

. . .  case statements for rest of program . . . 


Credit:  Guy Scharf

-------------------------------------------------------------------------------
How do I create my own Master Help Index?


I talked with the owner of the Master Index, and she said there is no
problem with using a like index to view online documentation for your
application. There are just a few catches:

o The documentation must be compiled as an .HLP file and not an .INF
  file. Note that this is the default for IPFC.
o The tagged indexed entries must have the "global" attribute. For
  example:

o :i1 global.giving examples
o or:
o :i1 id=examp global.giving examples

o The Index executable is a WPS object, not a standalone application.
  You will need to create your own copy of the object (instance) of class
  Mindex, either by using a REXX script  (e.g. SysCreateObject()) or
  within your app (e.g. WinCreateObject()).

For a REXX example:

    CALL   SysCreateObject  "Mindex", "Title", "<WP_DESKTOP>",
                            "INDEX=MYFILE;OBJECTID=<MY_INDEX>"

where MYFILE can be an environmental variable set to the path or
filespec of your .HLP files, or it can be the actual filespec itself
(e.g. INDEX=c:\myapp\myfile.hlp ). Note too that files can be
concatenated (e.g. INDEX=c:\myapp\myfile.hlp+c:\yourapp\yourfile.hlp).
o Objects of Mindex class CAN NOT be subclassed. There are no public
methods in this class and it must be used as is.


Credit:  John Webb

-------------------------------------------------------------------------------
How do I change the font in an MLE?
How do I change the font in an MLE?  WinSetPresParms doesn't work.

This is a function I used in 1.x to set the font for an MLE (haven't
ported it yet but it should be the same). I pass it the parent hwnd of
the MLE, the MLE's id, a facename, a pointsize, the maximum height for
an outline font, and fsSelection (FATTR_SEL_BOLD, etc). It first tries
to match on pointsize and facename. If it can't it uses an outline font
with the height requested.

 VOID EXPENTRY UtlSetMleFont( HWND hwndParent, USHORT usMleId, PSZ szFacename,
                              USHORT usPointSize, LONG lMaxHeight,
                              USHORT fsSelection )
 {
     PFONTMETRICS pfm;
     HDC          hdc;
     HPS          hps;
     HWND         hwndMle;
     LONG         lHorzRes, lVertRes, lRequestFonts = 0, lNumberFonts;
     FATTRS       fat;
     SHORT        sOutlineIndex = -1;
     INT          i;

     (void) memset( &fat, 0, sizeof( FATTRS ) );
     fat.usRecordLength  = sizeof( FATTRS );
     fat.fsSelection	 = fsSelection;
     strcpy( fat.szFacename, szFacename );
     hwndMle = WinWindowFromID( hwndParent, usMleId );
     hps = WinGetPS( hwndMle );
     hdc = GpiQueryDevice( hps );
     DevQueryCaps( hdc, CAPS_HORIZONTAL_FONT_RES, 1L, &lHorzRes );
     DevQueryCaps( hdc, CAPS_VERTICAL_FONT_RES,   1L, &lVertRes );
     lNumberFonts = GpiQueryFonts( hps, QF_PUBLIC, szFacename,
                                   &lRequestFonts, 0L, NULL);
     pfm = malloc( (SHORT) lNumberFonts * sizeof( FONTMETRICS ) );
     GpiQueryFonts( hps, QF_PUBLIC, szFacename,
                    &lNumberFonts, (LONG) sizeof( FONTMETRICS ), pfm );
     for( i = 0; i < (USHORT) lNumberFonts; i++ )
     {
         if( pfm[ i ].fsDefn & 1 )
         {
             sOutlineIndex = (SHORT) i;
             continue;
         }

         if (pfm[ i ].sXDeviceRes == (SHORT) lHorzRes &&
             pfm[ i ].sYDeviceRes == (SHORT) lVertRes &&
             pfm[ i ].sNominalPointSize == (SHORT) (usPointSize * 10) )
         {
             fat.lMatch          = pfm[ i ].lMatch;
             fat.lMaxBaselineExt = pfm[ i ].lMaxBaselineExt;
             fat.lAveCharWidth   = pfm[ i ].lAveCharWidth;
             break;
         }
     }

     if( i >= (USHORT) lNumberFonts )
         if( sOutlineIndex >= 0 )
             if( lMaxHeight )
             {
                 fat.fsFontUse = FATTR_FONTUSE_OUTLINE;
                 if( !(fat.usCodePage = pfm[ sOutlineIndex ].usCodePage) )
                     fat.usCodePage  = 850;
                 fat.lMaxBaselineExt = lMaxHeight;
                 WinSendMsg( hwndMle, MLM_SETFONT, MPFROMP( &fat ), 0 );
             }
     WinSendMsg( hwndMle, MLM_SETFONT, MPFROMP( &fat ), 0 );
     WinReleasePS( hps );
     free( pfm );
 }


Credit:  Rick Fishman

-------------------------------------------------------------------------------
Why can't I import files larger than 64KB into my MLE?


A MLE can not import or export text in bigger chunks than 64K. The below 
code shows how to solve this.

void Editor::impTxt(const char *filename)
{
  FILE *ptr;
  long dummy=-1;
  long result;

  ptr = fopen(filename,"rb");
  fseek(ptr,0,SEEK_END);
  unsigned long size = ftell(ptr);

  fseek(ptr,0,SEEK_SET);
  void *buf = malloc(0xF000);
  
  mle->sendEvent(MLM_SETIMPORTEXPORT, buf, (unsigned long)  0xF000);

  mle->sendEvent(MLM_FORMAT, MLFIE_CFTEXT, NULL);
  
  while (feof(ptr) == 0) {
    result = fread(buf, 1, 0xE000, ptr);
    mle->sendEvent(MLM_IMPORT, &dummy, result);
  } 
  fclose(ptr);
  free(buf);
}

The same applies to export. If the MLE contains >64K text you have to
export the text in chunks like this

void Editor::saveFile(void)
{
  IString filename;
  IEventData rc;
  IFileDialog::Settings settings;
  
  settings.setTitle("Save textfile");
  settings.setFileName(fname);
  settings.setOKButtonText("Save file");
  settings.setSaveAsDialog();

  IFileDialog file(desktopWindow(),this,settings,
                   IFileDialog::defaultStyle() 
                   | IFileDialog::selectableListbox);
  if (file.pressedOK() == false)
    return ;
  filename = file.fileName();
  if (filename.size() == 0)
    return;
  mle->exportToFile(filename);

  void *buf = malloc(0xF000);
  long ppt=0;

  mle->sendEvent(MLM_SETIMPORTEXPORT, buf, (unsigned long)  0xF000);

  mle->sendEvent(MLM_FORMAT, MLFIE_NOTRANS, NULL);
  ofstream fil(filename);

  do 
    {
      long amount = 0xE000;
      rc = mle->sendEvent(MLM_EXPORT, &ppt, &amount);
      fil.write((char *) buf, rc.asUnsignedLong());
    } while (rc.asUnsignedLong() > 0);
}

Credit: Ivar E. Hosteng

-------------------------------------------------------------------------------
How do I get PM screen size?


By calling the function WinQuerySysValue() with the tags; SV_CYSCREEN and
SV_CXSCREEN. One should always check screen size before creating windows
in PM, and size to the right it width and height. PM does not guarantee
that your window will be dimensioned right.
Below is an example
        .
        .
        .

    lHeight = WinQuerySysValue(HWND_DESKTOP,SV_CYSCREEN);
    lWidth  = WinQuerySysValue(HWND_DESKTOP,SV_CXSCREEN);

        .
        .
        .
 

With these values you size and position your window with WinSetWindowPos().


Credit: Andreas Almroth

-------------------------------------------------------------------------------
How do I attach Instance data to window created with WinCreateStdWindow?


I always use the two call to WinCreateWindow() method,	instead of the
WinCreateStdWindow() method,  because the latter does not allow you to
set instance data before your winproc is called for the first time.


Here's a cset/2 program (icc /Ss+ /W3 /Se /B"/pm:pm" foo.c):

 #define INCL_PM
 #include <os2.h>

 typedef struct _mydata {
     HWND hwndFrame;
     HWND hwndClient;
     char whatever[100];
 }MYDATA;

 static MRESULT EXPENTRY WinProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
 {
     MYDATA *mine;

    mine = (MYDATA *)WinQueryWindowPtr(hwnd, 0);  // will be null for WM_CREATE
     switch(msg){
         case WM_CREATE:
             mine = (MYDATA *)mp1;
             WinSetWindowPtr(hwnd, 0, mine);
             break;
         case WM_ERASEBACKGROUND:
             return (MRESULT)TRUE;
         default:
            return WinDefWindowProc(hwnd, msg, mp1, mp2);
     }
     return 0;
 }

 void main(void)
 {
     HAB hab;
     HMQ hmq;
     QMSG qmsg;
     char *class = "foo";
     FRAMECDATA fcdata;  /* frame creation data */
     MYDATA mydat;

     hab = WinInitialize(0);
     hmq = WinCreateMsgQueue(hab, hmq);

     WinRegisterClass(hab, class, WinProc, 0, sizeof(MYDATA *));

     /* create frame window (WC_FRAME) */
     fcdata.cb = sizeof(FRAMECDATA);
     fcdata.flCreateFlags = FCF_TITLEBAR | FCF_SYSMENU |
                            FCF_SHELLPOSITION | FCF_TASKLIST |
                            FCF_SIZEBORDER | FCF_MINMAX ;

     fcdata.hmodResources = 0;
     fcdata.idResources   = 1;	     /* resource id */
     mydat.hwndFrame = WinCreateWindow(HWND_DESKTOP, WC_FRAME, "Title",
                                       0, 0, 0, 0, 0,
                                       0,  /* no owner */
                                       HWND_TOP,fcdata.idResources,&fcdata,NULL);

     if(!mydat.hwndFrame)
         exit(3);

     /* WinProc() has not been called yet */

     /* Create Client window: */
     mydat.hwndClient = WinCreateWindow(mydat.hwndFrame, class, "text",
                         0, 0, 0, 0, 0,
                         mydat.hwndFrame,    /* frame is owner */
                         HWND_TOP, FID_CLIENT,
                         &mydat, /* passed as mp1 to wm_create */
                         NULL);

     WinShowWindow(mydat.hwndFrame, TRUE);

     while(WinGetMsg(hab, &qmsg, (HWND) NULL, 0, 0))
         WinDispatchMsg(hab, &qmsg);

 }



Credit:  Peter Fitzsimmons

-------------------------------------------------------------------------------
How do I get a list of fonts?


Shown is code for For v1.3.  Petzold has had a nice series on fonts in
PC MAG from about Nov 92-June 93.

#define INCL_DEV
#define INCL_DOSMEMMGR
#define INCL_GPILCIDS
#define INCL_WINWINDOWMGR
#define INCL_WINLISTBOXES
#include <os2.h>
#include <stdio.h>

VOID EXPENTRY FntQueryList(hwnd)
HWND hwnd; // listbox handle
  {
  PFONTMETRICS pfm;
  HDC          hdc;
  HPS          hps;
  LONG         alDevInfo[2];
  LONG         lFonts;
  LONG         lFontCnt = 0L;
  CHAR         pch[64];
  SEL          sel;
  USHORT       i;

  hdc = WinOpenWindowDC(hwnd);
  DevQueryCaps(hdc, CAPS_HORIZONTAL_FONT_RES, 2L, alDevInfo);
  hps = WinGetPS(hwnd);
  lFonts = GpiQueryFonts(hps, QF_PUBLIC, (PSZ)0, &lFontCnt,
                         (LONG)sizeof(FONTMETRICS), (PFONTMETRICS)0 );
  DosAllocSeg((USHORT)lFonts * sizeof(FONTMETRICS), &sel, SEG_NONSHARED);
  pfm = (PFONTMETRICS)MAKEP(sel, 0);
  GpiQueryFonts(hps, QF_PUBLIC, (PSZ)0, &lFonts,
                (LONG)sizeof(FONTMETRICS), pfm );
  for (i = 0; i < (USHORT)lFonts; i++) {
    if (!(pfm[i].fsDefn & FM_DEFN_OUTLINE)) {
      if ((pfm[i].sXDeviceRes == (SHORT)alDevInfo[0]) &&
          (pfm[i].sYDeviceRes == (SHORT)alDevInfo[1]) ) {
            sprintf(pch, "%2d.%s",
                    pfm[i].sNominalPointSize / 10,
                    pfm[i].szFacename );
        WinSendMsg(hwnd, LM_INSERTITEM,
                   MPFROMSHORT(LIT_END), MPFROMP(pch) );
        }
      }
    }
  DosFreeSeg(sel);
  WinReleasePS(hps);
  return;
  }


-------------------------------------------------------------------------------
How do I create a folder in C and put my pgm in it?

/* Create our folder on the desktop */

HOBJECT hFolder, hObject;

hFolder = WinCreateObject ("wpFolder", "My Folder",
                           "OBJECTID=<MY_FOLDER>", "<WP_DESKTOP>",
                           CO_REPLACEIFEXISTS);

/* Now, put our object into the folder. */

hObject = WinCreateObject ("wpProgram", "My Online Book",
                           "EXENAME=VIEW.EXE;PARAMETERS=MYBOOK.INF;\
                           OBJECTID=<MY_BOOK>", "<MY_FOLDER>",
                           CO_REPLACEIFEXISTS);

What that little code snippet does is create a folder called "My Folder"
on the desktop, and puts a program object for an online book in it.
Check out the documentation on the following functions for more
information:

WinCreateObject
wpSetup (for information on the setup string parameter to WinCreateObj)
WPFolder (for information on the setup strings of folders)
WPProgram programs etc.


-------------------------------------------------------------------------------
How do I do it in REXX?


You may have seen the Paradox 4.0 install script, which uses
SysCreateObject to make a WPS object


Hope they don't mind me posting a fragment of the script by:

/* Rob Gordon  Borland International 6/8/92  */

title = "Paradox 4.0"
classname = 'WPProgram'
location = '<WP_DESKTOP>'
setup = 'PROGTYPE=WINDOWEDVDM;EXENAME='pdoxpath'paradox.exe;
  STARTUPDIR='pdoxpath';SET DPMI_DOS_API=ENABLED;
  SET DPMI_MEMORY_LIMIT=4;ICONFILE 'pdoxpath'pdoxos2.ico;'

BldObj:
call charout , 'Building : 'title
result = SysCreateObject(classname, title, location, setup)


-------------------------------------------------------------------------------
How do I use the Font dialog (WinFontDlg)?


FIRST:  make your stack huge (>32K)  It is sparsely allocated and
you only use what you need. Here's some code:

 FONTDLG fontdlg ;
 FONTMETRICS fm ;
 CHAR            szBuf [200] ;
 CHAR            szFamily [CCHMAXPATH] ;
 static CHAR     szTitle [] = "Fonts Dialog" ;
 static CHAR     szMsgBoxTitle [] = "Result from 'WinFontDlg ()'" ;
 static CHAR     szPreview [] = "We hold these truths to be self-evident . . . " ;

 // Here, get an HPS and then do a WinQueryFontMetrics.
 // Continuing . . . 

 memset ((void *) &fontdlg, 0, sizeof (FONTDLG)) ;
 fontdlg.cbSize = sizeof (FONTDLG) ;
 fontdlg.hpsScreen = WinGetScreenPS (HWND_DESKTOP) ;
 fontdlg.hpsPrinter = NULLHANDLE ;
 fontdlg.pszTitle = szTitle ;
 fontdlg.pszPreview = szPreview ;
 fontdlg.pfnDlgProc = NULL ;
 strcpy (szFamily, fm.szFamilyname) ;
 fontdlg.pszFamilyname = szFamily ;
 fontdlg.usFamilyBufLen = sizeof (szFamily) ;
 fontdlg.fxPointSize = MAKEFIXED (fm.sNominalPointSize / 10, 0) ;
 fontdlg.fl = FNTS_CENTER | FNTS_HELPBUTTON |
     FNTS_INITFROMFATTRS | FNTS_NOSYNTHESIZEDFONTS |
     FNTS_RESETBUTTON ;
 fontdlg.sNominalPointSize = fm.sNominalPointSize ;
 fontdlg.flType = (LONG) fm.fsType ;
 fontdlg.clrFore = CLR_NEUTRAL ;
 fontdlg.clrBack = CLR_BACKGROUND ;
 fontdlg.usWeight = fm.usWeightClass ;
 fontdlg.usWidth = fm.usWidthClass ;

 hwndDlg = WinFontDlg (HWND_DESKTOP, hwnd, &fontdlg) ;



Credit: Wayne Kovsky

-------------------------------------------------------------------------------
How do I take control of frame sizing?


I want to "subclass" the Window Frame Class to allow me to control the
size of my main window when it's resized by the mouse. I want to control
the steps it take to resize too, (ie: by 8x8 steps).


--- CUT --- CUT ---- .H file --- CUT ---

HWND APIENTRY WinCreateMBWindow(

  HWND hwndParent,
  ULONG flStyle,
  PULONG pflCreateFlags,
  PSZ pszClientClass,
  PSZ pszTitle,
  ULONG styleClient,
  HMODULE hmod,
  USHORT idResources,
  PHWND phwndClient);


#define HMODFROMMP(mp) ((USHORT)(ULONG)(mp))

#define MB_CHILD_SIZE  WM_USER+204
          //Sent from child to frame indicating the new
          //requested size of the child area.  (high&width in MP1)
          //If ChildMax is shrinking below current size,
          // frame is redused, and evt scroll bars is hidden.
          //If Child is growing
          //   MP2=True -> grows frame to contain maximized child
          //   MP2=False-> enables scroll bars, keeps size.

#define MB_FIX_N       0x1 //Fix north border when sizing
#define MB_FIX_E       0x2 //Fix east  border when sizing

#define MB_FIX_NE       0x3 //Fix north east corner when sizing
#define MB_FIX_SE       0x2 //Fix south east corner when sizing
#define MB_FIX_NW       0x1 //Fix north west corner when sizing
#define MB_FIX_SW       0x0 //Fix south west corner when sizing

#define MB_QCHILD_SIZE_MAX     WM_USER+205
           //Sent from frame to child to queryw
           //max size of the child area.  (h&w in MR)
           //Used when sizing to determin if scroll bars are ness.

#define MB_QCHILD_GRID     WM_USER+206
           //Sent from frame to child before tracking
           //Child supposed to return x&y granularity in MR

#define MB_CREATE          WM_USER+207  //Internally shipped command.

typedef struct _FRM_DTA { /* Fram data */
    PFNWP    oldProc;
    SHORT    nTrkCnr,nYSclHor,nXSclVer,nTitle;
    SHORT    xMax,yMax;    //Max size of Client in PIX
    SHORT    ScrMaxX,ScrMaxY;
    POINTL   ptlBdr;
    HWND     hVert,hHori;
    SHORT    xCur,yCur;    //Current size of Client in PIX
    SHORT    nCorner;      //Which corner is to be stable
} FRM_DTA;
typedef FRM_DTA FAR * PFRM_DTA;



--- CUT --- CUT --- .C file --- CUT --- CUT ---

HWND	APIENTRY WinCreateMBWindow(HWND hParent, ULONG flStyle,
                                   PULONG pflCreate, PSZ pszClientClass,
                                   PSZ pszTitle, ULONG styleClient,
                                   HMODULE hmod,
                                   USHORT idResources, PHWND phClient)
{
  HWND         hFrame;
  PFRM_DTA     pFrm;

  hFrame = WinCreateStdWindow(hParent,flStyle,pflCreate,pszClientClass,
                              pszTitle,styleClient,hmod,idResources,phClient);

  //Allocate some storage for local parameters, and initialize it
  pFrm=malloc(sizeof( FRM_DTA));
  memset(pFrm,0,sizeof( FRM_DTA));

  WinSetWindowPtr(hFrame,QWL_USER,(PVOID) pFrm);

  pFrm->oldProc= WinSubclassWindow(hFrame,newFrameProc);

  //Now window is setup:

  WinSendMsg(hFrame,MB_CREATE,MP0,MP0);    //My own WM_CREATE

  WinShowWindow(hFrame,TRUE);

  WinUpdateWindow(hFrame);
  return hFrame;
}

VOID GetChildMax(HWND hFrame, PFRM_DTA	pFrm)
{ MRESULT mr;
  mr = WinSendMsg(WinWindowFromID(hFrame,FID_CLIENT),
                  MB_QCHILD_SIZE_MAX,MP0,MP0);
  pFrm->xMax=SHORT1FROMMR(mr);
  pFrm->yMax=SHORT2FROMMR(mr);
}

#define CLIENT_PROVIDED  FALSE
#define FRAME_PROVIDED	 TRUE

BOOL WinCalcFrameSWP(HWND hwnd, PSWP pSWP, BOOL bFrame)
{ //    TRUE       Frame rectangle provided
  //    FALSE      Client-area rectangle provided.
  RECTL rcl;
  BOOL bSuccess;

  rcl.xLeft   = pSWP->x;
  rcl.yBottom = pSWP->y;
  rcl.xRight  = pSWP->x+pSWP->cx;
  rcl.yTop    = pSWP->y+pSWP->cy;

  bSuccess = WinCalcFrameRect(hwnd, &rcl, bFrame );

  pSWP->x  =(SHORT) rcl.xLeft;
  pSWP->y  =(SHORT) rcl.yBottom;
  pSWP->cx =(SHORT) (rcl.xRight-rcl.xLeft);
  pSWP->cy =(SHORT) (rcl.yTop  -rcl.yBottom);

  return(bSuccess);
}


VOID SclBarEnable(HWND hwnd, PSWP pSWP, PFRM_DTA  pFrm)
{

  . . . . . 


  Your routines to determine and en/disable scroll bars

  . . . . . 
/*    Part of my code */
  swpClient = *(pSWP);
  WinCalcFrameSWP(hwnd, &swpClient, FRAME_PROVIDED);
  if (swpClient.cx >= pFrm->xMax-1)
  { //Turn off horisontal scroll bar
    WinSetParent(pFrm->hHori,HWND_OBJECT ,FALSE);
    nUpdt = FCF_HORZSCROLL;
  }  /* code for enabling left out. . . . .  */

  if (nUpdt != 0)
  {
    WinSendMsg (hwnd, WM_UPDATEFRAME, MPFROMSHORT(nUpdt), MP0);
  }
*/
}

/***************** Start of frame subclass procedure *****************/

MRESULT  EXPENTRY newFrameProc( HWND hwnd, USHORT msg,
MPARAM mp1, MPARAM mp2 )

{ PSWP	    pSWP;
  PFNWP     oldProc;
  PFRM_DTA  pFrm;

  pFrm=(PFRM_DTA)WinQueryWindowPtr(hwnd,QWL_USER);
  oldProc=pFrm->oldProc;

  switch(msg)
  {
    case MB_CREATE:
      //Start hiding scroll bars
      pFrm->hVert  = WinWindowFromID(hwnd,FID_VERTSCROLL);
      WinSetParent(pFrm->hVert,HWND_OBJECT ,FALSE);
      pFrm->nYSclHor = 0;
    break;

    case WM_MINMAXFRAME:
    { SWP swpClient;
      pSWP=(PSWP) PVOIDFROMMP(mp1);
      pSWP->fs |= SWP_SIZE;

      GetChildMax(hwnd, pFrm);

      if (pSWP->fs & SWP_MAXIMIZE) //MaxiMizeing Operation
      {
        //tprintf("WM_MINMAXFRAME SWP_MAXIMIZE FS: %X",pSWP->fs);
        if ((0!=pFrm->xMax) && (0!=pFrm->yMax))
        { //I have no data for all of screen, so act as Windowed OS/2

          memset(&swpClient,0,sizeof(swpClient));
          swpClient.cx=pFrm->xMax;
          swpClient.cy=pFrm->yMax;
          WinCalcFrameSWP(hwnd, &swpClient,CLIENT_PROVIDED);

          pSWP->cx = swpClient.cx;
          pSWP->cy = swpClient.cy;

          pSWP->y  = pFrm->ScrMaxY - swpClient.cy + (SHORT)pFrm->ptlBdr.y;


          SclBarEnable(hwnd, pSWP, pFrm);

          return MRFROMSHORT(FALSE);
        }
      }

      if (pSWP->fs & SWP_RESTORE) //MiniMizeing Operation



??? (restore i guess)

      {
        //tprintf("WM_MINMAXFRAME SWP_RESTORE FS: %X",pSWP->fs);
        if ((0!=pFrm->xMax) && (0!=pFrm->yMax))  //Why do I need this?
        {
          return MRFROMSHORT(FALSE);
        }
      }
    }
    return (MRESULT)(*oldProc)(hwnd, msg, mp1, mp2);


    case WM_QUERYTRACKINFO:
    {
      PTRACKINFO pTI;
      LONG i;

      if (TF_MOVE <= SHORT1FROMMP(mp1))
      {
        pFrm->nTrkCnr= 0;
        return (MRESULT)(*oldProc)(hwnd, msg, mp1, mp2);
      }

      pTI = (PTRACKINFO) PVOIDFROMMP (mp2);  //Get structure pointer;
      //tprintf("@+WM_QUERYTRACKINFO FS: %X",pTI->fs);
      if ((*oldProc)(hwnd, msg, mp1, mp2)==0) return MRFROMSHORT(FALSE);

      GetChildMax(hwnd, pFrm);
      if ((0!=pFrm->xMax) && (0!=pFrm->yMax))
      { //Setup track info structure
        pTI->ptlMaxTrackSize.x = (LONG) pFrm->xMax;
        pTI->ptlMaxTrackSize.y = (LONG) pFrm->yMax;

        WinCalcFrameRect(hwnd, &(pTI->rclTrack), FRAME_PROVIDED );

        { MRESULT mr;
          mr = WinSendMsg(WinWindowFromID(hwnd,FID_CLIENT),
                          MB_QCHILD_GRID,MP0,MP0);
          if (NULL!=mr)
          {
            pTI->cxGrid  = SHORT1FROMMR(mr);
            pTI->cyGrid  = SHORT2FROMMR(mr);

            //Setting height of client to integral cyGrid
            i = (pTI->rclTrack.yTop - pTI->rclTrack.yBottom)/pTI->cyGrid;
            pTI->rclTrack.yTop = i * pTI->cyGrid + pTI->rclTrack.yBottom;

            pTI->fs	|= TF_GRID;
            pFrm->nTrkCnr= WM_QUERYTRACKINFO;
            //Used in WM_ADJUSTWINODWPOS
          }
        }
      }
      return MRFROMSHORT(TRUE);
    }
    break;

    case WM_ADJUSTWINDOWPOS:
    { RECTL rcl;
      pSWP=(PSWP) PVOIDFROMMP(mp1);

      if (pSWP->fs & (SWP_SIZE | SWP_MOVE | SWP_MAXIMIZE))
      {
        GetChildMax(hwnd, pFrm);
        if (WM_QUERYTRACKINFO==pFrm->nTrkCnr)
          //As a result of tracking
        {
          //Go from client to frame values
          WinCalcFrameSWP(hwnd, pSWP, CLIENT_PROVIDED);
        }

        pFrm->nTrkCnr	 = 0;

        SclBarEnable(hwnd, pSWP, pFrm);
      }
      return (MRESULT)(*oldProc)(hwnd, msg, mp1, mp2);
    }
    break;

    case MB_CHILD_SIZE:      //xCur,yCur
    { SWP swp;




. . . . . . . . . . . . . 
do your actions as responce to client resize request
(Change of fontsize for instance)


      return (MRESULT)TRUE;
    }
    break;

    default:
    break;
  }
  return (MRESULT)(*oldProc)(hwnd, msg, mp1, mp2);
}



A lot is deleted (Som is very application specific), but i hope you get
the general picture.


Credit:  Henrik Wahlberg

-------------------------------------------------------------------------------
How do I use the 16-bit EPM toolkit?


Since there were a few requests, here are the changes I made to get the
ESIMPLE example from the EPM Toolkit to work with the C-Set++ compiler.
The ETK DLLs are 16-bit and need to be thunked to be called from a
32-bit app. Since I was playing around, some of the changes were not
necessary to get the program to run.  Changes are in DIFF format.

**** Changes to EDLL.H ****
254a255
> #pragma pack(2)
259,263c260,264
<    PSWP   pswp;         // positioning of edit window
<    PSZ    filename;     // file to be edited (with wildcard)
<    PVOID  hEditPtr;     // handle to editor pointer icon.
<    PVOID  hMarkPtr;     // handle to mark pointer icon.
<    PVOID  hEditorIcon;  // editor ICON.
>    PSWP _Seg16   pswp;         // positioning of edit window
>    PSZ _Seg16    filename;     // file to be edited (with wildcard)
>    HPOINTER  hEditPtr;  // handle to editor pointer icon.
>    HPOINTER  hMarkPtr;  // handle to mark pointer icon.
>    PVOID _Seg16  hEditorIcon;  // editor ICON.
267,269c268,270
<    PSZ    exfile;       // pre-compiled macro code file (EPM.EX)
<    PSZ    topmkr;       // top and bottom of file marker
<    PSZ    botmkr;       //
---
>    PSZ _Seg16    exfile;       // pre-compiled macro code file (EPM.EX)
>    PSZ _Seg16    topmkr;       // top and bottom of file marker
>    PSZ _Seg16    botmkr;       //
271,272c272,273
<    PSZ    exsearchpath; // a set of paths to search for ex's files
<    PSZ    exe_path;     // path where the application started
---
>    PSZ _Seg16    exsearchpath; // a set of paths to search for ex's files
>    PSZ _Seg16    exe_path;     // path where the application started
275c276,277
< typedef EDITORINFO far *PEDITORINFO;
---
> typedef EDITORINFO * PEDITORINFO;
> #pragma pack()
280c282
< #define ETKENTRY _loadds
---
> #define ETKENTRY CDECL16
285,286c287,288
< PSZ	 ETKENTRY EtkRegister( HAB hab, ULONG class_style );
< USHORT ETKENTRY EtkCreate( PEDITORINFO epm_p, PHWND hEwnd_p);
---
> PSZ _Seg16    ETKENTRY EtkRegister( HAB hab, ULONG class_style );
> USHORT ETKENTRY EtkCreate( PEDITORINFO _Seg16 epm_p, PHWND _Seg16 hEwnd_p);
304c306,307
< SHORT  ETKENTRY EtkSetSelection( HWND  hwndClient, LINE_INDEX_G  firstline,
---
> SHORT  ETKENTRY EtkSetSelection( HWND  hwndClient, LINE_INDEX_G  firstline,
>    FIDType fileid);

**** Changes to ESIMPLE.C ****
28a29,30
>
> #include "thunk.h"
47a50
>    HWND hwndEditFrame;
52c55
<
---
>    swp.fl = SWP_MOVE | SWP_SIZE;
58c61
<    epm.filename    = (PSZ)Fname;      // file to be edited (with wildca
---
>    epm.filename    = Fname;           // file to be
> edited (with wildca
64,65c67,70
<    epm.editorstyle = EDIT_STYLE_ACTIVATEFOCUS | EDIT_STYLE_DISABLEOWNERAFFECT
<                      EDIT_STYLE_CURSORON;
---
>    epm.editorstyle = EDIT_STYLE_ACTIVATEFOCUS |
>                      EDIT_STYLE_CURSORON |
>                      EDIT_STYLE_STATUSLINE |
>                      EDIT_STYLE_MESSAGELINE;
67,68c72,73
<    epm.pmstyle     = FCF_TITLEBAR | FCF_SIZEBORDER | FCF_VERTSCROLL;
<
---
>    epm.pmstyle     = FCF_TITLEBAR | FCF_SIZEBORDER |
> FCF_VERTSCROLL | FCF_MIN
>    epm.font = TRUE;  // large font
70,71c75,76
<    epm.topmkr      =                        // top and bottom file indicator
<    epm.botmkr      = (PSZ)" ";
---
>    epm.topmkr      = (PSZ)"<Top>";
> // top and bottom fi
>    epm.botmkr      = (PSZ)"<Bottom>";
75c80
<
---
>    epm.hini = NULLHANDLE;
77,79c82,86
<    EtkCreate( (EDITORINFO far *)&epm, (PHWND)&hwndEdit );
<
<    return( (HWND)hwndEdit );
---
>    EtkCreate( &epm, &hwndEdit );
>       hwndEditFrame = WinQueryWindow(hwndEdit, QW_PARENT);
>       WinSetWindowPos(hwndEditFrame, NULLHANDLE, swp.x, swp.y,
>           swp.cx, swp.cy, SWP_MOVE | SWP_SIZE);
>    return( hwndEdit );
95c102
< MRESULT FAR PASCAL TestWndProc( HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2
---
> MRESULT EXPENTRY TestWndProc( HWND hwnd, USHORT msg,
> MPARAM mp1, MPARAM mp2 )
144,146c151,155
<   case EPM_EDIT_ACTIVEHWND:
<      WinSetActiveWindow(HWND_DESKTOP,(HWND)mp1 );
<      break;
---
> case EPM_EDIT_ACTIVEHWND:
>   {
>      WinSetActiveWindow(HWND_DESKTOP,(HWND)mp1 );
>   }
>   break;
160c169
<                     (PSZ)mp1,
---
>                     (PSZ)_DosSelToFlat(mp1),
162c171
<                     NULL,
---
>                     NULLHANDLE,
235c244
<     hab = WinInitialize(NULL);              // Initialize app as a PM app
---
>     hab = WinInitialize(NULLHANDLE);        // Initialize app as a PM app
247c256
<



Credit:  Mat Kramer

-------------------------------------------------------------------------------
How do I get error info after using WinGetLastError()?


One should only use WinGetLastError() after a bad return value, ERRORID
is not always zeroed after a successful call to PM API.


ShowError Function:

/*
 * ShowError -- for debugging *    to use this, redirect the output of this program to a file and
 *    check the results in that file.
 *    Updated to display the error text in a MESSAGE BOX; ID_MSGBOX must be
 *    defined.
 */
 void ShowError(void)
 {
    PERRINFO p;
    char *cp;
    extern HAB hab;   /* kinda kludgy, should be a parameter */

    printf("ShowError    if((p = WinGetErrorInfo(hab)) == NULL)
       printf("NO ERROR\n");
    else
    {
       printf("idError = %#X\n", p->idError);
       cp = (char *)((ULONG)p + *(ULONG *)((ULONG)p + p->offaoffszMsg));
       printf("\"%s\"\n", cp);
       WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, (PSZ)cp, (PSZ)"Show Error",
                     ID_MSGBOX, MB_OK | MB_ICONEXCLAMATION);
       WinFreeErrorInfo(p);
    }
 }



Credit:  Justin V Frank

-------------------------------------------------------------------------------
Do you have code to save/restore the clipboard?

 typedef struct _CLPBRDDATA // INFORMATION ABOUT A CLIPBOARD FORMAT
 {
    ULONG ulFormat; // The format type
    PVOID pvData;   // Pointer to the data for this format
 } CLPBRDDATA, *PCLPBRDDATA;

 //***************
 // Save formats
 //***************
 INT SaveClipboardData( HAB hab, PCLPBRDDATA *ppcd )
 {
    INT iFmtCount = 0;

    if( WinOpenClipbrd( hab ) )
    {
       ULONG ulNextFmt, ulPrevFmt = 0;

       ulNextFmt = WinEnumClipbrdFmts( hab, ulPrevFmt );
       while( ulNextFmt )
       {
          iFmtCount++;
          ulPrevFmt = ulNextFmt;
          ulNextFmt = WinEnumClipbrdFmts( hab, ulPrevFmt );
       }

       if( iFmtCount )
       {
          *ppcd = (PCLPBRDDATA) malloc( sizeof( CLPBRDDATA ) * iFmtCount );
          if( *ppcd )
          {
             memset( *ppcd, 0, sizeof( CLPBRDDATA ) * iFmtCount );
              if( !GetClipboardFmts( hab, *ppcd ) )
                 iFmtCount = 0;
          }
          else
          {
             iFmtCount = 0;
             Msg( "Out of memory in SaveClipboardData" );
          }
       }
       WinCloseClipbrd( hab );
    }
    else
       Msg( "SaveClipboardData could not open the clipboard" );

    return iFmtCount;
 }

 BOOL GetClipboardFmts( HAB hab, PCLPBRDDATA pcd )
 {
    BOOL  fSuccess = TRUE;
    ULONG ulNextFmt, ulPrevFmt = 0;

    ulNextFmt = WinEnumClipbrdFmts( hab, ulPrevFmt );
    while( ulNextFmt )
    {
       pcd->ulFormat = ulNextFmt;
       switch( ulNextFmt )
       {
          case CF_TEXT:
             StoreClipboardText( hab, pcd );
             break;
          case CF_BITMAP:
             StoreClipboardBmp( hab, pcd );
             break;
          case CF_METAFILE:
             StoreClipboardMeta( hab, pcd );
             break;
          default:
             break;
       }

       pcd++;
       ulPrevFmt = ulNextFmt;
       ulNextFmt = WinEnumClipbrdFmts( hab, ulPrevFmt );
    }
    return fSuccess;
 }

 VOID StoreClipboardText( HAB hab, PCLPBRDDATA pcd )
 {
    PSZ szClipText = (PSZ) WinQueryClipbrdData( hab, CF_TEXT );
    if( szClipText )
    {
       pcd->pvData = malloc( strlen( szClipText ) + 1 );
       if( pcd->pvData )
          strcpy( pcd->pvData, szClipText );
       else
          Msg( "Out of memory in StoreClipboardText" );
    }
    else
       Msg( "StoreClipboardText found no TEXT in the clipboard" );
 }

 VOID StoreClipboardBmp( HAB hab, PCLPBRDDATA pcd )
 {
    HBITMAP hbmClip = WinQueryClipbrdData( hab, CF_BITMAP );
    if( hbmClip )
       pcd->pvData = (PVOID) CopyBitmap( hab, hbmClip );
    else
       Msg( "StoreClipboardBmp found no BITMAP in the clipboard" );
 }

 VOID StoreClipboardMeta( HAB hab, PCLPBRDDATA pcd )
 {
    HMF hmfClip = WinQueryClipbrdData( hab, CF_METAFILE );
    if( hmfClip )
    {
       HMF hmfNew = GpiCopyMetaFile( hmfClip );
       if( hmfNew == GPI_ERROR )
          Msg( "StoreClipboardMeta GpiCopyMetaFile RC = %x",
          (USHORT) WinGetLastError( hab ) );
       else
          pcd->pvData = (PVOID) hmfNew;
    }
    else
       Msg( "StoreClipboardMeta found no METAFILE in the clipboard" );
 }

 //******************
 // Restore formats
 //******************
 VOID RestClipboardData( HAB hab, INT iFmtCount, PCLPBRDDATA pcd )
 {
    PCLPBRDDATA pcdSave = pcd;
    INT  i;
    BOOL fFormatsExist = FALSE;

    for( i = 0; i < iFmtCount; i++, pcd++ )
    {
       if( pcd->ulFormat && pcd->pvData )
       {
          fFormatsExist = TRUE;
          break;
       }
    }

    pcd = pcdSave;
    if( fFormatsExist )
       if( WinOpenClipbrd( hab ) )
       {
          WinEmptyClipbrd( hab );
          for( i = 0; i < iFmtCount; i++, pcd++ )
          {
             switch( pcd->ulFormat )
             {
                case 0:
                   break;
                case CF_TEXT:
                   if( pcd->pvData )
                   {
                      RestClipboardText( hab, pcd->pvData );
                      free( pcd->pvData );
                   }
                   break;
                case CF_BITMAP:
                   if( pcd->pvData )
                   RestClipboardBmp( hab, (HBITMAP) pcd->pvData );
                   break;
                case CF_METAFILE:
                   if( pcd->pvData )
                      RestClipboardMeta( hab, (HMF) pcd->pvData );
                   break;
                default:
                   break;
             }
          }
          WinCloseClipbrd( hab );
       }
       else
          Msg( "RestClipboardData could not open the clipboard" );
    free( pcdSave );
 }

 VOID RestClipboardText( HAB hab, PSZ szTextIn )
 {
    PSZ szTextOut = NULL;
    ULONG ulRC =
    DosAllocSharedMem( (PVOID) &szTextOut, NULL,
                       strlen( szTextIn ) + 1,
                       PAG_WRITE | PAG_COMMIT | OBJ_GIVEABLE );
    if( !ulRC )
    {
       strcpy( szTextOut, szTextIn );
       if( !WinSetClipbrdData( hab, (ULONG) szTextOut, CF_TEXT,
                               CFI_POINTER ) )
          Msg( "RestClipboardText WinSetClipbrdData failed" );
    }
    else
       Msg( "RestClipboardText DosAllocSharedMem RC: %u", ulRC );
 }

 VOID RestClipboardBmp( HAB hab, HBITMAP hbm )
 {
  if( !WinSetClipbrdData( hab, (ULONG) hbm, CF_BITMAP, CFI_HANDLE ) )
     Msg( "RestClipboardBmp WinSetClipbrdData failed" );
 }

 VOID RestClipboardMeta( HAB hab, HMF hmf )
 {
  if( !WinSetClipbrdData( hab, (ULONG) hmf, CF_METAFILE, CFI_HANDLE ) )
     Msg( "RestClipboardMeta WinSetClipbrdData failed" );
 }

 HBITMAP CopyBitmap( HAB hab, HBITMAP hbmIn )
 {
    BITMAPINFOHEADER2 bmih;
    HBITMAP           hbmOut = NULLHANDLE;
    HDC               hdcIn = NULLHANDLE, hdcOut = NULLHANDLE;
    HPS               hpsIn = NULLHANDLE, hpsOut = NULLHANDLE;
    POINTL            aptl[ 3 ];
    SIZEL             sizel = {0,0};

    hdcIn = DevOpenDC( hab, OD_MEMORY, "*", 0, NULL, NULLHANDLE );
    if( !hdcIn )
    {
       Msg( "CopyBitmap DevOpenDC for hdcIn RC = %x",
            (USHORT) WinGetLastError( hab ) );
       goto BYEBYE;
    }

    hdcOut = DevOpenDC( hab, OD_MEMORY, "*", 0, NULL, NULLHANDLE );
    if( !hdcOut )
    {
       Msg( "CopyBitmap DevOpenDC for hdcOut RC = %x",
            (USHORT) WinGetLastError( hab ) );
       goto BYEBYE;
    }

    hpsIn  = GpiCreatePS( hab, hdcIn, &sizel,PU_PELS |
                          GPIF_DEFAULT | GPIT_MICRO | GPIA_ASSOC );
    if( !hpsIn )
    {
       Msg( "CopyBitmap GpiCreatePS for hpsIn RC = %x",
            (USHORT) WinGetLastError( hab ) );
       goto BYEBYE;
    }

    hpsOut = GpiCreatePS( hab, hdcOut, &sizel,PU_PELS |
                          GPIF_DEFAULT | GPIT_MICRO | GPIA_ASSOC );
    if( !hpsOut )
    {
       Msg( "CopyBitmap GpiCreatePS for hpsOut RC = %x",
            (USHORT) WinGetLastError( hab ) );
       goto BYEBYE;
    }

    bmih.cbFix = sizeof( BITMAPINFOHEADER2 );
    if( !GpiQueryBitmapInfoHeader( hbmIn, &bmih ) )
    {
       Msg( "CopyBitmap GpiQueryBitmapInfoHeader for hbmIn RC = %x",
            (USHORT) WinGetLastError( hab ) );
       goto BYEBYE;
    }

    hbmOut = GpiCreateBitmap( hpsOut, &bmih, 0, NULL, NULL );
    if( hbmOut )
    {
       if( HBM_ERROR == GpiSetBitmap( hpsIn, hbmIn ) )
       {
          Msg( "CopyBitmap GpiSetBitmap for hpsIn RC = %x",
               (USHORT) WinGetLastError( hab ) );
          hbmOut = NULLHANDLE;
          goto BYEBYE;
       }

       if( HBM_ERROR == GpiSetBitmap( hpsOut, hbmOut ) )
       {
          Msg( "CopyBitmap GpiSetBitmap for hpsOut RC = %x",
               (USHORT) WinGetLastError( hab ) );
          hbmOut = NULLHANDLE;
          goto BYEBYE;
       }

       aptl[ 0 ].x = 0;
       aptl[ 0 ].y = 0;
       aptl[ 1 ].x = bmih.cx;
       aptl[ 1 ].y = bmih.cy;
       aptl[ 2 ].x = 0;
       aptl[ 2 ].y = 0;

       if( GPI_ERROR == GpiBitBlt( hpsOut, hpsIn, 3, aptl, ROP_SRCCOPY,
                                   BBO_IGNORE ) )
       {
          Msg( "CopyBitmap GpiBitBlt for hpsOut RC = %x",
               (USHORT) WinGetLastError( hab ) );
          hbmOut = NULLHANDLE;
       }
    }
    else
       Msg( "CopyBitmap GpiCreateBitmap for hbmOut RC = %x",
            (USHORT) WinGetLastError( hab ) );

 BYEBYE:

    if( hpsIn )
       GpiDestroyPS( hpsIn );
    if( hpsOut )
       GpiDestroyPS( hpsOut );
    if( hdcIn )
       DevCloseDC( hdcIn );
    if( hdcOut )
       DevCloseDC( hdcOut );
    return hbmOut;
 }



Credit:  Rick Fishman

-------------------------------------------------------------------------------
How do I know what item was selected in a Combo box?


Catch the LN_SELECT message from the combo box, then send an
LM_QUERYSELECTION to get the index of the selection, then a
LM_QUERYITEMTEXT to get the selected text.


Credit:  Pete Norloff

-------------------------------------------------------------------------------
How do I get a bitmap into a dialog in a DLL?


You've hit on a known problem with bitmaps in dialogs in dlls.
This also used to be a problem for icons but that appears to be
working now.

First, define your bitmap in your dialog like this
(notice no mention of SS_BITMAP). It is strictly text at this point.
PM can handle this when loading the dialog

Below are all the files necessary to create dllbitmp.exe and
dlgdll.dll. Dlgdll has the dialog box and bitmap. Dllbitmp.exe
calls DllDialog() in dlgdll.dll to bring up the dialog box from
the dll's resource file. . . 


DLLBITMP.C:

 #define  INCL_GPI
 #define  INCL_WIN
 #include <os2.h>
 #include "dllbitmp.h"

 #define FRAME_FLAGS  (FCF_TASKLIST | FCF_TITLEBAR   |
                       FCF_MENU     | FCF_SYSMENU    |
                       FCF_MINMAX   | FCF_SIZEBORDER |
                       FCF_SHELLPOSITION)
 #define CLIENT_CLASS "DllBitmp"


 INT main( VOID );
 VOID EXPENTRY DllDialog( HWND );
 FNWP wpClient;


 INT main( VOID )
 {
    HAB   hab;
    HMQ   hmq;
    QMSG  qmsg;
    HWND  hwndFrame, hwndClient;
    ULONG flFrame = FRAME_FLAGS;

    hab = WinInitialize( 0 );
    hmq = WinCreateMsgQueue( hab, 0 );
    WinRegisterClass( hab, CLIENT_CLASS, wpClient, 0, 0 );
    hwndFrame = WinCreateStdWindow( HWND_DESKTOP, WS_VISIBLE, 
                                    &flFrame,CLIENT_CLASS, NULL,
                                    0,NULLHANDLE, ID_RESOURCES, 
                                    &hwndClient );
    while( WinGetMsg( hab, &qmsg, NULLHANDLE, 0, 0 ) )
       WinDispatchMsg( hab, &qmsg );

    WinDestroyWindow( hwndFrame );
    WinDestroyMsgQueue( hmq );
    WinTerminate( hab );
    return 0;
 }


 MRESULT EXPENTRY wpClient( HWND hwnd, ULONG msg,
                            MPARAM mp1, MPARAM mp2 )
 {
    switch( msg )
    {
       case WM_ERASEBACKGROUND:
          return (MRESULT) TRUE;
       case WM_COMMAND:
       switch( SHORT1FROMMP( mp1 ) )
       {
          case IDM_DOIT          {
             DllDialog( hwnd );
             return 0;
          }
       }

       break;
    }
    return WinDefWindowProc( hwnd, msg, mp1, mp2 );
 }



DLLBITMP.H:

 #define ID_RESOURCES	1
 #define IDD_DOIT	100
 #define IDM_DOIT	110
 #define ID_BITMAP	1100
 #define ID_BITMAPID	1200



DLLBITMP.DEF:

 NAME		DLLBITMP      WINDOWAPI
 PROTMODE
 HEAPSIZE	16384
 STACKSIZE	16384



DLLBITMP.RC:

 #include <os2.h>
 #include "dllbitmp.h"


 MENU ID_RESOURCES
 {
     MENUITEM "!~DoIt", IDM_DOIT
 }



DLGDLL.C:

 #define  INCL_DOS
 #define  INCL_GPI
 #define  INCL_WIN
 #include <os2.h>
 #include "dllbitmp.h"


 FNWP wpDlg;


 VOID EXPENTRY DllDialog( HWND hwnd )
 {
    HMODULE hmod;

    DosQueryModuleHandle( "DLGDLL", &hmod );
    WinDlgBox( HWND_DESKTOP, hwnd, wpDlg, hmod, IDD_DOIT, NULL );
 }


 MRESULT EXPENTRY wpDlg( HWND hwndDlg, ULONG msg, MPARAM mp1, MPARAM mp2 )
 {
    switch (msg)
    {
       case WM_INITDLG:
       {
          HWND    hwndBmp = WinWindowFromID( hwndDlg, ID_BITMAPID );
          HPS     hps = WinGetPS( hwndDlg );
          HBITMAP hbm;
          HMODULE hmod;

          DosQuieryModuleHandle( "DLGDLL", &hmod );
          hbm = GpiLoadBitmap( hps, hmod, ID_BITMAP, 0, 0 );
          WinSetWindowBits(hwndBmp,QWL_STYLE,SS_BITMAP,SS_BITMAP | 0x7f);
          WinSendMsg( hwndBmp, SM_SETHANDLE, MPFROMP( hbm ), NULL );
          WinSetWindowULong( hwndDlg, QWL_USER, (ULONG) hbm );
          WinReleasePS( hps );
          break;
       }
       case WM_DESTROY:
       {
          HBITMAP hbm = (HBITMAP) WinQueryWindowULong(hwndDlg,QWL_USER );
          GpiDeleteBitmap( hbm );
          break;
       }
    }
    return WinDefDlgProc( hwndDlg, msg, mp1, mp2 );
 }



DLGDLL.DLG:

 DLGTEMPLATE IDD_DOIT LOADONCALL MOVEABLE DISCARDABLE
 BEGIN
   DIALOG "", IDD_DOIT, 0, 0, 210, 154, FS_NOBYTEALIGN | FS_DLGBORDER |
              WS_VISIBLE | WS_CLIPSIBLINGS | WS_SAVEBITS, FCF_TITLEBAR
   BEGIN
       CONTROL "foo", ID_BITMAPID, 98, 56, 32, 32, WC_STATIC,
               SS_TEXT | DT_LEFT | DT_TOP | WS_VISIBLE
   END
 END



DLGDLL.DEF:

 LIBRARY	DLGDLL		INITINSTANCE	TERMINSTANCE
 PROTMODE
 CODE		LOADONCALL
 DATA		LOADONCALL	MULTIPLE	NONSHARED
 EXPORTS	DllDialog



DLGDLL.RC:

 #include <os2.h>
 #include "dllbitmp.h"


 BITMAP  ID_BITMAP "dlgdll.bmp"


 rcinclude dlgdll.dlg



MAKEFILE:

 all: dlgdll.dll dllbitmp.exe

 dlgdll.dll: $*.obj $*.res
     link386 /NOI /NOE /MAP /DE /NOL $*, $*.dll,, os2386, $*
     rc $*.res $*.dll
     implib $*.lib $*.def

 dllbitmp.exe: $*.obj $*.def $*.res
     link386  /NOI /NOE /MAP /DE /NOL $*,,, os2386 dlgdll, $*
     rc $*.res $*.exe

 dllbitmp.obj: $*.c
     icc /Q+ /Ss /W3 /Kbcepr /Gm- /Gd- /Ge+ /Ti+ /O- /C $*.c

 dllbitmp.res: $*.rc
     rc -r $*

 dlgdll.obj: $*.c
     icc /Q+ /Ss /W3 /Kbcepr /Gm- /Gd- /Ge- /Ti+ /O- /C $*.c

 dlgdll.res: $*.rc $*.dlg
     rc -r $*



Credit:  Rick Fishman

-------------------------------------------------------------------------------
How does programming PM compare to programming X?


Many people have said "PM is much cleaner."  Until I hear from someone
I trust with extensive experience with both (I only know X), however,
this FAQ will take no position.

There was an unsubstantiated rumor that someone was porting an X library
to OS/2 (X-Windows, not X-Mode or any other X), I dunno whether this has
been substantiated or not.

IBM and a few other manufacturers have built their own X servers for
OS/2.  [details?]

[Colin Jensen. . . ]

I have some limited experience with X and PM.  Raw Xlib is harder to
program than PM.  However most, if not all, of the X11 Toolkits are
*easier* to use than PM.  I have experience comparing XView to PM.
A friend of mine has experience with Windows versus Xt, and has the
same opinion (Windows isn't that far off conceptually from PM).

-------------------------------------------------------------------------------
How do I put bitmaps on buttons?


Stefan Gruendal (Stefan_Gruendel@wue.maus.de) writes:

But to my questione. bitmaps
on pushbuttons, that optically "move into the screen"? I didn't find any
way to achieve this with the Toolkit's Dialog Editor.  But as mentioned
above, I know there's a way.

Starting with OS/2 2.x, a new button style was added - BS_ICON - which
allows you to do what you are trying to accomplish.  It works, as far
as I know, only for pushbuttons, and the icon or bitmap is taken from
the resource file, whose resource id is specified in the pushbutton
text.

For example:


In FOO.H:

#define IDBM_BUTTON 256
#define IDPB_BUTTON 257

In FOO.RC:

BITMAP IDBM_BUTTON BUTTON.BMP



In FOO.C
sprintf(achText,"#%d",IDBM_BUTTON);

hwndButton=WinCreateWindow(hwndClient,WC_BUTTON,achText,
                           WS_VISIBLE|BS_PUSHBUTTON|BS_ICON,
                           10,10,32,32,hwndClient,HWND_TOP,
                           IDPB_BUTTON,NULL,NULL);



The bitmap is stretched or compressed to fill the button.


(Quoted almost directly from EDMI/2 Edition 1)

-------------------------------------------------------------------------------
Can a PM program tell if there's a previous instance of itself running?


Raja Thiagarajan (sthiagar@bronze.ucs.indiana.edu) writes:

Can a PM program tell if there's a previous instance of itself
running?  In Win3.x (but apparently NOT Win32), there's a hPrevInst
handle; is there an OS/2 2.x equivalent?  Basically, I'm thinking in
terms of a program that would try to borrow resources from a previous
instance if a previous instance is running.  (Specifically, if my
palette animation program gets started twice, the second instance oughta
share palettes with the first instance!)

What you're really asking is two questions:

o How can I determine if a previous instance of my application is already running?
o How can I share resources between multiple instances of my application?

There are some ways to achieve this; You can use semaphores or/and shared memory
or the other method described below.
Try to open your named semaphore, if it's there, your application is already
running, else create a semaphore... To share data, use shared memory.
There are some excellent examples in Schildt & Goosey "OS/2 Programming".

To answer your first question, you need to enumerate all of the main
windows present on the desktop, and figure out if any of them are yours.
This is achieved using the following code
HWND queryAppInstance(PCHAR pchClassWanted)
{
   HENUM heEnum;
   HWND hwndTop;
   HWND hwndClient;
   CHAR achClass[256];

   heEnum=WinBeginEnumWindows(HWND_DESKTOP);

   hwndTop=WinGetNextWindow(heEnum);
   while (hwndTop!=NULLHANDLE) {
      hwndClient=WinWindowFromID(hwndTop,FID_CLIENT);
      if (hwndClient!=NULLHANDLE) {
         WinQueryClassName(hwndClient,sizeof(achClass),achClass);
         if (strcmp(achClass,pchClassWanted)==0) {
            WinEndEnumWindows(heEnum);
            return hwndClient;
         } /* endif */
      } /* endif */

      hwndTop=WinGetNextWindow(heEnum);
   } /* endwhile */

   WinEndEnumWindows(heEnum);
   return NULLHANDLE;
}

To answer your second question, the only way that I know of to share
resources is to place them in a DLL.  The procedure to do this is as
follows:

o Create a dummy source file with an empty procedure in it.
o Compile the source file and link as a DLL.
o Add your resources to the DLL in the same manner as you would to an executable.

Then, when your application starts, you simply call WinLoadLibrary  (the
preferred way) or DosLoadModule to load the DLL.  These functions return
a handle to the DLL which must then be used in any function which loads
resources (e.g. GpiLoadBitmap, WinLoadPointer, etc.).

Note that this procedure does not require knowing the window handle of
any previous instance of your application because OS/2 implements DLLs
in a shared fashion (which I suspect is one of the reasons they were
created in the first place).  All you need to know is the name of the
DLL.  This technique can also be used to share resources between
different applications.


(Quoted almost directly from EDMI/2 Edition 1)

-------------------------------------------------------------------------------
How to avoid crash when using Drag and Drop?


You can avoid this by doing;

1) Call any DRG*() function in your application
2) Call DosLoadModule() to load the PMDRAG.DLL file


-------------------------------------------------------------------------------
Are there any PM bindings for GNU Ada?

Yes. There are some PM bindings for gnat 2.03 or higher. 
They can be found at cs.nyu.edu:/pub/gnat/contrib/os2/PM.



===============================================================================
Miscellaneous Programming


This section covers non-Presentation Manager programming.

-------------------------------------------------------------------------------
Explain the SYS_DLLS keywords.


Actually there are three key names that exist for the app name SYS_DLLS.

They are:

Load

DLL loaded and ordinal 1 entry point called for every msg queue
created in the system


LoadPerProcess

DLL loaded and ordinal 1 entry point called for only
the first message queue created on a process


LoadOneTime

DLL loaded and ordinal 1 entry point called for only the
shell's message queue (the first PM queue in the system)


You would use one over another depending on the type of initialization
that you require.  Most DLL's only need to be initialized once for the
system and are thus LoadOneTime, some DLLs like PMCTLS have per process
initialization and thus are LoadPerProcess, and then there is the very
rare case of DLLs that need to perform some function every time a
message queue is created and these are specified as Load.  Note:
that anything other than LoadOneTime is a significant performance hit on
the system.  Note: also that in addition to your
INITIALIZATION/TERMINATION function specified in the DEF file for your
DLL (which is a new feature in 2.0), your ORDINAL 1 entry point in your
DLL is also called.  Be careful with this as I have seen some pretty
strange results if you have an ordinal 1 entry point that has nothing to
do with initialization.  In addition, remember to never ever create a
message queue inside of your initialization routine.  Always call
WinQueryAnchorBlock if you need a HAB for some API.

As for shared resources, if you need resources on multiple processes
then they need to be created on multiple processes as they won't be
valid on any process other than the one you loaded.  I believe that this
is the reason PMCTLS is LoadPerProcess.  You have a few options here, 1)
you can store your resources in shared memory, and 2) for icons you can
call WinSetPoinerOwner to make your icons shared across the entire
system.

-------------------------------------------------------------------------------
How do I start another session?


This small program will start any program synchronously using
DosStartSession(). The important thing is the queue. When you specify
SSF_RELATED_CHILD and a TermQ name, OS/2 will write the return code to
the specified queue when the session terminates. I use this in an event
scheduler by creating a separate thread that does reads from the queue
but you can just as easily block on the main thread to catch the return
code. That will, in effect, provide for synchronous execution. Note that
one problem with SSF_RELATED_CHILD is that if the program that started
the child dies, so does the child.

 #define  INCL_DOSERRORS
 #define  INCL_DOSPROCESS
 #define  INCL_DOSQUEUES
 #define  INCL_DOSSESMGR
 #include <os2.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>

 #define QUEUE_NAME    "\\QUEUES\\STRTSYNC.QUE"

 int main( int argc, char *argv[] );

 int main( int argc, char *argv[] )
 {
     APIRET rc;
     HQUEUE hque;

     if( argc < 2 )
         return 1;

     rc = DosCreateQueue( &hque, QUE_FIFO | QUE_CONVERT_ADDRESS, QUEUE_NAME );
     if( !rc )
     {
         STARTDATA   stdata;
         PID         pidSession;
         CHAR        szObjFail[ 50 ];
         ULONG       ulLength, idSession;
         REQUESTDATA rd;
         PUSHORT     pusInfo = NULL;
         BYTE        bPriority;

         (void) memset( &stdata, 0, sizeof( stdata ) );

         stdata.Length       = sizeof( STARTDATA );
         stdata.FgBg         = SSF_FGBG_FORE;
         stdata.TraceOpt     = SSF_TRACEOPT_NONE;
         stdata.PgmTitle     = "Rick's Program";
         stdata.InheritOpt   = SSF_INHERTOPT_SHELL;
         stdata.SessionType  = SSF_TYPE_DEFAULT;
         stdata.PgmControl   = SSF_CONTROL_VISIBLE;
         stdata.ObjectBuffer = szObjFail;
         stdata.ObjectBuffLen= sizeof( szObjFail );
         stdata.Related      = SSF_RELATED_CHILD;
         stdata.TermQ        = QUEUE_NAME;
         stdata.PgmName      = argv[ 1 ];

         rc = DosStartSession( &stdata, &idSession, &pidSession );

         if( rc && rc != ERROR_SMG_START_IN_BACKGROUND )
         {
             printf( "DosStartSession RC(%u)\n", rc );
             return (INT) rc;
         }

         rc = DosReadQueue( hque, &rd, &ulLength, (PPVOID) &pusInfo, 0,
                            DCWW_WAIT, &bPriority, 0 );

         if( rc && rc != ERROR_QUE_EMPTY )
         {
             printf( "DosReadQueue RC(%u)\n", rc );
             return (INT) rc;
         }

         printf( "RetCode from Session %u: %u\n",
                  pusInfo[ 0 ], pusInfo[ 1 ]);

         DosCloseQueue( hque );
     }
     else
     {
         printf( "DosCreateQueue RC(%u)\n", rc );
         return (INT) rc;
     }

     return 0;
 }



Credit:  Rick Fishman

-------------------------------------------------------------------------------
How do I check if a filename is valid?


Here's some code that should help.  I found that you have to look at
each return code to see if it's really an error.  This routine does do
syntax checking, it's just a little more complicated than before
:^)

#define INCL_DOSFILEMGR
#define INCL_DOSERRORS
#include <os2.h>
#include <stdio.h>

int main( int argc, char **argv )
{
        int         rc;
        FILESTATUS3 piBuffer;

        if ( argc !=2 )
        {
                printf( "Must pass filename on command line!\n");
                return( -1 );
        }
        else
        {
                printf( "Checking on %s, ", argv[ 1 ] );
                rc = DosQueryPathInfo( (PSZ)argv[1], FIL_STANDARD, &piBuffer,
                                       sizeof(FILESTATUS3));

                if ( rc == 0 )
                        printf( "syntax valid and file exists\n" );
                else
                if ( rc == ERROR_FILE_NOT_FOUND )
                        printf( "syntax valid and file doesn't exist.\n");
                else
                if ( rc == ERROR_PATH_NOT_FOUND )
                        printf( "syntax valid, somthing in path was not found\n");
                else
                {
                        printf( "bad, rc=%d, ",rc );
                        switch( rc )
                        {
                                case ERROR_INVALID_DRIVE:
                                        printf( "drive name does not exist\n");
                                        break;

                                case ERROR_INVALID_NAME:
                                        printf( "invalid syntax for drive name\n");
                                        break;

                                case ERROR_FILENAME_EXCED_RANGE:
                                        printf( "dir name and/or filename too long\n");
                                        break;

                                case ERROR_SHARING_VIOLATION:
                                        printf( "sharing violation\n");
                                        break;

                                case ERROR_BUFFER_OVERFLOW:
                                        printf( "buffer overflow\n");
                                        break;

                                case ERROR_INVALID_LEVEL:
                                        printf( "invalid level requested\n");
                                        break;

                                case ERROR_INVALID_EA_NAME:
                                        printf( "invalid EA name\n");
                                        break;

                                case ERROR_EA_LIST_INCONSISTENT:
                                        printf( "EA list inconsistent\n");
                                        break;

                                default:
                                        printf("Undocumented return value.\n");
                        }
                        return( -1 );
            }
            return( 0 );
        }
}



Credit:  Mike Brown

-------------------------------------------------------------------------------
Why should I use _beginthread instead of DosCreateThread?


You must if you want to use the C runtime library.

-------------------------------------------------------------------------------
How do I open a file that is already in use?


Use DosOpen with OPEN_SHARE_DENYNONE.

/* this will copy an open program */

#define INCL_NOPM
#define INCL_DOS
#include <os2.h>
#include <stdio.h>
#include <string.h>

void usage(void)
{
    printf("USAGE: CopyOpen <source file> <dest file>\n\n");
    printf("       This program,  unlike the normal copy and xcopy commands,\n");
    printf("       will copy an open file.\n");
    printf("NOTE:  Wildcards are not supported\n");
}

int cdecl main(int argc, char **argv)
{
    HFILE hf, hfOut;
    USHORT usAction, rc, bytesRead, bytesWriten ;
    static BYTE buf[4096];
    long total=0l;
    int error = FALSE;

    if(argc!=3){
        usage();
        return 1;
    }
    rc = DosOpen(strupr(argv[1]),
        &hf,
        &usAction,
        0L,
        FILE_NORMAL,
        FILE_OPEN,
        OPEN_ACCESS_READONLY | OPEN_SHARE_DENYNONE,
        0L);
    if(rc){
        printf("SYS%04u: Could not open %s for read.\n", rc, argv[1]);
        return 3;
    }
    rc = DosOpen(strupr(argv[2]),
        &hfOut,
        &usAction,
        0L,
        FILE_NORMAL,
        FILE_CREATE | FILE_TRUNCATE,
        OPEN_ACCESS_WRITEONLY | OPEN_SHARE_DENYREADWRITE,
        0L);
    if(rc){
        printf("SYS%04u: Could not open %s for write.\n", rc, argv[2]);
        return 3;
    }
    else{
        do{
            rc = DosRead(hf, buf, sizeof(buf), &bytesRead);
            if(!rc){
               rc = DosWrite(hfOut, buf, bytesRead, &bytesWriten);
               if(!rc) total += bytesWriten;
            }
        }while(!rc &&
               sizeof(buf) == bytesRead &&
               bytesRead == bytesWriten);
        if(rc){
            printf("SYS%04u: while copying.\n", rc);
            error = TRUE;
        }
        if(bytesRead != bytesWriten){
            printf("Disk full?\n");
            error = TRUE;
        }
        DosClose(hf);
        if(!error)
            printf("%lu bytes copied.\n", total);
    }
    return( error? 3 : 0);
}



Credit:  Peter Fitzsimmons

-------------------------------------------------------------------------------
Can we use Vio in v2.0? Where are the docs for it?


Yes; check cdrom.com for PRCP.ZIP.

-------------------------------------------------------------------------------
Can I redirect stdin and stdout in a child process?


This is what I use to redirect stderr, stdout to a file from a program I
start using DosStartSession. You could do the same type of thing using a
pipe.

    ULONG  ulAction;
    ULONG  ulNew;
    HFILE  hfFile, hfNewStdOut = -1, hfNewStdErr = -1,
           hfStdOut = 1, hfStdErr = 2;

    // Open output file
    DosOpen( szOutputFile, &hfFile, &ulAction, 1, 0,
             FILE_OPEN | FILE_CREATE,
             OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYNONE, 0L );
    // Duplicate handles
    DosDupHandle( hfStdOut, phfNewStdOut );
    DosDupHandle( hfStdErr, phfNewStdErr );
    // Close existing handles for current process
    DosClose( hfStdOut );
    DosClose( hfStdErr );
    // Redirect existing handles to new file
    DosDupHandle( hfFile, &hfStdOut );
    DosDupHandle( hfFile, &hfStdErr );
    // Let started program inherit handles from parent
    stdata.InheritOpt = SSF_INHERITOPT_PARENT;
    // Start new session
    DosStartSession( &stdata, &ulSessionID, &pidSession );
    // Get back original handles
    DosDupHandle( hfNewStdOut, &hfStdOut );
    DosDupHandle( hfNewStdErr, &hfStdErr );
    // Close the duplicated handles - no longer needed
    DosClose( hfNewStdOut );
    DosClose( hfNewStdErr );



Credit:  Rick Fishman

-------------------------------------------------------------------------------
How do I use DosMon*() to stuff the kbd buf?


Here's a mini app (all error handling, comments, etc. removed.)


(also all this stuff to avoid compile warnings !!!!!).

compile with: icc -Sm -Kabgioprx+ -Ss+ -W3 -Gs+ -Gf+ -O+ KEY.C
start with: detach key
stop with: F11 or CTRL-F10
test envir: OS/2 2.0GA+SP, C-Set++ CSD 22.
no other functionality.



orginal src part of my glorious DOS/OS2 1.x/OS2 2.0 keyboard roboter
which inserts keys into the keyboard monitor queue controlled by an
source file (mini language).


      (C) Mario Semo 1777,1832,1967-92.


========= TOP OF FILE KEY.C ================

#define INCL_DOS
#define INCL_KBD
#define INCL_NOPM

#include<os2.h>
#include<stdio.h>
#include<conio.h>
#include<process.h>
#include<stdlib.h>
#include<string.h>

#define DosMonOpen  DOS16MONOPEN
#define DosMonClose DOS16MONCLOSE
#define DosMonReg   DOS16MONREG
#define DosMonRead  DOS16MONREAD
#define DosMonWrite DOS16MONWRITE

#define MONITOR_DEFAULT 0x0000
#define MONITOR_BEGIN   0x0001
#define MONITOR_END     0x0002

typedef SHANDLE HMONITOR;       /* hmon */
typedef HMONITOR *PHMONITOR;

#pragma pack(2)
typedef struct _MONIN {         /* mnin */
        USHORT cb;
        BYTE abReserved[18];
        BYTE abBuffer[108];
} MONIN;
typedef MONIN *PMONIN;

#pragma pack(2)
typedef struct _MONOUT {        /* mnout */
        USHORT cb;
        UCHAR buffer[18];
        BYTE abBuf[108];
} MONOUT;
typedef MONOUT *PMONOUT;

APIRET16 APIENTRY16 DosMonOpen(PSZ pszDevName, PHMONITOR phmon);
APIRET16 APIENTRY16 DosMonClose(HMONITOR hmon);
APIRET16 APIENTRY16 DosMonReg(HMONITOR hmon, PBYTE pbInBuf,
             PBYTE pbOutBuf, USHORT fPosition, USHORT usIndex);
APIRET16 APIENTRY16 DosMonRead(PBYTE pbInBuf, USHORT fWait,
                               PBYTE pbDataBuf,
                               PUSHORT pcbData);
APIRET16 APIENTRY16 DosMonWrite(PBYTE pbOutBuf, PBYTE pbDataBuf,
                                USHORT cbData);

#define DosGetInfoSeg    DOS16GETINFOSEG
APIRET16 APIENTRY16 DosGetInfoSeg(PSEL pselGlobal, PSEL pselLocal);

#pragma pack(2)
typedef struct _GINFOSEG { /* gis */
        ULONG   time; ULONG   msecs; UCHAR   hour;
        UCHAR   minutes; UCHAR   seconds; UCHAR   hundredths;
        USHORT  timezone; USHORT cusecTimerInterval; UCHAR   day;
        UCHAR   month; USHORT  year; UCHAR   weekday;
        UCHAR   uchMajorVersion; UCHAR uchMinorVersion;
        UCHAR   chRevisionLetter; UCHAR   sgCurrent;
        UCHAR   sgMax; UCHAR   cHugeShift;
        UCHAR   fProtectModeOnly; USHORT pidForeground;
        UCHAR   fDynamicSched; UCHAR   csecMaxWait;
        USHORT  cmsecMinSlice; USHORT  cmsecMaxSlice;
        USHORT  bootdrive; UCHAR   amecRAS[32];
        UCHAR   csgWindowableVioMax; UCHAR   csgPMMax;
} GINFOSEG;
typedef GINFOSEG *PGINFOSEG;

static PGINFOSEG gdt;

#define MAKEPGINFOSEG(sel)  ((PGINFOSEG)MAKEP(sel, 0))
#define MAKEPLINFOSEG(sel)  ((PLINFOSEG)MAKEP(sel, 0))

#pragma pack(2)
typedef struct _keypacket
  {
   USHORT     mnflags;
   KBDKEYINFO cp;
   USHORT     ddflags;
  } KEYPACKET;


#define RELEASE     0x40
#define CTL_F10_KEY 103
#define F11_KEY     133

#pragma stack16(8192)
#pragma seg16(HKBD)
#pragma seg16(MONIN)
#pragma seg16(MONOUT)
#pragma seg16(KEYPACKET)

static HKBD KBDHandle = (HKBD)0;
static PGINFOSEG _Seg16 gdt;
static MONIN monInbuf = {0};
static MONOUT monOutbuf = {0};
static HEV hevThreadDone = (HEV)0;

static void _System keyboard_monitor(ULONG Dummy);

int main(int argc, char *argv[], char *envp[])
{
 SEL gdt_descriptor, ldt_descriptor;
 PID pidKeybrd;

 monInbuf.cb  = sizeof(MONIN);
 monOutbuf.cb = sizeof(MONOUT);

 DosGetInfoSeg(&gdt_descriptor, &ldt_descriptor);

 gdt = MAKEPGINFOSEG(gdt_descriptor);

 DosMonOpen ( "KBD$", &KBDHandle );

 DosCreateEventSem(NULL, &hevThreadDone,0,FALSE);

 if (DosCreateThread(&pidKeybrd, &keyboard_monitor, 0L, 2L, 12000L))
         DosExit(EXIT_PROCESS,0);

 DosWaitEventSem(hevThreadDone, (ULONG)SEM_INDEFINITE_WAIT);
 DosMonClose(KBDHandle);

 DosBeep(100,100);

 DosExit(EXIT_PROCESS,0);
 return(0);
}


static void _System keyboard_monitor(ULONG Dummy)
{
 KEYPACKET keybuff;
 USHORT count;

 DosSetPrty(PRTYS_THREAD, PRTYC_TIMECRITICAL,0, 0);

 DosMonReg( KBDHandle, (PBYTE)&monInbuf, (PBYTE)&monOutbuf,
            MONITOR_BEGIN, gdt->sgCurrent);

 DosSetPrty(PRTYS_THREAD, PRTYC_REGULAR,0, 0);

 for(keybuff.cp.chChar = 0; ; )
   {
    count = sizeof(keybuff);
    DosMonRead( (PBYTE)&monInbuf, IO_WAIT, (PBYTE)&keybuff, &count);
    if (!(keybuff.ddflags & RELEASE))
       {
        if(keybuff.cp.chChar == 0)
          {
           switch (keybuff.cp.chScan)
             {
              case CTL_F10_KEY   :
              case F11_KEY       :
                 DosPostEventSem(hevThreadDone);
                 DosExit(EXIT_THREAD,0);
                 break;
             }
          }
       }
    DosMonWrite((PBYTE)&monOutbuf,(PBYTE)&keybuff,count);
   }
}



Credit:  Mario Semo

-------------------------------------------------------------------------------
How do I determine what file system a drive uses?


16 bit:  DosQFsInfo().
32 but:  DosQueryFSInfo()

The folling little 16 bit program produces the following output on my
computer:

 LOCAL  C:  FAT
 LOCAL  D:  HPFS
 LOCAL  E:  HPFS
 LOCAL  F:  FAT
 REMOTE P:  LAN   \\SERV1\C$
 REMOTE Q:  LAN   \\SERV1\D$
 REMOTE R:  LAN   \\SERV1\E$

Code. . . . 

 /* qdisk.c */
 #define INCL_NOPM
 #define INCL_DOS
 #include <os2.h>
 #include <stdio.h>
 #include <stdlib.h>


 void errorRC(USHORT rc)
 {
     char msg[256];
     USHORT bc;
     if(0 != (rc=DosGetMessage(NULL, 0, msg, sizeof(msg), rc,
                               "OSO001.MSG", &bc))) {
         printf("SYS%04u: Unable to access OSO001.MSG\n", rc);
     }
     else DosWrite(2, msg, bc, &bc);
 }

 void qdisk(char drv)
 {
     USHORT rc, len;
     char dev[3];
     void *buf;
     char *p;

     if(drv < 'C')
         return;
     sprintf(dev, "%c:", drv);
     buf = malloc(2048);
     len = 2048;
     rc = DosQFSAttach(dev, 0, FSAIL_QUERYNAME, buf, &len, 0L);
     if(rc){
         errorRC(rc);
         return;
     }
     switch((*(PUSHORT)buf)){
         case FSAT_CHARDEV  : printf("CHAR   "); break;
         case FSAT_PSEUDODEV: printf("DEV    "); break;
         case FSAT_LOCALDRV : printf("LOCAL  "); break;
         case FSAT_REMOTEDRV: printf("REMOTE "); break;
         default: printf("Unknown "); break;
     }
     p = buf;
     p += sizeof(USHORT);    /* itype */
     printf("%-3s ", p+sizeof(USHORT));
     p += (sizeof(USHORT) + (*(USHORT *)p) + 1);    /* cbName */
     printf("%-8s ", p+sizeof(USHORT));
     p += (sizeof(USHORT) + (*(USHORT *)p) + 1);    /* cbFSDName */
     if((*(USHORT *)p))                             /* cbFSAData */
         printf("%s", p+sizeof(USHORT));
     printf("\n");
     free(buf);
 }

 void cdecl main(void)
 {
     char drv;
     USHORT usDisk;
     ULONG ulDrives;
     DosQCurDisk(&usDisk, &ulDrives); /* gets current drive */
     for (drv = 'A'; drv <= 'Z'; drv++) {
         if (ulDrives & 1)		      /* if the drive bit is set, */
             qdisk(drv);
         ulDrives >>= 1;
     }
 }



ps: DosQSysInfo() will return the max path length that your
version of OS/2 supports.   But since this API is specific to OS/2,  and
not to a particular drive,  it does not answer your original question.


Credit: Peter Fitzsimmons

-------------------------------------------------------------------------------
How do I get the error message from a DOS API call?


For DOSAPI calls, you can issue a DosGetMessage as follows:

   RC = DosXXXX(. . . );
   DosGetMessage(NULL,0,Msg,sizeof(Msg),RC,"OSO001.MSG",&Msg_Length);


Credit:  Ken Kahn

-------------------------------------------------------------------------------
How do I set an exception handler?

/*
 * Simple example of an exception handler
 */

#define INCL_DOS
#include <os2.h>
#include <setjmp.h>
#include <stdio.h>
#include <string.h>

extern int main(void);

 /*
 * Exception registration record.  Stored on stack, with first
 * pointer to next registeration record, second pointer to
 * exception handler, and the rest defined by the author of
 * the exception handler.
 */

typedef struct {
  struct _EXCEPTIONREGISTRATIONRECORD * volatile prev_structure;
   _ERR * volatile ExceptionHandler;
   jmp_buf env;
} MYEXCEPTIONREGISTRATIONRECORD, *PMYEXCEPTIONREGISTRATIONRECORD;

/*
 * Exception handler that returns traps via longjmp().
 */

extern ULONG APIENTRY MyExceptionHandler
    (PEXCEPTIONREPORTRECORD pReportRecord,
    PEXCEPTIONREGISTRATIONRECORD pRegRecord,
    PCONTEXTRECORD pContextRecord, PVOID pReserved)
{
  ULONG rc = XCPT_CONTINUE_SEARCH;

  if (pReportRecord->ExceptionNum == XCPT_ACCESS_VIOLATION)
    longjmp(((PMYEXCEPTIONREGISTRATIONRECORD) pRegRecord)->env, -1);

  /*
   * If we return to here then we could not handle the exception.
   */

  return rc;
}

extern BOOL Trapper(PSZ psz)
{
  MYEXCEPTIONREGISTRATIONRECORD myExceptionRegRecord;

  /*
   * Insert my exception handler into the chain.
   */

  myExceptionRegRecord.prev_structure = NULL;
  myExceptionRegRecord.ExceptionHandler = MyExceptionHandler;
  DosSetExceptionHandler((PEXCEPTIONREGISTRATIONRECORD) &myExceptionRegRecord);

  if (setjmp(myExceptionRegRecord.env))
    goto OnException;

  /*
   * Now go about my business in safety.
   */

  if (strlen(psz))
    printf("Trapper says okay to '%s'\n", psz);
  else
    printf("Trapper says it is empty\n");

  /*
   * I'm done, so unchain my exception handler.
   */

  DosUnsetExceptionHandler((PEXCEPTIONREGISTRATIONRECORD)&myExceptionRegRecord)

  return TRUE;

  /*
   * The code below is only executed if a trap occurs.
   */

OnException:
  printf("Trapper says 'ouch!'\n");

  DosUnsetExceptionHandler((PEXCEPTIONREGISTRATIONRECORD)&myExceptionRegRecord)

  return FALSE;
}

extern int main()
{
  Trapper("Hello");
  Trapper(NULL);
  Trapper("");
  Trapper((PSZ) 42);
  Trapper("Goodbye");

  return 0;
}


Credit:  Dan Kehn

-------------------------------------------------------------------------------
How can I determine a diskette format and if a disk is in a drive?


Let's have a snippet from one of my functions

The 'ifdef M_I386's mean CSet/2


--- snip ---

EXPORT int GetFileSysAttach ( char cDrv, PVOID pInfo, UnsInt cbInfo )
{
CHAR szFileSys [4];

  szFileSys [0] = (CHAR) toupper ( cDrv );
  szFileSys [1] = ':';
  szFileSys [2] = '\0';

  DosError ( EXCEPTION_DISABLE );

#ifdef M_I386
  Errno = DosQueryFSAttach ( szFileSys, 0, FSAIL_QUERYNAME, pInfo, &cbInfo );
#else
  Errno = DosQFSAttach ( szFileSys, 0, FSAIL_QUERYNAME, pInfo, &cbInfo, 0L );
#endif

  DosError ( EXCEPTION_ENABLE );
  return Errno ? -1 : 0;
}


EXPORT	int	GetFileSysInfo ( PSZ pszFileSys, PFSysInfo pfsi )
{
USHORT	idDrv;
FSALLOCATE fsal;
FSINFO	fsinfo;
#ifdef M_I386
PFSQBUFFER2 fsq = NULL;
#else
PFSQBUFFER  fsq = NULL;
PUSHORT     pus;
#endif
CHAR    hbuf [L_FILENAME];
PCHAR   pch;
#ifndef _MT
BOOL    fProt = ProtectedMode ();
#else
#define fProt TRUE
#endif

  idDrv = tolower ( *pszFileSys ) - ('a' - 1);

  DosError ( EXCEPTION_DISABLE );

  if ( ! (Errno = DosQFSInfo ( idDrv, 1, (PBYTE) &fsal, sizeof ( fsal ) )) )
    Errno = DosQFSInfo ( idDrv, 2, (PBYTE) &fsinfo, sizeof ( fsinfo ) );

  DosError ( EXCEPTION_ENABLE );

  if ( Errno )
  {
    if ( Errno == ERROR_NO_VOLUME_LABEL )
      memset ( &fsinfo, 0, sizeof (fsinfo) );
    else
      return -1; // No disk inserted
  }

  if ( fProt )
  {
    if ( GetFileSysAttach ( (CHAR) (idDrv + 'a' - 1), hbuf, L_FILENAME ) )
      return -1;

#ifdef M_I386
    fsq = (PFSQBUFFER2) hbuf;
#else
    fsq = (PFSQBUFFER) hbuf;
#endif
  }

  DosError ( EXCEPTION_ENABLE );

  . . . 



--- snap ---


I think it's more than you want, the fsal-struct contains the size
information, but I think if you want to retrieve ALL information about a
logical drive that's what you need.


Credit:  Rainer Prem

-------------------------------------------------------------------------------
What do all those keywords mean when making a DLL?


Okay, with the help of some of my peers, I've solved the mystery
surrounding the seemingly redundant SINGLE/MULTIPLE and SHARED/NONSHARED
attributes.

First, some prep up work. . . 

Every DLL needs and gets a data segment ("automatic data segment").
It is the place where the DLL stores all its STATIC data--basically
the data declared in the DLL module but declared outside any function
AND (I venture) variables declared STATIC within a DLL function as
well. The STACK is NOT!! part of a DLL's automatic data segment (thoug
it seems to be for an EXE). The stack that is used when 'running' a
DLL function is that of the calling thread.

Now here's the clincher. . . 

DLLs CAN ALSO HAVE ADDITIONAL DATA SEGMENTS!! (as can EXEs, I imagine)

Bingo! Now we understand why there are two different sets of
attributes--specifically, NONE/SINGLE/MULTIPLE and SHARED/NONSHARED.
Though they basically do the same thing, they APPLY to different
things.

NONE/SINGLE/MULTIPLE
applies to the one and only default data segment. This is the data
segment that is "automatically" created for your DLL (or EXE). I imagine
that for 90% of the DLLs written, this is the only type of data segment
that exists. Whether or not this data segment is shared or whether a
private copy is created for each linking application is A DIRECT
FUNCTION OF THIS ATTRIBUTE--'SINGLE'=it's shared, 'MULTIPLE'=everyone
get's his own copy.
SHARED/NONSHARED
This SETS THE DEFAULT!! FOR ALL DATA SEGMENTS other than the
"automatic" data segment. DEFAULT is the key word, because you can
specify, through the SEGMENTS statement, the characteristics of these
additional segments on an individual basis (which in all certainty,
override this value.)

So. . . to answer my own question

>could {someone} explain what the following statements would
>produce:
>
>              DATA MULTIPLE SHARED


Every application which links to the DLL will have it's own
private copy of the automatic data segment. If the DLL
has no additional data segments, the SHARED keyword is meaningless
and can be omitted. If there are additional data segments,
only one copy of each will exist and they will be shared
by all applications (unless a SEGMENTS statement follows
which overrides this default for a specific segment)

>              or
>              DATA SINGLE NONSHARED


Every application which links to the DLL will share a single
automatic data segment (hence, each app that affects the DLL
STATIC data will affect it for all apps). If there are no
additional data segments, the NONSHARED keyword is meaningless
and can be omitted. If there are, a private copy will be created
for each app (unless overriden by a SEGMENTS statement).

Note that in the absence of BOTH a SINGLE/MUTLIPLE and
SHARED/NONSHARED keyword, the default is for ALL data segments
(automatic and additional ones) to be shared (i.e., only
one copy). In the absence of one OR the other, but not both,
the property of ALL data segments are mandated by the
single keyword. In other words,

DATA SINGLE
  and
DATA SHARED

do the same exact thing.


Credit:  John Cortell

-------------------------------------------------------------------------------
Where can I find serial port sample code?


Q)  Does anyone have any C sample code showing how to use the serial port
under OS/2 using DosOpen() and DosDevIoCtl()?

A) As a matter of fact, yes.  :-)

-=-= extracted from a silly & specialized program =-=-=-=-==-=-=

/* dtp.c -- D-dial Terminal Program: the first hack */

//-- an annoying detail

#define INCL_BASE
#define INCL_DOSDEVIOCTL  /* docs lie, this is NOT included by BASE */
#include <os2.h>

//-- initialization (in main() as written)

    HFILE portFd;
    ULONG action;

    if (DosOpen("COM1", &portFd, &action, 0, FILE_NORMAL, FILE_OPEN,
      OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYREADWRITE, 0) != 0) {
        fprintf(stderr, "Open of COM1 failed\n");
        goto error0;
    }

    {	DCBINFO di;
        ULONG dummy;

        dummy = sizeof(di);
        if (DosDevIOCtl(portFd, IOCTL_ASYNC, ASYNC_GETDCBINFO, 0, 0, 0,
                        &di, sizeof(di), &dummy) != 0)
            fprintf(stderr, "DosDevIOCtl failed\n");
        else {
            fprintf(stderr, "Timeouts: read = %u, write = %u\n",
                    di.usWriteTimeout, di.usReadTimeout);
            fprintf(stderr, "Flag bytes: %02x, %02x, %02x\n",
                    di.fbCtlHndShake, di.fbFlowReplace, di.fbTimeout);
        }
        di.fbTimeout = (di.fbTimeout & ~(3 << 1)) | (2 << 1);
                                          /* rcv = wait-for-something */
        di.usReadTimeout = 250;
        dummy = sizeof(di);
        if (DosDevIOCtl(portFd, IOCTL_ASYNC, ASYNC_SETDCBINFO,
                        &di, sizeof(di), &dummy, 0, 0, 0) != 0)
            fprintf(stderr, "DosDevIOCtl failed to set parameters!\a\n");
    }

    if (initSerialOutput(portFd) != 0)
        goto error1;

//-- the "running" variable is sort of a relic, I think

    running = 1;
    if (_beginthread(serialInputThread, 0, 8192, (void *)portFd) < 0) {
        fprintf(stderr, "can't start serial input thread\n");
        goto error2;
    }

    if (_beginthread(serialOutputThread, 0, 8192, (void *)portFd) < 0) {
        fprintf(stderr, "can't start serial output thread\n");
        goto error2;
    }

//-- wrapup code

    shutdownSerialOutput();
    DosClose(portFd);

//-- the rest of this lives outside of main(). . . 

//-- input side: gory details omitted

//-- the port has been setup in "wait for something" mode, so we can request
//-- more than one character at a time without blocking until the buffer is
//-- full.  At least, I *think* that's working now: this is used with 300 baud
//-- systems, so it's hard to tell <g>.  At least it isn't blocking until the
//-- buffer is filled. . . 

/*  serialInputThread -- reads port, writes to text window
*
*   arg is the port's handle for reading
*/
void serialInputThread(void *arg)
{
    HFILE inFd = (long)arg;
    FILE *logFile;
    UCHAR buf[10];

    ParserState ps = {0};

    logFile = fopen("dtp.log", "ab");

    for ( ; ; )
    {
        ULONG n;
        if (DosRead(inFd, buf, 10, &n) == 0)
        {
            ULONG i;
            for (i = 0; i <n; ++i)
            {
                if (logFile != 0)
                    putc(buf[i], logFile);
                if (runParser(&ps, buf[i]) != 0)
                    postChar(buf[i]);
            }
        }
    }
}

/* output side: I rather like this arrangement using queues
   except that I'd prefer an anonymous queue.
   For this, having the queue named in the
   filesystem's name space is at best a minor annoyance. */

/* * * *    SerialOutput subsystem
*/

#define MAX_CHUNK_SIZE 50

typedef struct
{
    USHORT nUsed;
    UCHAR buf[MAX_CHUNK_SIZE];
} SO_CHUNK;

#define NUM_SO_CHUNKS 6


HQUEUE soQueue, freeQueue;


int initSerialOutput (HFILE outFd)
{
    (void) outFd;     /* reserved for more general version */

    if (DosCreateQueue
       (&soQueue, QUE_FIFO, "\\queues\\dtp\\soQueue") != 0)
    {
        fprintf(stderr, "Failed to create serial output queue\n");
        goto error0;
    }
    if (DosCreateQueue
       (&freeQueue, QUE_FIFO, "\\queues\\dtp\\freeQueue") != 0)
    {
        fprintf(stderr, "Failed to create serial free queue\n");
        goto error1;
    }
    {   SO_CHUNK *p = malloc(sizeof(SO_CHUNK) * NUM_SO_CHUNKS);
        int i;
        if (p == 0)
        {
            fprintf(stderr,
            "Failed to allocate memory for serial chunks\n");
            goto error1;
        }
        for (i = NUM_SO_CHUNKS; 0 < i; --i)
            if (DosWriteQueue(freeQueue, 0, sizeof(SO_CHUNK), p++, 0)
            != 0)
            {
                fprintf(stderr, "Failed to initialize free queue\n");
                goto error1;
            }
    }

    return 0;

error1:
    DosCloseQueue(soQueue);
error0:
    return -1;
}


void shutdownSerialOutput(void)
{
    DosCloseQueue(freeQueue);
    DosCloseQueue(soQueue);
}


void writeSerial(UCHAR const *buf, USHORT n)
{

    while (0 < n)
    {
        REQUESTDATA rd;
        ULONG dataLength;
        PVOID data;
        BYTE priority;

        if (DosReadQueue(freeQueue, &rd, &dataLength, &data,
                         0, DCWW_WAIT, &priority, 0) == 0)
        {
            SO_CHUNK *sc = data;
            USHORT m = MAX_CHUNK_SIZE;
            if (n < m)
                m = n;
            memcpy(sc->buf, buf, m);
            sc->nUsed = m;
            DosWriteQueue(soQueue, 0, sizeof(SO_CHUNK), sc, 0);
            buf += m;
            n -= m;
        }
    }
}


void writeSerialString(UCHAR const *buf)
{
    writeSerial(buf, strlen(buf));
}


void serialOutputThread(void *arg)
{
    HFILE outFd = (long)arg;
    REQUESTDATA rd;
    ULONG dataLength;
    PVOID data;
    BYTE priority;

    for ( ; ; )
    {
        if (DosReadQueue(soQueue, &rd, &dataLength, &data,
                         0, DCWW_WAIT, &priority, 0) == 0)
        {
            if (rd.ulData == 0) {     /* simple data block */
                ULONG dummy;
                SO_CHUNK *sc = data;
                DosWrite(outFd, sc->buf, sc->nUsed, &dummy);
                DosWriteQueue(freeQueue, 0, sizeof(SO_CHUNK), sc, 0);
            }
            else
                ;  /* anything else is a test, ignore it */
        }
    }
}



The intention was that control messages could be posted to the queue
using null data packets (passing the actual message in the
REQUESTDATA.ulData field); these would allow for controlling the port's
baud rate and other settings.  This seems to work under 2.0, and even
appears to be intended to work (Deitel & Kogan's description), but I
haven't done anything with it yet, as you can see.  Haven't needed the
facility yet. . . 


Credit:  Martin Maney

-------------------------------------------------------------------------------
How do I disable <Ctrl><Alt><Del>?


You need to do a DosDevIOCtl with Category 4, Function 56.
Use a 0xFFFF for the Hot Key ID.  Set the other values to 0.
This will toggle <Ctrl><Alt><Del> and <Ctrl><Esc> and <Alt><Esc> on then
off on the next call. Note that this does a little more than what you
want since it also disables <Ctrl><Esc> and <Alt><Esc> so you may run
into problems getting around your different sessions when you've got it
disabled. Other than this there is NO WAY to disable <Ctrl><Alt><Del>
under OS/2 and believe me, people have tried!


Credit:  Mike Brown

-------------------------------------------------------------------------------
Why doesn't printf() produce output when I expect it to?


For historical reasons, most Unix C libraries' stdio default to using
line buffered streams, whereas most DOS and OS/2 C libraries' stdio
default to using fully buffered streams.  ANSI C species that standard
output should be line buffered when connected to an interactive
device, but not all libraries are ANSI compliant.  You can control the
buffering algorithm used for a particular stream with the setvbuf()
function.

If you didn't understand that paragraph, read on.

printf() is part of the Standard I/O (stdio) library, which uses
buffered streams for file IO.  ANSI C specifies three algorithms for
deciding when to flush the buffer (i.e. when to print buffered data to
the file):

o not buffered.  Data is flushed to the file as soon as
  possible, usually immediately after being received.
o line buffered.  Data is flushed to the file when a newline
  is received (and the newline is also flushed).
o fully buffered.  Data is flushed to the file when the buffer
  is full.

Buffered data is always flushed when the stream is closed or when
fflush() is called.  Since standard output is flushed when main()
exits, all data printed with printf() will appear at that time, if it
has not already.  However, ANSI C does not require that a stream be
flushed when scanf() is called on it.  Therefore, if you print to a
fully buffered stream and then request input on it, it is likely that
the input will be read before the printed data appears.

You can control the buffering algorithm used for a particular stream
with the setvbuf() function.  For example, the statement
setvbuf(stdout, NULL, _IOLBF, BUFSIZ) sets standard output to be
line-buffered, which is what most Unix programmers expect.  Any decent
C reference will cover all of this material.

[Colin Jensen. . . ]

After reading one too many bug reports about this phenomena, the gcc/2
maintainer changed its stdout to not-buffered whenever stdout is
interactive.  When stdout is sent to a pipe or file, stdout is still
fully-buffered.

-------------------------------------------------------------------------------
How do I write an OS/2 device driver?


The preferred method is to buy the OS/2 device driver kit (DDK) from
IBM.  It contains samples for display, printer, SCSI, and other drivers.
You can develop kernel and PM drivers.	NOTE:  The kernel debugger
comes with the IBM Toolkit, sold separately.

There's a book called Writing OS/2 2.0 Device Drivers in C
from Van&rbl.Nostrand-Reinhold and also IBM's flood of printed
material.

As reference to SCSI programming, check SCSI Programming FAQ, available
at ftp.regent.e-technik.tu-muenchen.de


-------------------------------------------------------------------------------
How do I change the master environment?


Quick and simple answer

In OS/2 it is literally impossible for you to change the master
environment from one of your programs.	To be able to do so would
fatally disrupt the programming paradigm that has existed for
ages:  Your program does not alter the master environment.
Your program is the slave, not the master.  Therefore, no
capability was built into OS/2 to facilitate this.

There is, however, a kludge.

As you know, a .CMD file can alter the master environment.  This is the
nature of batch files (ok, so I'm a MS-DOG dinosaur and still call them
batch files instead of command files or scripts).  Therefore, you can
place a sequence of commands in the batch file that will take your
program's output and alter the environment of your current shell.

That's as close as you are gonna get to the master environment.  You can
always create your own sort of environment variables, in the form of
shared memory or named pipes.

-------------------------------------------------------------------------------
What is the best way to communicate between processes?


There is more than one way - and you get to decide which is right for
you!


Shared Memory

Shared memory is pretty self-explanatory.  It is a memory segment that
is allocated by one program, and then made available to other programs.
When all the programs are done with it, then it is disposed of.  You can
name shared memory.  So if you want two programs to communicate, then
let them look for memory with the same name and communicate that way.


Named Pipes

Named pipes are a lot like shared memory, but think of a named pipe as
a file instead of a single block of memory.  Each process can create,
read, write, and destroy a named pipe, much like you can a file.

The difference between named pipes and shared memory is that a named
pipe link is hot; With shared memory, data can be left in (as a sleeper,
if you will), process 1 exits, process 2 accesses the data in memory,
then deallocates the memory.  With shared memory, a process doesn't even
have to exist to leave a message for another process.


Queues

An OS/2 queue is either a standard First-In First-Out (FIFO) queue
data/operation structure, a LIFO structure, or a user-based-priority
structure.  However, OS/2 makes it unique because more than one
process (or thread) can write to this queue.


-------------------------------------------------------------------------------
What is the best way to communicate between threads?


The best way to communicate between threads is sometimes also the best way to 
communicate between processes. However, when communicating between threads you 
can utilize two (very important) techniques:


Semaphores

In order to share application resources, and not write to the same space
at the same time, you have to have some kind of flags that tell the
thread when it should stop, when it should keep on going, and so on.
Semaphores provide this capability.  Semaphores are not for
passing data.  They merely exist as simple flags between threads and you
should treat them as such.


Global Variables

"Hey Jeff - I thought this was supposed to be about nifty OS/2-specific
tricks!"  It is!  You can now use those old nasty things, global
variables, in new ways.  In conjunction with semaphores, you can pass
data very easily between threads with global variables.  Here's a simple
example:

o Create a global variable called PassData.
o Create a semaphore called OkToPassData.
o Create a semaphore called DataPassed.
o Have two threads work at the same time:

o If the semaphore DataPassed is true:

o Set the semaphore OkToPassData to false.
o Read the data in the global.
o Set the semaphore DataPassed to false.
o Set the semaphore OkToPassData to true.

o When a thread wants to pass data, wait for the semaphore to be
  clear.
o Set the semaphore OkToPassData to false.
o Put the data in the global.
o Set the semaphore DataPassed to true.
o Set the semaphore OkToPassData to true.


Of course, there are issues of deadlock and other such nonsense that
corporate chaps get paid to consider, but that's beyond the scope of
this document.


-------------------------------------------------------------------------------
How to I write an IFS?


Starting with Issue #3, EDM/2 started carrying a series about writing an
IFS.

-------------------------------------------------------------------------------
How do I interface with OS/2's SCSI support?


A complete description and sample code can be found at ftp archive;

reseq.regent.e-technik.tu-muenchen.de

in /pub/comp/os/os2/drivers/source/scsipg.zip.

-------------------------------------------------------------------------------
How do I program full-screen graphics?


Several compilers come with their own VGA graphics libraries.  A 3rd
party developer has even developed an SVGA library (with drivers) for
emx/gcc.

Full-screen graphics programming is nearly identical to full-screen DOS
graphics programming, with the notable exception that you must lock and
unlock the screen under OS/2 before you can use it.

For fast direct-access screen graphics, check at ftp-os2.cdrom.com, the
file dlib06.zip.

-------------------------------------------------------------------------------
How do I program MMPM/2 programs?


IBM has created the MMPM/2 toolkit just for that.  However, if that is
too much trouble for you, there is a simple REXX interface to MMPM/2
available in OS/2 2.1.

IBM's Ultimedia Builder/2, Ultimedia Perfect Image/2, and Ultimedia
Workplace/2 are all in beta and scheduled to be released soon.


For the Emx/GCC enviroment there is the mm4emx package by Marc Van Woerkom.
mm4emx is freeware. All header and lib files included.

-------------------------------------------------------------------------------
How do I peripheral memory or an I/O port?


The basic thing to do is either mark it as IOPL or surround it in a
16-bit module.	emx/gcc and other compilers include some macros and
functions to assist you in doing this.

Check the file IOPL32.ZIP at ftp.cdrom.com:/pub/os2/os2_x/programs



===============================================================================
Porting


This section covers all aspects of porting programs.

-------------------------------------------------------------------------------
How do I port my DOS keyboard TSR to OS/2?


Use keyboard monitors (for fullscreen sessions) or hooks for the PM
session.

-------------------------------------------------------------------------------
How can I simulate (Unix feature) under OS/2?

o fork
o fork/exec
o select
o job control

A working version of select() is included with the emx/gcc libraries.
See the emx/gcc documentation for more information on its usage.

A working version of fork() comes with the emx/gcc libraries.  The
author cautions that this is not the way to multitask, though, because
it eats up a lot of resources (since it literally duplicates the current
process, leaving everything but the PID unchanged).  _beginthread() is
the suggested solution if at all possible.

-------------------------------------------------------------------------------
How can I recompile public domain/shareware source code for OS/2?


Most publicly available OS/2 programs come with binaries (since there
is currently only one OS/2 architecture).  If you are porting source
code from another system (for example, Unix), you will first need to
acquire a compiler.  See section 1 for information on compilers; in
particular, note that the GNU C compiler is available.

You should realize that many publicly available programs have already
been ported to OS/2.  Check the many FTP sites carrying OS/2 programs
before you reinvent any wheels (the OS/2 User's FAQ contains
information on FTP site).

Most Unix applications (through the use of emx/gcc) port with extreme
ease; DOS and Windows applications are a tougher problem, and require
many changes before they can be recompiled as a native OS/2 program.

(It is interesting to note that MicroSoft C v6.0 will compile bound OS/2
programs, which will run under DOS and OS/2 without
modification.)

-------------------------------------------------------------------------------
How can I port my DOS program to OS/2?


To the first approximation, you don't have to --- OS/2 2.x's DOS
support is excellent, and your DOS program will probably just work;
similarly, OS/2 2.x supports Windows 3.0 (and soon 3.1) programs.  See
the OS/2 User's FAQ for details.

[That was Barry Jaspan's opinion.  I believe that you should make
every effort to recompile your existing DOS programs for OS/2 2.x.  They
will run faster in many cases, and both (a) use less memory and (b) be
able to use more memory than their DOS counterparts. - Jeff]

If you truly want to port your DOS program over to OS/2, then study the
libraries available to you.  The core code (if you wrote it correctly)
will probably not change much.  You will just have to change the user
interface stuff.

If your program is a real simple one that uses standard input and
output, then you will probably not make very many changes to your
program when converting it to OS/2.

You should also realize that neato and nifty DOS tricks (like grabbing
an interrupt whenever you feel like it, or writing directly to almost
anywhere) are completely out of the question.

-------------------------------------------------------------------------------
How can I port my Windows program to OS/2?


A tool called Mirrors is available from Micrographx.  It does not
currently support Windows 3.1-specific programs and calls.  (although
you *can* call the functions in the new 3.1 DLLs)

o Rough ports can be done in under 1 day, but a detailed port still
  costs you a lot of effort.
o The ported application is heavily dependent on the underlying OS/2
  system.

There are also several toolkits available that allow you to make calls
to a common API library, and your source will work across the two
platforms without any changes at all.

However, if you want to bite the bullet and port it, then be prepared to
make a lot of changes.  Just like porting regular DOS programs, you will
have to scrap most, if not all, of your user interface.  Your core code,
if modular and abstract enough, should come through the port relatively
unscathed.

SMART - by One-up Corp is also available on Developers Connection CD.



===============================================================================
Miscellaneous


This section covers questions not covered in previous sections.

-------------------------------------------------------------------------------
Is OS/2 suitable for real time programs?


Yes!  There is a special priority you can assign your programs
(ForegroundServer Mode) via DosSetPriority() which will give your
process (note, not thread, but process) the maximum allowable CPU time.

Another route is to use DosEnterCritSec()/DosExitCritSec().  Calling the
former will disable thread switching (hopefully for a short period of
time), and calling the latter will enable thread switching again.

-------------------------------------------------------------------------------
What is available for multimedia programming under OS/2?


The OS/2 2.x Multimedia package is now available.  Call the
IBM Multimedia office at (800) 426-9402 ext. 150.

-------------------------------------------------------------------------------
What is available for AI/neural net programming under OS/2?


LISP and XScheme are available from cdrom.com.  There are also some
AI/neural net tools listed in tinf31.zip.

-------------------------------------------------------------------------------
Special software offers


Here are some of the OS/2 software products that represent particularly
good values.  Most prices do not include shipping and handling.

o Borland C++ for OS/2.  Available from Below Zero in Calgary (phone
  800-461-2777, 403-547-0669, or FAX 403-547-1018) for about $136 U.S.,
  including shipping.  Add GST in Canada.  Below Zero will export outside
  North America.

o IBM PL/I.  Not everyone is a PL/I programmer, but IBM is offering
  free copies of Workframe/2 with every purchase and free product videos.
  Phone 800-426-3346 ext. STL10 for more information on the two packages
  available.

(Quoted almost directly from OS/2 General FAQ)

-------------------------------------------------------------------------------
Technical Support


How can I get answers to my OS/2 questions?

If your question is not answered in this List, post a note to the
appropriate Usenet conference:

Newsgroup                    Description
=====================================================================
comp.os.os2.advocacy           deals with opinions and speculation
comp.os.os2.announce           carries important OS/2 announcements
comp.os.os2.apps               carries discussions related to finding or using 
                               any application running under OS/2
comp.os.os2.beta               explores beta releases of OS/2
comp.os.os2.bugs               discusses possible bugs found in released 
                               versions of the operating system
comp.os.os2.games              covers all question about OS/2 games
comp.os.os2.misc               is for any other OS/2-related discussion
comp.os.os2.multimedia         fosters conversation about OS/2 multimedia 
                               (including MMPM/2),
comp.os.os2.networking         looks at general networking issues
comp.os.os2.networking.misc    miscellaneous discussions about networking
comp.os.os2.networking.tcp-ip  covers TCP/IP issues
comp.os.os2.programmer.misc    addresses anything else related to OS/2 programming
comp.os.os2.programmer.oop     covers object-oriented programming discussions
comp.os.os2.programmer.porting helps programmers move applications over to OS/2 
                               from other operating systems and environments       
comp.os.os2.programmer.tools   covers tools for OS/2 application developing
comp.os.os2.setup              REPLACED BY comp.os.os2.setup.misc
comp.os.os2.comm               Modem/Fax hardware/drivers/apps/utils under OS/2
comp.os.os2.mail-news          Mail and news apps/utils (on- & offline) under OS/2
comp.os.os2.marketplace        forsale/wanted; shopping; commercial ads; job postings
comp.os.os2.networking.www     World Wide Web (WWW) apps/utils under OS/2
comp.os.os2.setup.misc         Installing/configuring OS/2; Misc hardware/drivers
comp.os.os2.setup.video        Base video hardware/drivers under OS/2
comp.os.os2.setup.storage      Disk/Tape/CD-ROM hardware/drivers under OS/2
comp.os.os2.utilities          General-purpose utils (shells/backup/compression/etc)
comp.os.os2.ver1x              HAS BEEN REMOVED
comp.lang.rexx                 discusses REXX programming

These groups are also watched closely by OS/2 experts from IBM.

A LISTSERVer distributes its own OS/2 conference by mail; send a single
line message with the word HELP to listserv@cc1.kuleuven.ac.be for full
instructions; or send the same message to listserv@frors12.circe.fr for
information on an unedited mailing list.  To subscribe to the
Multimedia Presentation Manager/2,
send a single line message with the phrase SUBSCRIBE MMOS2-L (Your
Name) to mail-server@knex.via.mind.org.

Your local FidoNet BBS may carry OS/2 echo conferences and/or OS2NET.
If not, ask your system operator to join them.  CompuServe (FIND OS/2)
and Prodigy are also excellent resources.

The IBM PC Co. BBS's (modem 404-835-6600) message areas, product
database, and PS/2 Assistant file(s) are invaluable resources.
Information on the new OS/2 BBS is included in the OS/2 2.0 package.
In the United States IBM has toll free technical support (phone
800-237-5511), an OS/2 Hotline (general information, orders, upgrades,
phone 800-3-IBM-OS2; ask about OS/2 videotapes, T-shirts, and other
accessories), the HelpWare Center (phone 800-PS2-2227), a software
order line (phone 800-IBM-CALL), two FAX information services (phone
800-IBM-4FAX and/or 800-IBM-3395), and an educational inquiries line
(phone 800-222-7257).  In Canada phone IBM Personal Systems Software at
800-465-1234.

Any of the regular DOS or Windows resources (e.g. books, magazines,
shareware/freeware sources) will be useful since both environments come
with OS/2 2.0.

(taken from OS/2 General FAQ)


-------------------------------------------------------------------------------
Developer's Assistance Program (DAP)


OS/2 2.0 developers should contact the IBM Developer Assistance Program
(phone 407-982-6408); membership is free.  (You may also join on
CompuServe with GO OS2DAP.)

The OS/2 Professional Developer's Kit CD-ROM, containing a wide selection
of development tools and code, and the OS/2 2.1 Beta CD-ROM are both 
available from IBM (phone 800-3-IBM-OS2 to order in the United States 
for between $15 and $20 each, shipping included; in Canada, phone 800-465-1234;
in Australia, phone Rohaini Cain or Mike Voris at 13-2426 ext. 7684; elsewhere,
contact the International OS/2 User Group by phoning 285-641175 in the
U.K.)

The OS/2 Device Driver Development Kit CD-ROM is also now available from 
IBM. To order phone 407-982-4239 (FAX 407-982-4218) in North America, 
61-2-354-7684 (FAX 61-2-354-7766) in most of the Far East and Pacific Rim, 
81-3-5563-5897 (FAX 81-3-5563-4957) in Japan, 81-2-528-1548 (FAX 82-2-528-1414)
in Korea, 011-52-627-1846 (FAX 011-52-395-7812) in Latin America, 
or +49-69-6645-3318 in Germany.

(taken from the OS/2 general FAQ)

-------------------------------------------------------------------------------
OS/2 Software Sources


The following BBSes hold large OS/2 libraries:

Name              Number

Fernwood                     (203) 483-0348
OS/2 Shareware               (703) 385-4325
Bay Area OS/2                (510) 657-7948
Gateway/2                    (314) 554-9313
Greater Chicago Online       (708) 895-4042
OS/2 Connection (San Diego)  (619) 549-4215
OS/2 Las Vegas               (702) 433-5535
IBM Germany                  049-711-785-7777
IBM Germany (Another)        +49-69-6645-3257
IBM Denmark                  45-42-88-72-22
OS/2 UK                      0454-633197
IBM UK                       0256-336655
IBM Norway                   47-66-99-94-50
IBM Sweden                   +8-793 22 00
OS/2 Norway                  47-22-38-09-49
OS/2 Australia               61-2-241-2466


Check OS/2 FAQ for a more extensive list of ftp sites.

(The monthly Worldwide OS/2 BBS Listing, available from these BBSes,
lists others.)  The IBM PC Company BBS (modem 404-835-6600) has some
shareware/freeware as well, along with CSDs and the PS/2 Assistant (an
invaluable resource for locating almost any sort of information on
OS/2). For information on IBM's new OS/2 BBS phone 800-547-1283.  IBM
Canada maintains several support BBSes:

o      (416) 946-4255
o      (514) 938-3022
o      (604) 664-6464
o      (416) 946-4244

The Usenet conference comp.binaries.os2 carries OS/2 software.  And
several sites are available via anonymous ftp.  (No ftp?  Send a single
line message with the word HELP to bitftp@pucc.bitnet or
ftpmail@decwrl.dec.com to learn about ftp mail servers.)  Some are (with
Internet node numbers and subdirectories):

Site                         IP  Address Home  OS/2 Directory
=====================================================================
american.megatrends.com      192.239.218.193   pub

atitech.ca
drivers for ATI video cards  198.133.44.2      pub/support/OS2

boombox.micro.umn.edu
home of gopher software      134.84.132.2      pub/gopher/os2

cirrus.com
Cirrus video drivers; further
sorted under /desktop, 
/laptop, etc.                141.131.7.10      pub/support

ctron.com
Cabletron - OS/2 drivers for
their cards                  134.141.197.25    pub/drivers

ftp.3com.com
drivers for 3com cards       129.213.128.5     /adaptors/drivers

ftp.cdrom.com                192.216.191.11    /pub/os2

ftp.creaf.com
Creative Labs archive
SoundBlaster drivers etc.    198.95.32.3
  
ftp.germany.eu.net
see under /newsarchive 
and /comp                    192.76.144.75     .../os2

ftp.ibm.net
latest beta                  165.87.194.246    /pub/WebExplorer

hobbes.nmsu.edu              128.123.35.151    pub/os2

software.watson.ibm.com      129.34.139.5      pub/os2

mtsg.ubc.ca                  137.82.27.1       os2

access.usask.ca              128.233.3.1       pub/archives/os2

luga.latrobe.edu.au          131.172.2.2       pub/os2

funic.funet.fi               128.214.6.100     pub/os2

pdsoft.lancs.ac.uk           148.88.64.2       micros/ibmpc/os2

ftp.uni-stuttgart.de         129.69.1.13       /pub/systems/os2

zaphod.cs.uwindsor.ca        137.207.224.3     pub/local/os2

ftp.luth.se                  130.240.18.2      pub/os2

src.doc.ic.ac.uk
mirrors ftp-os2.nmsu.edu/
ftp.cdrom.com                146.169.2.1       computing/systems/os2

ftp.informatik.tu-muenchen.de 131.159.0.198    /pub/comp/os/os2

ftp.informatik.uni-rostock.de 139.30.5.23      pub/os2

ftp.lexmark.com
also other directories for 
os2*.* files                  192.146.101.4    pub/driver

ftp.ncr.com
PCI SCSI drivers in /ncrchips/
scsi/drivers/os2              192.127.252.6    pub

ftp.netcom.com
Fan's WWW server web2-101.zip 192.100.81.1     /pub/kfan

ftp.novell.com
former NetWire libraries      137.65.1.3       pub/updates

ftp.pcco.ibm.com
most dirs contain relevant 
files                         198.79.74.29     ? all

ftp.uni-bielefeld.de          129.70.4.55      pub/systems/os2

ftp.uni-erlangen.de
has German updates            131.188.1.43     pub/os2

ftp.uni-kl.de
also /incoming/pc/os2         131.246.9.95     pub/pc/os2

ftp.uu.net
newsgroup archives - see 
comp.os.os2.*, among 
others                        192.48.96.2      /usenet  (e.g.)

ftp.uwp.edu
FAQ, drivers, etc. for PAS16  131.210.1.4      pub/msdos/proaudio


The cdrom.com library is available on CD-ROM from Walnut Creek
(phone 510-947-5996).  EMS (phone 301-924-3594) offers an OS/2
shareware/freeware library on diskette.

Other sources include CompuServe (FIND OS/2) and archive servers (send a
single line message with the word HELP to listserv@cc1.kuleuven.ac.be or
mail-server@rus.uni-stuttgart.de for more information, or use ftp).
TRICKLE servers are also available outside the United States.  For more
information on TRICKLE services, including automatic file subscription
procedures, send a single line message with the word HELP to any one of
the following sites nearest you:

Country     Address
=======================================
Austria     TRICKLE@AWIWUW11.BITNET
Belgium     TRICKLE@BANUFS11.BITNET
Colombia    TRICKLE@UNALCOL.BITNET
France      TRICKLE@FRMOP11.BITNET
Germany     TRICKLE@DEARN.BITNET
Israel      TRICKLE@TAUNIVM.BITNET
Italy       TRICKLE@IMIPOLI.BITNET
Netherlands TRICKLE@HEARN.BITNET
Sweden      TRICKLE@SEARN.BITNET
Turkey      TRICKLE@TREARN.BITNET
TRICKLE@TRMETU.BITNET

IBM has been releasing freely distributable employee written software
(e.g. Visual REXX) and OS/2 patches to these sites.

(The previous was taken almost verbatim from the OS/2 General FAQ)




===============================================================================
Bugs / Obtaining this FAQ / Contacting the Author

-------------------------------------------------------------------------------
Reporting Bugs and Errors in the FAQ


With the advent of all this nifty hypertext IPF stuff, there are bound
to be bugs and errors in this FAQ, simply by Murphy's Law.  (Anything
that can go wrong, will go wrong)  If you find an error, however,
insignificant, please send me a note (through one of the ways
listed below) telling me what's wrong with the FAQ.

-------------------------------------------------------------------------------
Obtaining this FAQ


This FAQ is distributed on a regular basis to:

o ftp-os2.cdrom.com:/os2/newsltr/pfaqxx.zip, on the Internet
o hobbes.nmsu.edu:/os2/newsltr/pfaqxx.zip, on the Internet

All other sites should receive this FAQ on a trickle-down basis from
these sites.

This FAQ is distributed in two versions, ASCII and INF.  The INF version
is binary and viewable only by the VIEW.EXE program that comes with
OS/2.  The ASCII version is text and is viewable by any program that can
view text (which includes most word processors).  The filename is
PFAQnn.ZIP, where 'nn' is the FAQ version.

-------------------------------------------------------------------------------
Contacting the Author


I can be contacted in a multitude of ways:

o Internet.  This is the preferred method.  E-mail me at
andreas@traci.almroth.pp.se. 
o Snail Mail

o Andreas Almroth
o Fanrik Stals Gata 153
o S-754 39 UPPSALA
o SWEDEN


-------------------------------------------------------------------------------
Credits

The following people have contributed in numerous and not-so-numerous
ways to this document to make it what it is today, and what it will be
tomorrow.  Give a big round of applause for. . . 

o Barry Jaspan <bjaspan@athena.mit.edu>
o Jeff Garzik <jgarzik@pantera.atl.ga.us>
o Byers R E James <zoorejb@nusunix2.nus.sg>
o Stefan Gruendal <Stefan_Gruendel@wue.maus.de>
o Raja Thiagarajan <sthiagar@bronze.ucs.indiana.edu>
o Larry Saloman <os2man@panix.com>
o Timothy Sipples <sip1@kimbark.uchicago.edu>
o Bob Smith <OECN_SMITH@MEC.OHIO.GOV>
o Tim Francis <francis@vnet.IBM.COM>
o Colin Jensen <cjensen@netcom.com>
o Bill Henning <bhenning@wimsey.com>
o Axel Uhl <auhl@fzi.de>
o R. Mahoney <rmahoney@bix.com>
o Frank Fuchs <ffu@softpro.de>
o James J. Song <jjs@acd4.acd.com>
o Morio Taneda <Morio_Taneda@ka.maus.de>
o Timur Tabi <timur@seas.gwu.edu>
o Paul Prescod <papresco@undergrad.math.uwaterloo.ca>
o Rich Wales <richw@mks.com>
o Craig Swanson <Craig_Swanson@f354.n202.z1.fidonet.org>
o Ivar E. Hosteng <nit3ieh@odin.oslo.nit.no or nit3ieh@accsys.oslo.nit.no>
o Eberhard Mattes <mattes@azu.informatik.uni-stuttgart.de>
o Phillip E. Parker <pparker@twsuvm.uc.twsu.edu>
o Lyle Sutton <lgs@roadnet.ups.com>
o William First <wfirst@magi.com>



Ok, so the list is a little short right now.  If you contributed to
Barry's or Jeff's FAQ, and you are not listed here, then please send me your
name and I'll be glad to include you in this list.


I have obtained some information from sources other than people also.
Besides being credited above, here is another list:

o OS/2 Frequently Asked Questions List v2.1  (Available from
  ftp-os2.cdrom.com:/os2/newsltr/faq21*.zip)
o Electronic Developers' OS/2 Magazine  (Available from 
  ftp-os2.cdrom.com:os2/newsltr/edmi*.zip)




