Joel details what you'll need to do to convert OS/2 1.x programs to work with OS/2 2.0's Workplace Shell, the user-interface replacement for Presentation Manager. The datacentric Workplace Shell lets users manipulate data directly, rather than first launching an executable and opening a data file.
April 01, 1993
URL:http://drdobbs.com/architecture-and-design/programming-for-the-os2-20-workplace-she/184408980
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
Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.