home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / sdktools / winnt / perfmon / alert.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-04-20  |  66.9 KB  |  2,486 lines

  1. //==========================================================================//
  2. //                                  Includes                                //
  3. //==========================================================================//
  4.  
  5. #include <stdio.h>
  6. #include "perfmon.h"
  7. #include "alert.h"         // External declarations for this file
  8.  
  9. #include "addline.h"       // for AddLine
  10. #include "fileutil.h"      // for FileRead
  11. #include "legend.h"
  12. #include "line.h"
  13. #include "pmemory.h"       // for MemoryXXX (mallloc-type) routines
  14. #include "owndraw.h"       // for OwnerDraw macros
  15. #include "perfdata.h"      // for UpdateLines
  16. #include "perfmops.h"      // for SystemAdd
  17. #include "playback.h"      // for PlaybackLines
  18. #include "status.h"        // for StatusUpdateAlerts   
  19. #include "system.h"        // for SystemGet
  20. #include "utils.h"
  21. #include "menuids.h"       // for IDM_VIEWALERT
  22. #include "fileopen.h"      // for FileGetName
  23. #include "counters.h"      // for CounterEntry
  24.  
  25. #include <lmcons.h>
  26. #include <lmmsg.h>
  27. #include <lmerr.h>
  28.  
  29.  
  30. #define MSG_ALERT_OCCURRED               ((DWORD)0x400007D0L)
  31. #define MSG_ALERT_SYSTEM                 ((DWORD)0x400007D1L)
  32.  
  33.  
  34. #define WM_SEND_NETWORK_ALERT (WM_USER + 101)
  35.  
  36. //==========================================================================//
  37. //                                  Typedefs                                //
  38. //==========================================================================//
  39.  
  40. typedef NET_API_STATUS
  41. (*pNetMessageBufferSend) (
  42.     IN  LPTSTR  servername,
  43.     IN  LPTSTR  msgname,
  44.     IN  LPTSTR  fromname,
  45.     IN  LPBYTE  buf,
  46.     IN  DWORD   buflen
  47.     );
  48.  
  49. typedef struct ALERTENTRYSTRUCT
  50.    {
  51.    SYSTEMTIME     SystemTime ;
  52.    PLINE          pLine ;
  53.    FLOAT          eValue ;
  54.    BOOL           bOver ;
  55.    FLOAT          eAlertValue ;
  56.    LPTSTR         lpszInstance ;
  57.    LPTSTR         lpszParent ;
  58.    INT            StringWidth ;
  59.    } ALERTENTRY ;
  60.  
  61. typedef ALERTENTRY *PALERTENTRY ;
  62.  
  63. static HBRUSH  hRedBrush ;
  64.  
  65. //====
  66. //====
  67.  
  68. //==========================================================================//
  69. //                                  Constants                               //
  70. //==========================================================================//
  71.  
  72.  
  73. #define szNumberFormat         TEXT("%12.3f")
  74. #define szMediumnNumberFormat  TEXT("%12.0f")
  75. #define szLargeNumberFormat    TEXT("%12.4e")
  76. #define eNumber                ((FLOAT) 99999999.999)
  77. #define eLargeNumber           ((FLOAT) 999999999999.0)
  78.  
  79. #define szNumberPrototype        TEXT("99999999.999")
  80.  
  81.  
  82. #define szConditionFormat        TEXT("  %c  ")
  83. #define szConditionPrototype     TEXT("  >  ")
  84.  
  85. #define szDatePrototype          TEXT("12/31/1999   ")
  86. #define szTimePrototype          TEXT("12:34:56.9 pm  ")
  87.  
  88. #define ALERTLOGMAXITEMS         1000
  89.  
  90. #define szAlertFormat TEXT("%s %s %s %c %s %s,  %s,  %s,  %s,  %s")
  91.  
  92.  
  93. //==========================================================================//
  94. //                                   Macros                                 //
  95. //==========================================================================//
  96.  
  97.  
  98. #define AlertItemTopMargin()     (yBorderHeight)
  99.  
  100.  
  101.  
  102. //==========================================================================//
  103. //                              Local Functions                             //
  104. //==========================================================================//
  105. INT ExportAlertLine (PLINE pLine, FLOAT eValue, SYSTEMTIME *pSystemTime, HANDLE hExportFile) ;
  106.  
  107. #if 0
  108. PALERT AlertData (HWND hWndAlert)
  109.    {
  110.    return (&Alert) ;
  111.    }
  112. #endif
  113.  
  114. void AlertFormatFloat (LPTSTR lpValueBuf, FLOAT eValue)
  115.    {
  116.    if (eValue <= eNumber)
  117.       {
  118.       TSPRINTF (lpValueBuf, szNumberFormat, eValue) ;
  119.       }
  120.    else if (eValue <= eLargeNumber)
  121.       {
  122.       TSPRINTF (lpValueBuf, szMediumnNumberFormat, eValue) ;
  123.       }
  124.    else
  125.       {
  126.       TSPRINTF (lpValueBuf, szLargeNumberFormat, eValue) ;
  127.       }
  128.    ConvertDecimalPoint (lpValueBuf) ;
  129.  
  130.    }  // AlertFormatFloat
  131.  
  132.  
  133. PALERT AllocateAlertData (HWND hWndAlert)
  134.    {
  135.    PALERT           pAlert ;
  136.  
  137.    pAlert = AlertData (hWndAlert) ;
  138.  
  139.    pAlert->hWnd = hWndAlert ;
  140.    pAlert->hAlertListBox = DialogControl (hWndAlert, IDD_ALERTLOG) ;
  141.    pAlert->iStatus = iPMStatusClosed ;
  142.    pAlert->bManualRefresh = FALSE ;
  143.    pAlert->bModified = FALSE ;
  144.  
  145.    pAlert->Visual.iColorIndex = 0 ;
  146.    pAlert->Visual.iWidthIndex = -1 ;
  147.    pAlert->Visual.iStyleIndex = -1 ;
  148.  
  149.    pAlert->iIntervalMSecs = iDefaultAlertIntervalSecs * 1000 ;
  150.    pAlert->pSystemFirst = NULL ;
  151.    pAlert->pLineFirst = NULL ;
  152.  
  153.    pAlert->MessageName[0] = TEXT('\0') ;
  154.  
  155.    pAlert->bLegendOn = TRUE ;
  156.  
  157.    return (pAlert) ;
  158.    }  // AllocateAlertData
  159.  
  160.  
  161. void FreeAlertData (PALERT pAlert)
  162.    {  // FreeAlertData
  163.    }  // FreeAlertData
  164.  
  165.  
  166. BOOL SetAlertTimer (PALERT pAlert)
  167.    {
  168.    if (pAlert->iStatus == iPMStatusCollecting)
  169.       KillTimer (pAlert->hWnd, AlertTimerID) ;
  170.  
  171.    pAlert->iStatus = iPMStatusCollecting ;
  172.    SetTimer (pAlert->hWnd, AlertTimerID, pAlert->iIntervalMSecs, NULL) ;
  173.    return (TRUE) ;
  174.    }
  175.  
  176.  
  177. BOOL ClearAlertTimer (PALERT pAlert)
  178.    {  // ClearAlertTimer
  179.    if (!PlayingBackLog())
  180.       {
  181.       KillTimer (pAlert->hWnd, AlertTimerID) ;
  182.       }
  183.    pAlert->iStatus = iPMStatusClosed ;
  184.  
  185.    return (TRUE) ;
  186.    }  // ClearAlertTimer
  187.  
  188.  
  189. BOOL AlertExec (LPTSTR lpszImageName, LPTSTR lpszCommandLine)
  190. /*
  191.    Effect:        WinExec is considered obsolete. We're supposed to use
  192.                   CreateProcess, which allows considerably more control.
  193.                   For perfmon, we only execute a program when an alert
  194.                   occurs, and we really don't know anything about the
  195.                   program, so can't really do much.  We just set some
  196.                   defaults and go.
  197.  
  198.    Called By:     SignalAlert only.
  199. */
  200.    {  // ExecProcess
  201.    STARTUPINFO    si ;
  202.    PROCESS_INFORMATION  pi ;
  203.    int            StringLen ;
  204.    TCHAR          TempBuffer [ 5 * FilePathLen ] ;
  205.    BOOL           RetCode;
  206.  
  207.    memset (&si, 0, sizeof (STARTUPINFO)) ;
  208.    si.cb = sizeof (STARTUPINFO) ;
  209.    si.dwFlags = STARTF_USESHOWWINDOW ;
  210.    si.wShowWindow = SW_SHOWNOACTIVATE ;
  211.    memset (&pi, 0, sizeof (PROCESS_INFORMATION)) ;
  212.  
  213.    lstrcpy (TempBuffer, lpszImageName) ;
  214.    StringLen = lstrlen (TempBuffer) ;
  215.    TempBuffer [StringLen] = TEXT(' ') ;
  216.    StringLen++ ;
  217.    lstrcpy (&TempBuffer[StringLen], lpszCommandLine) ;
  218.  
  219.    // DETACHED_PROCESS is needed to get rid of the ugly console window
  220.    // that SQL guys have been bitching..
  221.    RetCode = CreateProcess (NULL, TempBuffer,
  222.                           NULL, NULL, FALSE,
  223.                           (DWORD) NORMAL_PRIORITY_CLASS,
  224.                           NULL, NULL,
  225.                           &si, &pi) ;
  226.    if (RetCode)
  227.       {
  228.       if (pi.hProcess && pi.hProcess != INVALID_HANDLE_VALUE)
  229.          {
  230.          CloseHandle (pi.hProcess) ;
  231.          }
  232.       if (pi.hThread && pi.hThread != INVALID_HANDLE_VALUE)
  233.          {
  234.          CloseHandle (pi.hThread) ;
  235.          }
  236.       }
  237.  
  238.    return RetCode ;
  239.  
  240.    }  // ExecProcess
  241.  
  242.  
  243. BOOL SendNetworkMessage (LPTSTR pText, DWORD TextLen, LPTSTR pMessageName)
  244. {
  245.    NET_API_STATUS NetStatus;
  246.    HANDLE dllHandle ;
  247.    pNetMessageBufferSend SendFunction ;
  248.  
  249.    //
  250.    // Dynamically link to netapi32.dll.  If it's not there just return.  Return
  251.    // TRUE so we won't try to send this alert again.
  252.    //
  253.  
  254.    dllHandle = LoadLibrary(TEXT("NetApi32.Dll")) ;
  255.    if ( !dllHandle || dllHandle == INVALID_HANDLE_VALUE )
  256.       {
  257.       return(TRUE) ;
  258.       }
  259.  
  260.    //
  261.    // Get the address of the service's main entry point.  This
  262.    // entry point has a well-known name.
  263.    //
  264.  
  265.    SendFunction = (pNetMessageBufferSend)GetProcAddress(
  266.       dllHandle, "NetMessageBufferSend") ;
  267.  
  268.    if (SendFunction == NULL)
  269.       {
  270.       FreeLibrary (dllHandle) ;
  271.       return(TRUE) ;
  272.       }
  273.  
  274.    NetStatus = (*SendFunction) (NULL, pMessageName,
  275.       NULL, (LPBYTE)pText, TextLen * sizeof(TCHAR)) ;
  276.    if (NetStatus != NERR_Success)
  277.       {
  278.       FreeLibrary (dllHandle) ;
  279.       return (FALSE) ;
  280.       }
  281.  
  282.    FreeLibrary (dllHandle) ;
  283.  
  284.    return (TRUE) ;   
  285. }
  286.  
  287.  
  288. // this is the child thread used to send out network alert message.
  289. // This thread is created at init time and is destroyed at close time
  290. void NetAlertHandler (LPVOID *pDummy)
  291.    {
  292.    MSG      msg;
  293.  
  294.    while (GetMessage (&msg, NULL, 0, 0))
  295.       {
  296.       // we are only interested in this message
  297.       if (LOWORD(msg.message) == WM_SEND_NETWORK_ALERT)
  298.          {
  299.          SendNetworkMessage ((LPTSTR)(msg.wParam),
  300.             lstrlen ((LPTSTR)(msg.wParam)),
  301.             (LPTSTR)(msg.lParam)) ;
  302.          MemoryFree ((LPTSTR)(msg.wParam)) ;
  303.          }
  304.       }
  305.    }  // NetAlertHandler
  306.  
  307.  
  308. void SignalAlert (HWND hWnd,
  309.                   HWND hWndAlerts,
  310.                   PLINE pLine,
  311.                   FLOAT eValue,
  312.                   SYSTEMTIME *pSystemTime,
  313.                   LPTSTR pSystemName,
  314.                   DWORD dwSystemState)
  315. /*
  316.    Effect:        Perform any actions necessary when a given alert line's
  317.                   condition is true. In particular, add the alert to the
  318.                   alert log. Also, depending on the user's wishes, signal
  319.                   a network alert or beep or run a program.
  320.  
  321.                   If we are not viewing the alert screen, add one alert to 
  322.                   the unviewed list.
  323. */
  324.    {
  325.    int            iIndex ;
  326.    PALERT         pAlert ;
  327.    PALERTENTRY    pAlertEntry ;
  328.    TCHAR          szTime [20] ;
  329.    TCHAR          szDate [20] ;
  330.    TCHAR          szInstance [256] ;
  331.    TCHAR          szParent [256] ;
  332.    TCHAR          szText [256 * 4] ;
  333.    TCHAR          eValueBuf [40] ;
  334.    TCHAR          eAlertValueBuf [40] ;
  335.    TCHAR          eAlertValueBuf1 [42] ;
  336.    FLOAT          eLocalValue ;
  337.    DWORD          TextSize ;
  338.    LPTSTR         lpAlertMsg ;
  339.    LPTSTR         lpEventLog[7] ;
  340.    TCHAR          NullBuff [2] ;
  341.  
  342.    NullBuff [0] = NullBuff [1] = TEXT('\0') ;
  343.  
  344.    pAlert = AlertData (hWnd) ;
  345.  
  346.    pAlertEntry = MemoryAllocate (sizeof (ALERTENTRY)) ;
  347.    if (!pAlertEntry)
  348.       {
  349.       return ;
  350.       }
  351.    pAlertEntry->SystemTime = *pSystemTime ;
  352.    pAlertEntry->pLine= pLine ;
  353.    pAlertEntry->eValue = eValue ;
  354.    if (pLine)
  355.       {
  356.       pAlertEntry->bOver = pLine->bAlertOver ;
  357.       pAlertEntry->eAlertValue = pLine->eAlertValue ;
  358.       }
  359.  
  360.  
  361.    //=============================//
  362.    // Determine Instance, Parent  //
  363.    //=============================//
  364.  
  365.    // It's possible that there will be no instance, therefore
  366.    // the lnInstanceName would be NULL.
  367.  
  368.    if (pLine && pLine->lnObject.NumInstances > 0)
  369.       {
  370.       // Test for the parent object instance name title index.
  371.       // If there is one, it implies that there will be a valid
  372.       // Parent Object Name and a valid Parent Object Instance Name.
  373.  
  374.       // If the Parent Object title index is 0 then
  375.       // just display the instance name.
  376.  
  377.       lstrcpy (szInstance, pLine->lnInstanceName) ;
  378.       if (pLine->lnInstanceDef.ParentObjectTitleIndex && pLine->lnPINName)
  379.          {
  380.          // Get the Parent Object Name.
  381.          lstrcpy (szParent, pLine->lnPINName) ;
  382.          }
  383.       else
  384.          {
  385.          szParent[0] = TEXT(' ') ;
  386.          szParent[1] = TEXT('\0') ;
  387.          }
  388.       }
  389.    else
  390.       {
  391.       if (pLine)
  392.          {
  393.          szInstance[0] = TEXT(' ') ;
  394.          szInstance[1] = TEXT('\0') ;
  395.          szParent[0] = TEXT(' ') ;
  396.          szParent[1] = TEXT('\0') ;
  397.          }
  398.       else
  399.          {
  400.          // this is a system down/reconnect alert
  401.          StringLoad (
  402.             dwSystemState == SYSTEM_DOWN ?
  403.                IDS_SYSTEM_DOWN : IDS_SYSTEM_UP,
  404.             szInstance) ;
  405.          lstrcpy (szParent, pSystemName) ;
  406.          }
  407.       }
  408.  
  409.  
  410.    pAlertEntry->lpszInstance = StringAllocate (szInstance) ;
  411.    pAlertEntry->lpszParent = StringAllocate (szParent) ;
  412.    
  413.    //=============================//
  414.    // Add alert to Alert Log      //
  415.    //=============================//
  416.  
  417.    if (LBNumItems (hWndAlerts) >= ALERTLOGMAXITEMS)
  418.       LBDelete (hWndAlerts, 0) ;
  419.  
  420.    iIndex = LBAdd (hWndAlerts, (LPARAM) pAlertEntry) ;
  421.    LBSetSelection (hWndAlerts, iIndex) ;
  422.  
  423.    // no need to check other things if we
  424.    // are playing back log
  425.    if (PlayingBackLog())
  426.       {
  427.       return ;
  428.       }
  429.  
  430.    //=============================//
  431.    // Update Status Line          //
  432.    //=============================//
  433.  
  434.    if (iPerfmonView != IDM_VIEWALERT)
  435.       {
  436.       if (pAlert->bSwitchToAlert)
  437.          {
  438.          SendMessage (hWndMain, WM_COMMAND, (LONG)IDM_VIEWALERT, 0L) ;
  439.          }
  440.       else
  441.          {
  442.          // if iUnviewedAlerts is over 100, we will display "++"
  443.          // so, no need to keep updating the icon...
  444.          if (iUnviewedAlerts < 100)
  445.             {
  446.             iUnviewedAlerts ++ ;
  447.             if (pLine)
  448.                {
  449.                crLastUnviewedAlert = pLine->Visual.crColor ;
  450.                }
  451.             StatusUpdateIcons (hWndStatus) ;
  452.             }
  453.          }
  454.       }
  455.  
  456.    //===================================//
  457.    //  Check if we need to do anything  //
  458.    //===================================//
  459.    szText[0] = TEXT('\0') ;
  460.    if ((pAlert->bNetworkAlert && pAlert->MessageName[0])
  461.                ||
  462.        (pLine) &&
  463.        (pLine->lpszAlertProgram &&
  464.        (pLine->bEveryTime || !pLine->bAlerted))
  465.                ||
  466.        (pAlert->bEventLog))
  467.       {
  468.       // format the alert line to be exported
  469.       SystemTimeDateString (pSystemTime, szDate) ;
  470.       SystemTimeTimeString (pSystemTime, szTime, TRUE) ;
  471.  
  472.       if (pLine)
  473.          {
  474.          AlertFormatFloat (eValueBuf, pAlertEntry->eValue) ;
  475.  
  476.          eLocalValue = pAlertEntry->eAlertValue ;
  477.          if (eLocalValue < (FLOAT) 0.0)
  478.             {
  479.             eLocalValue = -eLocalValue ;
  480.             }
  481.          AlertFormatFloat (eAlertValueBuf, pAlertEntry->eAlertValue) ;
  482.  
  483.          TSPRINTF (szText,
  484.             szAlertFormat,
  485.             szDate,
  486.             szTime,
  487.             eValueBuf,
  488.             (pLine->bAlertOver ? TEXT('>') : TEXT('<')),
  489.             eAlertValueBuf,
  490.             pLine->lnCounterName,
  491.             szInstance,
  492.             szParent,
  493.             pLine->lnObjectName,
  494.             pLine->lnSystemName) ;
  495.          }
  496.       else
  497.          {
  498.          lstrcpy (eValueBuf, DashLine) ;
  499.          lstrcpy (eAlertValueBuf, eValueBuf) ;
  500.          TSPRINTF (szText,
  501.             szAlertFormat,
  502.             szDate,
  503.             szTime,
  504.             eValueBuf,
  505.             TEXT(' '),
  506.             eAlertValueBuf,
  507.             szInstance,             // system up/down message
  508.             NullBuff,
  509.             NullBuff,
  510.             NullBuff,
  511.             szParent) ;
  512.          }
  513.  
  514.       TextSize = sizeof(TCHAR) * (lstrlen(szText)+1) ;
  515.       }
  516.  
  517.    if (szText[0] == TEXT('\0'))
  518.       {
  519.       // nothing to do
  520.       return ;
  521.       }
  522.  
  523.    //=============================//
  524.    // Network Alert?              //
  525.    //=============================//
  526.  
  527.    SetHourglassCursor() ;
  528.  
  529.    if (pAlert->bNetworkAlert && pAlert->MessageName[0])
  530.       {  // if
  531.  
  532.       if (pAlert->dwNetAlertThreadID)
  533.          {
  534.          // use thread to send the network alert.
  535.          // the memory will be released by the child thread when done
  536.          lpAlertMsg =
  537.             (LPTSTR) MemoryAllocate (TextSize) ;
  538.          if (lpAlertMsg)
  539.             {
  540.             lstrcpy (lpAlertMsg, szText) ;
  541.             PostThreadMessage (pAlert->dwNetAlertThreadID,
  542.                WM_SEND_NETWORK_ALERT,
  543.                (WPARAM)lpAlertMsg,
  544.                (LPARAM)pAlert->MessageName) ;
  545.             }
  546.          }
  547.       else
  548.          {
  549.          // no thread available, use the slow way to send the network alert
  550.          SendNetworkMessage (szText,
  551.             (DWORD) lstrlen(szText),
  552.             pAlert->MessageName) ;
  553.          }
  554.       }
  555.  
  556.  
  557.    //=============================//
  558.    // Run Program?                //
  559.    //=============================//
  560.  
  561.    if (pLine &&
  562.        pLine->lpszAlertProgram &&
  563.        (pLine->bEveryTime || !pLine->bAlerted))
  564.       {
  565.       AlertExec (pLine->lpszAlertProgram, szText) ;
  566.       pLine->bAlerted = TRUE ;
  567.       }
  568.  
  569.    //===================================//
  570.    // Log event to Application Log?     //
  571.    //===================================//
  572.    if (pAlert->bEventLog)
  573.       {
  574.       if (hEventLog)
  575.          {
  576.          if (pLine)
  577.             {
  578.             lpEventLog[0] = pLine->lnSystemName ;
  579.             lpEventLog[1] = pLine->lnObjectName ;
  580.             lpEventLog[2] = pLine->lnCounterName ;
  581.             lpEventLog[3] = szInstance ;
  582.             lpEventLog[4] = szParent ;
  583.             lpEventLog[5] = eValueBuf ;
  584.             eAlertValueBuf1[0] = pLine->bAlertOver ? TEXT('>') : TEXT('<') ;
  585.             eAlertValueBuf1[1] = TEXT(' ') ;
  586.             lstrcpy (&eAlertValueBuf1[2], eAlertValueBuf) ;
  587.             lpEventLog[6] = eAlertValueBuf1 ;
  588.             }
  589.          else
  590.             {
  591.             lpEventLog[0] = szParent ;
  592.             lpEventLog[1] = szInstance ;
  593.             }
  594.  
  595.          ReportEvent (hEventLog,
  596.             (WORD)EVENTLOG_INFORMATION_TYPE,
  597.             (WORD)0,
  598.             (DWORD) (pLine ? MSG_ALERT_OCCURRED : MSG_ALERT_SYSTEM),
  599.             (PSID)NULL,
  600.             (WORD) (pLine ? 7 : 2),
  601.             (DWORD)TextSize,
  602.             (LPCTSTR *)lpEventLog,
  603.             (LPVOID)(szText)) ;
  604.          }
  605.       }
  606.     
  607.    SetArrowCursor() ;
  608.  
  609.    }  // SignalAlert
  610.  
  611.  
  612.  
  613. BOOL AlertCondition (PLINE pLine, FLOAT eValue)
  614. /*
  615.    Effect:        Return whether the alert test passed for line pLine,
  616.                   with current data value eValue.
  617.  
  618.    Internals:     Don't *ever* say (bFoo == bBar), as non-FALSE values
  619.                   could be represented by any nonzero number.  Use
  620.                   BoolEqual or equivalent.
  621. */
  622.    {  // AlertCondition
  623.    BOOL           bOver ;
  624.  
  625.    bOver = eValue > pLine->eAlertValue ;
  626.  
  627.    return (BoolEqual (bOver, pLine->bAlertOver)) ;
  628.    }  // AlertCondition
  629.  
  630.  
  631. INT static CheckAlerts (HWND hWnd,
  632.                         HWND hWndAlerts,
  633.                         SYSTEMTIME *pSystemTime,
  634.                         PLINE pLineFirst,
  635.                         HANDLE hExportFile,
  636.                         PPERFSYSTEM pSystemFirst)
  637.    {  // CheckAlerts
  638.    FLOAT          eValue ;
  639.    PLINE          pLine ;
  640.    BOOL           bAnyAlerts ;
  641.    INT            ErrCode = 0 ;
  642.    PPERFSYSTEM    pSystem ;
  643.  
  644.    bAnyAlerts = FALSE ;
  645.    if (!PlayingBackLog())
  646.       {
  647.       LBSetRedraw (hWndAlerts, FALSE) ;
  648.  
  649.       // check for system up/down
  650.       for (pSystem = pSystemFirst ;
  651.          pSystem ;
  652.          pSystem = pSystem->pSystemNext)
  653.          {
  654.          if (pSystem->dwSystemState == SYSTEM_DOWN)
  655.             {
  656.             pSystem->dwSystemState = SYSTEM_DOWN_RPT ;
  657.             SignalAlert (hWnd,
  658.                hWndAlerts,
  659.                NULL,
  660.                (FLOAT) 0.0,
  661.                pSystemTime,
  662.                pSystem->sysName,
  663.                SYSTEM_DOWN) ;
  664.             }
  665.          else if (pSystem->dwSystemState == SYSTEM_RECONNECT)
  666.             {
  667.             pSystem->dwSystemState = SYSTEM_RECONNECT_RPT ;
  668.             SignalAlert (hWnd,
  669.                hWndAlerts,
  670.                NULL,
  671.                (FLOAT) 0.0,
  672.                pSystemTime,
  673.                pSystem->sysName,
  674.                SYSTEM_RECONNECT) ;
  675.             }
  676.          }
  677.       }  // !PlayingBackLog()
  678.  
  679.  
  680.    for (pLine = pLineFirst ;
  681.         pLine ;
  682.         pLine = pLine->pLineNext)
  683.       {
  684.       if (pLine->bFirstTime)
  685.          {
  686.          // skip until we have collect enough samples for the first data
  687.          continue ;
  688.          }
  689.  
  690.       // Get the new value for this line.
  691.       eValue = CounterEntry (pLine) ;
  692.       if (AlertCondition (pLine, eValue))
  693.          {
  694.          bAnyAlerts = TRUE ;
  695.  
  696.          // the case that hExportFile is !NULL is when playingback log and that the
  697.          // listbox is overflowed with alert.  In this case, we have to
  698.          // walk the log file again to re-generate all the alerts.
  699.          if (hExportFile)
  700.             {
  701.             ErrCode = ExportAlertLine (pLine, eValue, pSystemTime, hExportFile) ;
  702.             if (ErrCode)
  703.                {
  704.                break ;
  705.                }
  706.             }
  707.          else
  708.             {
  709.             SignalAlert (hWnd,
  710.                hWndAlerts,
  711.                pLine,
  712.                eValue,
  713.                pSystemTime,
  714.                NULL,
  715.                0) ;
  716.             }
  717.          }
  718.       }  // for
  719.  
  720.    if (!PlayingBackLog())
  721.       {
  722.       LBSetRedraw (hWndAlerts, TRUE) ;
  723.       }
  724.  
  725.    return (ErrCode) ;
  726.    }  // CheckAlerts
  727.  
  728.  
  729.  
  730.  
  731. void DrawAlertEntry (HWND hWnd,
  732.                      PALERT pAlert,
  733.                      PALERTENTRY pAlertEntry,
  734.                      LPDRAWITEMSTRUCT lpDI,
  735.                      HDC hDC)
  736.    {  // DrawAlertEntry
  737.    PLINE          pLine ;
  738.    RECT           rectUpdate ;
  739.  
  740.    TCHAR          szTime [20] ;
  741.    TCHAR          szDate [20] ;
  742.    TCHAR          szText [256] ;
  743.  
  744.    HBRUSH         hBrushPrevious ;
  745.    FLOAT          eLocalValue ;
  746.    COLORREF       preBkColor ;
  747.    COLORREF       preTextColor ;
  748.  
  749.    pLine = pAlertEntry->pLine ;
  750.  
  751.    SystemTimeDateString (&(pAlertEntry->SystemTime), szDate) ;
  752.    SystemTimeTimeString (&(pAlertEntry->SystemTime), szTime, TRUE) ;
  753.  
  754.    if (DISelected (lpDI)) 
  755.       {  // if
  756.       preTextColor = SetTextColor (hDC, GetSysColor (COLOR_HIGHLIGHTTEXT)) ;
  757.       preBkColor = SetBkColor (hDC, GetSysColor (COLOR_HIGHLIGHT)) ;
  758.       }  // if
  759.  
  760.    //=============================//
  761.    // Draw Color Dot              //
  762.    //=============================//
  763.  
  764.    rectUpdate.left = 0 ;
  765.    rectUpdate.top = lpDI->rcItem.top ;
  766.    rectUpdate.right = pAlert->xColorWidth ;
  767.    rectUpdate.bottom = lpDI->rcItem.bottom ;
  768.  
  769.    ExtTextOut (hDC, rectUpdate.left, rectUpdate.top,
  770.                ETO_CLIPPED | ETO_OPAQUE,
  771.                &rectUpdate,
  772.                NULL, 0,
  773.                NULL) ;
  774.  
  775.    if (pLine)
  776.       {
  777.       hBrushPrevious = SelectBrush (hDC, pLine->hBrush) ;
  778.       }
  779.    else
  780.       {
  781.       if (hRedBrush == NULL)
  782.          {
  783.          hRedBrush = CreateSolidBrush (RGB (0xff, 0x00, 0x00)) ;
  784.          }
  785.       hBrushPrevious = SelectBrush (hDC, hRedBrush) ;
  786.       }
  787.  
  788.    Ellipse (hDC,
  789.             rectUpdate.left + 2,
  790.             rectUpdate.top + 2,
  791.             rectUpdate.right - 2,
  792.             rectUpdate.bottom - 2) ;
  793.  
  794.    SelectBrush (hDC, hBrushPrevious) ;
  795.  
  796.    //=============================//
  797.    // Draw Date                   //
  798.    //=============================//
  799.  
  800.    rectUpdate.left = rectUpdate.right ;
  801.    rectUpdate.right = rectUpdate.left + pAlert->xDateWidth ;
  802.  
  803.    ExtTextOut (hDC, rectUpdate.left, rectUpdate.top,
  804.                ETO_CLIPPED | ETO_OPAQUE,
  805.                &rectUpdate,
  806.                szDate, lstrlen (szDate),
  807.                NULL) ;
  808.  
  809.    //=============================//
  810.    // Draw Time                   //
  811.    //=============================//
  812.  
  813.    rectUpdate.left = rectUpdate.right ;
  814.    rectUpdate.right = rectUpdate.left + pAlert->xTimeWidth ;
  815.  
  816.    ExtTextOut (hDC, rectUpdate.left, rectUpdate.top,
  817.                ETO_CLIPPED | ETO_OPAQUE,
  818.                &rectUpdate,
  819.                szTime, lstrlen (szTime),
  820.                NULL) ;
  821.  
  822.    //=============================//
  823.    // Draw Alert Value            //
  824.    //=============================//
  825.  
  826.    SetTextAlign (hDC, TA_RIGHT) ;
  827.  
  828.    rectUpdate.left = rectUpdate.right ;
  829.    rectUpdate.right = rectUpdate.left + pAlert->xNumberWidth ;
  830.  
  831.    if (pLine)
  832.       {
  833.       AlertFormatFloat (szText, pAlertEntry->eValue) ;
  834.       }
  835.    else
  836.       {
  837.       lstrcpy (szText, DashLine) ;
  838.       }
  839.  
  840.    ExtTextOut (hDC, rectUpdate.right, rectUpdate.top,
  841.                ETO_CLIPPED | ETO_OPAQUE,
  842.                &rectUpdate,
  843.                szText, lstrlen (szText),
  844.                NULL) ;
  845.  
  846.    //=============================//
  847.    // Draw Alert Condition        //
  848.    //=============================//
  849.  
  850.    rectUpdate.left = rectUpdate.right ;
  851.    rectUpdate.right = rectUpdate.left + pAlert->xConditionWidth ;
  852.  
  853.    TSPRINTF (szText, szConditionFormat,
  854.       pLine ?
  855.          (pAlertEntry->bOver ? TEXT('>') : TEXT('<')) :
  856.          TEXT(' ')
  857.       ) ;
  858.  
  859.    ExtTextOut (hDC, rectUpdate.right, rectUpdate.top,
  860.                ETO_CLIPPED | ETO_OPAQUE,
  861.                &rectUpdate,
  862.                szText, lstrlen (szText),
  863.                NULL) ;
  864.  
  865.    //=============================//
  866.    // Draw Trigger Value          //
  867.    //=============================//
  868.  
  869.    rectUpdate.left = rectUpdate.right ;
  870.    rectUpdate.right = rectUpdate.left + pAlert->xNumberWidth ;
  871.  
  872.    if (pLine)
  873.       {
  874.       eLocalValue = pAlertEntry->eAlertValue ;
  875.       if (eLocalValue < (FLOAT) 0.0)
  876.          {
  877.          eLocalValue = -eLocalValue ;
  878.          }
  879.       AlertFormatFloat (szText, pAlertEntry->eAlertValue) ;
  880.       }
  881.    else
  882.       {
  883.       lstrcpy (szText, DashLine) ;
  884.       }
  885.  
  886.    ExtTextOut (hDC, rectUpdate.right, rectUpdate.top,
  887.                ETO_CLIPPED | ETO_OPAQUE,
  888.                &rectUpdate,
  889.                szText, lstrlen (szText),
  890.                NULL) ;
  891.  
  892.    //=============================//
  893.    // Draw Rest                   //
  894.    //=============================//
  895.  
  896.    SetTextAlign (hDC, TA_LEFT) ;
  897.  
  898.    rectUpdate.left = rectUpdate.right ;
  899.    rectUpdate.right = 10000 ;
  900.  
  901.    if (pLine)
  902.       {
  903.       TSPRINTF (szText,
  904.          TEXT("    %s,  %s,  %s,  %s,  %s"),
  905.          pLine->lnCounterName,
  906.          pAlertEntry->lpszInstance,
  907.          pAlertEntry->lpszParent,
  908.          pLine->lnObjectName,
  909.          pLine->lnSystemName) ;
  910.       }
  911.    else
  912.       {
  913.       TSPRINTF (szText,
  914.          TEXT("    %s, , , ,  %s"),
  915.          pAlertEntry->lpszInstance,
  916.          pAlertEntry->lpszParent) ;
  917.       }
  918.  
  919.    ExtTextOut (hDC, rectUpdate.left, rectUpdate.top,
  920.                ETO_OPAQUE,
  921.                &rectUpdate,
  922.                szText, lstrlen (szText),
  923.                NULL) ;
  924.  
  925.    // check if we need to bring-up or resize the horiz scrollbar
  926.    if (pAlertEntry->StringWidth == 0)
  927.       {
  928.       pAlertEntry->StringWidth = TextWidth (hDC, szText) + xScrollWidth +
  929.                   rectUpdate.left ;
  930.       }
  931.  
  932.    if (pAlertEntry->StringWidth > pAlert->xTextExtent)
  933.       {
  934.       pAlert->xTextExtent = pAlertEntry->StringWidth ;
  935.       LBSetHorzExtent (pAlert->hAlertListBox, pAlertEntry->StringWidth) ;
  936.       }
  937.  
  938.    if (DISelected (lpDI))
  939.       {  // if
  940.       preTextColor = SetTextColor (hDC, preTextColor) ;
  941.       preBkColor = SetBkColor (hDC, preBkColor) ;
  942.       }  // if
  943.    }  // DrawAlertEntry
  944.  
  945.  
  946.  
  947. //==========================================================================//
  948. //                              Message Handlers                            //
  949. //==========================================================================//
  950.  
  951.  
  952. void static OnDrawItem (HWND hWnd,
  953.                         LPDRAWITEMSTRUCT lpDI)
  954.    {  // OnDrawItem
  955.    HFONT          hFontPrevious ;
  956.    HDC            hDC ;
  957.    PALERT         pAlert ;
  958.    PALERTENTRY    pAlertEntry ;
  959. //   PLINESTRUCT    pLine ;
  960.    int            iLBIndex ;
  961.  
  962.    hDC = lpDI->hDC ;
  963.    iLBIndex = DIIndex (lpDI) ;
  964.  
  965.    pAlert = AlertData (hWnd) ;
  966.  
  967.    if (iLBIndex == -1)
  968.       {
  969.       pAlertEntry = NULL ;
  970.       }
  971.    else
  972.       {
  973.       pAlertEntry = (PALERTENTRY) LBData (pAlert->hAlertListBox, iLBIndex) ;
  974.       if (pAlertEntry == (PALERTENTRY) LB_ERR)
  975.          {
  976.          pAlertEntry = NULL ;
  977.          }
  978.       }
  979.  
  980.    //=============================//
  981.    // Draw Legend Item            //
  982.    //=============================//
  983.  
  984.    if (pAlertEntry)
  985.       {
  986.       hFontPrevious = SelectFont (hDC, pAlert->hFontItems) ;
  987.       DrawAlertEntry (hWnd, pAlert, pAlertEntry, lpDI, hDC) ;
  988.       SelectFont (hDC, hFontPrevious) ;
  989.       }
  990.  
  991.    //=============================//
  992.    // Draw Focus                  //
  993.    //=============================//
  994.  
  995.    if (DIFocus (lpDI))
  996.       DrawFocusRect (hDC, &(lpDI->rcItem)) ;
  997.  
  998.    }  // OnDrawItem
  999.  
  1000.  
  1001.  
  1002. int static OnCtlColor (HWND hDlg,
  1003.                        HDC hDC)
  1004.    {
  1005.    SetTextColor (hDC, crBlack) ;
  1006.    SetBkColor (hDC, crLightGray) ;
  1007.    return ((int) hbLightGray) ;
  1008.    }
  1009.  
  1010. void AlertCreateThread (PALERT pAlert)
  1011.    {
  1012.    pAlert->hNetAlertThread = CreateThread(NULL, (DWORD)1024L,
  1013.       (LPTHREAD_START_ROUTINE)NetAlertHandler,
  1014.       NULL, (DWORD)0, &(pAlert->dwNetAlertThreadID)) ;
  1015.  
  1016.    if (!(pAlert->hNetAlertThread))
  1017.       {
  1018.       // CreateThread failure, set its ID to zero
  1019.       // so we will not use the thread
  1020.       pAlert->dwNetAlertThreadID = 0 ;
  1021.       }
  1022.    else
  1023.       {
  1024.       SetThreadPriority (pAlert->hNetAlertThread, THREAD_PRIORITY_HIGHEST) ;
  1025.       }
  1026.    }
  1027.  
  1028. void static OnInitDialog (HWND hDlg)
  1029.    {
  1030.    HDC            hDC ;
  1031.    PALERT         pAlert ;
  1032.  
  1033.    iUnviewedAlerts = 0 ;
  1034.  
  1035.    pAlert = AllocateAlertData (hDlg) ;
  1036.    if (!pAlert)
  1037.       return ;
  1038.  
  1039.    pAlert->iStatus = iPMStatusClosed ;
  1040.    pAlert->hFontItems = hFontScales ;
  1041.  
  1042.    hDC = GetDC (hDlg) ;
  1043.    SelectFont (hDC, pAlert->hFontItems) ;
  1044.  
  1045.    pAlert->yItemHeight = FontHeight (hDC, TRUE) + 2 * AlertItemTopMargin () ;
  1046.  
  1047.    pAlert->xColorWidth = pAlert->yItemHeight ;
  1048.    pAlert->xDateWidth = TextWidth (hDC, szDatePrototype) ;
  1049.    pAlert->xTimeWidth = TextWidth (hDC, szTimePrototype) ;
  1050.    pAlert->xNumberWidth = TextWidth (hDC, szNumberPrototype) ;
  1051.    pAlert->xConditionWidth = TextWidth (hDC, szConditionPrototype) ;
  1052.  
  1053.    // no Horz. scroll bar to begin with
  1054.    pAlert->xTextExtent = 0 ;
  1055.    pAlert->hNetAlertThread = 0;
  1056. #if 0
  1057.    pAlert->hNetAlertThread = CreateThread(NULL, (DWORD)1024L,
  1058.       (LPTHREAD_START_ROUTINE)NetAlertHandler,
  1059.       NULL, (DWORD)0, &(pAlert->dwNetAlertThreadID)) ;
  1060.  
  1061.    if (!(pAlert->hNetAlertThread))
  1062.       {
  1063.       // CreateThread failure, set its ID to zero
  1064.       // so we will not use the thread
  1065.       pAlert->dwNetAlertThreadID = 0 ;
  1066.       }
  1067.    else
  1068.       {
  1069.       SetThreadPriority (pAlert->hNetAlertThread, THREAD_PRIORITY_HIGHEST) ;
  1070.       }
  1071. #endif
  1072.    ReleaseDC (hDlg, hDC) ;
  1073.  
  1074.    hWndAlertLegend = DialogControl (hDlg, IDD_ALERTLEGEND) ;
  1075.    UpdateAlertDisplay (hDlg) ;
  1076.    }
  1077.  
  1078.  
  1079.  
  1080. void static OnMeasureItem (HWND hWnd,
  1081.                            LPMEASUREITEMSTRUCT lpMI)
  1082. /*
  1083.    Note:          Since we have an LB_OWNERDRAWFIXED item in the alert
  1084.                   dialog, we get this message *before* the WM_INITDIALOG
  1085.                   message.  Therefore we can't rely on any of the values
  1086.                   set in that message handler.
  1087. */
  1088.    {  // OnMeasureItem
  1089.    HDC            hDC ;
  1090.  
  1091.    hDC = GetDC (hWnd) ;
  1092.    SelectFont (hDC, hFontScales) ;
  1093.  
  1094.    lpMI->itemHeight = FontHeight (hDC, TRUE) + 2 * AlertItemTopMargin () ;
  1095.  
  1096.    ReleaseDC (hWnd, hDC) ;
  1097.    }  // OnMeasureItem
  1098.  
  1099.  
  1100. void static OnDeleteItem (HDLG hDlg,
  1101.                           WPARAM wControlID,
  1102.                           LPDELETEITEMSTRUCT lpDI)
  1103.    {  // OnDeleteItem
  1104.    PALERTENTRY    pAlertEntry ;
  1105.  
  1106.    pAlertEntry = (PALERTENTRY) lpDI->itemData ;
  1107.  
  1108.    MemoryFree (pAlertEntry->lpszParent) ;
  1109.    MemoryFree (pAlertEntry->lpszInstance) ;
  1110.  
  1111.    MemoryFree (pAlertEntry) ;
  1112.    }  // OnDeleteItem
  1113.  
  1114.  
  1115.  
  1116. void static OnSize (HWND hDlg,
  1117.                     int xWidth,
  1118.                     int yHeight)
  1119.    {  // OnSize
  1120.    SizeAlertComponents (hDlg) ;
  1121.    }
  1122.  
  1123.  
  1124. void static OnDestroy (HWND hWnd)
  1125. /*
  1126.    Effect:        Perform any actions necessary when an AlertDisplay window
  1127.                   is being destroyed. In particular, free the instance
  1128.                   data for the log.
  1129.  
  1130.                   Since we really only have one log window and one global
  1131.                   log data structure, we don't free the structure. We do,
  1132.                   however, delete the objects allocated within the structure.
  1133. */
  1134.    {  // OnDestroy
  1135.    PALERT           pAlert ;
  1136.  
  1137.    pAlert = AlertData (hWnd) ;
  1138.    FreeAlertData (pAlert) ;
  1139.  
  1140.    if (pAlert->dwNetAlertThreadID)
  1141.       {
  1142.       CloseHandle (pAlert->hNetAlertThread) ;
  1143.       }
  1144.    }  // OnDestroy
  1145.  
  1146.  
  1147.  
  1148. //==========================================================================//
  1149. //                             Exported Functions                           //
  1150. //==========================================================================//
  1151.  
  1152.  
  1153. BOOL AlertInitializeApplication (void)
  1154.    {  // AlertInitializeApplication
  1155.    return (TRUE) ;
  1156.    }  // AlertInitializeApplication
  1157.  
  1158.  
  1159. int APIENTRY AlertDisplayDlgProc (HWND hDlg,
  1160.                                   unsigned iMessage,
  1161.                                   WPARAM wParam,
  1162.                                   LONG lParam)
  1163. /*
  1164.    Note:          This function must be exported in the application's
  1165.                   linker-definition file, perfmon.def.
  1166. */
  1167.    {  // AlertDisplayDlgProc
  1168. //   HDC            hDC ;
  1169.  
  1170.    switch (iMessage)
  1171.       {
  1172.       case WM_CTLCOLORDLG:
  1173.       case WM_CTLCOLOREDIT:
  1174.       case WM_CTLCOLORBTN:
  1175.       case WM_CTLCOLORSTATIC:
  1176.          return (OnCtlColor (hDlg, (HDC) wParam)) ;
  1177.          break ;
  1178.  
  1179.       case WM_DELETEITEM:
  1180.          OnDeleteItem (hDlg, wParam, (LPDELETEITEMSTRUCT) lParam) ;
  1181.          break ;
  1182.  
  1183.       case WM_DRAWITEM:
  1184.          OnDrawItem (hDlg, (LPDRAWITEMSTRUCT) lParam) ;
  1185.          break ;
  1186.  
  1187.       case WM_INITDIALOG:
  1188.          OnInitDialog (hDlg) ;
  1189.          break ;
  1190.  
  1191.       case WM_LBUTTONDOWN:
  1192.          DoWindowDrag (hDlg, lParam) ;
  1193.          break ;
  1194.  
  1195.       case WM_LBUTTONDBLCLK:
  1196.          SendMessage (hWndMain, WM_LBUTTONDBLCLK, wParam, lParam) ;
  1197.          break ;
  1198.  
  1199.       case WM_MEASUREITEM:
  1200.          OnMeasureItem (hDlg, (LPMEASUREITEMSTRUCT) lParam) ;
  1201.          break ;
  1202.  
  1203.       case WM_SIZE:
  1204.          OnSize (hDlg, LOWORD (lParam), HIWORD (lParam)) ;
  1205.          break ;
  1206.  
  1207.       case WM_TIMER:
  1208.          AlertTimer (hDlg, FALSE) ;
  1209.          break ;
  1210.  
  1211.       case WM_DESTROY:
  1212.          OnDestroy (hDlg) ;
  1213.          return (FALSE) ;
  1214.          break ;
  1215.  
  1216.       default:
  1217.          return (FALSE) ;
  1218.       } // switch
  1219.  
  1220.    return (TRUE) ;
  1221.    }  // AlertDisplayDlgProc
  1222.  
  1223.  
  1224. HWND CreateAlertWindow (HWND hWndParent)
  1225. /*
  1226.    Effect:        Create the Alert window. This window is a child of
  1227.                   hWndMain.
  1228.  
  1229.    Note:          We dont worry about the size here, as this window
  1230.                   will be resized whenever the main window is resized.
  1231.  
  1232. */
  1233.    {  // CreateAlertWindow
  1234.    HWND           hWnd ;
  1235.    hWnd = CreateDialog (hInstance,
  1236.                         MAKEINTRESOURCE (idDlgAlertDisplay),
  1237.                         hWndParent,
  1238.                         (DLGPROC) AlertDisplayDlgProc) ;
  1239.  
  1240.    return (hWnd) ;
  1241.    }  // CreateAlertWindow
  1242.  
  1243.  
  1244.  
  1245. void UpdateAlertDisplay (HWND hWnd)
  1246. /*
  1247.    Effect:        Set the values for the various controls in the Alert
  1248.                   display.
  1249.  
  1250.    Called By:     OnInitDialog, any other routines that change these
  1251.                   values.
  1252. */
  1253.    {  // UpdateAlertDisplay
  1254.    PALERT         pAlert ;
  1255.  
  1256.    pAlert = AlertData (hWnd) ;
  1257.  
  1258.    DialogSetInterval (hWnd, IDD_ALERTINTERVAL, pAlert->iIntervalMSecs) ;
  1259.    }  // UpdateAlertDisplay
  1260.  
  1261.  
  1262. BOOL AlertInsertLine (HWND hWnd, PLINE pLine)
  1263.    {
  1264.    PALERT         pAlert ;
  1265.    PLINE          pLineEquivalent ;
  1266.  
  1267.    pAlert = AlertData (hWnd) ;
  1268.    pAlert->bModified = TRUE ;
  1269.  
  1270.    pLineEquivalent = FindEquivalentLine (pLine, pAlert->pLineFirst) ;
  1271.    if (pLineEquivalent)
  1272.       {
  1273.       LINESTRUCT  tempLine ;
  1274.  
  1275.       tempLine = *pLineEquivalent ;
  1276.  
  1277.       // copy the new alert line attributes
  1278.       pLineEquivalent->Visual = pLine->Visual ;
  1279.       pLineEquivalent->bAlertOver = pLine->bAlertOver ;
  1280.       pLineEquivalent->eAlertValue = pLine->eAlertValue ;
  1281.       pLineEquivalent->bEveryTime = pLine->bEveryTime ;
  1282.  
  1283.       pLineEquivalent->lpszAlertProgram = pLine->lpszAlertProgram ;
  1284.       pLine->lpszAlertProgram = tempLine.lpszAlertProgram ;
  1285.  
  1286.       pLineEquivalent->hBrush = pLine->hBrush ;
  1287.       pLine->hBrush = tempLine.hBrush ;
  1288.  
  1289.       if (PlayingBackLog ())
  1290.          {
  1291.          PlaybackAlert (hWnd, 0) ;
  1292.          WindowInvalidate (hWnd) ;
  1293.          }
  1294.  
  1295.       return (FALSE) ;
  1296.       }
  1297.    else
  1298.       {
  1299.       SystemAdd (&pAlert->pSystemFirst, pLine->lnSystemName) ;
  1300.  
  1301.       LineAppend (&pAlert->pLineFirst, pLine) ;
  1302.  
  1303.       LegendAddItem (hWndAlertLegend, pLine) ;
  1304.  
  1305.       if (!bDelayAddAction)
  1306.          {
  1307.          SizeAlertComponents (hWndAlert) ;
  1308.          LegendSetSelection (hWndAlertLegend, 
  1309.                              LegendNumItems (hWndAlertLegend) - 1) ;
  1310.          }
  1311.       }
  1312.  
  1313.    if (!bDelayAddAction)
  1314.       {
  1315.       if (PlayingBackLog ())
  1316.          {
  1317.          PlaybackAlert (hWnd, 0) ;
  1318.          WindowInvalidate (hWnd) ;
  1319.          }
  1320.  
  1321.       else if (pAlert->iStatus == iPMStatusClosed)
  1322.          SetAlertTimer (pAlert) ;
  1323.       }
  1324.  
  1325.    return (TRUE) ;
  1326.    }  // AlertInsertLine
  1327.  
  1328.  
  1329. void AlertAddAction ()
  1330.    {
  1331.    PALERT           pAlert ;
  1332.  
  1333.    pAlert = AlertData (hWndAlert) ;
  1334.  
  1335.    SizeAlertComponents (hWndAlert) ;
  1336.    LegendSetSelection (hWndAlertLegend,
  1337.       LegendNumItems (hWndAlertLegend) - 1) ;
  1338.  
  1339.    if (PlayingBackLog ())
  1340.       {
  1341.       PlaybackAlert (hWndAlert, 0) ;
  1342.       WindowInvalidate (hWndAlert) ;
  1343.       }
  1344.    else if (pAlert->iStatus == iPMStatusClosed)
  1345.       SetAlertTimer (pAlert) ;
  1346.    }
  1347.    
  1348. void SizeAlertComponents (HWND hDlg)
  1349.    {  // SizeAlertComponents
  1350.    RECT           rectClient ;
  1351.    int            xWidth, yHeight ;
  1352.    int            yLegendHeight ;
  1353.    int            yLegendTextHeight ;
  1354.    int            yLogHeight ;
  1355.    int            yLogTextHeight ;
  1356.    int            yIntervalHeight ;
  1357.    int            xIntervalTextWidth ;
  1358.    int            StartYPos ;
  1359.    PALERT         pAlert = AlertData(hDlg) ;
  1360.  
  1361.    GetClientRect (hDlg, &rectClient) ;
  1362.    xWidth = rectClient.right ;
  1363.    yHeight = rectClient.bottom ;
  1364.  
  1365.    if (pAlert->bLegendOn)
  1366.       {
  1367.       yLegendHeight = LegendHeight (hWndAlertLegend, yHeight) ;
  1368.       }
  1369.    else
  1370.       {
  1371.       yLegendHeight = 0 ;
  1372.       }
  1373.  
  1374.    if (yHeight < 7 * xScrollWidth)
  1375.       {
  1376.       // too small, just display the alert logs and hide all other
  1377.       // items
  1378.       DialogShow (hDlg, IDD_ALERTLEGEND, FALSE) ;
  1379.       DialogShow (hDlg, IDD_ALERTLEGENDTEXT, FALSE) ;
  1380.  
  1381.       DialogShow (hDlg, IDD_ALERTINTERVAL, FALSE) ;
  1382.       DialogShow (hDlg, IDD_ALERTINTERVALTEXT, FALSE) ;
  1383.  
  1384.       yLogTextHeight = DialogHeight (hDlg, IDD_ALERTLOGTEXT) ;
  1385.  
  1386.       if (yHeight - yLogTextHeight > 3 * xScrollWidth)
  1387.          {
  1388.          DialogMove (hDlg, IDD_ALERTLOGTEXT,
  1389.                      xScrollWidth,
  1390.                      xScrollWidth / 2,
  1391.                      NOCHANGE, NOCHANGE) ;
  1392.          yLogTextHeight += xScrollWidth / 2 ;
  1393.          DialogShow (hDlg, IDD_ALERTLOGTEXT, TRUE) ;
  1394.          }
  1395.       else
  1396.          {
  1397.          yLogTextHeight = 0 ;
  1398.          DialogShow (hDlg, IDD_ALERTLOGTEXT, FALSE) ;
  1399.          }
  1400.       DialogMove (hDlg, IDD_ALERTLOG,
  1401.                   xScrollWidth,
  1402.                   xScrollWidth / 2 + yLogTextHeight,
  1403.                   xWidth - 2 * xScrollWidth,
  1404.                   yHeight - xScrollWidth) ;
  1405.       }
  1406.    else if (yHeight <= 2 * yLegendHeight + 5 * xScrollWidth)
  1407.       {
  1408.       if (pAlert->bLegendOn)
  1409.          {
  1410.          yLegendHeight = min (yLegendHeight,
  1411.             (yHeight - xScrollWidth) / 2) ;
  1412.          }
  1413.       else
  1414.          {
  1415.          yLegendHeight = 0 ;
  1416.          }
  1417.  
  1418.  
  1419.       yLogHeight = yHeight - yLegendHeight - xScrollWidth - 2 ;
  1420.  
  1421.       DialogShow (hDlg, IDD_ALERTLEGENDTEXT, FALSE) ;
  1422.       DialogShow (hDlg, IDD_ALERTINTERVAL, FALSE) ;
  1423.       DialogShow (hDlg, IDD_ALERTINTERVALTEXT, FALSE) ;
  1424.  
  1425.       yLogTextHeight = DialogHeight (hDlg, IDD_ALERTLOGTEXT) ;
  1426.       if (yLogHeight - yLogTextHeight > 3 * xScrollWidth)
  1427.          {
  1428.          DialogMove (hDlg, IDD_ALERTLOGTEXT,
  1429.                      xScrollWidth,
  1430.                      xScrollWidth / 2,
  1431.                      NOCHANGE, NOCHANGE) ;
  1432.          yLogTextHeight += xScrollWidth / 2 ;
  1433.          DialogShow (hDlg, IDD_ALERTLOGTEXT, TRUE) ;
  1434.          }
  1435.       else
  1436.          {
  1437.          yLogTextHeight = 0 ;
  1438.          DialogShow (hDlg, IDD_ALERTLOGTEXT, FALSE) ;
  1439.          }
  1440.  
  1441.       DialogMove (hDlg, IDD_ALERTLOG,
  1442.                   xScrollWidth,
  1443.                   xScrollWidth / 2 + yLogTextHeight,
  1444.                   xWidth - 2 * xScrollWidth,
  1445.                   yLogHeight - yLogTextHeight) ;
  1446.  
  1447.       DialogMove (hDlg, IDD_ALERTLEGEND,
  1448.                   xScrollWidth,
  1449.                   yLogHeight + xScrollWidth - 2,
  1450.                   xWidth - 2 * xScrollWidth,
  1451.                   yLegendHeight) ;
  1452.  
  1453.       DialogShow (hDlg, IDD_ALERTLEGEND, pAlert->bLegendOn ? TRUE : FALSE) ;
  1454.       }
  1455.    else
  1456.       {
  1457.       if (pAlert->bLegendOn)
  1458.          {
  1459.          DialogMove (hDlg, IDD_ALERTLEGEND,
  1460.             xScrollWidth, yHeight - xScrollWidth / 2 - yLegendHeight,
  1461.             xWidth - 2  * xScrollWidth,
  1462.             yLegendHeight) ;
  1463.          DialogMove (hDlg, IDD_ALERTLEGENDTEXT,
  1464.             xScrollWidth,
  1465.             DialogYPos (hDlg, IDD_ALERTLEGEND) - xScrollWidth,
  1466.             NOCHANGE, NOCHANGE) ;
  1467.  
  1468.          yLegendTextHeight = DialogYPos (hDlg, IDD_ALERTLEGENDTEXT) ;
  1469.          }
  1470.       else
  1471.          {
  1472.          yLegendTextHeight = yHeight - xScrollWidth / 2 - yLegendHeight ;
  1473.          }
  1474.  
  1475.       yLogTextHeight = DialogHeight (hDlg, IDD_ALERTLOGTEXT) ;
  1476.       yIntervalHeight = DialogHeight (hDlg, IDD_ALERTINTERVAL) ;
  1477.       yLogHeight = yLegendTextHeight - 4 * xScrollWidth ;
  1478.  
  1479.       if (yLogHeight < 2 * xScrollWidth)
  1480.          {
  1481.          yLogHeight = yLegendTextHeight - yLogTextHeight - xScrollWidth ;
  1482.          }
  1483.       DialogMove (hDlg, IDD_ALERTLOG,
  1484.                   xScrollWidth,
  1485.                   yLegendTextHeight - yLogHeight - xScrollWidth / 2,
  1486.                   xWidth - 2 * xScrollWidth,
  1487.                   yLogHeight) ;
  1488.       DialogMove (hDlg, IDD_ALERTLOGTEXT,
  1489.                   xScrollWidth,
  1490.                   yLogTextHeight = DialogYPos (hDlg, IDD_ALERTLOG) - xScrollWidth,
  1491.                   xWidth - 2 * xScrollWidth, NOCHANGE) ;
  1492.  
  1493.       DialogShow (hDlg, IDD_ALERTLEGEND, pAlert->bLegendOn ? TRUE : FALSE) ;
  1494.       DialogShow (hDlg, IDD_ALERTLEGENDTEXT, pAlert->bLegendOn ? TRUE : FALSE) ;
  1495.       DialogShow (hDlg, IDD_ALERTLOGTEXT, TRUE) ;
  1496.  
  1497.  
  1498.       if (yLogTextHeight >= yIntervalHeight + xScrollWidth)
  1499.          {
  1500.          StartYPos = (yLogTextHeight - yIntervalHeight) / 2 ;
  1501.          xIntervalTextWidth = DialogWidth (hDlg, IDD_ALERTINTERVALTEXT) ;
  1502.          DialogMove (hDlg, IDD_ALERTINTERVALTEXT,
  1503.                      xScrollWidth,
  1504.                      StartYPos + 1,
  1505.                      NOCHANGE, NOCHANGE) ;
  1506.          DialogMove (hDlg, IDD_ALERTINTERVAL,
  1507.                      xScrollWidth + xIntervalTextWidth + 4,
  1508.                      StartYPos,
  1509.                      NOCHANGE, NOCHANGE) ;
  1510.  
  1511.          DialogShow (hDlg, IDD_ALERTINTERVAL, TRUE) ;
  1512.          DialogShow (hDlg, IDD_ALERTINTERVALTEXT, TRUE) ;
  1513.          }
  1514.       else
  1515.          {
  1516.          DialogShow (hDlg, IDD_ALERTINTERVAL, FALSE) ;
  1517.          DialogShow (hDlg, IDD_ALERTINTERVALTEXT, FALSE) ;
  1518.          }
  1519.       }
  1520.  
  1521.    WindowInvalidate (hDlg) ;
  1522.    }  // SizeAlertComponents
  1523.  
  1524.  
  1525. INT PlaybackAlert (HWND hWndAlert, HANDLE hExportFile)
  1526.    {  // PlaybackAlert
  1527.    PALERT         pAlert ;
  1528.    LOGPOSITION    lp ;
  1529.    PLOGINDEX      pLogIndex ;
  1530.    SYSTEMTIME     SystemTime ;
  1531.    SYSTEMTIME     PreviousSystemTime ;
  1532.    BOOL           bFirstTime = TRUE ;
  1533.    INT            ErrCode = 0 ;
  1534.    int            iDisplayTics ;
  1535.    DWORD          TimeDiff ;
  1536.  
  1537.    pAlert = AlertData (hWndAlert) ;
  1538.  
  1539.    if (!pAlert->pLineFirst)
  1540.       {
  1541.       // nothing to check
  1542.       return ErrCode;
  1543.       }
  1544.  
  1545.    lp = PlaybackLog.StartIndexPos ;
  1546.    iDisplayTics = PlaybackLog.iSelectedTics;
  1547.  
  1548.    if (!hExportFile)
  1549.       {
  1550.       LBReset (pAlert->hAlertListBox) ;
  1551.       LBSetRedraw (pAlert->hAlertListBox, FALSE) ;
  1552.       }
  1553.  
  1554.    while (iDisplayTics) {
  1555.  
  1556.       pLogIndex = IndexFromPosition (&lp) ;
  1557.       if (pLogIndex)
  1558.          SystemTime = pLogIndex->SystemTime ;
  1559.       else
  1560.          GetLocalTime (&SystemTime) ;
  1561.       
  1562.       if (!bFirstTime)
  1563.          {
  1564.          // check if it is time to do the alert checking
  1565.          TimeDiff = (DWORD) SystemTimeDifference (&PreviousSystemTime, &SystemTime) ;
  1566.          if (TimeDiff * 1000 >= pAlert->iIntervalMSecs)
  1567.             {
  1568.             PlaybackLines (pAlert->pSystemFirst,
  1569.                            pAlert->pLineFirst,
  1570.                            lp.iPosition) ;
  1571.             ErrCode = CheckAlerts (hWndAlert,
  1572.                             pAlert->hAlertListBox,
  1573.                             &SystemTime,
  1574.                             pAlert->pLineFirst,
  1575.                             hExportFile,
  1576.                             NULL) ;
  1577.             if (ErrCode)
  1578.                {
  1579.                break ;
  1580.                }
  1581.  
  1582.             PreviousSystemTime = SystemTime ;
  1583.             }
  1584.          }
  1585.       else
  1586.          {
  1587.          // setup the data for the first time
  1588.          bFirstTime = FALSE ;
  1589.          PreviousSystemTime = SystemTime ;
  1590.          PlaybackLines (pAlert->pSystemFirst,
  1591.                         pAlert->pLineFirst,
  1592.                         lp.iPosition) ;
  1593.          }
  1594.  
  1595.       if (!NextIndexPosition (&lp, FALSE))  
  1596.          break;
  1597.  
  1598.       iDisplayTics-- ;
  1599.       }
  1600.  
  1601.    if (!hExportFile)
  1602.       {
  1603.       LBSetRedraw (pAlert->hAlertListBox, TRUE) ;
  1604.       }
  1605.  
  1606.    return (ErrCode) ;
  1607.    }  // PlaybackAlert
  1608.  
  1609.  
  1610. #if 0
  1611. PLINESTRUCT CurrentAlertLine (HWND hWndAlert)
  1612.    {  // CurrentAlertLine
  1613.    UNREFERENCED_PARAMETER (hWndAlert) ;
  1614.  
  1615.    return (LegendCurrentLine (hWndAlertLegend)) ;
  1616.    }  // CurrentAlertLine
  1617. #endif
  1618.  
  1619. BOOL AddAlert (HWND hWndParent)
  1620.    {
  1621.    PALERT         pAlert ;
  1622.    PLINE          pCurrentLine ;
  1623.  
  1624.    pAlert = AlertData (hWndAlert) ;
  1625.    pCurrentLine = CurrentAlertLine (hWndAlert) ;
  1626.  
  1627.    return (AddLine (hWndParent,
  1628.                     &(pAlert->pSystemFirst),
  1629.                     &(pAlert->Visual),
  1630.                     pCurrentLine ? pCurrentLine->lnSystemName : NULL,
  1631.                     LineTypeAlert)) ;
  1632.    }
  1633.  
  1634.  
  1635.  
  1636. BOOL EditAlert (HWND hWndParent)
  1637.    {  // EditAlert
  1638.    PALERT        pAlert ;
  1639.    BOOL          bOK ;
  1640.  
  1641.    pAlert = AlertData (hWndAlert) ;
  1642.  
  1643.    bOK = EditLine (hWndParent,
  1644.                    &(pAlert->pSystemFirst),
  1645.                    CurrentAlertLine (hWndAlert),
  1646.                    LineTypeAlert) ;
  1647.  
  1648.    if (bOK && PlayingBackLog())
  1649.       {
  1650.       //re-do the alert using the new condition
  1651.       PlaybackAlert (hWndAlert, 0) ;
  1652.       WindowInvalidate (hWndAlert) ;
  1653.       }
  1654.  
  1655.    return (bOK) ;
  1656.    }  // EditAlert
  1657.  
  1658. // RemoveLineFromAlertListBox is called when we are deleting a line 
  1659. // while monitoring current activity.  We have to clear all the alert
  1660. // entries of this line because we are already doing this when 
  1661. // playing back from log.  Moreover, we are using the line structure
  1662. // while drawing the item.
  1663. //
  1664. void RemoveLineFromAlertListBox (PALERT pAlert, PLINE pLine)
  1665.    {
  1666.    int            iIndex ;
  1667.    int            iNumOfAlerts ;
  1668.    PALERTENTRY    pAlertEntry ;
  1669.  
  1670.    iNumOfAlerts = LBNumItems (pAlert->hAlertListBox) ;
  1671.  
  1672.    if (iNumOfAlerts == 0 || iNumOfAlerts == (int) LB_ERR)
  1673.       {
  1674.       return ;
  1675.       }
  1676.  
  1677.    LBSetRedraw (pAlert->hAlertListBox, FALSE) ;
  1678.  
  1679.    // go thru the listbox from bottom to top
  1680.    for (iIndex = iNumOfAlerts - 1; iIndex >= 0; iIndex-- )
  1681.       {
  1682.       pAlertEntry = (PALERTENTRY) LBData (pAlert->hAlertListBox, iIndex) ;
  1683.       if (pAlertEntry != (PALERTENTRY) NULL && pAlertEntry)
  1684.          {
  1685.          if (pAlertEntry->pLine == pLine)
  1686.             {
  1687.             // remove it from the alert listbox.
  1688.             LBDelete (pAlert->hAlertListBox, iIndex) ;
  1689.             }
  1690.          }
  1691.       }
  1692.    LBSetRedraw (pAlert->hAlertListBox, TRUE) ;
  1693.    }
  1694.  
  1695. BOOL AlertDeleteLine (HWND hWndAlert,
  1696.                       PLINE pLine)
  1697. /*
  1698.    Effect:        Delete the line pLine from the alerts associated with
  1699.                   window hWnd.  Return whether the line could be deleted.
  1700. */
  1701.    {  // DeleteAlert
  1702.    PALERT         pAlert ;
  1703.  
  1704.    pAlert = AlertData (hWndAlert) ;
  1705.    pAlert->bModified = TRUE ;
  1706.  
  1707.    LineRemove (&pAlert->pLineFirst, pLine) ;
  1708.    LegendDeleteItem (hWndAlertLegend, pLine) ;
  1709.  
  1710.    if (!pAlert->pLineFirst)
  1711.       {
  1712.       // no need to collect data
  1713.       ClearAlertTimer (pAlert) ;
  1714.  
  1715.       // clear legend
  1716.       ClearLegend (hWndAlertLegend) ;
  1717.  
  1718.       // reset visual data
  1719.       pAlert->Visual.iColorIndex = 0 ;
  1720.       pAlert->Visual.iWidthIndex = 0 ;
  1721.       pAlert->Visual.iStyleIndex = 0 ;
  1722.       }
  1723.    else
  1724.       {
  1725.       BuildValueListForSystems (
  1726.          pAlert->pSystemFirst,
  1727.          pAlert->pLineFirst) ;
  1728.       }
  1729.  
  1730.    if (!PlayingBackLog())
  1731.       {
  1732.       // delete any alert entry for this line
  1733.       RemoveLineFromAlertListBox (pAlert, pLine) ;
  1734.       }
  1735.  
  1736.    SizeAlertComponents (hWndAlert) ;
  1737.  
  1738.    if (PlayingBackLog ())
  1739.       {
  1740.       if (pAlert->pLineFirst)
  1741.          PlaybackAlert (hWndAlert, 0) ;
  1742.       else
  1743.          {
  1744.          LBReset (pAlert->hAlertListBox) ;
  1745.          }
  1746.  
  1747.       WindowInvalidate (hWndAlert) ;
  1748.       }
  1749.  
  1750.    return (TRUE) ;
  1751.    }  // DeleteAlert
  1752.  
  1753.  
  1754.  
  1755. BOOL ToggleAlertRefresh (HWND hWnd)
  1756.    {  // ToggleAlertRefresh
  1757.    PALERT        pAlert ;
  1758.  
  1759.    pAlert = AlertData (hWnd) ;
  1760.  
  1761.    if (pAlert->bManualRefresh)
  1762.       SetAlertTimer (pAlert) ;
  1763.    else
  1764.       ClearAlertTimer (pAlert) ;
  1765.  
  1766.    pAlert->bManualRefresh = !pAlert->bManualRefresh ;
  1767.    return (pAlert->bManualRefresh) ;
  1768.    }  // ToggleAlertRefresh
  1769.  
  1770. BOOL AlertRefresh (HWND hWnd)
  1771.    {  // ToggleAlertRefresh
  1772.    PALERT        pAlert ;
  1773.  
  1774.    pAlert = AlertData (hWnd) ;
  1775.  
  1776.    return (pAlert->bManualRefresh) ;
  1777.    }  // AlertRefresh
  1778.  
  1779.  
  1780. void AlertTimer (HWND hWnd, BOOL bForce)
  1781. /*
  1782.    Effect:        Perform all actions neccesary when an alert timer tick
  1783.                   or manual refresh occurs. In particular, get the current
  1784.                   values for each line in the alert window, and compare
  1785.                   the value against the alert conditions. For each alert
  1786.                   that may have occured, call SignalAlert.
  1787.  
  1788.    Called By:     AlertWndProc, in response to a WM_TIMER message.
  1789.                   OnCommand, in response to a IDM_REFRESHALERT notification.
  1790. */
  1791.    {  // AlertTimer
  1792.    PALERT         pAlert ;
  1793.    SYSTEMTIME     SystemTime ;
  1794.  
  1795.    pAlert = AlertData (hWnd) ;
  1796.  
  1797.    if (PlayingBackLog ())
  1798.       return ;
  1799.  
  1800.    if (bForce || !pAlert->bManualRefresh)
  1801.       {  // if
  1802.       UpdateLines (&(pAlert->pSystemFirst), pAlert->pLineFirst) ;
  1803.       GetLocalTime (&SystemTime) ;
  1804.       CheckAlerts (hWnd,
  1805.                    pAlert->hAlertListBox,
  1806.                    &SystemTime,
  1807.                    pAlert->pLineFirst,
  1808.                    FALSE,
  1809.                    pAlert->pSystemFirst) ;
  1810.       }  // if
  1811.    }  // AlertTimer
  1812.  
  1813.  
  1814.  
  1815. BOOL OpenAlertVer1 (HANDLE hFile,
  1816.                     DISKALERT *pDiskAlert,
  1817.                     PALERT pAlert,
  1818.                     DWORD dwMinorVersion)
  1819.                   
  1820.    {  // OpenAlertVer1
  1821.    bDelayAddAction = TRUE ;
  1822.    pAlert->Visual = pDiskAlert->Visual ;
  1823.    pAlert->iIntervalMSecs = pDiskAlert->dwIntervalSecs ;
  1824.    if (dwMinorVersion < 3)
  1825.       {
  1826.       pAlert->iIntervalMSecs *= 1000 ;
  1827.       }
  1828.  
  1829.    pAlert->bNetworkAlert = pDiskAlert->bNetworkAlert ;
  1830.    pAlert->bManualRefresh = pDiskAlert->bManualRefresh ;
  1831.    pAlert->bSwitchToAlert = pDiskAlert->bSwitchToAlert ;
  1832.  
  1833.    if (dwMinorVersion >= 2)
  1834.       {
  1835.       lstrcpy (pAlert->MessageName, pDiskAlert->MessageName) ;
  1836.       }
  1837.  
  1838.    pAlert->bLegendOn = TRUE ;
  1839.  
  1840.    if (dwMinorVersion >= 4)
  1841.       {
  1842.       if (dwMinorVersion == 4)
  1843.          pAlert->bEventLog = pDiskAlert->MiscOptions ;
  1844.       else
  1845.          {
  1846.          pAlert->bEventLog = pDiskAlert->MiscOptions & 0x01 ;
  1847.          pAlert->bLegendOn = pDiskAlert->MiscOptions & 0x02 ;
  1848.          }
  1849.       }
  1850.    else
  1851.       {
  1852.       pAlert->bEventLog = FALSE ;
  1853.  
  1854.       // have to move the file pointer back for old pma file
  1855.       FileSeekCurrent (hFile, -((int) (sizeof (pDiskAlert->MiscOptions)))) ;
  1856.       }
  1857.  
  1858.    if (pAlert->bNetworkAlert && pAlert->hNetAlertThread == 0)
  1859.       {
  1860.       AlertCreateThread (pAlert) ;
  1861.       }
  1862.  
  1863.    ReadLines (hFile, pDiskAlert->dwNumLines,
  1864.                &(pAlert->pSystemFirst), &(pAlert->pLineFirst), IDM_VIEWALERT) ;
  1865.  
  1866.    bDelayAddAction = FALSE ;
  1867.  
  1868.    AlertAddAction () ;
  1869.  
  1870.    return (TRUE) ;
  1871.    }  // OpenAlertVer1
  1872.  
  1873.  
  1874.  
  1875. BOOL OpenAlert (HWND hWndAlert,
  1876.                 HANDLE hFile,
  1877.                 DWORD dwMajorVersion,
  1878.                 DWORD dwMinorVersion,
  1879.                 BOOL bAlertFile)
  1880.    {  // OpenAlert
  1881.    PALERT         pAlert ;
  1882.    DISKALERT      DiskAlert ;
  1883.    BOOL           bSuccess = TRUE ;
  1884.  
  1885.    pAlert = AlertData (hWndAlert) ;
  1886.    if (!pAlert)
  1887.       {
  1888.       bSuccess = FALSE ;
  1889.       goto Exit0 ;
  1890.       }
  1891.  
  1892.    if (!FileRead (hFile, &DiskAlert, sizeof (DISKALERT)))
  1893.       {
  1894.       bSuccess = FALSE ;
  1895.       goto Exit0 ;
  1896.       }
  1897.  
  1898.  
  1899.    switch (dwMajorVersion)
  1900.       {
  1901.       case (1):
  1902.  
  1903.          SetHourglassCursor() ;
  1904.  
  1905.          ResetAlertView (hWndAlert) ;
  1906.  
  1907.          OpenAlertVer1 (hFile, &DiskAlert, pAlert, dwMinorVersion) ;
  1908.  
  1909.          // change to alert view if we are opening a 
  1910.          // alert file
  1911.          if (bAlertFile && iPerfmonView != IDM_VIEWALERT)
  1912.             {
  1913.             SendMessage (hWndMain, WM_COMMAND, (LONG)IDM_VIEWALERT, 0L) ;
  1914.             }
  1915.  
  1916.          if (iPerfmonView == IDM_VIEWALERT)
  1917.             {
  1918.             SetPerfmonOptions (&DiskAlert.perfmonOptions) ;
  1919.             }
  1920.          UpdateAlertDisplay (hWndAlert) ;
  1921.  
  1922.          SetArrowCursor() ;
  1923.  
  1924.          break ;
  1925.       }  // switch
  1926.  
  1927. Exit0:
  1928.  
  1929.    if (bAlertFile)
  1930.       {
  1931.       CloseHandle (hFile) ;
  1932.       }
  1933.  
  1934.    return (bSuccess) ;
  1935.    }  // OpenAlert
  1936.  
  1937.  
  1938. void ResetAlertView (HWND hWndAlert)
  1939.    {  
  1940.    PALERT         pAlert ;
  1941.  
  1942.    pAlert = AlertData (hWndAlert) ;
  1943.    if (!pAlert)
  1944.       return ;
  1945.  
  1946.    ChangeSaveFileName (NULL, IDM_VIEWALERT) ;
  1947.  
  1948.    if (pAlert->pSystemFirst)
  1949.       {
  1950.       ResetAlert (hWndAlert) ;
  1951.       }
  1952.    }  // ResetAlertView
  1953.  
  1954. void ResetAlert (HWND hWndAlert)
  1955.    {  // ResetAlert
  1956.    PALERT         pAlert ;
  1957.  
  1958.  
  1959.    pAlert = AlertData (hWndAlert) ;
  1960.    if (!pAlert)
  1961.       return ;
  1962.  
  1963.    ClearAlertTimer (pAlert) ;
  1964.  
  1965.    ClearLegend (hWndAlertLegend) ;
  1966.    if (pAlert->pLineFirst)
  1967.       {
  1968.       FreeLines (pAlert->pLineFirst) ;
  1969.       pAlert->pLineFirst = NULL ;
  1970.       }
  1971.  
  1972.    if (pAlert->pSystemFirst)
  1973.       {
  1974.       FreeSystems (pAlert->pSystemFirst) ;
  1975.       pAlert->pSystemFirst = NULL ;
  1976.       }
  1977.  
  1978.    pAlert->bModified = FALSE ;
  1979.  
  1980.    // reset visual data
  1981.    pAlert->Visual.iColorIndex = 0 ;
  1982.    pAlert->Visual.iWidthIndex = 0 ;
  1983.    pAlert->Visual.iStyleIndex = 0 ;
  1984.  
  1985.    iUnviewedAlerts = 0 ;
  1986.    if (iPerfmonView != IDM_VIEWALERT)
  1987.       {
  1988.       StatusUpdateIcons (hWndStatus) ;
  1989.       }
  1990.  
  1991.    // remove the horiz. scrollbar
  1992.    pAlert->xTextExtent = 0 ;
  1993.    LBSetHorzExtent (pAlert->hAlertListBox, pAlert->xTextExtent) ;
  1994.  
  1995.    LBReset (pAlert->hAlertListBox) ;
  1996.    SizeAlertComponents (hWndAlert) ;
  1997.  
  1998. //   WindowInvalidate (hWndAlert) ;
  1999.    }  // ResetAlert
  2000.    
  2001.  
  2002. void ClearAlertDisplay (HWND hWnd)
  2003.    {
  2004.    PALERT         pAlert ;
  2005.  
  2006.    pAlert = AlertData (hWnd) ;
  2007.  
  2008.    // remove the horiz. scrollbar
  2009.    pAlert->xTextExtent = 0 ;
  2010.    LBSetHorzExtent (pAlert->hAlertListBox, pAlert->xTextExtent) ;
  2011.  
  2012.    LBReset (pAlert->hAlertListBox) ;
  2013.    }
  2014.  
  2015. BOOL SaveAlert (HWND hWndAlert, HANDLE hInputFile, BOOL bGetFileName)
  2016.    {
  2017.    PALERT         pAlert ;
  2018.    PLINE          pLine ;
  2019.    HANDLE         hFile ;
  2020.    DISKALERT      DiskAlert ;
  2021.    PERFFILEHEADER FileHeader ;
  2022.    TCHAR          szFileName [256] ;
  2023.    BOOL           newFileName = FALSE ;
  2024.  
  2025.    if (hInputFile)
  2026.       {
  2027.       // use the input file handle if it is available
  2028.       // this is the case for saving workspace data
  2029.       hFile = hInputFile ;
  2030.       }
  2031.    else
  2032.       {
  2033.       if (pAlertFullFileName)
  2034.          {
  2035.          lstrcpy (szFileName, pAlertFullFileName) ;
  2036.          }
  2037.       if (bGetFileName || pAlertFullFileName == NULL)
  2038.          {
  2039.          if (!FileGetName (hWndAlert, IDS_ALERTFILE, szFileName))
  2040.             {
  2041.             return (FALSE) ;
  2042.             }
  2043.          newFileName = TRUE ;
  2044.          }
  2045.  
  2046.       hFile = FileHandleCreate (szFileName) ;
  2047.  
  2048.       if (hFile && newFileName)
  2049.          {
  2050.          ChangeSaveFileName (szFileName, IDM_VIEWALERT) ;
  2051.          }
  2052.       else if (!hFile)
  2053.          {
  2054.          DlgErrorBox (hWndAlert, ERR_CANT_OPEN, szFileName) ;
  2055.          }
  2056.       }
  2057.  
  2058.    if (!hFile)
  2059.       return (FALSE) ;
  2060.  
  2061.    pAlert = AlertData (hWndAlert) ;
  2062.    if (!pAlert)
  2063.       {
  2064.       if (!hInputFile)
  2065.          {
  2066.          CloseHandle (hFile) ;
  2067.          }
  2068.       return (FALSE) ;
  2069.       }
  2070.  
  2071.    if (!hInputFile)
  2072.       {
  2073.       memset (&FileHeader, 0, sizeof (FileHeader)) ;
  2074.       lstrcpy (FileHeader.szSignature, szPerfAlertSignature) ;
  2075.       FileHeader.dwMajorVersion = AlertMajorVersion ;
  2076.       FileHeader.dwMinorVersion = AlertMinorVersion ;
  2077.    
  2078.       if (!FileWrite (hFile, &FileHeader, sizeof (PERFFILEHEADER)))
  2079.          {
  2080.          goto Exit0 ;
  2081.          }
  2082.       }
  2083.  
  2084.    DiskAlert.Visual = pAlert->Visual ;
  2085.    DiskAlert.dwIntervalSecs = pAlert->iIntervalMSecs ;
  2086.    DiskAlert.dwNumLines = NumLines (pAlert->pLineFirst) ;
  2087.  
  2088.    // fill in misc alert options
  2089.    DiskAlert.MiscOptions = 0 ;
  2090.    if (pAlert->bEventLog)
  2091.       DiskAlert.MiscOptions = 0x01 ;
  2092.    if (pAlert->bLegendOn)
  2093.       DiskAlert.MiscOptions += 0x02 ;
  2094.  
  2095. //   DiskAlert.bEventLog = pAlert->bEventLog ;
  2096.    DiskAlert.bNetworkAlert = pAlert->bNetworkAlert ;
  2097.    DiskAlert.bSwitchToAlert = pAlert->bSwitchToAlert ;
  2098.    DiskAlert.bManualRefresh = pAlert->bManualRefresh ;
  2099.    DiskAlert.perfmonOptions = Options ;
  2100.    lstrcpy (DiskAlert.MessageName, pAlert->MessageName) ;
  2101.    if (!FileWrite (hFile, &DiskAlert, sizeof (DISKALERT)))
  2102.       {
  2103.       goto Exit0 ;
  2104.       }
  2105.  
  2106.    for (pLine = pAlert->pLineFirst ;
  2107.         pLine ;
  2108.         pLine = pLine->pLineNext)
  2109.       {  // for
  2110.       if (!WriteLine (pLine, hFile))
  2111.          {
  2112.          goto Exit0 ;
  2113.          }
  2114.       }  // for
  2115.  
  2116.    if (!hInputFile)
  2117.       {
  2118.       CloseHandle (hFile) ;
  2119.       }
  2120.  
  2121.    return (TRUE) ;
  2122.  
  2123. Exit0:
  2124.    if (!hInputFile)
  2125.       {
  2126.       CloseHandle (hFile) ;
  2127.  
  2128.       // only need to report error if not workspace 
  2129.       DlgErrorBox (hWndAlert, ERR_SETTING_FILE, szFileName) ;
  2130.       }
  2131.    return (FALSE) ;
  2132.    }
  2133.  
  2134.  
  2135. BOOL ExportAlertEntry (HANDLE hFile, PALERTENTRY pAlertEntry)
  2136. {
  2137.    TCHAR          UnicodeBuff [LongTextLen] ;
  2138.    CHAR           TempBuff [LongTextLen * 2] ;
  2139.    int            StringLen ;
  2140.    PLINE          pLine ;
  2141.  
  2142.    pLine = pAlertEntry->pLine ;
  2143.  
  2144.    // export the alert date-time
  2145.    
  2146.    strcpy (TempBuff, LineEndStr) ;
  2147.    StringLen = strlen (TempBuff) ;
  2148.    SystemTimeDateString (&(pAlertEntry->SystemTime), UnicodeBuff) ;
  2149.    ConvertUnicodeStr (&TempBuff[StringLen], UnicodeBuff) ;
  2150.  
  2151.    strcat (TempBuff, pDelimiter) ;
  2152.    SystemTimeTimeString (&(pAlertEntry->SystemTime), UnicodeBuff, FALSE) ;
  2153.    StringLen = strlen (TempBuff) ;
  2154.    ConvertUnicodeStr (&TempBuff[StringLen], UnicodeBuff) ;
  2155.    strcat (TempBuff, pDelimiter) ;
  2156.    if (!FileWrite (hFile, TempBuff, strlen(TempBuff)))
  2157.       {
  2158.       goto Exit0 ;
  2159.       }
  2160.  
  2161.    // export alert value and trigger condition
  2162.  
  2163.    if (pLine)
  2164.       {
  2165.       TSPRINTF (UnicodeBuff, szNumberFormat, pAlertEntry->eValue) ;
  2166.       ConvertDecimalPoint (UnicodeBuff) ;
  2167.       ConvertUnicodeStr (TempBuff, UnicodeBuff) ;
  2168.       strcat (TempBuff, pDelimiter) ;
  2169.       StringLen = strlen (TempBuff) ;
  2170.       TempBuff[StringLen] = pAlertEntry->bOver ? '>' : '<' ;
  2171.       StringLen++ ;
  2172.       TSPRINTF (UnicodeBuff, szNumberFormat, pAlertEntry->eAlertValue) ;
  2173.       ConvertDecimalPoint (UnicodeBuff) ;
  2174.       ConvertUnicodeStr (&TempBuff[StringLen], UnicodeBuff) ;
  2175.       strcat (TempBuff, pDelimiter) ;
  2176.       }
  2177.    else
  2178.       {
  2179.       strcpy (TempBuff, pDelimiter) ;
  2180.       strcat (TempBuff, pDelimiter) ;
  2181.       }
  2182.    if (!FileWrite (hFile, TempBuff, strlen(TempBuff)))
  2183.       {
  2184.       goto Exit0 ;
  2185.       }
  2186.  
  2187.    // export Counter, Instance, & Parent names
  2188.    
  2189.    if (pLine)
  2190.       {
  2191.       ConvertUnicodeStr (TempBuff, pLine->lnCounterName) ;
  2192.       }
  2193.    else
  2194.       {
  2195.       // system up/down message is stored in lpszInstance.
  2196.       ConvertUnicodeStr (TempBuff, pAlertEntry->lpszInstance) ;
  2197.       }
  2198.  
  2199.    strcat (TempBuff, pDelimiter) ;
  2200.    StringLen = strlen (TempBuff) ;
  2201.  
  2202.    if (pLine && !(strempty(pAlertEntry->lpszInstance)))
  2203.       {
  2204.       ConvertUnicodeStr (&TempBuff[StringLen], pAlertEntry->lpszInstance) ;
  2205.       }
  2206.  
  2207.    strcat (TempBuff, pDelimiter) ;
  2208.    StringLen = strlen (TempBuff) ;
  2209.    
  2210.    if (pLine && !(strempty(pAlertEntry->lpszParent)))
  2211.       {
  2212.       ConvertUnicodeStr (&TempBuff[StringLen], pAlertEntry->lpszParent) ;
  2213.       }
  2214.    strcat (TempBuff, pDelimiter) ;
  2215.    
  2216.    if (!FileWrite (hFile, TempBuff, strlen(TempBuff)))
  2217.       {
  2218.       goto Exit0 ;
  2219.       }
  2220.  
  2221.    // export object, & computer names
  2222.    TempBuff[0] = '\0' ;   
  2223.    if (pLine)
  2224.       {
  2225.       ConvertUnicodeStr (TempBuff, pLine->lnObjectName) ;
  2226.       }
  2227.    strcat (TempBuff, pDelimiter) ;
  2228.    StringLen = strlen (TempBuff) ;
  2229.  
  2230.    if (pLine)
  2231.       {
  2232.       ConvertUnicodeStr (&TempBuff[StringLen], pLine->lnSystemName) ;
  2233.       }
  2234.    else
  2235.       {
  2236.       // system name for the system that is up or down is in
  2237.       // lpszParent.
  2238.       ConvertUnicodeStr (&TempBuff[StringLen], pAlertEntry->lpszParent) ;
  2239.       }
  2240.  
  2241.    if (!FileWrite (hFile, TempBuff, strlen(TempBuff)))
  2242.       {
  2243.       goto Exit0 ;
  2244.       }
  2245.  
  2246.    return (TRUE) ;
  2247.  
  2248. Exit0:
  2249.    return (FALSE) ;
  2250.  
  2251. }  // ExportAlertEntry
  2252.  
  2253. INT ExportAlertLine (PLINE pLine, FLOAT eValue, SYSTEMTIME *pSystemTime, HANDLE hExportFile)
  2254. {
  2255.    ALERTENTRY     AlertEntry ;
  2256.    TCHAR          szInstance [256] ;
  2257.    TCHAR          szParent [256] ;
  2258.    INT            ErrCode = 0 ;
  2259.  
  2260.    AlertEntry.SystemTime = *pSystemTime ;
  2261.    AlertEntry.pLine= pLine ;
  2262.    AlertEntry.eValue = eValue ;
  2263.    AlertEntry.bOver = pLine->bAlertOver ;
  2264.    AlertEntry.eAlertValue = pLine->eAlertValue ;
  2265.  
  2266.  
  2267.    //=============================//
  2268.    // Determine Instance, Parent  //
  2269.    //=============================//
  2270.  
  2271.    // It's possible that there will be no instance, therefore
  2272.    // the lnInstanceName would be NULL.
  2273.  
  2274.    if (pLine->lnObject.NumInstances > 0)
  2275.       {
  2276.       // Test for the parent object instance name title index.
  2277.       // If there is one, it implies that there will be a valid
  2278.       // Parent Object Name and a valid Parent Object Instance Name.
  2279.  
  2280.       // If the Parent Object title index is 0 then
  2281.       // just display the instance name.
  2282.  
  2283.       lstrcpy (szInstance, pLine->lnInstanceName) ;
  2284.       if (pLine->lnInstanceDef.ParentObjectTitleIndex && pLine->lnPINName)
  2285.          {
  2286.          // Get the Parent Object Name.
  2287.          lstrcpy (szParent, pLine->lnPINName) ;
  2288.          }
  2289.       else
  2290.          {
  2291.          szParent[0] = TEXT(' ') ;
  2292.          szParent[1] = TEXT('\0') ;
  2293.          }
  2294.       }
  2295.    else
  2296.       {
  2297.       szInstance[0] = TEXT(' ') ;
  2298.       szInstance[1] = TEXT('\0') ;
  2299.       szParent[0] = TEXT(' ') ;
  2300.       szParent[1] = TEXT('\0') ;
  2301.       }
  2302.  
  2303.    AlertEntry.lpszInstance = szInstance ;
  2304.    AlertEntry.lpszParent = szParent ;
  2305.  
  2306.    if (!ExportAlertEntry (hExportFile, &AlertEntry))
  2307.       {
  2308.       ErrCode = ERR_EXPORT_FILE ;
  2309.       }
  2310.  
  2311.    return ErrCode ;
  2312.  }
  2313.  
  2314.  
  2315. BOOL ExportAlertLabels (HANDLE hFile)
  2316. {
  2317.    TCHAR          UnicodeBuff [LongTextLen] ;
  2318.    CHAR           TempBuff [LongTextLen * 2] ;
  2319.    int            StringLen ;
  2320.  
  2321.    strcpy (TempBuff, LineEndStr) ;
  2322.    StringLen = strlen (TempBuff) ;
  2323.    StringLoad (IDS_EXPORT_DATE, UnicodeBuff) ;
  2324.    ConvertUnicodeStr (&TempBuff[StringLen], UnicodeBuff) ;
  2325.    strcat (TempBuff, pDelimiter) ;
  2326.    StringLen = strlen (TempBuff) ;
  2327.  
  2328.    StringLoad (IDS_EXPORT_TIME, UnicodeBuff) ;
  2329.    ConvertUnicodeStr (&TempBuff[StringLen], UnicodeBuff) ;
  2330.    strcat (TempBuff, pDelimiter) ;
  2331.    StringLen = strlen (TempBuff) ;
  2332.  
  2333.    StringLoad (IDS_LABELVALUE, UnicodeBuff) ;
  2334.    ConvertUnicodeStr (&TempBuff[StringLen], UnicodeBuff) ;
  2335.    strcat (TempBuff, pDelimiter) ;
  2336.    StringLen = strlen (TempBuff) ;
  2337.  
  2338.    StringLoad (IDS_ALERT_TRIGGER, UnicodeBuff) ;
  2339.    ConvertUnicodeStr (&TempBuff[StringLen], UnicodeBuff) ;
  2340.    strcat (TempBuff, pDelimiter) ;
  2341.  
  2342.    if (!FileWrite (hFile, TempBuff, strlen(TempBuff)))
  2343.       {
  2344.       goto Exit0 ;
  2345.       }
  2346.  
  2347.    StringLoad (IDS_COUNTERNAME, UnicodeBuff) ;
  2348.    ConvertUnicodeStr (TempBuff, UnicodeBuff) ;
  2349.    strcat (TempBuff, pDelimiter) ;
  2350.    StringLen = strlen (TempBuff) ;
  2351.  
  2352.    StringLoad (IDS_INSTNAME, UnicodeBuff) ;
  2353.    ConvertUnicodeStr (&TempBuff[StringLen], UnicodeBuff) ;
  2354.    strcat (TempBuff, pDelimiter) ;
  2355.    StringLen = strlen (TempBuff) ;
  2356.  
  2357.    StringLoad (IDS_PARENT, UnicodeBuff) ;
  2358.    ConvertUnicodeStr (&TempBuff[StringLen], UnicodeBuff) ;
  2359.    strcat (TempBuff, pDelimiter) ;
  2360.    StringLen = strlen (TempBuff) ;
  2361.  
  2362.    StringLoad (IDS_OBJNAME, UnicodeBuff) ;
  2363.    ConvertUnicodeStr (&TempBuff[StringLen], UnicodeBuff) ;
  2364.    strcat (TempBuff, pDelimiter) ;
  2365.    StringLen = strlen (TempBuff) ;
  2366.  
  2367.    StringLoad (IDS_LABELSYSTEM, UnicodeBuff) ;
  2368.    ConvertUnicodeStr (&TempBuff[StringLen], UnicodeBuff) ;
  2369.  
  2370.    if (!FileWrite (hFile, TempBuff, strlen(TempBuff)))
  2371.       {
  2372.       goto Exit0 ;
  2373.       }
  2374.  
  2375.    return (TRUE) ;
  2376.  
  2377. Exit0:
  2378.    return (FALSE) ;
  2379.  
  2380. }  // ExportAlertLabels
  2381.  
  2382.  
  2383. void ExportAlert (void)
  2384. {
  2385.    PALERT         pAlert ;
  2386.    HANDLE         hFile = 0 ;
  2387.    HWND           hWndAlerts ;
  2388.    PALERTENTRY    pAlertEntry ;
  2389.    int            AlertTotal ;
  2390.    int            iIndex ;
  2391.    LPTSTR         pFileName = NULL ;
  2392.    INT            ErrCode = 0 ;
  2393.  
  2394.    if (!(pAlert = AlertData (hWndAlert)))
  2395.       {
  2396.       return ;
  2397.       }
  2398.  
  2399.    // see if there is anything to export..
  2400.    if (!(pAlert->pLineFirst))
  2401.       {
  2402.       return ;
  2403.       }
  2404.  
  2405.    if (!(hWndAlerts = pAlert->hAlertListBox))
  2406.       {
  2407.       return ;
  2408.       }
  2409.  
  2410.    AlertTotal = LBNumItems (hWndAlerts) ;
  2411.    if (AlertTotal == LB_ERR || AlertTotal == 0)
  2412.       {
  2413.       return ;
  2414.       }
  2415.  
  2416.    SetHourglassCursor() ;
  2417.    
  2418.    if (ErrCode = ExportFileOpen (hWndAlert, &hFile, pAlert->iIntervalMSecs, &pFileName))
  2419.       {
  2420.       goto Exit0 ;
  2421.       }
  2422.  
  2423.    if (!pFileName)
  2424.       {
  2425.       // the case when user cancel.
  2426.       goto Exit0 ;
  2427.       }
  2428.  
  2429.    // export the column labels
  2430.    if (!ExportAlertLabels (hFile))
  2431.       {
  2432.       ErrCode = ERR_EXPORT_FILE ;
  2433.       goto Exit0 ;
  2434.       }
  2435.    if (AlertTotal < ALERTLOGMAXITEMS || !PlayingBackLog())
  2436.       {
  2437.       for (iIndex = 0 ; iIndex < AlertTotal ; iIndex++)
  2438.          {
  2439.          // get the alert data
  2440.          pAlertEntry = (PALERTENTRY) LBData (hWndAlerts, iIndex) ;
  2441.  
  2442.          if (!pAlertEntry || pAlertEntry == (PALERTENTRY)LB_ERR)
  2443.             {
  2444.             // skip this entry if we hit an error
  2445.             continue ;
  2446.             }
  2447.          
  2448.          // export the alert line
  2449.          if (!ExportAlertEntry (hFile, pAlertEntry))
  2450.             {
  2451.             ErrCode = ERR_EXPORT_FILE ;
  2452.             break ;
  2453.             }
  2454.          }
  2455.       }
  2456.    else
  2457.       {
  2458.       // we are playingback log and that the listbox does not
  2459.       // contain all the alerts.  In this case, have to walk the
  2460.       // log file to re-generate all the alerts.
  2461.       ErrCode = PlaybackAlert (hWndAlert, hFile) ;
  2462.       }
  2463.  
  2464. Exit0:
  2465.  
  2466.    SetArrowCursor() ;
  2467.  
  2468.    if (hFile)
  2469.       {
  2470.       CloseHandle (hFile) ;
  2471.       }
  2472.  
  2473.    if (pFileName)
  2474.       {
  2475.       if (ErrCode)
  2476.          {
  2477.          DlgErrorBox (hWndAlert, ErrCode, pFileName) ;
  2478.          }
  2479.  
  2480.       MemoryFree (pFileName) ;
  2481.       }
  2482.  
  2483. }  // ExportAlert
  2484.  
  2485. 
  2486.