Channels ▼
RSS

Design

Programming for the OS/2 2.0 Workplace Shell

Source Code Accompanies This Article. Download It Now.


APR93: PROGRAMMING FOR THE OS/2 2.0 WORKPLACE SHELL

This article contains the following executables: OS2WPS.ARC

Joel Barnum

Joel is a programming instructor at Descriptor Systems, P.O. Box 461, Marion, IA 52302. He can be reached on CompuServe at 70047,442.


If you're familiar with OS/2, Version 1.x but new to 2.0, you're in for an immediate surprise. That's because 2.0 sports a new user interface called the Workplace Shell (WPS) that replaces Presentation Manager. This new shell is data oriented, rather than application oriented. In other words, users manipulate data directly, rather than first launching an executable and then opening a data file. This datacentric approach is more natural for novice computer users--and for experienced users, once they get used to it.

For example, to modify a spreadsheet under Workplace Shell, simply open the folder containing the spreadsheet data and double-click on the data-file object. WPS will launch the associated .EXE file, passing the data-file name as an argument. Of course, earlier versions of OS/2 (and Windows) also let the user associate data files with executable files by file extension, but WPS takes this concept much further. For example, it is possible to print data files via drag and drop without first starting the .EXE file. For this to work, however, the application developer must do some extra work. In this article, I'll detail what developers must do to convert OS/2 1.x programs to fit in the WPS. I'll approach this in a stepwise fashion, starting with the minimum changes required, and working up to a full-blown WPS-compliant program.

Launching by File Extension

Associating an executable with data by the data file's extension is a simple process--open the Settings notebook by clicking the right mouse button on the appropriate .EXE file and set up an association between that .EXE file, and a list of file extensions. WPS will write entries in the OS2.INI profile that specify the associations. When the user double-clicks on a data file, WPS launches the appropriate .EXE and passes the data-file name as argv[1].

While this technique works, it's not very robust. First of all, the file extension is not a great way to specify a file type. After all, you've only three characters to work with, and it's quite possible that several application programs might use the same extension for their data files. Second, since users can give their files any name, there's no guarantee that a .TXT really contains text, for example. Finally, under the high-performance file system (HPFS), filenames aren't required to have extensions at all. In that case, the system completely breaks down. Fortunately, OS/2 provides a better way to associate file types.

.TYPE Extended Attributes

Beginning with OS/2 1.2, both the operating system and applications can assign information, called "extended attributes" (EAs), to a file. OS/2's two file systems--file allocation table (FAT) and high-performance file system (HPFS)--store extended attributes differently. In neither case, however, are the EAs stored in the file itself. HPFS stores EAs in sectors near the file data, while the FAT system stores them in a hidden system file in the volume root directory called EA DATA. SF. Therefore, an application can easily set and query EAs under either file system.

OS/2 supports a text-string extended attribute called .TYPE. Examples of .TYPE are Plain Text (a system-defined type) and Brand X Spreadsheet, which spreadsheet vendor X defines. Once the application programmer associates a program's .TYPE with its data files, WPS will use this .TYPE instead of (or in addition to) the file extension to launch an associated .EXE file. To attach the .TYPE-extended attribute to the data file, the application must call DosSetFileInfo and pass a variable-length data structure containing the extended attribute. The settype function in Listing One (page 94) shows how to assign a .TYPE EA to a file.

An application must also tell WPS what .TYPEs it recognizes by defining an ASSOCTABLE resource in its resource file; see Listing Two, page 95. You can list several .TYPEs and extension filters if you wish. The icon file isn't used in OS/2 2.0, but is still required syntax. The first time the user displays the .EXE file in a Drives folder (or in file manager under OS/2 1.3), the system writes an entry into OS2.INI. This entry indicates that files of the given type "belong" to the executable. When the user double-clicks on a data file, WPS (or file manager) searches OS2.INI and then launches the .EXE file, passing the data file's name to the application as the first argument.

WPS takes one more very important action when the user displays the .EXE--it adds a template for the data file to the templates folder. Templates are an integral part of the WPS. They let users create new data files without first running the application file. Instead, users "tear off" an entry from the template and drop it in the folder of their choice. WPS then creates a new file in the target folder, complete with the .TYPE-extended attribute. A user can then launch the .EXE with the new data file by double-clicking on it. However, templates can cause problems for existing programs that write headers to their data files that indicate the file contents (such as a file signature, copyright, and so on). Many such programs will refuse to open any files that don't have the required header. That's a problem with templates because WPS creates the new files without any data. Therefore, you may need to modify existing applications so that they successfully open zero-length files.

The System Object Model

In a completely datacentric environment, the user should be able to print a file by dragging and dropping it onto a printer object. But with what we've covered so far, drag-and-drop printing won't work. After all, only your program knows how to print its own data: You can't expect WPS to print a spreadsheet or a chart, for example. The catch is that when the user drops a data file on a printer object, WPS does not launch the .EXE. Instead, it's up to the data file to print itself. Of course, a simple data file doesn't know how to print itself. Resolving this problem means that we must effectively turn the data file into an object, which is exactly what the System Object Model (SOM) lets you do.

The SOM is a set of tools, header files, and macros that let you define object-oriented classes and objects. Its goal is to let developers design object classes (encapsulated functions and data) and then implement the class in any supported programming language. Theoretically, a developer could define a class in SOM, then write the code for the class in Smalltalk. Another developer could create instances of the class using, say, C++. Currently, however, the only supported language is C, even though C isn't an OOP language. (C++ is in beta.)

Listing Three (page 95) shows a class definition for WPSpreadFile: a data file subclassed from the predefined WPDataFile class. WPDataFile contains methods that support delete and copy operations, drag and drop, plain text printing, and so on. WPSpreadFile overrides six methods from its parent class, most of which are class methods, meaning that they apply to all instances of the class. (wpPrintObject, on the other hand, is referred to as an "instance method.") WPS calls the class methods when the object class is registered and will call the wpPrintObject method only when the user drags and drops an instance of the class on a printer-object icon. We override the class methods to define the icon for our data files and our data files' .TYPE-extended attribute. Once the class definition is complete, run the SOM compiler (that comes with the OS/2 Programmer's Toolkit) to generate a stubbed-out .C file that, when completed, will implement the class.

Listing Four (page 96) shows a completed class implementation. Most of the code in Listing Four was generated by the SOM compiler, including the function definitions and the first two lines of each method. The functions were completed according to Table 1. I've included a make script file (see "Availability," page 5) to compile the resulting listings.

Table 1: Functions used to complete the listing generated by the SOM compiler.

  Function                  Description
  ----------------------------------------------------------------------

  wpclsQueryTitle           Return a string that WPS uses as the default
                             name for new objects.
  wpclsQueryInstanceType    Return a string that WPS assign as .TYPE EA
                             for new object's file.  This matches the
                             ASSOCTABLE in the .EXE's resource file.
  wpclsQueryInstanceFilter  Return a string that WPS uses to associate
                             the .EXE by file extension.
  wpclsInitData             Load the icon for our class objects as a
                             resource.  Note that we reference the
                             class data item with_hicon.
  wpclsQueryIcon            Return the icon's handle.
  wpPrintObject             Call a function that actually prints the
                             data file.

The installation program shown in Listing Five (page 97) can be used to create a folder on the desktop for our program, register our new subclass, and create a reference object (WPProgram) to the application file. When you run the installation program, WPS automatically creates a template for the data files and connects it to the object DLL. When the user manipulates an object created from the template, WPS calls methods in the DLL. For example, when the user drops a data-file object on a printer object, WPS calls the wpPrintObject method. The DLL then prints the data file that the object represents. This example actually prints the file in the method. In a real-world case, we would start a background process to print, so the WPS wouldn't be frozen during the print. Also, to keep the code simple I copied the openfile function from Listing One (the .EXE) to Listing Four (the DLL). In a real-life program, you could put such shared code in another DLL that both the .EXE and DLL could call.

Finally, the WPS automatically sets up an association between the data files and the .EXE file so that the user can launch the .EXE by double-clicking on the data-file object. Note that you will have to modify the installation program to reflect the directory in which the object's DLL resides.

Conclusions

While all existing 1.x programs should run under OS/2 2.0, you can make minor changes that will allow users to put the Workplace Shell to its greatest use. The good news is that you don't have to convert the program all at once -- you can make incremental changes and increase WPS compliance as you go.

Acknowledgments

I'd like to thank Peter Magid in Shell Development at IBM Boca Raton for help above and beyond the call of duty.



_PROGRAMMING FOR THE OS/2 2.0 WORKPLACE SHELL_
by Joel Barnum


[LISTING ONE]
<a name="010c_000b">

// spread.c -- a sample WPS application

#define  INCL_WIN
#define  INCL_GPI
#include <os2.h>
#include <stdlib.h>
#include <string.h>
#include "spread.h"

#define WM_INIT WM_USER

// Internal function prototypes
int main ( int argc, char *argv[] );
BOOL savefile ( PSZ szFname, LONG alValues[] );
BOOL openfile ( PSZ szFname, LONG alValues[] );
BOOL settype ( HFILE hf, PSZ pszType );
MRESULT EXPENTRY ValueDlgProc ( HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2 );
MRESULT EXPENTRY ClientWinProc ( HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2);
// global variables
    HAB  hab;               // Anchor block handle
int main ( int argc, char *argv[] )
{
    HMQ  hmq;               // Message queue handle
    HWND hwndFrame;         // Frame window handle
    HWND hwndClient;        // Client window handle
    QMSG qmsg;              // Message from queue
    ULONG flCreate;         // Window creation flags
    BOOL  fSuccess;         // return from API
    hab = WinInitialize ( 0 );
    hmq = WinCreateMsgQueue ( hab, 0 );
    fSuccess = WinRegisterClass (hab,"spread",ClientWinProc,CS_SIZEREDRAW,0);
    flCreate = FCF_SYSMENU | FCF_SIZEBORDER | FCF_TITLEBAR |
               FCF_MINMAX  | FCF_SHELLPOSITION | FCF_TASKLIST | FCF_ICON;
    hwndFrame = WinCreateStdWindow ( HWND_DESKTOP, WS_VISIBLE
                 , &flCreate, "spread", NULL, 0L, 0 , ID_WINDOW, &hwndClient );
    if ( hwndFrame == NULLHANDLE )
        DosExit ( 1, 1 );
  // send the client a message passing arg count and arguments
    WinSendMsg ( hwndClient, WM_INIT, MPFROMSHORT ( argc ) ,MPFROMP ( argv ));
    while ( WinGetMsg ( hab, &qmsg, NULLHANDLE, 0, 0 ) != FALSE )
        WinDispatchMsg  ( hab, &qmsg );
    fSuccess = WinDestroyWindow ( hwndFrame );
    fSuccess = WinDestroyMsgQueue ( hmq );
    fSuccess = WinTerminate ( hab );
    return 0;
}
//************************************************************
MRESULT EXPENTRY ClientWinProc ( HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2 )
{
        BOOL    fSuccess;       // return from API
static  LONG    alValues[2];    // spreadsheet values
static  HWND    hwndMenu;       // popup menu handle
static  CHAR    szFname[255];   // file name
    switch( msg )
    {
        case WM_BUTTON2DOWN:
            {
                POINTL  ptl;
              // display a popup menu at the coordinates the user clicked
                ptl.x = SHORT1FROMMP ( mp1 );
                ptl.y = SHORT2FROMMP ( mp1 );
                WinMapWindowPoints ( hwnd, HWND_DESKTOP, &ptl, 1 );
                fSuccess = WinPopupMenu (
                       HWND_DESKTOP , hwnd , hwndMenu , ptl.x , ptl.y
                     , 0 , PU_KEYBOARD | PU_NONE | PU_MOUSEBUTTON1 );
            }
            return (MRESULT)FALSE;
        case WM_CLOSE:
            savefile ( szFname, alValues );
            WinPostMsg( hwnd, WM_QUIT, 0L, 0L );
            return (MRESULT) NULL;
        case WM_COMMAND:
            switch ( SHORT1FROMMP ( mp1 ) )
            {
                case IDM_CHANGE:
                    {
                        ULONG   result;
                      // display a modal dialog to let user enter in new values
                        result = WinDlgBox ( HWND_DESKTOP
                             , WinQueryWindow ( hwnd, QW_PARENT ), ValueDlgProc
                             , NULLHANDLE , DLG_VALUES , alValues );
                        if ( result == DID_OK )
                            WinInvalidateRect ( hwnd, NULL, TRUE );
                    }
                    break;
            }
            return (MRESULT)NULL;
        case WM_CREATE:
          //load our popup menu
            hwndMenu = WinLoadMenu ( HWND_DESKTOP , NULLHANDLE, ID_MENU );
            return (MRESULT)FALSE;
        case WM_INIT:           // user-defined message
            {
                int     argc;           // argument count
                CHAR    **argv;         // input arguments
                CHAR    szTitle[255];   // titlebar text
              // extract argument count and strings
                argc = SHORT1FROMMP ( mp1 );
                argv = PVOIDFROMMP  ( mp2 );
              // if there were no input arguments, exit
                if ( argc < 2 )
                {
                    WinMessageBox (
                        HWND_DESKTOP , WinQueryWindow ( hwnd, QW_PARENT )
                        , "You must specify an input file"
                        , "Error", 0 , MB_OK | MB_ERROR );
                    DosExit ( 1, 1 );
                }
              // attempt to open input file
                strcpy ( szFname, argv[1] );
                if ( openfile ( argv[1], alValues ) == FALSE )
                {
                    WinMessageBox (
                             HWND_DESKTOP
                            , WinQueryWindow ( hwnd, QW_PARENT ) , argv[1]
                            , "Unable to open file", 0 , MB_OK | MB_ERROR );
                    DosExit ( 1, 1 );
                }
              // update the titlebar text
                strcpy ( szTitle, "Spreadsheet  - " );
                strcat ( szTitle, szFname );
                WinSetWindowText ( WinQueryWindow ( hwnd, QW_PARENT ),szTitle);
            }
            return (MRESULT)NULL;
        case WM_PAINT:
            {
                LONG    lSuccess;       // return from API
                HPS     hps;            // cached PS
                POINTL  ptl;            // coordinates for draw
                CHAR    sz[50];         // temp string
                hps = WinBeginPaint ( hwnd , NULLHANDLE, NULL );
                fSuccess = GpiErase ( hps );
              // draw the values and their sum
                ptl.x = 100; ptl.y = 125;
                _itoa ( alValues[0], sz, 10 );
                lSuccess = GpiCharStringAt ( hps, &ptl, strlen ( sz ) , sz );
                ptl.y = 100;
                _itoa ( alValues[1], sz, 10 );
                lSuccess = GpiCharStringAt ( hps, &ptl, strlen ( sz ) , sz );
                ptl.y = 75;
                lSuccess = GpiMove ( hps, &ptl );
                ptl.x = 200;
                lSuccess = GpiLine ( hps, &ptl );
                ptl.x = 100; ptl.y = 50;
                _itoa ( alValues[0] + alValues[1], sz, 10 );
                lSuccess = GpiCharStringAt ( hps, &ptl, strlen ( sz ) , sz );
                ptl.x = 50; ptl.y = 25;
                strcpy ( sz, "Press the right mouse button to change values" );
                lSuccess = GpiCharStringAt ( hps, &ptl, strlen ( sz ) , sz );
                fSuccess = WinEndPaint ( hps );
            }
            return (MRESULT) NULL;
        default:
            return
                WinDefWindowProc( hwnd, msg, mp1, mp2 );
    }
    return WinDefWindowProc( hwnd, msg, mp1, mp2 );
}
// savefile:  saves the current values to a file
//      RETURNS:  TRUE if successful, FALSE if not
BOOL savefile ( PSZ szFname, LONG alValues[] )
{
    HFILE   h;                  // file handle
    ULONG   ulAction;           // action taken by OPEN
    ULONG   ulActualWritten;    // count written to file
    APIRET  rc;                 // return code
  // open the current file
    rc = DosOpen ( szFname, &h, &ulAction, 0L
            , 0, OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW
            , OPEN_SHARE_DENYREADWRITE | OPEN_ACCESS_READWRITE, NULL );
    if ( rc != 0 )
        return FALSE;
  // write the two values
    rc = DosWrite ( h, alValues, 8, &ulActualWritten );
    if ( ( rc != 0 )  || ( ulActualWritten != 8 ) )
    {
        DosClose ( h );
        return FALSE;
    }
  // write our .TYPE EA on the file
    settype ( h, "XX Company Spreadsheet" );
  // close the file
    DosClose ( h );
    return TRUE;
}
// openfile: reads spreadsheet values from the specified file
//      RETURNS:  TRUE if successful, FALSE if not
BOOL openfile ( PSZ szFname, LONG alValues[] )
{
    HFILE   h;                  // file handle
    ULONG   ulAction;           // action taken by OPEN
    ULONG   ulActualRead;       // count read from file
    APIRET  rc;                 // return code
  // open the file
    rc = DosOpen ( szFname, &h, &ulAction, 0L , 0
            , OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW
            , OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE , NULL );
    if ( rc != 0 )
        return FALSE;
  // read the values
    rc = DosRead ( h, alValues, 8, &ulActualRead );
    if ( rc != 0 )
    {
        DosClose ( h );
        return FALSE;
    }
  // zero length files are OK, but otherwise less than 8 bytes means bad file
    if ( ulActualRead < 8 )
        if ( ulActualRead != 0 )
        {
            DosClose ( h );
            return FALSE;
        }
  // close the file
    DosClose ( h );
    return TRUE;
}
// settype: sets the .TYPE ea for a data file
BOOL settype ( HFILE hf, PSZ pszType )
{
// define a .TYPE EA structure
typedef struct _TYPEEALIST
{
    ULONG   cbList;         // length of all EAs in list
    ULONG   ulNextEa;       // offset of next EA
    BYTE    bFlags;         // EA flags
    BYTE    cbName;         // length of name
    USHORT  cbEA;           // sizeof EA
    CHAR    szName[6];      // ".TYPE"
    USHORT  usType;         // EA data type
    USHORT  cbValue;        // sizeof value
    CHAR    achValue[1];    // placeholder for EA value
} TEALIST, *PTEALIST;
    EAOP2       eaop;           // extended attributes structure
    PTEALIST    ptea;           // points to TYPE EA list
    PSZ         psz1, psz2;     // temp pointers
    USHORT      cb;             // structure length
    APIRET      rc;             // return from API
  // allocate memory for the TYPE EA list
  // -1 because the structure itself defines 1 char
    cb = strlen ( pszType ) - 1   +
         sizeof ( TEALIST );
    ptea = (PTEALIST)malloc ( cb );
  // initialize the EA structures
  // fill in the EA value itself: for .TYPE it's the file type
  // can't use strcpy!! (needs to add '\0')
    psz1 = pszType;
    psz2 = ptea->achValue;
    while ( *psz1 != '\0' )
        *psz2++ = *psz1++;
  // fill in length of the EA value
    ptea->cbValue = strlen ( pszType );
  // fill type of EA value
    ptea->usType = 0xfffd;            // length-preceded ASCII
  // length of EA (includes value + type and length fields)
    ptea->cbEA = ptea->cbValue + sizeof(ptea->usType) + sizeof (ptea->cbValue);
  // fill in the EA name (it's a null terminated string so strcpy is OK)
    strcpy ( ptea->szName, ".TYPE" );
  // fill in EA name length
    ptea->cbName = (BYTE)strlen ( ".TYPE" );
  // point to the TYPE EA list structure
    eaop.fpFEA2List = (PFEA2LIST)ptea;
    eaop.fpGEA2List = NULL;
    ptea->cbList = cb;                  // structure length
    ptea->ulNextEa = 0;                 // no more EAs
    ptea->bFlags = 0;                   // noncritical
  // attach the .TYPE extended attribute to the file
    rc = DosSetFileInfo ( hf, 2, (PBYTE)&eaop , sizeof (EAOP2) );
    free ( ptea );
    return (BOOL)rc;
}
MRESULT EXPENTRY ValueDlgProc ( HWND hwnd, ULONG msg , MPARAM mp1, MPARAM mp2 )
{
static  PLONG   alValues;
    switch ( msg )
    {
        case WM_INITDLG:
          // retrieve a pointer to values array
            alValues = PVOIDFROMMP ( mp2 );
          // write current values into entry fields
            WinSetDlgItemShort ( hwnd, DLG_VALUE1, (SHORT)alValues[0], TRUE );
            WinSetDlgItemShort ( hwnd, DLG_VALUE2, (SHORT)alValues[1], TRUE );
          // set the focus to the first entryfield
            WinSetFocus ( HWND_DESKTOP, WinWindowFromID ( hwnd, DLG_VALUE1 ) );
            return (MRESULT)TRUE;
        case WM_COMMAND:
            switch ( SHORT1FROMMP ( mp1 ) )
            {
                case DID_CANCEL:
                    WinDismissDlg ( hwnd, DID_CANCEL );
                    break;
                case DID_OK:
                  // retrieve values from entry fields
                    WinQueryDlgItemShort ( hwnd, DLG_VALUE1
                            , (PSHORT)&alValues[0], TRUE );
                    WinQueryDlgItemShort ( hwnd, DLG_VALUE2
                            , (PSHORT)&alValues[1], TRUE );
                    WinDismissDlg ( hwnd, DID_OK );
            }
            return (MRESULT)NULL;
        default:
            return
                WinDefDlgProc( hwnd, msg, mp1, mp2 );
    }
    return WinDefDlgProc( hwnd, msg, mp1, mp2 );
}





<a name="010c_000c">
<a name="010c_000d">
[LISTING TWO]
<a name="010c_000d">

#include <os2.h>
#include "spread.h"
rcinclude spread.dlg

ICON    ID_WINDOW spread.ico
MENU ID_MENU
BEGIN
        MENUITEM "~Change values",       IDM_CHANGE
END
ASSOCTABLE      1
{
        "XX Company Spreadsheet", "*.SPR", spread.ico
}





<a name="010c_000e">
<a name="010c_000f">
[LISTING THREE]
<a name="010c_000f">

# Subclass of WPDataFile for Spreadsheet sample WPS application
#include <wpdataf.sc>
class:  WPSpread,
        external stem = wpspread, local,
        external prefix = wpspread_,
        classprefix = wpspreadM_,
        major version = 1,
        minor version = 2;
parent: WPDataFile;
passthru: C.ih;
   #define INCL_WIN
   #define INCL_DOS
   #define INCL_DEV
   #define INCL_GPI
   #define INCL_WPCLASS
   #define INCL_WPFOLDER
   #include <os2.h>
   #include <stdlib.h>
   #include <string.h>
endpassthru;   /* .ih */
data:
        HPOINTER        hicon,          class;  // class icon
methods:
        override        wpclsQueryTitle,                class;
        override        wpclsInitData,                  class;
        override        wpclsQueryIcon,                 class;
        override        wpclsQueryInstanceFilter,       class;
        override        wpclsQueryInstanceType,         class;
        override        wpPrintObject;





<a name="010c_0010">
<a name="010c_0011">
[LISTING FOUR]
<a name="010c_0011">

/* This file was generated by the SOM Compiler. FileName: wpspread.c.
 * Generated using: SOM Precompiler spc: 1.22 SOM Emitter emitc: 1.24 */
#define WPSpread_Class_Source
#include "wpspread.ih"

BOOL openfile ( PSZ szFname, LONG alValues[] );
BOOL printspread ( WPSpread *somSelf, PPRINTDEST pPrintDest );;

#undef SOM_CurrentClass
#define SOM_CurrentClass SOMMeta
SOM_Scope PSZ   SOMLINK wpspreadM_wpclsQueryTitle(M_WPSpread *somSelf)
{
    M_WPSpreadData *somThis = M_WPSpreadGetData(somSelf);
    M_WPSpreadMethodDebug("M_WPSpread","wpspreadM_wpclsQueryTitle");
    return _wpclsQueryInstanceType( somSelf );
}
SOM_Scope void   SOMLINK wpspreadM_wpclsInitData(M_WPSpread *somSelf)
{
    HMODULE hmod;               // module handle
    PSZ     psz;                // module file name
    APIRET  rc;                 // return from API
    M_WPSpreadData *somThis = M_WPSpreadGetData(somSelf);
    M_WPSpreadMethodDebug("M_WPSpread","wpspreadM_wpclsInitData");
  // initialize the parent classes first
    parent_wpclsInitData ( somSelf );
  // query our module name
    psz = _somLocateClassFile( SOMClassMgrObject, SOM_IdFromString( "WPChart" )
                          , WPSpread_MajorVersion , WPSpread_MinorVersion );
  // query our module handle
    if ( psz != NULL )
        rc = DosQueryModuleHandle ( psz, &hmod );
  // load the icon (same as pointer) and store in class data
    _hicon = WinLoadPointer ( HWND_DESKTOP, hmod, 1 );
}
SOM_Scope HPOINTER   SOMLINK wpspreadM_wpclsQueryIcon(M_WPSpread *somSelf)
{
    M_WPSpreadData *somThis = M_WPSpreadGetData(somSelf);
    M_WPSpreadMethodDebug("M_WPSpread","wpspreadM_wpclsQueryIcon");
    return _hicon;
}
SOM_Scope PSZ   SOMLINK wpspreadM_wpclsQueryInstanceFilter(M_WPSpread *somSelf)
{
    M_WPSpreadData *somThis = M_WPSpreadGetData(somSelf);
    M_WPSpreadMethodDebug("M_WPSpread","wpspreadM_wpclsQueryInstanceFilter");
    return ".SPR";
}
SOM_Scope PSZ   SOMLINK wpspreadM_wpclsQueryInstanceType(M_WPSpread *somSelf)
{
    M_WPSpreadData *somThis = M_WPSpreadGetData(somSelf);
    M_WPSpreadMethodDebug("M_WPSpread","wpspreadM_wpclsQueryInstanceType");
    return "XX Company Spreadsheet";
}
#undef SOM_CurrentClass
#define SOM_CurrentClass SOMInstance
SOM_Scope BOOL   SOMLINK wpspread_wpPrintObject(WPSpread *somSelf,
        PPRINTDEST pPrintDest,
        ULONG ulReserved)
{
    /* WPSpreadData *somThis = WPSpreadGetData(somSelf); */
    WPSpreadMethodDebug("WPSpread","wpspread_wpPrintObject");
    return printspread ( somSelf, pPrintDest );
}
//************* Following code NOT generated by SOM compiler
// printspread: prints the file this object represents
//      RETURNS:  TRUE if successful, FALSE if not
BOOL printspread ( WPSpread *somSelf, PPRINTDEST pPrintDest )
{
    HDC     hdcPrinter;             // printer DC
    HAB     hab;                    // anchor block handle
    HPS     hps;                    // micro PS
    SIZEL   sizel;                  // presentation page size
    LONG    alValues[2];            // spreadsheet values
    BOOL    fSuccess;               // return from API
    BOOL    lSuccess;               // return from API
    CHAR    szFname[255];           // file name
    ULONG   cb;                     // filename length
    POINTL  ptl;                    // drawing coordinates
    CHAR    sz[50];                 // temporary string
  // create a printer device context
    hab = WinQueryAnchorBlock ( HWND_DESKTOP );
    hdcPrinter = DevOpenDC ( hab , pPrintDest->lType
                , pPrintDest->pszToken , pPrintDest->lCount
                , pPrintDest->pdopData , NULLHANDLE );
  // create a micro PS associated with the printer DC
    sizel.cx = sizel.cy = 0;
    hps = GpiCreatePS ( hab, hdcPrinter, &sizel
            , GPIT_MICRO | PU_LOENGLISH | GPIA_ASSOC );
  // open the file associated with this object
    cb = 255;
    _wpQueryRealName ( somSelf, szFname, &cb, TRUE );
    fSuccess = openfile ( szFname , alValues );
  // start a printer job
    lSuccess = DevEscape ( hdcPrinter, DEVESC_STARTDOC
            , strlen ( _wpQueryTitle ( somSelf ) )
            , _wpQueryTitle ( somSelf ) , NULL, NULL );
  // print the values
    ptl.x = 100; ptl.y = 125;
    _itoa ( alValues[0], sz, 10 );
    lSuccess = GpiCharStringAt ( hps, &ptl, strlen ( sz ) , sz );
    ptl.y = 100;
    _itoa ( alValues[1], sz, 10 );
    lSuccess = GpiCharStringAt ( hps, &ptl, strlen ( sz ) , sz );
    ptl.y = 75;
    lSuccess = GpiMove ( hps, &ptl );
    ptl.x = 200;
    lSuccess = GpiLine ( hps, &ptl );
    ptl.x = 100; ptl.y = 50;
    _itoa ( alValues[0] + alValues[1], sz, 10 );
    lSuccess = GpiCharStringAt ( hps, &ptl, strlen ( sz ) , sz );
  // end a printer job
    lSuccess = DevEscape ( hdcPrinter, DEVESC_ENDDOC , 0 , NULL, NULL, NULL );
  // clean up
    DevCloseDC ( hdcPrinter );
    GpiDestroyPS ( hps );
    return TRUE;
}
// openfile: reads spreadsheet values from the specified file
//      RETURNS:  TRUE if successful, FALSE if not
BOOL openfile ( PSZ szFname, LONG alValues[] )
{
    HFILE   h;                  // file handle
    ULONG   ulAction;           // action taken by OPEN
    ULONG   ulActualRead;       // count read from file
    APIRET  rc;                 // return code
  // open the file
    rc = DosOpen ( szFname, &h, &ulAction, 0L
            , 0, OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW
            , OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE, NULL );
    if ( rc != 0 )
        return FALSE;
  // read the values
    rc = DosRead ( h, alValues, 8, &ulActualRead );
    if ( ( rc != 0 )  || ( ulActualRead != 8 ) )
    {
        DosClose ( h );
        return FALSE;
    }
  // close the file
    DosClose ( h );
    return TRUE;
}




<a name="010c_0012">
<a name="010c_0013">
[LISTING FIVE]
<a name="010c_0013">

// install.c -- installation program for spreadsheet app
//  compile and link with: icc /Ss /Ti install.c

#define INCL_WINWORKPLACE
#include <os2.h>
int main ( int argc, char *argv[] )
{
    HAB     hab;                    // anchor block
    HOBJECT hobjFolder;             // folder object
    HOBJECT hobjProg;               // program object
    BOOL    fSuccess;               // return from API
  // create an anchor block so we can retrieve errors
    hab = WinInitialize ( 0 );
  // create a folder on the desktop for our program object
    hobjFolder = WinCreateObject ( "WPFolder", "My Folder"
              , "OBJECTID=<MY_FOLDER>" , "<WP_DESKTOP>" , CO_REPLACEIFEXISTS );
    if ( hobjFolder == NULLHANDLE )
    {
        ULONG   ul;             // error code
        ul = WinGetLastError ( hab );
        printf ("Unable to create folder, error = %x\n", ERRORIDERROR ( ul ) );
    }
  // register our object class for our data files
    fSuccess = WinRegisterObjectClass ( "WPSpread" , "c:\\book\\wpsart\\wpspread.dll" );
    if ( fSuccess == FALSE )
    {
        ULONG   ul;             // error code
        ul = WinGetLastError ( hab );
        printf ("Unable to register class, error = %x\n", ERRORIDERROR ( ul ) );
    }
  // create a program object for our EXE file
    hobjProg = WinCreateObject ( "WPProgram", "Spreadsheet App"
                    , "EXENAME=c:\\book\\wpsart\\spread.exe;"
                      "ASSOCTYPE=XX Company Spreadsheet,,;"
                      "ASSOCFILTER=*.SPR,," , "<MY_FOLDER>"
                    , CO_REPLACEIFEXISTS );
    if ( hobjProg == NULLHANDLE )
    {
        ULONG   ul;             // error code
        ul = WinGetLastError ( hab );
        printf ("Unable to create program object, error = %x\n", ERRORIDERROR ( ul ) );
    }
    WinTerminate ( hab );
}












Copyright © 1993, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video