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