Wilson WindowWare Tech Support

WinBatch WinBatch+Compiler WebBatch
Home | Tech Database | Tech BBS | White Papers | Purchase


Software Metering Example

Keywords:   software metering

Question:

Does anyone have an example of using Winbatch for Software Metering?

Answer:

Here's an app to meter applications that are offerred from an NT Server. Enjoy!
;----------------------------------------------------------------------
; SECTION: PdcMeter
;
; Written by Brad Adkins
; badkins@ix.netcom.com
;
; Language: WIL - Windows Interface Language
; Compiler: WinBatch Compiler, Version 97D (2.4dbp)
;
; Setup:
;
; For each application to be metered, install PdcMeter in the application
; directory on your server. Configure PdcMeter support files for the 
; application. Schedule the periodic "METER" task using your favorite 
; scheduler, (AT probably won't cut it.)
;
; PdcMeter Support Files:
; 
;   Create Directories:
;     appdir\License                Permissions (RX)
;     appdir\Log                    Permissions (RWX)
;     appdir\Lock                   Permissions (RWXD)
;   Create Files:
;     appdir\pdcmeter.ini           (see SAMPLE INI FILE below)
;     appdir\License\license.dat    one line text file, contains # of licenses
;     appdir\License\current.dat    one line text file, can be empty to start
;     appdir\License\exception.dat  one line text file, can be empty to start
;     appdir\Log\license.log        use "LOG_RESET" option to create this file
;     appdir\Log\error.log          use "LOG_RESET" option to create this file
; 
; Description:
;
; Provides an application metering process. When executed with the "RUN" 
; option will launch the target application, if the current license count 
; is below the maximum threshold. The metering process is periodically 
; executed using the "METER" option to update the current count and list 
; of current users (every 10 minutes is adequate). This information is
; accessed by the "RUN" option. The "STATS" option will generate usage
; statistics. The "SERVICE" option will start the automatic "METER" 
; mode. For the "SERVICE" option to work properly, it must be installed 
; with the NT SRVANY utility first.
;
; Notes:
;
; The "SERVICE" capability is still in development and testing, so expect
; that you'll have to do some work here, but have fun if you choose to 
; finish it. I currently use SmartBatch to schedule the "METER" task to 
; run every 10 minutes for each application that is being metered.
;
; ErrorLevel:
;   0 = Success
;   1 = Initialization Error
;   2 = Param File Error
;   3 = File Locking Error
;   4 = License count exceeds maximum licenses allowed*
;
;   *this can occur if someone executes the metered app without using
;    pdcmeter to launch it
;
; Program Initialization Calling Sequence:
;
;   Task 1                Task 2-7              Task 20-21
;   --------------------- --------------------- ---------------------
;   ReadParamApplication  ReadParamApplication  ReadParamService
;   ReadParamClient       ReadParamServer       ReadParamNotification
;   ReadParamNotification ReadParamNotification ParamDerived
;   ReadDerived           ParamDerived          
;
;----------------------------------------------------------------------/

PNAME = "PdcMeter"
PVERSION = "Version 1.0  June 8, 1998"
COPYRIGHT = "Copyright 1998"
AUTHOR = "Brad Adkins, badkins@ix.netcom.com"

;----------------------------------------------------------------------
; SECTION: Global
;----------------------------------------------------------------------/

;string formatting constants
C1 = "%@CRLF%"
C2 = "%@CRLF%%@CRLF%"

;project version information
s = StrFill(" ", 5)
VERINFO = StrCat(s, PVERSION, "|", s, COPYRIGHT, @CRLF, s, AUTHOR)

;constants
APP_RUN = "APP_RUN"
APP_METER = "APP_METER"
APP_MOFF = "APP_MOFF"
LOG_RESET = "LOG_RESET"
LOG_OPEN = "LOG_OPEN"
LOG_CLOSE = "LOG_CLOSE"
LOG_STATS = "LOG_STATS"
LOG_SHOW = "LOG_SHOW"
SRV_START = "SRV_START"
SRV_STOP = "SRV_STOP"

LOCK_ATTEMPTS = 3
LOCK_DELAY = 0.5

;variables
LicenseLock = "A_Lock.lok"
LogLock = "B_Lock.lok"
ErrLock = "C_Lock.lok"

;----------------------------------------------------------------------
; SECTION: Environment
;----------------------------------------------------------------------/

WinHide("")
WinTitle("", PNAME)

TestMode = @FALSE
ProductionMode = @TRUE

;#12 - supress critical error dialog when being forceably shutdown
IntControl(12,5,0,0,0)

;#50 - no "go to web page" button on WIL critical error boxes
IntControl(50, 0, 0, 0, 0) 

;#33 - listbox control allows only single items to be selected
IntControl(33, 0, 0, 0, 0) 


;#1000 - application return code on exit using value in ErrorLevel
ErrorLevel = 0

;----------------------------------------------------------------------
; SECTION: Main
;----------------------------------------------------------------------/
gosub Initialize

select Task
    case 1
        ;APP_RUN
        gosub RunApp
        break
    case 2
        ;APP_METER
        gosub MeterApp
        break
    case 3
        ;LOG_STATS
        gosub RunStats
        break
    case 4
        ;LOG_SHOW
        gosub ShowStats
        break
    case 5
        ;LOG_RESET
        gosub CreateLogFiles
        break
    case 6
        ;LOG_OPEN
        gosub LogOpen
        break
    case 7
        ;LOG_CLOSE
        gosub LogClose
        break
    case 20
        ;SRV_START
        gosub StartService
        break
    case 21
        ;SRV_STOP
        gosub StopService
        break
endselect

gosub ProgramExit

;----------------------------------------------------------------------
; SUBROUTINE: Initialize
;----------------------------------------------------------------------/
:Initialize
    gosub IsWinNT
    if Result
        ;Windows NT
        OS = 2
    else
        ;Windows 95
        OS = 1
    endif
    
    Task = 1
    
    if ProductionMode    
        HomeDir = DirHome()
    else
        HomeDir = "C:\Projects\wbt\PdcMeter\"
    endif  

    IniFile = "%HomeDir%pdcmeter.ini"
    if !FileExist(IniFile)
        MsgTxt = "Program initialization file not found"
        ErrorLevel = 1
        gosub ErrorExit
    endif

    ;holding down the shift key at startup reveals version information
    keycheck=IsKeyDown(@SHIFT)
    if keycheck == @YES
        gosub DisplayVerInfo
        gosub ProgramExit
    endif

    wildll = StrCat(HomeDir, "wbdbp32i.dll")
    net95dll = StrCat(HomeDir, "www9532i.dll")
    netntdll = StrCat(HomeDir, "wwwnt32i.dll")
    inetdll = StrCat(HomeDir, "wwwsk32i.dll")
        
    MsgTxt = "Required application file not found"
    if ProductionMode
        if !FileExist(wildll) 
            ErrorLevel = 1
            gosub ErrorExit
        endif
        if !FileExist(net95dll)
            ErrorLevel = 1
            gosub ErrorExit
        endif
        if !FileExist(netntdll)
            ErrorLevel = 1
            gosub ErrorExit
        endif
        if !FileExist(inetdll)
            ErrorLevel = 1
            gosub ErrorExit
        endif
    endif
    
    AddExtender(inetdll)
    if OS == 1
        AddExtender(net95dll)
        UserName = StrLower(w95GetUser(@default))
    endif
    if OS == 2
        AddExtender(netntdll)
        UserName = StrLower(wNTGetUser(@default))
    endif
    ComputerName = RegQueryValue(@REGMACHINE,"System\CurrentControlSet\control\ComputerName\ComputerName[ComputerName]")

    ;process the command line
    
    if Param0 > 1
        MsgTxt = "Invalid command line"
        ErrorLevel = 1
        gosub ErrorExit
    endif
    
    if Param0 == 0
        Param0 = 1
        Param1 = "APP_RUN"
    endif
        
    goodparam = @FALSE
    if Param1 == APP_RUN
        goodparam = @TRUE
        Task = 1
    endif
    if Param1 == APP_METER
        goodparam = @TRUE
        Task = 2
    endif
    if Param1 == LOG_STATS 
        goodparam = @TRUE
        Task = 3
    endif
    if Param1 == LOG_SHOW
        goodparam = @TRUE
        Task = 4
    endif
    if Param1 == LOG_RESET
        goodparam = @TRUE
        Task = 5
    endif
    if Param1 == LOG_OPEN
        goodparam = @TRUE
        Task = 6
    endif
    if Param1 == LOG_CLOSE
        goodparam = @TRUE
        Task = 7
    endif
    if Param1 == SRV_START 
        goodparam = @TRUE
        Task = 20
    endif
    if Param1 == SRV_STOP
        goodparam = @TRUE
        Task = 21
    endif

    AppParm = ""
    if !goodparam
        ;assume thing passed is a filname
        ;this will override the value in the ini file if one is given there 
        AppParm = Param1
    endif

    if Task > 1
        if OS <> 2
            MsgTxt = "Requested task only runs under Windows NT"
            ErrorLevel = 1
            gosub ErrorExit
        endif
    endif
return

;----------------------------------------------------------------------
; SUBROUTINE: StartService
;----------------------------------------------------------------------/
:StartService
    ;start metering and loop forever or until service stop request is received
    ;when installed as a service, the service stop request will unload this app
    ;based on the window title which is modified below for uniqueness

    ;read pdcmeter.ini file
    gosub ReadParamService
    gosub ParamDerived
    
    wtitle = "%PNAME%Service"
    if WinExist(wtitle)
        ErrorLevel = 2
        MsgTxt = "Service is already started"
        goto ErrorExit
    endif    
    
    WinTitle("", wtitle)
    
    ;must write to each application log file!!!
    a =StrFix(SRV_START, " ", 9)
    LogTxt = "%a%|%AppName%"
    gosub WriteLogFile

    if OpenLogOnStart
        ;must write to each application log file!!!
        gosub LogOpen
    endif

    while @TRUE
        ;meter all configured applications on each pass
        ;mechanism provided to return here if ErrorExit is called by any one application
        
        ;is it metering time
        p1 = MeterFrom
        p2 = MeterTo
        gosub QualifyTime
        if result
            ;meter away
            for meterc = 1 to MeterCount
                ErrorLevel = 0
                ;open application metering definition file
                IniFile = ItemExtract(meterc, ApplicationList, "|")
                gosub ParamSetup
                gosub ReadParamApplication
                gosub ReadParamServer
                gosub ParamFinish
                Yield()
            
                ;perform metering
                if ErrorLevel == 0
                    gosub MeterApp
                endif
                Yield()
            next
        else
            ;probably don't want to to this!!!
            a =StrFix(APP_MOFF, " ", 9)
            LogTxt = "%a%|%AppName%"
            gosub WriteLogFile
        endif                   
        
        ;wait
        TimeDelay(MeterInterval)
    endwhile

    ErrorLevel = 0
return

;----------------------------------------------------------------------
; SUBROUTINE: StopService
;----------------------------------------------------------------------/
:StopService
    ;read pdcmeter.ini file
    gosub ReadParamService
    gosub ParamDerived
    
    result = @FALSE    
    wtitle = "%PNAME%Service"
    
    if WinExist(wtitle)
        IntControl(47, wtitle, 0, 0, 0)
        result = @TRUE
    
        a =StrFix(SRV_STOP, " ", 9)
        ;probably want to do this to all logs!!!
        LogTxt = "%a%|%AppName%"
        gosub WriteLogFile
        
        if CloseLogOnStop
            ;probably want to do this to all logs!!!
            gosub LogClose
        endif
    endif
    
    if !result
        ErrorLevel = 2
        MsgTxt = "Unable to stop Service"
        gosub ErrorExit
    endif        
return

;----------------------------------------------------------------------
; SUBROUTINE: RunApp
;----------------------------------------------------------------------/
:RunApp    
    ; step 1 - get license count (maximum users)
    ; step 2 - get count of current users
    ; step 3 - run or abort based on count

    ;read pdcmeter.ini file
    gosub ReadParamApplication
    gosub ReadParamClient
    gosub ReadParamNotification
    gosub ParamDerived
    
    gosub ReadLicenseData
    
    if CurrentCount < MaxCount
        a =StrFix(APP_RUN, " ", 9)
        LogTxt = "%a%|%AppName%|username=%UserName%"
        gosub WriteLogFile

        LastError()
        ErrorMode(@OFF)
        Run(AppFileName, AppParm)
        ErrorMode(@CANCEL)
        return
    endif
    
    ;we are at or over the limit
    
    if CurrentCount == MaxCount
        MsgTxt = "Maximum number of licenses is currently in use for|%AppName%|Please try again later or contact your System Administrator"
        gosub DisplayMessage
        return
    endif

    if CurrentCount > MaxCount
        MsgTxt = "Maximum number of licenses has been exceeded for|%AppName%|Please contact your System Administrator"
        ErrorLevel = 4
        gosub ErrorExit
    endif        
return

;----------------------------------------------------------------------
; SUBROUTINE: MeterApp
;----------------------------------------------------------------------/
:MeterApp
    ;NET FILE sample output
    ;---------+---------+---------+---------+---------+---------+---------+----
    ;833931     C:\NTRESKIT\WINSCL.EXE                  ADKINSB               0     

    ;read pdcmeter.ini file
    gosub ReadParamApplication
    gosub ReadParamServer
    gosub ReadParamNotification
    gosub ParamDerived
    
    ;step 1 - read current license info to get MaxCount and ExceptionList
    gosub ReadLicenseData
    
    ;step 2 - run NET FILE command and write output to license directory
    RunWait("cmd.exe", "/c net file > %LicenseDirectory%netfile.txt")
    
    ;step 3 - parse netfile output looking for CheckString
    CurrentCount = 0
    UserList = ""
    hf = FileOpen("%LicenseDirectory%netfile.txt", "READ")
    while @TRUE
        s = FileRead(hf)
        if s == "*EOF*" then break
        p = StrIndexNc(s, CheckString, 1, @FWDSCAN)
        if p > 0
            CurrentCount = CurrentCount + 1
            UserList = StrCat(UserList, StrTrim(StrSub(s, 52, 20)), "|")
        endif
    endwhile
    FileClose(hf)
    
    ;step 4 - parse log in binary buffer rebuilding the user exception list
    gosub ReadLogFile
    ;for each name in UserList (netfile)
    ;if name is not in the buffer (license log contents)
    ;we have a user that has not used the wrapper to launch the app
    ic = ItemCount(UserList, "|")
    for i = 1 to ic
        ulname = StrCat("username=", StrLower(ItemExtract(i, UserList, "|")))
        ;Message("", ulname) 
        if BinaryIndexNc(binbuf, 1, ulname, @FWDSCAN) == 0
            ;bingo name from netfile is not in the license log
            ulname = StrReplace(ulname, "username=", "")
            ExceptionList = StrCat(ExceptionList, ulname, "|")
        endif
    next
    BinaryFree(binbuf)
    
    ;step 5 - write current user count & current user list
    gosub WriteLicenseData

    ;step 6 - append a metering record to Log File
    a =StrFix(APP_METER, " ", 9)
    LogTxt = "%a%|%AppName%|%CurrentCount%|%MaxCount%"
    gosub WriteLogFile    
return

;----------------------------------------------------------------------
; SUBROUTINE: RunStats
;----------------------------------------------------------------------/
:RunStats
    ;read pdcmeter.ini file
    gosub ReadParamApplication
    gosub ReadParamServer
    gosub ReadParamNotification
    gosub ParamDerived
    
    Output = "FILE"
    gosub SetupLogFiles
    
    ;compile summary information
    gosub BuildSummary
    if SendStats
        thelog = SummaryLog
        gosub SendReport
    endif    
return

;----------------------------------------------------------------------
; SUBROUTINE: ShowStats
;----------------------------------------------------------------------/
:ShowStats
    ;read pdcmeter.ini file
    gosub ReadParamApplication
    gosub ReadParamServer
    gosub ReadParamNotification
    gosub ParamDerived
    
    Output = "TEMP"
    gosub SetupLogFiles
    
    ;compile summary information
    gosub BuildSummary
    Run("notepad.exe", TmpSum)
    if SendShow
        thelog = TmpSum
        gosub SendReport
    endif    
return

;----------------------------------------------------------------------
; SUBROUTINE: CreateLogFiles
;----------------------------------------------------------------------/
:CreateLogFiles
    ;read pdcmeter.ini file
    gosub ReadParamApplication
    gosub ReadParamServer
    gosub ReadParamNotification
    gosub ParamDerived
    
    a =StrFix(LOG_RESET, " ", 9)
    header = StrCat(TimeYmdHms(), "|%a%|%AppName%")
    
    hf = FileOpen(LogFile, "WRITE")
    FileWrite(hf, header)
    FileClose(hf)
    
    hf = FileOpen(ErrorLog, "WRITE")
    FileWrite(hf, header)
    FileClose(hf)
    
    hf = FileOpen(CurrentDataFile, "WRITE")
    FileWrite(hf, header)
    FileWrite(hf, "0")
    FileWrite(hf, "")
    FileClose(hf)
    
    hf = FileOpen(ExceptionDataFile, "WRITE")
    FileWrite(hf, header)
    FileWrite(hf, "")
    FileClose(hf)
    
    Drop(header)    
return

;----------------------------------------------------------------------
; SUBROUTINE: LogOpen
;----------------------------------------------------------------------/
:LogOpen
    ;read pdcmeter.ini file
    gosub ReadParamApplication
    gosub ReadParamServer
    gosub ReadParamNotification
    gosub ParamDerived
    
    a =StrFix(LOG_OPEN, " ", 9)
    
    LogTxt = "%a%|%AppName%"
    gosub WriteLogFile

    ErrTxt = "%a%|%AppName%"
    gosub WriteErrorFile
return

;----------------------------------------------------------------------
; SUBROUTINE: LogClose
;----------------------------------------------------------------------/
:LogClose
    ;read pdcmeter.ini file
    gosub ReadParamApplication
    gosub ReadParamServer
    gosub ReadParamNotification
    gosub ParamDerived
    
    a =StrFix(LOG_CLOSE, " ", 9)
    
    LogTxt = "%a%|%AppName%"
    gosub WriteLogFile

    ErrTxt = "%a%|%AppName%"
    gosub WriteErrorFile
return

;----------------------------------------------------------------------
; SUBROUTINE: WriteLicenseData
;----------------------------------------------------------------------/
:WriteLicenseData
    if StrSub(UserList, StrLen(UserList), 1) == "|"
        UserList = StrSub(UserList, 1, StrLen(UserList) - 1)
    endif
    if StrSub(ExceptionList, StrLen(ExceptionList), 1) == "|"
        ExceptionList = StrSub(ExceptionList, 1, StrLen(ExceptionList) - 1)
    endif

    a =StrFix(LOG_RESET, " ", 9)
    header = StrCat(TimeYmdHms(), "|%a%|%AppName%")
    
    ;lock the license files
    LockFile = LicenseLockFile
    gosub LockFiles
    
    hf = FileOpen(CurrentDataFile, "WRITE")
    FileWrite(hf, header)
    FileWrite(hf, CurrentCount)
    FileWrite(hf, UserList)
    FileClose(hf)

    hf = FileOpen(ExceptionDataFile, "WRITE")
    FileWrite(hf, header)
    FileWrite(hf, ExceptionList)
    FileClose(hf)

    ;unlock the license files
    gosub UnLockFiles
    
    Drop(header)                                                                              
return

;----------------------------------------------------------------------
; SUBROUTINE: ReadLicenseData
;----------------------------------------------------------------------/
:ReadLicenseData
    ;lock the license files
    LockFile = LicenseLockFile
    gosub LockFiles
    
    ;read maximum user count
    hf = FileOpen(LicenseDataFile, "READ")
    MaxCount = FileRead(hf)
    FileClose(hf)

    ;read current data
    hf = FileOpen(CurrentDataFile, "READ")
    header = FileRead(hf)
    CurrentCount = FileRead(hf)
    UserList = FileRead(hf)
    FileClose(hf)
    
    ;read exception list
    hf = FileOpen(ExceptionDataFile, "READ")
    header = FileRead(hf)        
    ExceptionList = FileRead(hf)
    FileClose(hf)
                  
    ;unlock the license files
    gosub UnLockFiles
          
    Drop(header)                                                                        
return

;----------------------------------------------------------------------
; SUBROUTINE: ReadLogFile
;----------------------------------------------------------------------/
:ReadLogFile
    ;lock the log files
    LockFile = LogLockFile
    gosub LockFiles
    
    ;read log file into a binary buffer
    fs = FileSize(LogFile)
    binbuf = BinaryAlloc(fs)
    if binbuf == 0
        MsgTxt = "Critical Error: BinaryAlloc Failed"
        ErrorLevel = 2
        gosub ErrorExit
    else
        ;read the file into the buffer
        BinaryRead(binbuf, LogFile)
    endif    
    
    ;unlock the log files
    gosub UnLockFiles
return

;----------------------------------------------------------------------
; SUBROUTINE: WriteLogFile
;----------------------------------------------------------------------/
:WriteLogFile
    ;lock the log files
    LockFile = LogLockFile
    gosub LockFiles
    
    hf = FileOpen(LogFile, "APPEND")
    FileWrite(hf, StrCat(TimeYmdHms(), "|", LogTxt))
    FileClose(hf)
    
    ;unlock the log files
    gosub UnLockFiles                                                                              
return

;----------------------------------------------------------------------
; SUBROUTINE: WriteErrorFile
;----------------------------------------------------------------------/
:WriteErrorFile
    ;do not call LockFiles or loop might occur
    LockFile = ErrLockFile

    i = 0
    while @TRUE
        if FileExist(LockFile)
            TimeDelay(LOCK_DELAY)
            i = i + 1
            if i > LOCK_ATTEMPTS
                ;unable to get a lock, out of luck
                ;this should never happen :-)
                if Task > 19 then return
                exit
            endif
        else
            break
        endif
    endwhile
    
    ;create the requested lock file
    hf = FileOpen(LockFile, "WRITE")
    FileWrite(hf, "LOCKED")
    FileClose(hf)
    
    ;write to the error log    
    hf = FileOpen(ErrorLog, "APPEND")
    FileWrite(hf, StrCat(TimeYmdHms(), "|", ErrTxt))
    FileClose(hf)
    
    ;unlock the error log
    gosub UnLockFiles                                                                              
return

;----------------------------------------------------------------------
; SUBROUTINE: SetupLogFiles
;----------------------------------------------------------------------/
:SetupLogFiles
    ;lock the log file
    LockFile = LogLockFile
    gosub LockFiles
    
    ;rename the log file and  error log in preparation for statistical processing
    if Output == "FILE"
        FileRename(LogFile, BackupLog)
        FileRename(ErrorLog, BackupErr)
        gosub CreateLogFiles
    endif
    if Output == "TEMP"
        if FileExist(TmpLog) then FileDelete(TmpLog)
        FileCopy(LogFile, TmpLog, @FALSE)
        if FileExist(TmpErr) then FileDelete(TmpErr)
        FileCopy(ErrorLog, TmpErr, @FALSE)
    endif
                          
    ;unlock the log file
    gosub UnLockFiles                                                                                  
return

;----------------------------------------------------------------------
; SUBROUTINE: BuildSummary
;----------------------------------------------------------------------/
:BuildSummary
    ;first load up the current licensing information
    gosub ReadLicenseData
    
    ;get current service status
    LastError()
    ErrorMode(@OFF)
    cstate = wntSvcStatus("", "PdcMeter", 0, 2)
    ErrorMode(@CANCEL)
    if LastError() == 0
        switch cstate
            case 1   
                currentstate = "SERVICE_STOPPED"
                break
            case 2   
                currentstate = "SERVICE_START_PENDING"
                break
            case 3   
                currentstate = "SERVICE_STOP PENDING"
                break
            case 4   
                currentstate = "SERVICE_RUNNING"
                break
            case 5   
                currentstate = "SERVICE_CONTINUE_PENDING"
                break
            case 6   
                currentstate = "SERVICE_PAUSE_PENDING"
                break
            case 7   
                currentstate = "SERVICE_PAUSED"
                break
        endswitch
    else
        currentstate = "SERVICE_NOT_INSTALLED"    
    endif
    
    ms = ""
    me = ""
    md = ""
    ma = ""
    mc = 0
    ec = 0
    ul = ""
    ilm = 0
    iem = 0
    igm = 0

    if Output == "FILE"
        fn_log = BackupLog
        fn_err = BackupErr
        fn_sum = SummaryLog
    endif
    if Output == "TEMP"
        fn_log = TmpLog
        fn_err = TmpErr
        fn_sum = TmpSum
    endif
    
    hf = FileOpen(fn_log, "READ")
    s = FileRead(hf)
    ms = StrSub(s, 1, 19)
    FileClose(hf)

    ReadingLog = @FALSE
    hf = FileOpen(fn_log, "READ")
    while @TRUE
        s = FileRead(hf)
        if StrIndex(s, LOG_OPEN, 1, @FWDSCAN) > 0 then ReadingLog = @TRUE
        if StrIndex(s, LOG_CLOSE, 1, @FWDSCAN) > 0 then s = "*EOF*"
        if s == "*EOF*" then break
        if ReadingLog
            me = StrSub(s, 1, 19)
            ;process this line
            if StrIndex(s, "|%APP_METER%", 1, @FWDSCAN) > 0
                mc = mc + 1
                icc = ItemExtract(4, s, "|")
                if icc < MaxCount then ilm = ilm + 1
                if icc == MaxCount then iem = iem + 1
                if icc > MaxCount then igm =igm + 1
            endif
            if StrIndex(s, "|%APP_RUN%", 1, @FWDSCAN) > 0
                ec = ec + 1
                un = ItemExtract(4, s, "|")
                un = StrReplace(un, "username=", "")
                if StrIndex(ul, un, 1, @FWDSCAN) < 1
                    ul = StrCat(ul, un, "|")
                endif
            endif
        endif
    endwhile    
    FileClose(hf)

    md = TimeDiffSecs(me, ms)
    p1 = md
    p2 = mc
    gosub TimeDiffFormat
    md = retval1
    ma = retval2
    uc = ItemCount(ul, "|")    

    ;setup notification temporary varibles
    nof = "OFF"
    nom = "NO"
    nop = "NO"
    noe = "NO"
    if Notification then nof = "ON"
    if SendEmail then nom = "YES"
    if SendPage then nop = "YES"
    if EventLog then noe = "YES"

    ExceptionCount = ItemCount(ExceptionList, "|")
    
    hf = FileOpen(fn_sum, "WRITE")
    FileWrite(hf, "")
    FileWrite(hf, "PDCMETER")
    FileWrite(hf, StrCat(StrFix("  REPORT DATE", " ", 22), "= ", TimeYmdHms()))
    FileWrite(hf, StrCat(StrFix("  GENERATED BY", " ", 22), "= ", ComputerName, " ", UserName))

    FileWrite(hf, "")
    FileWrite(hf, "APPLICATION")
    FileWrite(hf, StrCat(StrFix("  NAME", " ", 22), "= %AppName%"))
    FileWrite(hf, StrCat(StrFix("  LICENSES", " ", 22), "= %MaxCount%"))
    FileWrite(hf, StrCat(StrFix("  FILE", " ", 22), "= ", StrLower(AppFile)))
    FileWrite(hf, StrCat(StrFix("  PARAMETERS", " ", 22), "= %AppParm%"))

    FileWrite(hf, "")
    FileWrite(hf, "NOTIFICATION")
    FileWrite(hf, StrCat(StrFix("  CURRENT STATE", " ", 22), "= %nof%"))
    FileWrite(hf, StrCat(StrFix("  EVENT LOG", " ", 22), "= %noe%"))
    FileWrite(hf, StrCat(StrFix("  PAGER", " ", 22), "= %nop%"))
    FileWrite(hf, StrCat(StrFix("  EMAIL", " ", 22), "= %nom%"))

    FileWrite(hf, "")
    FileWrite(hf, "CURRENT INTERVAL")
    FileWrite(hf, StrCat(StrFix("  USERS", " ", 22), "= %CurrentCount%"))
    j = ItemCount(UserList, "|")
    for i = 1 to j
        FileWrite(hf, StrCat("  ===> ", ItemExtract(i, UserList, "|")))
    next

    FileWrite(hf, "")
    FileWrite(hf, "METERING")
    FileWrite(hf, StrCat(StrFix("  START", " ", 22), "= %ms%"))
    FileWrite(hf, StrCat(StrFix("  LAST", " ", 22), "= %me%"))
    FileWrite(hf, StrCat(StrFix("  ELAPSED", " ", 22), "= %md%"))
    FileWrite(hf, StrCat(StrFix("  INTERVAL (AVG)", " ", 22), "= %ma%"))
    FileWrite(hf, StrCat(StrFix("  NUMBER OF INTERVALS", " ", 22), "= %mc%"))
    FileWrite(hf, StrCat(StrFix("  INTERVALS LT MAX LC", " ", 22), "= %ilm%"))
    FileWrite(hf, StrCat(StrFix("  INTERVALS EQ MAX LC", " ", 22), "= %iem%"))
    FileWrite(hf, StrCat(StrFix("  INTERVALS GT MAX LC", " ", 22), "= %igm%"))
    FileWrite(hf, StrCat(StrFix("  INVOCATIONS", " ", 22), "= %ec%"))
    FileWrite(hf, StrCat(StrFix("  LICENSE EXCEPTIONS", " ", 22), "= %ExceptionCount%"))
    for i = 1 to ExceptionCount
        FileWrite(hf, StrCat("  ===> ", ItemExtract(i, ExceptionList, "|")))
    next
    FileWrite(hf, StrCat(StrFix("  UNIQUE USER COUNT", " ", 22), "= %uc%"))
    for i = 1 to uc
        FileWrite(hf, StrCat("  ===> ", ItemExtract(i, ul, "|")))
    next

    FileWrite(hf, "")
    FileWrite(hf, "SERVICE")
    FileWrite(hf, StrCat(StrFix("  SERVICE NAME", " ", 22), "= PdcMeter"))
    FileWrite(hf, StrCat(StrFix("  CURRENT STATE", " ", 22), "= %currentstate%"))
    FileWrite(hf, StrCat(StrFix("  METER INTERVAL", " ", 22), "= <-registry"))
    FileWrite(hf, StrCat(StrFix("  METER FROM", " ", 22), "= <-registry"))
    FileWrite(hf, StrCat(StrFix("  METER TO", " ", 22), "= <-registry"))
    FileWrite(hf, StrCat(StrFix("  APPLICATIONS", " ", 22), "= <-registry"))
    ;for i = 1 to al
    ;    FileWrite(hf, StrCat("  ===> ", ItemExtract(i, al, "|")))
    ;next

    FileWrite(hf, "")
    FileWrite(hf, "ERROR LOG")
    FileWrite(hf, "  RESPONSE=0 (WAITING RESPONSE)")
    ReadingLog = @FALSE
    hf2 = FileOpen(fn_err, "READ")
    while @TRUE
        s = FileRead(hf2)
        if StrIndex(s, LOG_OPEN, 1, @FWDSCAN) > 0 then ReadingLog = @TRUE
        if StrIndex(s, LOG_CLOSE, 1, @FWDSCAN) > 0 then s = "*EOF*"
        if s == "*EOF*" then break        
        if StrIndex(s, "RESPONSE=0", 1, @FWDSCAN) > 0
            FileWrite(hf, StrCat("  ===> ", s))
        endif
    endwhile    
    FileClose(hf2)
    
    FileWrite(hf, "")
    FileWrite(hf, "ERROR LOG")
    FileWrite(hf, "  RESPONSE=1 (RESPONDED)")
    ReadingLog = @FALSE
    hf2 = FileOpen(fn_err, "READ")
    while @TRUE
        s = FileRead(hf2)
        if StrIndex(s, LOG_OPEN, 1, @FWDSCAN) > 0 then ReadingLog = @TRUE
        if StrIndex(s, LOG_CLOSE, 1, @FWDSCAN) > 0 then s = "*EOF*"
        if s == "*EOF*" then break
        if StrIndex(s, "RESPONSE=1", 1, @FWDSCAN) > 0
            FileWrite(hf, StrCat("  ===> ", s))
        endif
    endwhile
    FileWrite(hf, "")
    FileClose(hf2)

    FileClose(hf)

    ;this is ug-ly
    Drop(nof, nom, noe, nop, ul, uc, ec, mc, md, ma, me, ms, fn_log, fn_err, fn_sum, ilm, iem, igm, retval1, retval2)        
return

;----------------------------------------------------------------------
; SUBROUTINE: LockFiles
;----------------------------------------------------------------------/
:LockFiles
    ;see if somebody else has a lock already
    i = 0
    while @TRUE
        if FileExist(LockFile)
            TimeDelay(LOCK_DELAY)
            i = i + 1
            if i > LOCK_ATTEMPTS
                ;unable to grab a lock
                MsgTxt = "Unable to obtain file lock|Please contact your System Administrator"
                ErrorLevel = 3
                gosub ErrorExit
            endif
        else
            break
        endif
    endwhile
    
    ;create the requested lock file
    hf = FileOpen(LockFile, "WRITE")
    FileWrite(hf, "LOCKED")
    FileClose(hf)
return

;----------------------------------------------------------------------
; SUBROUTINE: UnLockFiles
;----------------------------------------------------------------------/
:UnLockFiles
    if FileExist(LockFile) then FileDelete(LockFile)       
return

;----------------------------------------------------------------------
; SUBROUTINE: ParamSetup
;----------------------------------------------------------------------/
:ParamSetup
    e1 = "Entry invalid"
    e2 = "Directory not found"
    e3 = "File not found"
    e4 = "Notification entry invalid"
    e5 = "Service entry invalid"
return

;----------------------------------------------------------------------
; SUBROUTINE: ParamClean
;----------------------------------------------------------------------/
:ParamClean
    Drop(en, e1, e2, e3, e4, en)    
return

;----------------------------------------------------------------------
; SUBROUTINE: ReadParamApplication
;----------------------------------------------------------------------/
:ReadParamApplication
    gosub ParamSetup
    Section = "Application"
    
    AppName = IniReadPvt(Section, "AppName", "", IniFile)        
    AppFile = IniReadPvt(Section, "AppFile", "", IniFile)
    if StrLen(AppParm) < 1
        AppParm = IniReadPvt(Section, "AppParm", "", IniFile)        
    endif

    en = 0
    if StrLen(AppName) < 1 then en = en + 1 
    if StrLen(AppFile) < 1 then en = en + 1 
    if en > 0
        ErrorLevel = 2
        MsgTxt = "Error %en% - %e1%"
        gosub ErrorExit
    endif

    gosub ParamClean    
return

;----------------------------------------------------------------------
; SUBROUTINE: ReadParamClient
;----------------------------------------------------------------------/
:ReadParamClient
    gosub ParamSetup
    Section = "Run"
    
    AppPath = IniReadPvt(Section, "AppPath", "", IniFile)
    LicensePath = IniReadPvt(Section, "LicensePath", "", IniFile)
    
    gosub ParamValidateDf    
    gosub ParamClean    
return
    
;----------------------------------------------------------------------
; SUBROUTINE: ReadParamServer
;----------------------------------------------------------------------/
:ReadParamServer
    gosub ParamSetup
    Section = "Meter"
    
    AppPath = IniReadPvt(Section, "AppPath", "", IniFile)
    LicensePath = IniReadPvt(Section, "LicensePath", "", IniFile)
    LogEventPath = IniReadPvt(Section, "LogEventPath", "", IniFile)

    gosub ParamValidateLe
    gosub ParamValidateDf
    
    gosub ParamClean    
return

;----------------------------------------------------------------------
; SUBROUTINE: ParamValidateLe
;----------------------------------------------------------------------/
:ParamValidateLe
    ;validate logevent path
    if StrSub(StrLen(LogEventPath), 1, 1) <> "\"
        LogEventPath = "%LogEventPath%\"
    endif    
    LogEventPath = StrUpper(StrCat(LogEventPath, "logevent.exe"))
    if !FileExist(LogEventPath) 
        ErrorLevel = 2
        MsgTxt = "File not found|%LogEventPath%"
        gosub ErrorExit
    endif
return    

;----------------------------------------------------------------------
; SUBROUTINE: ParamValidateDf
;----------------------------------------------------------------------/
:ParamValidateDf
    ;validate required entries have been made
    en = 16
    if StrLen(AppPath) < 1 then en = en + 1 
    if StrLen(LicensePath) < 1 then en = en + 1 
    if en > 16
        ErrorLevel = 2
        MsgTxt = "Error %en% - %e1%"
        gosub ErrorExit
    endif

    ;fix-up path strings
    if StrSub(StrLen(AppPath), 1, 1) <> "\"
        AppPath = "%AppPath%\"
    endif    
    if StrSub(StrLen(LicensePath), 1, 1) <> "\"
        LicensePath = "%LicensePath%\"
    endif    

    ;set derived values
    AppFileName = StrUpper(StrCat(AppPath, AppFile))
    CheckString = AppFile
    LogDirectory = StrUpper(StrCat(LicensePath, "log\"))
    LockDirectory = StrUpper(StrCat(LicensePath, "lock\"))
    LicenseDirectory = StrUpper(StrCat(LicensePath, "license\"))
    LicenseDataFile = StrUpper(StrCat(LicenseDirectory, "license.dat"))
    CurrentDataFile = StrUpper(StrCat(LicenseDirectory, "current.dat"))
    ExceptionDataFile = StrUpper(StrCat(LicenseDirectory, "exception.dat"))
    LogFile = StrUpper(StrCat(LogDirectory, "license.log"))
    ErrorLog = StrUpper(StrCat(LogDirectory, "error.log"))
                
    ;check directories
    en = 32
    if !DirExist(LogDirectory) then en = en + 1
    if !DirExist(LockDirectory) then en = en + 1
    if !DirExist(LicenseDirectory) then en = en + 1
    if en > 32
        ErrorLevel = 2
        MsgTxt = "Error %en% - %e2%"
        gosub ErrorExit
    endif

    ;check files
    en = 48
    if !FileExist(AppFileName) then en = en + 1
    if !FileExist(LicenseDataFile) then en = en + 1
    if !FileExist(CurrentDataFile) then en = en + 1
    if !FileExist(ExceptionDataFile) then en = en + 1
    if !FileExist(LogFile) then en = en + 1
    if !FileExist(ErrorLog) then en = en + 1
    if en > 48
        ErrorLevel = 2
        MsgTxt = "Error %en% - %e3%"
        gosub ErrorExit
    endif
return

;----------------------------------------------------------------------
; SUBROUTINE: ReadParamNotification
;----------------------------------------------------------------------/
:ReadParamNotification
    gosub ParamSetup
    Section = "Notification"

    Notification = IniReadPvt(Section, "Notification", "", IniFile)
    EventLog = IniReadPvt(Section, "EventLog", "", IniFile)
    AlertList = IniReadPvt(Section, "AlertList", "", IniFile)
    FromAddress = IniReadPvt(Section, "FromAddress", "", IniFile)
    ReportAddress = IniReadPvt(Section, "ReportAddress", "", IniFile)
    PagerKeyList = IniReadPvt(Section, "PagerKeyList", "", IniFile)
    NotificationText = IniReadPvt(Section, "NotificationText", "", IniFile)
    MailServer = IniReadPvt(Section, "MailServer", "", IniFile)
    PagingAddress = IniReadPvt(Section, "PagingAddress", "", IniFile)
    SendEmail = IniReadPvt(Section, "SendEmail", "", IniFile)
    SendPage = IniReadPvt(Section, "SendPage", "", IniFile)
    SendShow = IniReadPvt(Section, "SendShow", "", IniFile)
    SendStats = IniReadPvt(Section, "SendStats", "", IniFile)

    ;validate required entries have been made
    en = 64
    if StrLen(Notification) < 1 then en = en + 1
    if StrLen(EventLog) < 1 then en = en + 1
    if StrLen(AlertList) < 1 then en = en + 1
    if StrLen(FromAddress) < 1 then en = en + 1
    if StrLen(ReportAddress) < 1 then en = en + 1
    if StrLen(PagerKeyList) < 1 then en = en + 1
    if StrLen(NotificationText) < 1 then en = en + 1
    if StrLen(MailServer) < 1 then en = en + 1
    if StrLen(PagingAddress) < 1 then en = en + 1
    if StrLen(SendEmail) < 1 then en = en + 1
    if StrLen(SendPage) < 1 then en = en + 1
    if StrLen(SendShow) < 1 then en = en + 1
    if StrLen(SendStats) < 1 then en = en + 1
    if en > 64
        ErrorLevel = 2
        MsgTxt = "Error %en% - %e4%"
        gosub ErrorExit
    endif

    en = 80
    if (Notification == "YES") || (Notification == "NO")
        if Notification == "YES"
            Notification = @TRUE
        else
            Notification = @FALSE
        endif
    else
        en = en + 1
    endif
    if (EventLog == "YES") || (EventLog == "NO")
        if EventLog == "YES"
            EventLog = @TRUE
        else
            EventLog = @FALSE
        endif
    else
        en = en + 1
    endif
    if (SendEmail == "YES") || (SendEmail == "NO")
        if SendEmail == "YES"
            SendEmail = @TRUE
        else
            SendEmail = @FALSE
        endif
    else
        en = en + 1
    endif
    if (SendPage == "YES") || (SendPage == "NO")
        if SendPage == "YES"
            SendPage = @TRUE
        else
            SendPage = @FALSE
        endif
    else
        en = en + 1
    endif
    if (SendShow == "YES") || (SendShow == "NO")
        if SendShow == "YES"
            SendShow = @TRUE
        else
            SendShow = @FALSE
        endif
    else
        en = en + 1
    endif
    if (SendStats == "YES") || (SendStats == "NO")
        if SendStats == "YES"
            SendStats = @TRUE
        else
            SendStats = @FALSE
        endif
    else
        en = en + 1
    endif
    if en > 80
        ErrorLevel = 2
        MsgTxt = "Error %en% - %e4%"
        gosub ErrorExit
    endif

    ;list counts must be identical
    alc = ItemCount(AlertList, "|")
    klc = ItemCount(PagerKeyList, "|")
    if alc <> klc
        ErrorLevel = 2
        MsgTxt = "Error 97 %e4%|List counts are invalid"
        gosub ErrorExit
    endif
    Drop(alc, klc)

    gosub ParamClean    
return

;----------------------------------------------------------------------
; SUBROUTINE: ReadParamService
;----------------------------------------------------------------------/
:ReadParamService
    gosub ParamSetup    
    Section = "Service"

    AppName = "PdcMeter Service"
    AppFile = ""
    AppParm = ""

    AppPath = IniReadPvt(Section, "AppPath", "", IniFile)
    LicensePath = IniReadPvt(Section, "LicensePath", "", IniFile)
    LogEventPath = IniReadPvt(Section, "LogEventPath", "", IniFile)

    gosub ParamValidateLe
    gosub ParamValidateDf

    MeterInterval = IniReadPvt(Section, "MeterInterval", "", IniFile)
    MeterFrom = IniReadPvt(Section, "MeterFrom", "", IniFile)
    MeterTo = IniReadPvt(Section, "MeterTo", "", IniFile)
    OpenLogOnStart = IniReadPvt(Section, "OpenLogOnStart", "", IniFile)
    CloseLogOnStop = IniReadPvt(Section, "CloseLogOnStop", "", IniFile)

    en  = 112
    if !IsNumber(MeterInterval)
        en = en + 1
        MsgTxt = "Error %en% - %e5%"
        ErrorLevel = 2
        gosub ErrorExit
    endif

    ct1 = MeterFrom
    ct2 = MeterTo
    gosub CheckTime
    if !result
        en = en + 1
        MsgTxt = "Error %en% - %e5%"
        ErrorLevel = 2
        gosub ErrorExit
    endif
    
    en = 128
    if (OpenLogOnStart == "YES") || (OpenLogOnStart == "NO")
        if OpenLogOnStart == "YES"
            OpenLogOnStart = @TRUE
        else
            OpenLogOnStart = @FALSE
        endif
    else
        en = en + 1
    endif
    if (CloseLogOnStop == "YES") || (CloseLogOnStop == "NO")
        if CloseLogOnStop == "YES"
            CloseLogOnStop = @TRUE
        else
            CloseLogOnStop = @FALSE
        endif
    else
        en = en + 1
    endif
    if en > 128
        ErrorLevel = 2
        MsgTxt = "Error %en% - %e5%"
        gosub ErrorExit
    endif

    ;read the ApplicationList and set the MeterCount
    en = 144
    MeterCount = 0
    ApplicationList = ""
    i = 1
    while @TRUE
        a = IniReadPvt(Section, "Application%i%", "", IniFile)
        if StrLen(a) > 0
            MeterCount = MeterCount + 1
            ApplicationList = StrCat(ApplicationList, a, "|")
        else
            break
        endif
        
        i = i + 1
    endwhile
    if MeterCount < 1
        en = en + 1
        MsgTxt = "Error %en% - %e5%"
        ErrorLevel = 2
        gosub ErrorExit
    endif

    gosub ParamClean    
return

;----------------------------------------------------------------------
; SUBROUTINE: ParamDerived
;----------------------------------------------------------------------/
:ParamDerived
    LicenseLockFile = StrCat(LockDirectory, LicenseLock)
    LogLockFile = StrCat(LockDirectory, LogLock)
    ErrLockFile = StrCat(LockDirectory, ErrLock)

    gosub GetTimeFn

    BackupLog =  StrCat(LogDirectory, "Reports\_", TimeFn, "_LOG_BAK.txt")
    BackupErr = StrCat(LogDirectory, "Reports\_", TimeFn, "_ERR_BAK.txt")
    SummaryLog = StrCat(LogDirectory, "Reports\_", TimeFn, "_REPORT.txt")        

    TmpLog = StrCat(LogDirectory, "Reports\_PDCMETER_LOG_TMP.txt")
    TmpErr = StrCat(LogDirectory, "Reports\_PDCMETER_ERR_TMP.txt")
    TmpSum = StrCat(LogDirectory, "Reports\_PDCMETER_CUR_TMP.txt")        
    
    if !DirExist(StrCat(LogDirectory, "Reports"))
        DirMake(StrCat(LogDirectory, "Reports"))
    endif    
return

;----------------------------------------------------------------------
; SUBROUTINE: IsWinNT
;----------------------------------------------------------------------/
:IsWinNT
    Result = @TRUE
    platform=WinMetrics(-4)
    if platform <> 4
        Result = @FALSE
    endif
    Drop(platform)    
return    

;----------------------------------------------------------------------
; SUBROUTINE: CheckTime                                              
;----------------------------------------------------------------------/
:CheckTime

    result = @FALSE
    i = 1
    while IsDefined(ct%i%)
        if StrLen(ct%i%) <> 5 then return
        if StrSub(ct%i%, 3, 1) <> ":" then return
        s1 = StrSub(ct%i%, 1, 2)
        s2 = StrSub(ct%i%, 4, 2)
        StrReplace(s1, "0", "")
        StrReplace(s2, "0", "")
        if !IsNumber(s1) then return
        if !IsNumber(s2) then return
        if s1 > 23 then return
        if s2 > 59 then return
        i = i + 1
    endwhile
    j = i - 1
    result = @TRUE

    ;cleanup
    for i = 1 to j
        Drop(ct%i%)
    next
    Drop(i, j, s1, s2)    
return

;----------------------------------------------------------------------
; SUBROUTINE: QualifyTime
;----------------------------------------------------------------------/
:QualifyTime
    ;assumes valid from-time in p1, to-time in p2, and current-time in p3
    ;valid format is 00:00 (midnight) to 23:59
    ;time window cannot span midnight
    result = @FALSE
    
    p3 = StrSub(TimeYmdHms(), 12, 5)
    p3 = StrReplace(p3, ":", "")
    p2 = StrReplace(p2, ":", "")
    p1 = StrReplace(p1, ":", "")
    
    if (p1 < p3) && (p3 < p2) then result = @TRUE
    ;a = StrCat(" p1(from)=", p1, " p2(to)=", p2, " p3(current)= ", p3)
    ;LogTxt = a
    ;gosub WriteLogFile        
return

;----------------------------------------------------------------------
; SUBROUTINE: GetTimeFn
;----------------------------------------------------------------------/
:GetTimeFn    
    TimeFn = TimeYmdHms()
    TimeFn = StrReplace(TimeFn, ":", "")    
return

;----------------------------------------------------------------------
; SUBROUTINE: TimeDiffFormat
;----------------------------------------------------------------------/
:TimeDiffFormat
    sd = 86400
    sh = 3600
    sm = 60    

    for i = 1 to 2
        if i == 1 then x = p1
        if (i == 2) && (p2 > 0) 
            x = p1 / p2
        else
            x = 0
        endif
        ;format seconds to "DD:HH:MM:SS" string
        d = 0
        h = 0
        m = 0
        s = 0
        if x / sd > 0
            d = x / sd
            x = x mod sd
        endif
        if x / sh > 0
            h = x / sh
            x = x mod sh
        endif
        if x / sm > 0
            m = x / sm
            x = x mod sm
        endif
        s = x
        retval = StrCat(StrFixLeft(d, "0", 2), ":", StrFixLeft(h, "0", 2), ":", StrFixLeft(m, "0", 2), ":", StrFixLeft(s, "0", 2))
        if i == 1 then retval1 = retval
        if i == 2 then retval2 = retval
    next
    
    Drop(p1, p2, sd, sh, sm, d, h, m, s, x, retval)    
return

;----------------------------------------------------------------------
; SUBROUTINE: DisplayMessage
;----------------------------------------------------------------------/
:DisplayMessage

    s = StrReplace(MsgTxt, "|", C2)
        
    if Task < 2
        ;RUN
        if IsDefined(AppName)
            if StrLen(AppName) > 1 then PNAME = AppName
        endif
        
        Message(PNAME, s)
    else
        ;all other options
        Display(3, PNAME, s)
    endif
return    

;----------------------------------------------------------------------
; SUBROUTINE: SendNotification
;----------------------------------------------------------------------/
:SendNotification    
    ;set up message text
    NotificationText = StrCat("%PNAME%: ", NotificationText, " ERROR=%ErrorLevel%")
    nerrtxt = "%PNAME%: Error Sending Notification"
    subject = "%PNAME%: Alert"

    ;write to NT Event Log
    if EventLog    
        if Task > 1
            Run(LogEventPath, StrCat('"', NotificationText, '"'))
        endif
    endif

    ;this is how many notifications we will need to send out    
    ic = ItemCount(AlertList, "|")

    ;send email notifications, one per
    ;if unsuccessful, record in the NT Event Log
    if SendEmail
        for i = 1 to ic
            ;for each operator on the alert list
            toaddr = ItemExtract(i, AlertList, "|")
            ErrorMode(@OFF)
            result = smtpSendText(MailServer, FromAddress, toaddr, subject, NotificationText)
            ErrorMode(@CANCEL)
            if !result
                if Task > 1
                    Run(LogEventPath, StrCat('"', nerrtxt, '"'))
                endif
            endif
        next    
    endif
    
    ;send pager notifications, one-shot
    ;if unsuccessful, record in the NT Event Log
    if SendPage
        key = ""
        for i = 1 to ic
            ;for each operator on the alert list                            
            k = ItemExtract(i, PagerKeyList, "|")
            key = StrCat(key, k, ",")
        next
        key = StrSub(key, 1, StrLen(key) - 1)
        ptext = StrCat("key: ", key, @CRLF, "msg: ", NotificationText)
        ErrorMode(@OFF)
        result = smtpSendText(MailServer, FromAddress, PagingAddress, subject, ptext)
        ErrorMode(@CANCEL)
        if !result
            if Task > 1
                Run(LogEventPath, StrCat('"', nerrtxt, '"'))
            endif
        endif
    endif
return

;----------------------------------------------------------------------
; SUBROUTINE: SendReport
;----------------------------------------------------------------------/
:SendReport    
    nerrtxt = "%PNAME%: Error Sending Statistics"
    subject = "%PNAME%: Statistics Report"
    
    if Notification
        ErrorMode(@OFF)
        result = smtpSendFile(MailServer, FromAddress, ReportAddress, subject, p1)
        ErrorMode(@CANCEL)
        if !result
            ;report can only be run from NT
            Run(LogEventPath, StrCat('"', nerrtxt, '"'))
        endif
    endif    
return

;----------------------------------------------------------------------
; SUBROUTINE: DisplayVerInfo
;----------------------------------------------------------------------/
:DisplayVerInfo
    MsgTxt = VERINFO
    gosub DisplayMessage
return

;----------------------------------------------------------------------
; SUBROUTINE: ErrorExit
;----------------------------------------------------------------------/
:ErrorExit
    IntControl(1000, ErrorLevel, 0, 0, 0)
    gosub DisplayMessage
    if ErrorLevel > 2
        MsgTxt = StrReplace(MsgTxt, "|", "@")
        ErrTxt = "ERROR|RESPONSE=0|ERRORLEVEL=%ErrorLevel%|%MsgTxt%"
        gosub WriteErrorFile
        if Notification
            gosub SendNotification
        endif
        ;if running as a service, do not exit
        if Task > 19 then return
    endif
    exit    
return    

;----------------------------------------------------------------------
; SUBROUTINE: ProgramExit
;----------------------------------------------------------------------/
:ProgramExit
    IntControl(1000, ErrorLevel, 0, 0, 0)
    exit    
return    

;----------------------------------------------------------------------
; SAMPLE INI FILE (save and remove semi-colons)
;----------------------------------------------------------------------/
;[Application]
;AppName=Symantec Visual Page
;AppFile=vpage.exe
;AppParms=
;
;[Run]
;AppPath=c:\app
;LicensePath=c:\app
;
;[Meter]
;AppPath=c:\app
;LicensePath=c:\app
;LogEventPath=c:\app
;
;[Notification]
;Notification=NO
;EventLog=YES
;AlertList=name1@yourdomain.com|name2@yourdomain.com
;PagerKeyList=pagerkey1|pagerkey2
;FromAddress=yourname@yourdomain.com
;ReportAddress=yourname@yourdomain.com
;NotificationText=An Error has occurred metering Symantec Visual Page. Please review the application error log for details.
;MailServer=mail.host.com
;PagingAddress=page@pager.host.com
;SendEmail=YES
;SendPage=YES
;SendShow=YES
;SendStats=YES
;
;[Service]
;MeterInterval=300
;MeterFrom=06:00
;MeterTo=18:00
;OpenLogOnStart=YES
;CloseLogOnStop=YES
;Application1=c:\app\pdcmeter.ini
;Application2=
;Application3=



Article ID:   W13798
Filename:   Software Metering Example.txt