Tips 'N Techniques - Jun 1994

From EDM2
Jump to: navigation, search

Check out these tips and techniques!

Workplace Shell Tips:

TIP: Set the Major and Minor Versions in a Workplace Shell class.

TECHNIQUE: The Object Interface Definition Language file (.CSC) is used to define the attributes of a SOM class. The class section of the .CSC file allows the Major and Minor Version to be defined. Workplace Shell classes must have the Major Version be equal to or greater than 1. The Minor Version must be equal to or greater than 2. This does not apply to non-Workplace Shell applications written in SOM.

TIP: Provide functions for Workplace Shell Objects that are not provided by the Workplace Shell.

TECHNIQUE: The Workplace Shell is implemented using Presentation Manager windows. If there is some function you need that isn't provided by the Workplace Shell, you can send PM messages to the Workplace Shell's window. This works well for any object that you know the class of window that the Workplace Shell uses. This works well for Folders. They use container windows and accept container window messages. Override the wpOpen() method in your subclass. The value returned from the parent's wpOpen() call is the handle to the window.

TIP: Attach data to PM windows for Workplace Shell objects.

TECHNIQUE: Never create static variables in your window procedures for Workplace Shell objects. This static data is shared among all instances of this object. Use the Window Words to keep separate instances of data you need for each window. Use WinSetWindowPtr() to attach the data to the window and WinQueryWindowPtr() to retrieve the data.

Device Driver Tips:

TIP: Install a small bootable partition to use when developing a device driver on a single machine.

TECHNIQUE: Because many boot cycles are required for testing, install a small bootable partition that contains a line pointing to the device driver under development. No changes to CONFIG.SYS files are needed between booting into the test environment or the development environment. And, there is no risk of making the primary environment unbootable.

TIP: When installing a timer handler in your driver, don't call SetTimer or TickCount until the end of the Init section, just before returning to the kernel. Timer handlers usually reference DS-relative data items, and these items become dereferenced when the driver fails and returns 0 code and data offsets. What happens is that the timer handler is still in the list of timer handlers to be called, and gets called after the driver fails but before the timer handler is removed, causing a GP fault. An alternative is to always call ResetTimer if an error occurs, prior to returning the 0 code and data segment offsets, or make the call to SetTimer/TickCount the last operation in the Init section.

TIP: To set a breakpoint in a particular place in your driver, just insert an INT 3 instruction in assembly language, or write a simple assembly language function that is callable from C to perform the INT 3. Insert the INT 3 or call to INT3() in your driver source, recompile, link and reboot. Make sure you have the kernel debugger installed, or your system will not boot.

TIP: When designing your drivers, put a little extra effort in the design process to try to "layer" your approach. Attempt to separate the software specific parts of your driver from the hardware specific portion, much like the ADD model. This layering approach will leave you with large portions of reusable code, and gets you thinking about reusability.

Programming Tip:

TIP: Test the DLL you're using, or any file, to determine if it's locked by renaming the file to itself!

TECHNIQUE: One technique follows:

rename cdll pmwin.dll
SYS0032: The process cannot access the file because it is being used by another process.

But you actually can use this trick in the following REXX command script:

TESTUSE.CMD

/* This program tests to see if files are in use       */
/* by trying to rename the files to themselves.        */
parse arg src rest
if src =  then do
   parse upper source . . myname .
   say 'Usage: 'myname' <file 1> Yfile 2" ...'
   say 'Reports whether files are in use by other processes'
   end
do while src <> 
   dst = filespec("name",src)
   '@ren 'src' 'dst' >NUL 2>
   if rc > 0 then
      say 'File 'src' is in use!'
   else
      say 'File 'src' is NOT in use!'
   parse var rest src rest
end

Or, you can use the following C program:

TESTUSE.C
/*
  This program will indicate whether each of the files specified is in use -- open by
  any other process (for any reason).
  To see whether a file is in use, we try to open it as read-only via the DosOpen API,
  specifying OPEN_SHARE_DENYREADWRITE as the open mode.  This tells OS/2 to give us
  EXCLUSIVE access to the file, so if any process has the file open for any reason, our
  open will fail.
*/
#include <stdio.h>              /* Bring in definitions for printf
*/
#define  INCL_DOSERRORS         /* Request OS/2 return code equates     */
#define  INCL_DOSFILEMGR        /* Request the DosOpen prototype        */
#include <os2.h>
int main(  int   argc,  char *argv[])
{
int    arg, rc;
ULONG  action;
char  *thisfile, *pszMessage;
HFILE  hFile;
if (1 == argc) {                    /* If no command-line parms specified */
printf("Usage: %s <file 1> [file 2 [file 3 ...]]\n", argv[0]);
printf("Reports whether files are in use by other processes.\n");
rc=1;
}
for (arg=1; arg<argc; arg++) {       /* Process all filespecs given       */
thisfile = argv[arg];                /* Point to the current filespec     */
rc = DosOpen(                        /* Try to open the file as R/O       */
thisfile,                            /* Here is the filespec              */
                                     /* Return the file handle here       */
                                     /* Return the action taken here      */
0L,                                  /* Filelength doesn't matter for R/O */
FILE_NORMAL,                         /* Attributes don't matter for R/O   */
 OPEN_ACTION_FAIL_IF_NEW             /* Succeed only if file exists       */
 | OPEN_ACTION_OPEN_IF_EXISTS,       /* Open it if it does exist          */
OPEN_SHARE_DENYREADWRITE,            /* Only open if no one else has it   */
0);                                  /* No extended attributes involved   */
switch(rc) {                         /* Process the DosOpen return code   */
case NO_ERROR:                 /* We opened the file!               */
pszMessage = "is NOT in use!";       /* ...so the file was not open       */
DosClose(hFile);                     /* Close it (since it's open now)    */
break;
case ERROR_FILE_NOT_FOUND:     /* File does not exist               */
case ERROR_PATH_NOT_FOUND:     /* ...or has an invalid path         */
case ERROR_OPEN_ FAILED:       /* ...or we got a general open error */
pszMessage = "could not be found.";  /* The file is inaccessible          */
break;
default:                       /* Any other error                   */
pszMessage = "is in use!";           /* The file is in use                */
break;   }                           /* End process DosOpen return code   */
printf("File '%s' %s\n", thisfile, pszMessage);
                                     /* Display the status of this file   */
}                                    /* Go on to the next file            */
return(rc);                          /* Bye!                              */
}
TESTUSE.MAK
testuse.exe: testuse.obj
  link386 /PMobj: testuse.c
  icc /c /Kb /O testuse.c

Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation