home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 11 Util / 11-Util.zip / PMF202.ZIP / PMFLOPPY.C < prev    next >
C/C++ Source or Header  |  1990-06-09  |  21KB  |  663 lines

  1. /*
  2.            pmfloppy.c
  3.            
  4.            main program
  5.  
  6.            PM based disk utility.  Use separate threads for disk actions to
  7.            prevent slowing up PM too much.  Use huge memory allocation for
  8.            single pass reads.
  9.  
  10.            Copyright G. Bryant, 1990
  11.  
  12.    
  13.   Change Log
  14.    8-Jun-90   Correct array bounds violation in FreeThread
  15.    9-Jun-90   Correct the EXPORTS statement in pmfloppy.def
  16.  
  17. */
  18.  
  19. #define INCL_PM
  20. #define INCL_BASE
  21. #define INCL_DOSERRORS
  22. #define INCL_DOSPROCESS
  23. #define INCL_DOSDEVIOCTL
  24. #define INCL_DOSSESMGR
  25. #define INCL_GPILCIDS
  26. #define INCL_WINSWITCHLIST
  27. #include <os2.h>
  28. #include <stdlib.h>
  29. #include <stdarg.h>
  30. #include <stdio.h>
  31. #include <string.h>
  32. #include <process.h>
  33. #include "pmfloppy.h"
  34. #include "DskIm.h"
  35.  
  36. // prototypes for current file
  37. MRESULT  EXPENTRY ClientWndProc(HWND, USHORT, MPARAM, MPARAM);
  38. MRESULT  Panic(PCH, USHORT);
  39. MRESULT  DisplayStatus(HWND, HPS, USHORT);
  40. MRESULT  DisplayDone(HWND , HPS, USHORT);
  41. VOID     FreeThread(USHORT);
  42. VOID     DisplayImageStat(HWND, HPS);
  43. VOID     PutBox(PSZ, PSZ);
  44.  
  45. // prototypes from copydlgs.c
  46. extern MRESULT EXPENTRY ReadDlgProc(HWND, USHORT, MPARAM, MPARAM);
  47. extern MRESULT EXPENTRY WriteDlgProc(HWND, USHORT, MPARAM, MPARAM);
  48. extern MRESULT EXPENTRY AboutDlgProc(HWND, USHORT, MPARAM, MPARAM);
  49. extern MRESULT EXPENTRY DeleteDlgProc(HWND, USHORT, MPARAM, MPARAM);
  50. extern MRESULT EXPENTRY LoadDlgProc(HWND, USHORT, MPARAM, MPARAM);
  51. extern MRESULT EXPENTRY SaveDlgProc(HWND, USHORT, MPARAM, MPARAM);
  52. extern MRESULT EXPENTRY CompDlgProc(HWND, USHORT, MPARAM, MPARAM);
  53.  
  54. // prototypes from dskcpy.c
  55. extern VOID FAR readsource(USHORT);
  56. extern VOID FAR writetarget(USHORT);
  57. extern VOID FAR LoadImage(USHORT);
  58. extern VOID FAR SaveImage(USHORT);
  59. extern VOID FAR CompImages(USHORT);
  60.  
  61. /* GLOBAL VARIABLES */
  62.  
  63. // PM vbls
  64. HAB    hab;
  65. HMQ    hmq;
  66. HWND   hWndFrame ;
  67. static CHAR szClientClass[]="PMFloppy";
  68. ULONG  ctldata = FCF_STANDARD & ~FCF_TASKLIST;
  69. HWND   hWndClient;
  70. HWND   hwndDeskTop;
  71. QMSG   qmsg;
  72. LONG   CharHeight;
  73. LONG   CharWidth;
  74.  
  75. // User response vbls
  76. DskImage ImageBuffers[NUM_IMAGES];
  77. USHORT   BufferNum;                  // only use in foreground thread
  78. USHORT   CompNum;                    // only use in foreground thread
  79.  
  80. // Thread variables
  81. ThreadContext tcThBufs[NUM_THREADS];
  82.  
  83.  
  84. int cdecl main(void)
  85. {
  86. SWCNTRL swctl;
  87. HSWITCH hswitch;
  88. PID pid;
  89.  
  90.   hab = WinInitialize(0);
  91.   hmq = WinCreateMsgQueue(hab, 0);
  92.   hwndDeskTop = WinQueryDesktopWindow(hab, NULL);
  93.  
  94.   if (!WinRegisterClass(hab, szClientClass, ClientWndProc, CS_SIZEREDRAW, 0))
  95.     return(0);
  96.  
  97.   hWndFrame = WinCreateStdWindow(HWND_DESKTOP,
  98.                                  WS_VISIBLE,
  99.                                  &ctldata,
  100.                                  szClientClass,
  101.                                  "Floppy Disk Utility",
  102.                                  0L,
  103.                                  0,
  104.                                  IDR_PMFLOPPY,
  105.                                  &hWndClient);
  106.  
  107.   if (hWndFrame != NULL)
  108.   {
  109.  
  110.     WinQueryWindowProcess(hWndFrame, &pid, NULL);
  111.  
  112.     swctl.hwnd = hWndFrame;
  113.     swctl.hwndIcon = NULL;
  114.     swctl.hprog = NULL;
  115.     swctl.idProcess = pid;
  116.     swctl.idSession = 0;
  117.     swctl.uchVisibility = SWL_VISIBLE;
  118.     swctl.fbJump = SWL_JUMPABLE;
  119.     swctl.szSwtitle[0] = '\0';
  120.  
  121.     hswitch = WinAddSwitchEntry(&swctl);
  122.  
  123.     while ( WinGetMsg(hab, &qmsg, NULL, 0, 0) )
  124.         WinDispatchMsg(hab, &qmsg);
  125.  
  126.     WinDestroyWindow(hWndFrame);
  127.   }
  128.  
  129.   WinDestroyMsgQueue(hmq);
  130.   WinTerminate(hab);
  131.  
  132.   DosExit(EXIT_PROCESS, 0);
  133. } /* main */
  134.  
  135.  
  136. // ClientWndProc - main window processing
  137. //
  138. //  note: UM messages are User-defined (in pmfloppy.h).
  139. //        all UM messages use mp2 to indicate the drive the message concerns,
  140. //        and mp1 is data specific to that message.  Then UM_xxxMSG is a 
  141. //        generic msg.  It is currently only used for DONE messages.  If I
  142. //        need more, set up some magic numbers and pass them through mp1. 
  143. //
  144. MRESULT EXPENTRY ClientWndProc(HWND hwnd
  145.                   , USHORT id
  146.                   , MPARAM mp1
  147.                   , MPARAM mp2) {
  148.  
  149. HPS         hPS;
  150. HWND        hMenu;
  151. CHAR        szTxtBuf[MAXMSGLEN];
  152. int far     *stkptr;
  153. FONTMETRICS fm;
  154. USHORT      curBuff;
  155. USHORT      gotSource;
  156. BOOL        DriveActive;
  157. USHORT      curTh;
  158. RECTL       rctPaint;
  159.  
  160.   switch (id) {
  161.     case WM_CREATE:
  162.       for (curBuff=0;curBuff < NUM_IMAGES;curBuff++)
  163.         ImageBuffers[curBuff].BufferName[0] = '\0';
  164.       for (curTh=0;curTh < NUM_THREADS;curTh++) {
  165.         tcThBufs[curTh].ThID = 0;
  166.         tcThBufs[curTh].ImageNumber = NUM_IMAGES+1;
  167.         tcThBufs[curTh].CompNumber = NUM_IMAGES+1;
  168.       }
  169.       hPS = WinGetPS(hwnd);
  170.       GpiQueryFontMetrics(hPS, (long)sizeof(fm), &fm);
  171.       CharHeight = fm.lMaxBaselineExt;
  172.       CharWidth = fm.lMaxCharInc;
  173.       WinReleasePS(hPS);
  174.       break;
  175.  
  176.     case WM_PAINT:
  177.       hPS = WinGetPS(hwnd);
  178.       GpiErase(hPS);
  179.       WinQueryUpdateRect(hwnd,&rctPaint);
  180.       DisplayImageStat(hwnd, hPS);
  181.       WinValidateRect(hwnd, &rctPaint, FALSE);
  182.       WinReleasePS(hPS);
  183.       break;
  184.  
  185.     case WM_COMMAND:
  186.       switch (COMMANDMSG(&id)->cmd) {
  187.         case IDM_READ:
  188.           if (WinDlgBox(HWND_DESKTOP,hWndFrame,ReadDlgProc,0,READ_DLG,NULL)) {
  189.             // send off thread to read
  190.             for (curTh=0;
  191.                  (curTh < NUM_THREADS) && (tcThBufs[curTh].ThID != 0);
  192.                  curTh++) ;
  193.  
  194.             if (curTh < NUM_THREADS) {
  195.               tcThBufs[curTh].ImageNumber = BufferNum;
  196.               DosAllocSeg(STACK_SIZE, &(tcThBufs[curTh].selStk), 0);
  197.               stkptr = (int far *)MAKEP(tcThBufs[curTh].selStk, STACK_SIZE);
  198.               *(--stkptr) = curTh;
  199.  
  200.               if (DosCreateThread(readsource,
  201.                                   &(tcThBufs[curTh].ThID),
  202.                                   (PBYTE)stkptr)) {
  203.                 PutBox("Drive Read","DosCreateThread failed.");
  204.  
  205.                       DosFreeSeg(tcThBufs[curTh].selStk);
  206.                       return FALSE;
  207.               } /* if can't create the thread. */
  208.             }
  209.             else {
  210.               PutBox("Drive Read","Out of program thread resources.");
  211.                     return FALSE;
  212.             } /* if can't create the thread. */
  213.           }
  214.           break;
  215.  
  216.         case IDM_WRITE:
  217.           if (WinDlgBox(HWND_DESKTOP,hWndFrame,WriteDlgProc,0,WRITE_DLG,NULL)) {
  218.             // send off thread to write
  219.             for (curTh=0;
  220.                  (curTh < NUM_THREADS) && (tcThBufs[curTh].ThID != 0);
  221.                  curTh++) ;
  222.  
  223.             if (curTh < NUM_THREADS) {
  224.               tcThBufs[curTh].ImageNumber = BufferNum;
  225.               DosAllocSeg(STACK_SIZE, &(tcThBufs[curTh].selStk), 0);
  226.               stkptr = (int far *)MAKEP(tcThBufs[curTh].selStk, STACK_SIZE);
  227.               *(--stkptr) = curTh;
  228.  
  229.               if (DosCreateThread(writetarget,
  230.                                   &(tcThBufs[curTh].ThID),
  231.                                   (PBYTE)stkptr)) {
  232.                 PutBox("Drive Write","DosCreateThread failed.");
  233.                       DosFreeSeg(tcThBufs[curTh].selStk);
  234.                       return FALSE;
  235.               } /* if can't create the thread. */
  236.             }
  237.             else {
  238.               PutBox("Drive Write","Out of program thread resources.");
  239.                     return FALSE;
  240.             } /* if can't create the thread. */
  241.           }
  242.           break;
  243.  
  244.         case IDM_ABOUT:
  245.           WinDlgBox(HWND_DESKTOP,hWndFrame,AboutDlgProc,0,ABOUT_DLG,NULL);
  246.           break;
  247.  
  248.         case IDM_DELETE:
  249.           WinDlgBox(HWND_DESKTOP,hWndFrame,DeleteDlgProc,0,DELETE_DLG,NULL);
  250.           hPS = WinGetPS(hwnd);
  251.           GpiErase(hPS);
  252.           DisplayImageStat(hwnd, hPS);
  253.           WinReleasePS(hPS);
  254.           break;
  255.  
  256.         case IDM_LOAD:
  257.           if (WinDlgBox(HWND_DESKTOP,hWndFrame,LoadDlgProc,0,LOAD_DLG,NULL)) {
  258.             // send off thread to load
  259.             for (curTh=0;
  260.                  (curTh < NUM_THREADS) && (tcThBufs[curTh].ThID != 0);
  261.                  curTh++) ;
  262.  
  263.             if (curTh < NUM_THREADS) {
  264.               tcThBufs[curTh].ImageNumber = BufferNum;
  265.               DosAllocSeg(STACK_SIZE, &(tcThBufs[curTh].selStk), 0);
  266.               stkptr = (int far *)MAKEP(tcThBufs[curTh].selStk, STACK_SIZE);
  267.               *(--stkptr) = curTh;
  268.  
  269.               if (DosCreateThread(LoadImage,
  270.                                   &(tcThBufs[curTh].ThID),
  271.                                   (PBYTE)stkptr)) {
  272.                 PutBox("Image Load","DosCreateThread failed.");
  273.                       DosFreeSeg(tcThBufs[curTh].selStk);
  274.                       return FALSE;
  275.               } /* if can't create the thread. */
  276.             }
  277.             else {
  278.               PutBox("Image Load","Out of program thread resources.");
  279.                     return FALSE;
  280.             } /* if can't create the thread. */
  281.           }
  282.           break;
  283.  
  284.         case IDM_SAVE:
  285.           if (WinDlgBox(HWND_DESKTOP,hWndFrame,SaveDlgProc,0,SAVE_DLG,NULL)) {
  286.             // send off thread to save
  287.             for (curTh=0;
  288.                  (curTh < NUM_THREADS) && (tcThBufs[curTh].ThID != 0);
  289.                  curTh++) ;
  290.  
  291.             if (curTh < NUM_THREADS) {
  292.               tcThBufs[curTh].ImageNumber = BufferNum;
  293.               DosAllocSeg(STACK_SIZE, &(tcThBufs[curTh].selStk), 0);
  294.               stkptr = (int far *)MAKEP(tcThBufs[curTh].selStk, STACK_SIZE);
  295.               *(--stkptr) = curTh;
  296.  
  297.               if (DosCreateThread(SaveImage,
  298.                                   &(tcThBufs[curTh].ThID),
  299.                                   (PBYTE)stkptr)) {
  300.                 PutBox("Image Save","DosCreateThread failed.");
  301.                       DosFreeSeg(tcThBufs[curTh].selStk);
  302.                       return FALSE;
  303.               } /* if can't create the thread. */
  304.             }
  305.             else {
  306.               PutBox("Image Save","Out of program thread resources.");
  307.                     return FALSE;
  308.             } /* if can't create the thread. */
  309.           }
  310.           break;
  311.  
  312.         case IDM_COMP:
  313.           if (WinDlgBox(HWND_DESKTOP,hWndFrame,CompDlgProc,0,COMP_DLG,NULL)) {
  314.             // send off thread to compare
  315.             for (curTh=0;
  316.                  (curTh < NUM_THREADS) && (tcThBufs[curTh].ThID != 0);
  317.                  curTh++) ;
  318.  
  319.             if (curTh < NUM_THREADS) {
  320.               tcThBufs[curTh].ImageNumber = BufferNum;
  321.               tcThBufs[curTh].CompNumber  = CompNum;
  322.               DosAllocSeg(STACK_SIZE, &(tcThBufs[curTh].selStk), 0);
  323.               stkptr = (int far *)MAKEP(tcThBufs[curTh].selStk, STACK_SIZE);
  324.               *(--stkptr) = curTh;
  325.  
  326.               if (DosCreateThread(CompImages,
  327.                                   &(tcThBufs[curTh].ThID),
  328.                                   (PBYTE)stkptr)) {
  329.                 PutBox("Image Compare","DosCreateThread failed.");
  330.                       DosFreeSeg(tcThBufs[curTh].selStk);
  331.                       return FALSE;
  332.               } /* if can't create the thread. */
  333.             }
  334.             else {
  335.               PutBox("Image Compare","Out of program thread resources.");
  336.                     return FALSE;
  337.             } /* if can't create the thread. */
  338.           }
  339.           break;
  340.  
  341.         case IDM_EXIT:
  342.           WinPostMsg(hWndFrame, WM_CLOSE, NULL, NULL);
  343.           break;
  344.       }
  345.       break;
  346.  
  347.     case WM_INITMENU:
  348.       //set the allowable menu choices.
  349.       hMenu = WinWindowFromID(hWndFrame,FID_MENU);
  350.  
  351.       // If we don't have anything to write, disable the write, save, and 
  352.       // delete menus
  353.       gotSource = 0;
  354.       for (curBuff=0;curBuff < NUM_IMAGES;curBuff++) {
  355.         if (ImageBuffers[curBuff].Percent == 100) gotSource++;
  356.       }
  357.  
  358.       WinSendMsg(hMenu,MM_SETITEMATTR,
  359.         MPFROM2SHORT(IDM_WRITE,TRUE),
  360.         MPFROM2SHORT(MIA_DISABLED,gotSource ? 0 : MIA_DISABLED));
  361.  
  362.       WinSendMsg(hMenu,MM_SETITEMATTR,
  363.         MPFROM2SHORT(IDM_SAVE,TRUE),
  364.         MPFROM2SHORT(MIA_DISABLED,gotSource ? 0 : MIA_DISABLED));
  365.  
  366.       WinSendMsg(hMenu,MM_SETITEMATTR,
  367.         MPFROM2SHORT(IDM_DELETE,TRUE),
  368.         MPFROM2SHORT(MIA_DISABLED,gotSource ? 0 : MIA_DISABLED));
  369.  
  370.       WinSendMsg(hMenu,MM_SETITEMATTR,
  371.         MPFROM2SHORT(IDM_COMP,TRUE),
  372.         MPFROM2SHORT(MIA_DISABLED,(gotSource > 1) ? 0 : MIA_DISABLED));
  373.       break;
  374.  
  375.  
  376.     case UM_STATUS:
  377.       curTh = SHORT1FROMMP(mp1);
  378.       hPS = WinGetPS(hwnd);
  379.       DisplayStatus(hwnd, hPS, curTh);
  380.       WinReleasePS(hPS);
  381.       break;
  382.  
  383.     case UM_DONE:
  384.       curTh = SHORT1FROMMP(mp1);
  385.       curBuff = tcThBufs[curTh].ImageNumber;
  386.       hPS = WinGetPS(hwnd);
  387.       WinAlarm(HWND_DESKTOP,WA_NOTE);
  388.       DisplayDone(hwnd, hPS,curBuff);
  389.       FreeThread(curTh);
  390.       WinReleasePS(hPS);
  391.       break;
  392.  
  393.     case UM_ERROR:
  394.       curTh = SHORT1FROMMP(mp1);
  395.       curBuff = tcThBufs[curTh].ImageNumber;
  396.       switch (ImageBuffers[curBuff].Busy) {
  397.         case BUSY_READ:
  398.           sprintf(szTxtBuf,"Read Error on drive %c", ImageBuffers[curBuff].DriveID[0]);
  399.           break;
  400.         case BUSY_WRITE:
  401.           sprintf(szTxtBuf,"Write Error on drive %c", ImageBuffers[curBuff].DriveID[0]);
  402.           break;
  403.         case BUSY_LOAD:
  404.           sprintf(szTxtBuf,"Load Error on drive %s", ImageBuffers[curBuff].FileName);
  405.           break;
  406.         case BUSY_SAVE:
  407.           sprintf(szTxtBuf,"Save Error on file %s", ImageBuffers[curBuff].FileName);
  408.           break;
  409.         }
  410.       Panic(szTxtBuf, tcThBufs[curTh].ErrorCode);
  411.       hPS = WinGetPS(hwnd);
  412.       DisplayStatus(hwnd, hPS, curBuff);
  413.       FreeThread(curTh);
  414.       WinReleasePS(hPS);
  415.       break;
  416.  
  417.     case UM_COMPOK:
  418.       curTh = SHORT1FROMMP(mp1);
  419.       curBuff = tcThBufs[curTh].ImageNumber;
  420.       WinAlarm(HWND_DESKTOP,WA_NOTE);
  421.       PutBox("Image Compare","Images are identical!");
  422.       hPS = WinGetPS(hwnd);
  423.       DisplayDone(hwnd, hPS,curBuff);
  424.       FreeThread(curTh);
  425.       WinReleasePS(hPS);
  426.       break;
  427.  
  428.     case UM_COMPERR:
  429.       curTh = SHORT1FROMMP(mp1);
  430.       curBuff = tcThBufs[curTh].ImageNumber;
  431.       WinAlarm(HWND_DESKTOP,WA_WARNING);
  432.       PutBox("Image Compare","Images are different");
  433.       hPS = WinGetPS(hwnd);
  434.       DisplayDone(hwnd, hPS,curBuff);
  435.       FreeThread(curTh);
  436.       WinReleasePS(hPS);
  437.       break;
  438.  
  439.     case WM_CLOSE:
  440.       DriveActive = FALSE;
  441.       for (curBuff=0;curBuff < NUM_IMAGES;curBuff++)
  442.         if (ImageBuffers[curBuff].Busy) DriveActive = TRUE;
  443.  
  444.       if (DriveActive) {
  445.             if (MBID_OK != WinMessageBox(HWND_DESKTOP,
  446.                                      hWndFrame,
  447.                                      "are you sure you want to quit?",
  448.                                      "A Drive is still running - ",
  449.                                      0,
  450.                                      MB_OKCANCEL | MB_QUERY))
  451.         break;
  452.       }
  453.       // else just fall through to default to get to the default proc
  454.     default:
  455.       return(WinDefWindowProc(hwnd, id, mp1, mp2));
  456.  
  457.   } /* switch id */
  458.  
  459.   return 0L;
  460.  
  461. } /* clientwndproc */
  462.  
  463.  
  464. //  Panic  --  Put up a message box with an error message.
  465. //
  466. //  Inputs:   pszCaption  --  Caption text for message box
  467. //
  468. //  Returns:  1L, for error signalling from window procedures.
  469. MRESULT Panic(PCH pszCaption,USHORT ErrorNum)
  470. {
  471. HWND hwndCurFocus;
  472. CHAR buf[1024];
  473. USHORT cbBuf;
  474.  
  475.   if (ErrorNum == DSKIM_ERROR_WRONG_FORMAT)
  476.     sprintf(buf, "Error - target disk has incorrect format");
  477.   else if (ErrorNum == DSKIM_ERROR_WRONG_FILE)
  478.     sprintf(buf, "Error - file is not in correct format");
  479.   else if (DosGetMessage(NULL, 0, buf, 1024, ErrorNum, "oso001.msg", &cbBuf))
  480.   {
  481.     sprintf(buf, "SYS%04d: error text unavailable", ErrorNum);
  482.     cbBuf = 31;
  483.   }
  484.   buf[cbBuf] = (char)0;
  485.  
  486.  
  487.   hwndCurFocus = WinQueryFocus(HWND_DESKTOP, FALSE);
  488.   WinAlarm(HWND_DESKTOP, WA_ERROR);
  489.  
  490.   PutBox(pszCaption,buf);
  491.  
  492.   return(1L);
  493. } // panic
  494.  
  495.  
  496. // DisplayStatus - Display the status for the drive in the PS
  497. //
  498. //  in:  usPct   Percent done
  499. //       Drive   drive letter
  500. //       hwnd    window handle to display on
  501. //       op      operation (eg read, write)
  502. //
  503. MRESULT DisplayStatus(HWND hwnd, HPS hPS, USHORT curTh) {
  504.  
  505. RECTL  rctStart;
  506. CHAR   szTxtBuf[MAXMSGLEN];
  507. CHAR   op[10];
  508. CHAR   Object[6];
  509. CHAR   Device[80];
  510. USHORT curBuff;
  511. USHORT compBuff;
  512.  
  513.   curBuff = tcThBufs[curTh].ImageNumber;
  514.   compBuff = tcThBufs[curTh].CompNumber;
  515.  
  516.   switch (ImageBuffers[curBuff].Busy) {
  517.     case BUSY_READ:
  518.       strcpy(op,"reading");
  519.       strcpy(Object,"Drive");
  520.       Device[0] = ImageBuffers[curBuff].DriveID[0];
  521.       Device[1] = '\0';
  522.       break;
  523.     case BUSY_WRITE:
  524.       strcpy(op,"writing");
  525.       strcpy(Object,"Drive");
  526.       Device[0] = ImageBuffers[curBuff].DriveID[0];
  527.       Device[1] = '\0';
  528.       break;
  529.     case BUSY_LOAD:
  530.       strcpy(op,"loading");
  531.       strcpy(Object,"file");
  532.       strcpy(Device,ImageBuffers[curBuff].FileName);
  533.       break;
  534.     case BUSY_SAVE:
  535.       strcpy(op,"saving");
  536.       strcpy(Object,"file");
  537.       strcpy(Device,ImageBuffers[curBuff].FileName);
  538.       break;
  539.     case BUSY_COMP:
  540.       strcpy(op,"comparing");
  541.       strcpy(Object,"image");
  542.       strcpy(Device,ImageBuffers[compBuff].BufferName);
  543.       break;
  544.   }
  545.  
  546.   rctStart.xLeft = 5L;
  547.   rctStart.xRight = 5L + (CharWidth * MAXMSGLEN);
  548.   rctStart.yBottom = (curBuff * CharHeight) + 5;
  549.   rctStart.yTop = rctStart.yBottom + CharHeight;
  550.   sprintf(szTxtBuf,
  551.           "Image %s %s %s %s (%d%% complete)",
  552.           ImageBuffers[curBuff].BufferName,
  553.           op,
  554.           Object,
  555.           Device,
  556.           ImageBuffers[curBuff].Percent);
  557.   WinDrawText(hPS, -1, szTxtBuf, &rctStart, CLR_NEUTRAL, CLR_BACKGROUND,
  558.               DT_LEFT | DT_ERASERECT);
  559.   WinValidateRect(hwnd, &rctStart, FALSE);
  560.   return 0;
  561. }
  562.  
  563.  
  564. // DisplayDone - Display an operation complete
  565. //
  566. //  in:  Drive   drive letter
  567. //       hwnd    window handle to display on
  568. //       op      operation (eg read, write)
  569. //
  570. MRESULT DisplayDone(HWND hwnd, HPS hPS, USHORT curBuff) {
  571.  
  572. RECTL   rctStart;
  573. CHAR    szTxtBuf[MAXMSGLEN];
  574.  
  575.   rctStart.xLeft = 5L;
  576.   rctStart.xRight = 5L + (CharWidth * MAXMSGLEN);
  577.   rctStart.yBottom = (curBuff * CharHeight) + 5;
  578.   rctStart.yTop = rctStart.yBottom + CharHeight;
  579.   sprintf(szTxtBuf, "Image %s is full", ImageBuffers[curBuff].BufferName);
  580.   WinDrawText(hPS, -1, szTxtBuf, &rctStart, CLR_NEUTRAL, CLR_BACKGROUND,
  581.               DT_LEFT | DT_ERASERECT);
  582.   WinValidateRect(hwnd, &rctStart, FALSE);
  583.   return 0;
  584. }
  585.  
  586. // FreeThread - clean up after a child thread
  587. //
  588. //   in: curTh  - Thread to clean up
  589. //
  590. // Note: there doesn't seem to be an easy way to determine when a thread is
  591. //       gone.  all the wait routines just work on processes.  So we cheat and
  592. //       check the priority until we get a failure.  Like all busy waits,
  593. //       this is incredibly idiotic, but until we get a "twait" function, I
  594. //       can't think of a better way.
  595. //
  596. VOID FreeThread(USHORT curTh) {
  597.  
  598. USHORT curBuff;
  599. USHORT compBuff;
  600. USHORT Prty;
  601.  
  602. // sit here until the thread is gone
  603.   while (DosGetPrty(PRTYS_THREAD,
  604.                     &Prty,
  605.                     tcThBufs[curTh].ThID) != ERROR_INVALID_THREADID) ;
  606.   curBuff  = tcThBufs[curTh].ImageNumber;
  607.   compBuff = tcThBufs[curTh].CompNumber;
  608.  
  609.   tcThBufs[curTh].ThID = 0;
  610.   tcThBufs[curTh].ImageNumber = NUM_IMAGES+1;
  611.   tcThBufs[curTh].CompNumber = NUM_IMAGES+1;
  612.   tcThBufs[curTh].ErrorCode = 0;
  613.   DosFreeSeg(tcThBufs[curTh].selStk);
  614.   ImageBuffers[curBuff].Busy = FALSE;
  615.   if (compBuff < NUM_IMAGES) ImageBuffers[compBuff].Busy = FALSE;
  616.  
  617. }
  618.  
  619.  
  620. VOID DisplayImageStat(HWND hwnd, HPS hPS) {
  621. RECTL  rctStart;
  622. RECTL  rctPaint;
  623. USHORT curBuff;
  624.  
  625.   WinQueryWindowRect(hwnd,&rctPaint);
  626.   if (ImageBuffers[0].BufferName[0] == '\0') {
  627.     rctStart.xLeft = 5L;
  628.     rctStart.xRight = 5L + (CharWidth * 16);
  629.     rctStart.yBottom = 5L;
  630.     rctStart.yTop = rctStart.yBottom + CharHeight;
  631.     WinDrawText(hPS,
  632.                 -1,
  633.                 "No images in use",
  634.                 &rctStart,
  635.                 CLR_NEUTRAL,
  636.                 CLR_BACKGROUND,
  637.                 DT_LEFT | DT_ERASERECT);
  638.   }
  639.   else {
  640.     for (curBuff=0;curBuff < NUM_IMAGES;curBuff++)
  641.       if (ImageBuffers[curBuff].BufferName[0] != '\0') {
  642.         if (ImageBuffers[curBuff].Busy) DisplayStatus(hwnd, hPS, curBuff);
  643.         else DisplayDone(hwnd,hPS, curBuff);
  644.  
  645.       }
  646.   }
  647. }
  648.  
  649.  
  650. //
  651. // Put the messages in an application modal message box
  652. //
  653. VOID PutBox(PSZ msg1, PSZ msg2) {
  654.  
  655.   WinMessageBox(HWND_DESKTOP,
  656.                 hWndFrame,
  657.                 msg2,
  658.                 msg1,
  659.                 0,
  660.                 MB_OK | MB_ICONEXCLAMATION);
  661.   return;
  662. } // PutBox
  663.