This article contains the following executables: OS2WPS.ARC
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.
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.
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.
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.
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.
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.
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]
// 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 );
}
[LISTING TWO]
#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
}
[LISTING THREE]
# 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;
[LISTING FOUR]
/* 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;
}
[LISTING FIVE]
// 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