OutlookMonitor.ps1


<#PSScriptInfo
 
.VERSION 5.1.2
 
.GUID 992ee67a-f846-4563-9a00-18291f13798c
 
.AUTHOR julianzs
 
.COMPANYNAME
 
.COPYRIGHT
 
.TAGS
 
.LICENSEURI
 
.PROJECTURI
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
 
 
.PRIVATEDATA
 
#>
 










<#
 
.DESCRIPTION
To install, run PowerShell.exe -executionpolicy bypass -command "& .\OutlookMonitor.ps1 -install" in an administrative command prompt window"
 
#>
 
[CmdletBinding()]
Param(
    #Do not hide Powershell console so you can see error and debug output
    [switch]$doNotHideConsole,
    #Install script and create scheduled task
    [switch]$install,
    #Uninstall Install script and remove scheduled task
    [switch]$uninstall
)

Begin {
    If ($PSBoundParameters['Debug']) {
        $DebugPreference = 'Continue'
    }

    ##########Types and objects for generating actionable messages##########

#Template for Actionable Message JSON
$jsonTemplate = @'
{
    "type": "AdaptiveCard",
    "originator": "c0c27934-de27-4cbf-8624-d048c9fe505b",
    "body": [
        {
            "type": "Container",
            "spacing": "None",
            "style": "emphasis",
            "items": [
                {
                    "type": "ColumnSet",
                    "columns": [
                        {
                            "type": "Column",
                            "items": [
                                {
                                    "type": "TextBlock",
                                    "text": "Please help us make meetings better at Microsoft",
                                    "wrap": true,
                                    "size": "Large"
                                }
                            ],
                            "width": "stretch",
                            "padding": "None"
                        },
                        {
                            "type": "Column",
                            "items": [
                                {
                                    "type": "Image",
                                    "id": "4b3986aa-ca62-8df9-f4c9-5d9bc8585978",
                                    "url": "https://docs.microsoft.com/en-us/workplace-analytics/images/icon-queries.png",
                                    "horizontalAlignment": "Right"
                                }
                            ],
                            "width": "auto",
                            "padding": "None",
                            "verticalContentAlignment": "Center"
                        }
                    ],
                    "padding": "None"
                }
            ],
            "padding": "Default",
            "id": "header"
        },
        {
            "type": "Container",
            "items": [
                {
                    "type": "TextBlock",
                    "size": "Medium",
                    "weight": "Bolder",
                    "text": "Which of these meetings did you or will you attend?",
                    "wrap": true
                }
            ],
            "padding": "Default",
            "spacing": "None",
            "separator": true,
            "id": "question"
        },
        {
            "type": "Container",
            "items": [
                {
                    "type": "Input.ChoiceSet",
                    "id": "meetingList",
                    "choices": [
                        {
                            "title": "Cat",
                            "value": "Dog"
                        },
                        {
                            "title": "Cat2",
                            "value": "Dog2"
                        }
                    ],
                    "style": "expanded",
                    "isMultiSelect": true,
                    "isRequired": true
                },
                {
                    "type": "ColumnSet",
                    "id": "2b0cc00e-5f5c-0779-597f-a0511b95842f",
                    "columns": [
                        {
                            "type": "Column",
                            "id": "93ee484f-6c32-2b35-f9ff-fb2b1d51a22a",
                            "padding": "None",
                            "width": "auto",
                            "items": [
                                {
                                    "type": "ActionSet",
                                    "actions": [
                                        {
                                            "type": "Action.Http",
                                            "title": "Submit",
                                            "method": "POST",
                                            "url": "https://prod-02.westcentralus.logic.azure.com:443/workflows/9dbce91ff591424889c7c5820b33888d/triggers/manual/paths/invoke?api-version=2016-06-01&sp=%2Ftriggers%2Fmanual%2Frun&sv=1.0&sig=FMv4psfYoQVgasWtECMDiGMuJ6qPhUjCxmQ6V6RFup4",
                                            "body":"",
                                            "headers": [
                                                {
                                                    "name": "Authorization",
                                                    "value":""
                                                }
                                            ]
                                        }
                                    ],
                                    "id": "submitButton"
                                }
                            ],
                            "horizontalAlignment": "Center"
                        }
                    ],
                    "padding": "None"
                },
                {
                    "type": "Container",
                    "id": "707c08bc-3c5d-0671-4f63-ae2ff4209e5e",
                    "padding": "None",
                    "items": [
                        {
                            "type": "TextBlock",
                            "id": "b902c86f-851a-a420-4f30-a57fcc87f2a9",
                            "text": "Our goal as the Workplace Intelligence Team is to help you be more productive. To do that, we are gathering information on how you use your calendar and which meetings you attend. We'll use it to help you and our customers optmize how much time is spent in meetings and focusing on personal work. Thanks for taking the time to help.",
                            "wrap": true
                        }
                    ],
                    "separator": true,
                    "spacing": "ExtraLarge"
                }
            ],
            "padding": "Default",
            "spacing": "None",
            "id": "meetingChoices",
            "separator": true
        }
    ],
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "version": "1.0",
    "padding": "None",
    "@type": "AdaptiveCard",
    "@context": "http://schema.org/extensions",
    "fallbackText": "If you can't see this message on your mobile device, please view it in desktop Outlook. This is a known issue that will be fixed soon. "
}
'@


#Header prepended to JSON to generate HTML message
$HTMLHeader = @"
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <script type="application/adaptivecard+json">
"@


#Footer appended to JSON to generate HTML message
$HTMLFooter = @"
 </script>
</head>
<body>
</body>
</html>
"@


    ##########Functions for generating actionable messages##########
    
    #Get calendar folder object
    Function Get-OutlookCalendarFolder {
        #Add-type -assembly "Microsoft.Office.Interop.Outlook" | out-null
        $olFolders = "Microsoft.Office.Interop.Outlook.OlDefaultFolders" -as [type]
        #$outlook = new-object -comobject outlook.application
        $namespace = $outlook.GetNameSpace("MAPI")
        $namespace.getDefaultFolder($olFolders::olFolderCalendar)
    }
      
    #Get Outlook calendar appointments for today
    Function Get-OutlookCalendarAppointmentsToday
    {      
        $folder = Get-OutlookCalendarFolder

        #Calculate today as 24 hours starting at midnight of the current day
        $Start = (Get-Date -Hour 0 -Minute 00 -Second 00).ToShortDateString() + " 00:00"
        $End = (Get-Date).AddDays(+1).ToShortDateString() + " 00:00"

        #$filter = "[MessageClass]='IPM.Appointment' AND [Start] >= '$Start' AND [End] <= '$End'"
        $filter = "[MessageClass]='IPM.Appointment' AND (([Start] >= '$Start' AND [End] <= '$End') OR ([Start] <= '$Start' AND [End] >= '$Start') OR ([Start] <= '$end' AND [End] >= '$start'))"
        Write-Debug -Message "Outlook calendar filter being used is $($filter)"

        $Appointments =  $folder.Items
        Release-OutlookComObject -comObject $folder
        $Appointments.IncludeRecurrences = $true
        $Appointments.Sort("[Start]")
        $Appointments.restrict($filter)
        #Write-Debug -Message "Appointments found on calendar $($Appointments | Select-Object subject,start,end)"
    }

    #Get overlapping meetings in today's appointments
###BUGBUG I don't think it captures meetings that don't start and end today properly.
   Function Get-OverlappingMeetings {
        Param(
            $appointmentList
        )

        $appointmentCount = $appointmentList.count
        #Iterate though each appointment and check for overlap with other appointments. http://wiki.c2.com/?TestIfDateRangesOverlap is the algorithm I used
        $overlappingMeetingArrayList = @(@())
        for($i=0; $i -lt $appointmentCount; $i++) {
            $overlappingMeetingArray = @()
            #put the first item in the overlapping array and add any overlapping items. If none are added and count remains one, I assume there's no overlap.
            $overlappingMeetingArray += $appointmentList[$i] 
            #If this isn't the first appointment in the list, check if its start and end times match the previous one. If the do, skip any further comparison as the prevoius one captured all the overlaps.
            if(!(($i -gt 0) -and ($appointmentList[$i].start -eq $appointmentList[$i-1].start) -and ($appointmentList[$i].end -eq $appointmentList[$i-1].end))) {  
                for($j=0; $j -lt $appointmentCount; $j++) {
                    #Make sure you're not comparing the item to itself.
                    if($appointmentList[$i] -ne $appointmentList[$j]) { 
                        if(($appointmentList[$i].start -lt $appointmentList[$j].end) -and ($appointmentList[$j].start -lt $appointmentList[$i].end)) {
                            write-debug "$($appointmentList[$i].start)-$($appointmentList[$i].end) overlaps with $($appointmentList[$j].start)-$($appointmentList[$j].end)"
                            $overlappingMeetingArray += $appointmentList[$j]
                        }
                        else {
                            write-debug "$($appointmentList[$i].start)-$($appointmentList[$i].end) does not overlap with $($appointmentList[$j].start)-$($appointmentList[$j].end)"
                        }
                    }
                }
                #If there's only one item, it's the meeting I'm comparing to the others. If more, there were overlaps.
                if($overlappingMeetingArray.count -gt 1) {
                    $overlappingMeetingArrayList += ,@($overlappingMeetingArray)
                }
            }
        }
        ,$overlappingMeetingArrayList
    }

     #Get overlapping meetings in today's appointments
###BUGBUG I don't think it captures meetings that don't start and end today properly.
    Function Get-RandomMeetingAndOverlaps {
        Param(
            $appointmentList
        )

        #Select a random appointment
        $randomMeetingAndOverlaps = @() 
        $randomMeetingAndOverlaps += $appointmentList | get-random
        Write-Debug "Meeting $($randomMeetingAndOverlaps.subject) selected to find overlaps"
        #Iterate though each appointment and check for overlap with other appointments. http://wiki.c2.com/?TestIfDateRangesOverlap is the algorithm I used
        $appointmentCount = $appointmentList.count          
        foreach($appointment in $appointmentList) {
            #Make sure you're not comparing the item to itself.
            if($randomMeetingAndOverlaps[0]-ne $appointment) { 
                if(($randomMeetingAndOverlaps[0].start -lt $appointment.end) -and ($appointment.start -lt $randomMeetingAndOverlaps[0].end)) {
                    write-debug "$($randomMeetingAndOverlaps[0].start)-$($randomMeetingAndOverlaps[0].end) overlaps with $($appointment.start)-$($appointment.end)"
                    $randomMeetingAndOverlaps += $appointment
                }
                else {
                    write-debug "$($randomMeetingAndOverlaps[0].start)-$($randomMeetingAndOverlaps[0].end) does not overlap with $($appointment.start)-$($appointment.end)"
                }
            }
        }
        $randomMeetingAndOverlaps
    }


    #Get owner of the mailbox from an item
 <# Function Get-MailboxOwner {
        Param(
            $appointment
        )
 
        if($appointment.recurrence
        #>


    #Generate Actionable Message Body from a list of appointments
    Function New-ActionableMessageBody {
        Param (
            $jsonTemplate,
            $overlappingMeetingList,
            $HTMLHeader,
            $HTMLFooter
        )

        #Create a unique ID for the message that will be included in all items so I can correlate them later
        $sessionID = (new-guid).Guid
     
        #Iterate through meetings and create a choice to appear in the Adaptive card and an object with the full item metadata
        $overlappingMeetingListforJson = @()
        $overlappingMeetingChoiceListforJson = @()
        foreach($overlappingMeeting in $overlappingMeetingList) {        
             $overlappingMeetingChoiceListforJson += [pscustomobject]@{
                Title = "$($overlappingMeeting.subject) ($($overlappingMeeting.Start.DayOfWeek) $($overlappingMeeting.Start.ToShortTimeString())-$($overlappingMeeting.end.ToShortTimeString()))"
                Value = $overlappingMeeting.GlobalAppointmentID        
            }
            $overlappingMeetingListforJson += [pscustomobject]@{
                SessionID = $sessionID
                Subject = $overlappingMeeting.Subject
                Start = $overlappingMeeting.Start
                End = $overlappingMeeting.End
                MailboxOwner = $overlappingMeeting.session.CurrentUser.name
                Attachments = $overlappingMeeting.Attachments
                Categories = $overlappingMeeting.Categories
                Companies = $overlappingMeeting.Companies
                ConversationIndex = $overlappingMeeting.ConversationIndex
                CreationTime = $overlappingMeeting.CreationTime
                EntryID = $overlappingMeeting.EntryID
                Importance = $overlappingMeeting.Importance
                LastModificationTime = $overlappingMeeting.LastModificationTime
                MessageClass = $overlappingMeeting.MessageClass
                NoAging = $overlappingMeeting.NoAging
                Sensitivity = [Microsoft.Office.Interop.Outlook.OlSensitivity].GetEnumName($overlappingMeeting.Sensitivity)
                Size = $overlappingMeeting.Size
                UnRead = $overlappingMeeting.UnRead
                AllDayEvent = $overlappingMeeting.AllDayEvent
                BusyStatus = [Microsoft.Office.Interop.Outlook.OlBusyStatus].GetEnumName($overlappingMeeting.BusyStatus)
                Duration = $overlappingMeeting.Duration
                IsOnlineMeeting = $overlappingMeeting.IsOnlineMeeting
                IsRecurring = $overlappingMeeting.IsRecurring
                Location = $overlappingMeeting.Location
                MeetingStatus = [Microsoft.Office.Interop.Outlook.OlMeetingStatus].GetEnumName($overlappingMeeting.MeetingStatus)
                NetMeetingAutoStart = $overlappingMeeting.NetMeetingAutoStart
                NetMeetingOrganizerAlias = $overlappingMeeting.NetMeetingOrganizerAlias
                NetMeetingServer = $overlappingMeeting.NetMeetingServer
                NetMeetingType = $overlappingMeeting.NetMeetingType
                OptionalAttendees = $overlappingMeeting.OptionalAttendees
                Organizer = $overlappingMeeting.Organizer
                Recipients = $overlappingMeeting.Recipients
                RecurrenceState = [Microsoft.Office.Interop.Outlook.OlRecurrenceState ].GetEnumName($overlappingMeeting.RecurrenceState)
                ReminderMinutesBeforeStart = $overlappingMeeting.ReminderMinutesBeforeStart
                ReminderOverrideDefault = $overlappingMeeting.ReminderOverrideDefault
                ReminderPlaySound = $overlappingMeeting.ReminderPlaySound
                ReminderSet = $overlappingMeeting.ReminderSet
                ReminderSoundFile = $overlappingMeeting.ReminderSoundFile
                ReplyTime = $overlappingMeeting.ReplyTime
                RequiredAttendees = $overlappingMeeting.RequiredAttendees
                Resources = $overlappingMeeting.Resources
                ResponseRequested = $overlappingMeeting.ResponseRequested
                ResponseStatus = [Microsoft.Office.Interop.Outlook.OlResponseStatus].GetEnumName($overlappingMeeting.ResponseStatus)
                Links = $overlappingMeeting.Links
                GlobalAppointmentID = $overlappingMeeting.GlobalAppointmentID
                StartUTC = $overlappingMeeting.StartUTC
                EndUTC = $overlappingMeeting.EndUTC
                StartInStartTimeZone = $overlappingMeeting.StartInStartTimeZone
                EndInEndTimeZone = $overlappingMeeting.EndInEndTimeZone
                StartTimeZone = $overlappingMeeting.StartTimeZone
                EndTimeZone = $overlappingMeeting.EndTimeZone
                ConversationID = $overlappingMeeting.ConversationID
                DoNotForwardMeeting = $overlappingMeeting.DoNotForwardMeeting
                FOthersAppt = $overlappingMeeting.FOthersAppt            
            }
            Release-OutlookComObject -comObject $overlappingMeeting
        }

        #Add an option for not attending any of the meetings
        $overlappingMeetingChoiceListforJson += [pscustomobject]@{
            Title = "None of these"
            Value = "No conflicting meeting attended"        
        }

        #add choices to JSON
        $json = Convertfrom-Json -InputObject $jsonTemplate -Verbose
        $json.body[2].items[0].choices = $overlappingMeetingChoiceListforJson

        #Add meeting list with full details to JSON
        $HTTPPOSTBody = @()
        $HTTPPOSTBody += [pscustomobject]@{
            attendedItemID = "{{meetingList.value}}"        
        }
        $HTTPPOSTBody += ,@($overlappingMeetingListforJson)
        #$json.body[2].items[1].actions[0].body = $HTTPPOSTBody | ConvertTo-Json
        $json.body[2].items[1].columns[0].items[0].actions[0].body = $HTTPPOSTBody | ConvertTo-Json
        $HTMLBody = $HTMLheader + ($json | ConvertTo-Json -Depth 100) + $HTMLFooter
        $HTMLBody
    }

    #Create new actionable message and send it
    Function Send-ActionableMessage {
        Param (
            $jsonTemplate,
            $overlappingMeetingList,
            $HTMLHeader,
            $HTMLFooter
        )

        #$outlook = new-object -comobject outlook.application
        Write-Debug "Creating actionable message"
        $Mail = $Outlook.CreateItem(0) 
        $Mail.To = $Mail.parent.Store.DisplayName
        Write-Debug "Recipient $($Mail.To)"
        $Mail.Subject = "Could you please answer one question to help Workplace Intelligence make meetings better at Microsoft?" 
        Write-Debug "Recipient $($Mail.subject)"
        $Mail.HTMLBody =  New-ActionableMessageBody -jsonTemplate $jsonTemplate -overlappingMeetingList $overlappingMeetingList -HTMLHeader $HTMLHeader -HTMLFooter $HTMLFooter
        Write-Debug "Sending actionable message"
        $mail.send()
        Release-OutlookComObject -comObject $Mail
    }

    #Generate a new actionable message
    Function New-ActionableMessage {
        #$overlappingMeetingArrayList = @(@())
        $AppointmentList = Get-OutlookCalendarAppointmentsToday
        $overlappingMeetingArray = Get-RandomMeetingAndOverlaps -appointmentList $AppointmentList
        Write-Debug "Selected the following meetings for Actionable Message:"
        foreach ($overlappingMeeting in $overlappingMeetingArray) {
            Write-Debug "$($overlappingMeeting.subject), $($overlappingMeeting.globalappointmentid)"
        }   
        Send-ActionableMessage -jsonTemplate $jsonTemplate -overlappingMeetingList $overlappingMeetingArray -HTMLHeader $HTMLHeader -HTMLFooter $HTMLFooter
        <#$overlappingMeetingArrayList += Get-OverlappingMeetings -appointmentList $AppointmentList
        if($overlappingMeetingArrayList.count -eq 0) {
            Write-Debug "There are no overlapping meetings, not sending an actionable message"
        }
        else {
            if($overlappingMeetingArrayList.count -eq 1) {
                Write-Debug "One overlapping meeting found, sending actionable message"
                $overlappingMeetingArray = $overlappingMeetingArrayList[0]
            }
            else {
                Write-Debug "Multiple overlapping meetings found, choosing one at random and sending actionable message"
                $overlappingMeetingArray = $overlappingMeetingArrayList[(get-random -Minimum 0 -Maximum $overlappingMeetingArrayList.count)]
            }
            Write-Debug "Selected the following meetings for Actionable Message:"
            foreach ($overlappingMeeting in $overlappingMeetingArray) {
                Write-Debug "$($overlappingMeeting.subject), $($overlappingMeeting.globalappointmentid)"
            }
            Send-ActionableMessage -jsonTemplate $jsonTemplate -overlappingMeetingList $overlappingMeetingArray -HTMLHeader $HTMLHeader -HTMLFooter $HTMLFooter
        }
        #>


    }

    #Check time of day and see if it's the scheduled time or after. If so, send the message.
    Function Send-ActionableMessageOnSchedule {
        $actionableMessageSendTime = "2:00 PM"
        $currentDate = get-date
        $calendarFolder = Get-OutlookCalendarFolder
        $lastActionableMessageSendDate = $calendarFolder.description
        #$lastActionableMessageSendDate = [System.Environment]::getEnvironmentVariable('lastActionableMessageSendDate',[System.EnvironmentVariableTarget]::User)
        Write-Debug "Last actionable message was sent on date $lastActionableMessageSendDate and messages are configured to be sent daily at $actionableMessageSendTime" 
        if(($currentDate.ToShortDateString() -ne $lastActionableMessageSendDate) -and ($currentDate -ge (get-date $actionableMessageSendTime))) {            
            Write-Debug "Current date and time is $currentDate, sending actionable message"
            New-ActionableMessage
            $calendarFolder.description= $currentDate.ToShortDateString()
            #[System.Environment]::SetEnvironmentVariable('lastActionableMessageSendDate', ($currentDate.ToShortDateString()), [System.EnvironmentVariableTarget]::User)
        }
        else {
            Write-Debug "Current date and time is $currentDate, will not send actionable message"
        }
    }

    ##########Types and objects for Outlook Monitor###########

#Add types to get which window is in focus to detect if Outlook is in use
Add-Type @"
using System;
using System.Runtime.InteropServices;
public class UserWindows {
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
}
"@
  

#Add types to hide the console window
Add-Type -Name Window -Namespace Console -MemberDefinition '
[DllImport("Kernel32.dll")]
public static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);
'


#Add type to get machine idle time to determine if user walked away but left Outlook open
Add-Type @'
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
 
namespace PInvoke.Win32 {
 
    public static class UserInput {
 
        [DllImport("user32.dll", SetLastError=false)]
        private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
 
        [StructLayout(LayoutKind.Sequential)]
        private struct LASTINPUTINFO {
            public uint cbSize;
            public int dwTime;
        }
 
        public static DateTime LastInput {
            get {
                DateTime bootTime = DateTime.UtcNow.AddMilliseconds(-Environment.TickCount);
                DateTime lastInput = bootTime.AddMilliseconds(LastInputTicks);
                return lastInput;
            }
        }
 
        public static TimeSpan IdleTime {
            get {
                return DateTime.UtcNow.Subtract(LastInput);
            }
        }
 
        public static int LastInputTicks {
            get {
                LASTINPUTINFO lii = new LASTINPUTINFO();
                lii.cbSize = (uint)Marshal.SizeOf(typeof(LASTINPUTINFO));
                GetLastInputInfo(ref lii);
                return lii.dwTime;
            }
        }
    }
}
'@


    #Create Outlook state object that will maintain the Outlook status
    $Script:OutlookState = [pscustomobject]@{
        #Item info
        ItemSubject = $null
        ItemToRecipientList = $null
        ItemCCRecipientList = $null
        ItemBCCRecipientList = $null
        ItemSender = $null
        ItemType = $null
        ItemBodyLength = 0
        ItemImportance = $null
        ItemSize = 0
        ItemCreationTime = $null
        ItemFolderName = $null
        ItemVisibleAttachmentCount = 0
        ItemEmbeddedAttachmentCount = 0
        ItemMessageID = $null
        #Machine user
        UserName = $env:USERNAME
        #Outlook state info
        DateTimeUTC = $null
        OutlookInFocus = $false
        UserIsReading = $false
        UserIsEditing = $false
    }

    ##########Functions concerning getting machine and application idle/in use state##########
    
    #Monitors Outlook state
    Function Get-OutlookUsageState {
        $timer.Stop()
        if(!$Outlook) {
                Write-Debug "There's no Outlook COM object, checking if Outlook is running and retrieving Outlook COM object if so."
                $script:outlook = Get-OutlookComObject 
        }
        $script:OutlookState.DateTimeUTC = (get-date).ToUniversalTime()
        #Check if Outlook is the window in focus and the machine is not idle to determine if Outlook is in use
        if((Get-OutlookFocusState) -and !(Get-MachineIdleState -timeLimit 120)) {
            $OutlookState.OutlookInFocus = $true
            #Get the active Outlook window (assuming non-active windows likely aren't in use)"
            try {
                $activeWindow = $outlook.ActiveWindow()     
            }
            catch  [System.Management.Automation.MethodInvocationException]
            {
                #If Outlook is closed and reopened, get a new com object
                Write-Debug "Caught an exception using an Outlook COM object, will get a new one." 
                $script:outlook = new-object -comobject outlook.application
                $activeWindow = $outlook.ActiveWindow() 
            }
            #Retreive info about the open window and any items within it
            switch ($activeWindow.class) {
                34 {Get-OutlookExplorerState -explorer $activeWindow}
                35 {Get-OutlookInspectorState -inspector $activeWindow}
            }
            #Send the log file based on the schedule
            Send-LogFileOnSchedule 
            #Send the actionable message based on the schedule
            Send-ActionableMessageOnSchedule
            Release-OutlookComObject($activeWindow)
        }
        else
        {
            #Outlook is not in use
            $OutlookState.OutlookInFocus = $false
            $OutlookState.UserIsEditing = $false
            $OutlookState.UserIsReading = $false
            Clear-OutlookItemInfo
        }
        #Output current state to a CSV file
        $OutlookState | Export-Csv $logFilePath -Append
        Update-OutlookMonitorGUI -OutlookState $OutlookState
        $timer.Start()
    }

    #Check if the Outlook Window is in focus on the machine, get the foreground window and map that to process name to confirm it's Outlook.
    Function Get-OutlookFocusState {          
        $processID = 0
        try {
            $ActiveHandle = [UserWindows]::GetForegroundWindow()
            $null = [UserWindows]::GetWindowThreadProcessId($ActiveHandle,[ref]$processID)

            if((Get-Process -id $processID).name -eq "Outlook") {
                $true
            }
            else
            {
                $false
            }
        }
        catch {
            Write-Error "Failed to get active Window details. More Info: $_"      
        }
    }

    #Check if machine is idle
    Function Get-MachineIdleState {
        Param(
            [int]$timeLimit
        )

        $idleTime = [PInvoke.Win32.UserInput]::IdleTime.TotalSeconds
        if($idleTime -gt $timeLimit) {
            $true
        }
        else {
            $false
        }
    }

    ##########Functions concerning getting Outlook folder and item info##########

    #Get recipient Info
    Function Get-OutlookItemRecipientInto {
        Param(
            $outlookItemRecipientList
        )
#####BUGBUG##### it counts a DG as one recipient, can we fix that? Not for external DGs, but maybe internal
        foreach($outlookItemRecipient in $OutlookItemRecipientList) {
            if($outlookItemRecipient.type -eq [Microsoft.Office.Interop.Outlook.OlMailRecipientType]::Olto) {
                $OutlookState.ItemToRecipientList += "$($outlookItemRecipient.name);"
                #$OutlookState.ItemToRecipientCount += Get-OutlookItemRecipientCount -outlookItemRecipient $outlookItemRecipient
            }
            if($outlookItemRecipient.type -eq [Microsoft.Office.Interop.Outlook.OlMailRecipientType]::OlCC) {
                $OutlookState.ItemCCRecipientList += "$($outlookItemRecipient.name);"
                #$OutlookState.ItemCCRecipientCount += Get-OutlookItemRecipientCount -outlookItemRecipient $outlookItemRecipient
            }
            if($outlookItemRecipient.type -eq [Microsoft.Office.Interop.Outlook.OlMailRecipientType]::OlBCC) {
                $OutlookState.ItemBCCRecipientList += "$($outlookItemRecipient.name);"
                #$OutlookState.ItemBCCRecipientCount += Get-OutlookItemRecipientCount -outlookItemRecipient $outlookItemRecipient
            }
            Release-OutlookComObject($outlookItemRecipient)
        }
    }

    #Get metadata from an Outlook item
    Function Get-OutlookItemInfo {
        Param (
            $outlookItem
        )

        #If the message ID hasn't changed, the same item is in focus, no need to retreive the data again. If it's "saved", it has been modified since last save so capture that.
        write-debug "Checking to see if item has changed by comparing messageid of current and previous item and checking for saved state."
        Write-Debug "Message ID at time of last check: $($OutlookState.ItemMessageID)"
        Write-Debug "Message ID at current check: $($outlookitem.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x1035001F"))"
        Write-Debug "Message has not been changed since last save: $($outlookItem.saved)"
        if($OutlookState.ItemMessageID -ne $outlookitem.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x1035001F") -or !$outlookItem.saved) {
            Write-Debug "Message has been determined to need to be checked again, either because it's a new message ID or it's changed since last save"
            Clear-OutlookItemInfo
            $OutlookState.ItemMessageID = $outlookitem.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x1035001F")
            #Get recipient info
            $recipients = $outlookItem.recipients
            Get-OutlookItemRecipientInto -outlookItemRecipientList $recipients
            Release-OutlookComObject($recipients)
            #Fill out remaining message properties
            $OutlookState.ItemSender = $outlookItem.Sender.name
            $OutlookState.ItemBodyLength = $outlookItem.body.length
###BUGBUG#### see if you can get the length of the most recent reply
            $OutlookState.ItemFolderName = $outlookItem.parent.FullFolderPath
            $OutlookState.ItemImportance = [Microsoft.Office.Interop.Outlook.OlImportance]$outlookItem.importance
            $OutlookState.ItemcreationTime = $outlookItem.creationTime
            $OutlookState.ItemSize = $outlookItem.size
            $OutlookState.ItemSubject = $OutlookItem.Subject
            $OutlookState.ItemType = [Microsoft.Office.Interop.Outlook.OlObjectClass]$outlookItem.class
            Get-OutlookAttachmentInfo -item $outlookItem
        } 
        else
        {
            Write-Debug "Message has been determined to not need to be checked again, message ID is the same or it's an item being composed that hasn't been changed since last save"
        }
    }

    #Gets counts of attachment on an item
    Function Get-OutlookAttachmentInfo {
        Param(
            $item 
        )
        
        #Loop through each attachmetn and look at various flags to determine if it's a visible attachment or embedded
        foreach($attachment in $item.attachments) {
            $attachFlags = $attachment.PropertyAccessor.GetProperty('http://schemas.microsoft.com/mapi/proptag/0x37140003')
            $attachMethod = $attachment.PropertyAccessor.GetProperty('http://schemas.microsoft.com/mapi/proptag/0x37050003')
            $attachContentID = $attachment.PropertyAccessor.GetProperty('http://schemas.microsoft.com/mapi/proptag/0x3712001F')
            $attachContentLocation = $attachment.PropertyAccessor.GetProperty('http://schemas.microsoft.com/mapi/proptag/0x3713001F')
            Write-Debug "Attachment name: $($attachment.filename)"
            write-debug "PR_ATTACH_CONTENT_ID: $attachContentID"
            write-debug "PR_ATTACH_CONTENT_LOCATION: $attachContentLocation"
            write-debug "attach flags: $attachFlags"
            write-debug "attach method: $attachmethod"
            write-debug "attach type: $($attachment.type)"
            #Cloudy attachment: $attachContentID -and -not ($attachFlags -eq 4 -and $attachMethod -eq 7)
            #Other two checks from http://social.msdn.microsoft.com/Forums/en-SG/outlookdev/thread/4e005509-56de-47c0-99cb-b5ac5a972789
            if(($attachContentID -ne "" -and $outlookItem.HTMLBody.Contains($attachContentID)) -and !($attachFlags -eq 4 -and $attachMethod -eq 7) -or ($attachMethod -eq 6)) {
                $outlookState.ItemEmbeddedAttachmentCount++
                Write-Debug "Attachment determined to be embedded"
            }
            else {
                $outlookState.ItemVisibleAttachmentCount++
                Write-Debug "Attachment determined to be visible"
            } 
            Release-OutlookComObject($attachment)
        }
    }

    #If no Outlook item is selected, remove any item info
    Function Clear-OutlookItemInfo {
        $OutlookState.ItemBCCRecipientList = $null
        $OutlookState.ItemBodyLength = $null
        $OutlookState.ItemCCRecipientList = $null
        $Outlookstate.ItemSender = $null
        $OutlookState.ItemFolderName = $null
        $OutlookState.ItemImportance = $null
        $OutlookState.ItemcreationTime = $null
        $OutlookState.ItemSize = $null
        $OutlookState.ItemSubject = $null
        $OutlookState.ItemToRecipientList = $null
        $OutlookState.ItemType = $null
        $OutlookState.ItemEmbeddedAttachmentCount = 0
        $OutlookState.ItemVisibleAttachmentCount = 0
        $OutlookState.ItemMessageID = $null
    }

    #Get info on the explorer window
     Function Get-OutlookExplorerState {
        Param(
            $explorer
        )
        #If it's an explorer and the folder has items, assume user is reading. Note that we can't check which item is selected since this is broken for group folders, it always shows nothign selected
        write-debug "Checking if the user is reading, composing, or doing nothing in the current explorer"
        write-debug "Item count of current folder is $($explorer.CurrentFolder.items.count) and number of items selected is $($explorer.CurrentFolder.items.count)"
        if(($explorer.CurrentFolder.items.count -eq 0) -and ($explorer.selection.Count -eq 0)) {
            write-debug "User has been determined to not be reading or editing."
            $OutlookState.UserIsReading = $false
            $OutlookState.UserIsEditing = $false
            Clear-OutlookItemInfo
        } 
        else {
            #An item is selected so the user is either reading or editing.
            if($response = $explorer.activeinlineresponse) {
                write-debug "User has been determined to be editing due to an active inline response."
                $OutlookState.UserIsEditing = $True
                $OutlookState.UserIsReading = $false
                Get-OutlookItemInfo -outlookItem $response
                Release-OutlookComObject($response)    
            }
            else {
                write-debug "User has been determined to be reading since there's no active inline response."
                $OutlookState.UserIsEditing = $false
                $OutlookState.UserIsReading = $true
                #If an item is selected, get its info. If not, it may be a group folder with no item.
                if($explorer.selection.count -gt 0) {
                    $currentItem = $explorer.selection.item(1)
                    Get-OutlookItemInfo -outlookItem $currentItem
                    Release-OutlookComObject($currentItem)
                }
                else {
                    Clear-OutlookItemInfo
                }
            }                    
        }
    }

     #Get info on an inspector window
     Function Get-OutlookInspectorState {
        Param(
            $inspector
        )

        #if an item isn't listed as "sent", it's being edited
        if($inspector.currentitem.sent) {
            $OutlookState.UserIsEditing = $false
            $OutlookState.UserIsReading = $true
        }
        else {
            $OutlookState.UserIsEditing = $True
            $OutlookState.UserIsReading = $false
        }
        $currentItem = $inspector.CurrentItem
        Get-OutlookItemInfo -outlookItem $currentItem
        Release-OutlookComObject($currentItem)
    }

    ##########Functions to create and update Forms GUI##########

    #Create Outlook monitor GUI window
    Function New-OutlookMonitorGUI {
        Add-Type -AssemblyName System.Windows.Forms
        [System.Windows.Forms.Application]::EnableVisualStyles()

        $Form                            = New-Object system.Windows.Forms.Form
        $Form.ClientSize                 = '475,871'
        $Form.text                       = "Outlook Monitor"
        $Form.TopMost                    = $false

        $UserIsReading                   = New-Object system.Windows.Forms.Label
        $UserIsReading.text              = "User is reading"
        $UserIsReading.AutoSize          = $true
        $UserIsReading.width             = 25
        $UserIsReading.height            = 10
        $UserIsReading.location          = New-Object System.Drawing.Point(28,61)
        $UserIsReading.Font              = 'Microsoft Sans Serif,16'

        $UserIsEditing                   = New-Object system.Windows.Forms.Label
        $UserIsEditing.text              = "User is editing"
        $UserIsEditing.AutoSize          = $true
        $UserIsEditing.width             = 25
        $UserIsEditing.height            = 10
        $UserIsEditing.location          = New-Object System.Drawing.Point(28,110)
        $UserIsEditing.Font              = 'Microsoft Sans Serif,16'

        $ItemInfoDataGridView            = New-Object system.Windows.Forms.DataGridView
        $ItemInfoDataGridView.width      = 416
        $ItemInfoDataGridView.height     = 594
        $ItemInfoDataGridView.location   = New-Object System.Drawing.Point(25,165)

        $OutlookInFocus                  = New-Object system.Windows.Forms.Label
        $OutlookInFocus.text             = "Outlook in focus"
        $OutlookInFocus.AutoSize         = $true
        $OutlookInFocus.width            = 25
        $OutlookInFocus.height           = 10
        $OutlookInFocus.location         = New-Object System.Drawing.Point(28,13)
        $OutlookInFocus.Font             = 'Microsoft Sans Serif,16'

        $LogFileLabel                    = New-Object system.Windows.Forms.Label
        $LogFileLabel.text               = "Log file path:"
        $LogFileLabel.AutoSize           = $false
        $LogFileLabel.width              = 403
        $LogFileLabel.height             = 87
        $LogFileLabel.location           = New-Object System.Drawing.Point(33,770)
        $LogFileLabel.Font               = 'Microsoft Sans Serif,10'

        $Form.controls.AddRange(@($UserIsReading,$UserIsEditing,$ItemInfoDataGridView,$OutlookInFocus,$LogFileLabel))

        $Form.Add_FormClosing({ Close-Form -sender $this -cancelEventArgs $_ })

        $Form.MaximizeBox   = $false
        $form.showintaskbar = $false
        $Form.Add_Shown({New-OutlookMonitor -checkIntervalMilliseconds 500 -sendLogFileIntervalMinutes 720})
        $form.windowstate = [System.Windows.Forms.FormWindowState]::Minimized

        $LogFileLabel.text = "Outlook Monitoring log is stored here: $logFilePath"

        #Configure datagridview to show item properties
        $ItemInfoDataGridView.RowHeadersVisible = $false
        $ItemInfoDataGridView.AutoSizeRowsMode =  [System.Windows.Forms.DataGridViewAutoSizeColumnMode]::AllCells
        $ItemInfoDataGridView.ColumnCount = 2
        $ItemInfoDataGridView.ColumnHeadersVisible = $true
        $ItemInfoDataGridView.ColumnHeadersHeightSizeMode = 2
        $ItemInfoDataGridView.Columns[0].Name = "Property"
        $ItemInfoDataGridView.Columns[0].AutoSizeMode = 6
        $ItemInfoDataGridView.Columns[1].DefaultCellStyle.WrapMode =  [System.Windows.Forms.DataGridViewTriState]::True
        $ItemInfoDataGridView.Columns[1].Name = "Value"
        $ItemInfoDataGridView.Columns[1].AutoSizeMode = 16

        if(!$doNotHideConsole) {
            Hide-PowerShellConsole
        }
        Initialize-SystemTrayIcon
        [void]$Form.ShowDialog()
    }

    #Hide powershell console as the GUI runs
    Function Hide-PowerShellConsole {
        # Hide PowerShell Console
        $consolePtr = [Console.Window]::GetConsoleWindow()
        $null = [Console.Window]::ShowWindow($consolePtr, 0)
    }

    #If the form is closed, minimize to system tray and keep running
    Function Close-Form {
        Param (
            [Object]$sender,
            [Object]$cancelEventArgs
        )
        #If user clicks the X to minimize, keep it running, if closed from context menu then really close.
        if($sender -eq $form) {
            $systemTrayIcon.BalloonTipText = "Outlook Monitor will continue runnning in your system tray, right click to close"
            $systemTrayIcon.ShowBalloonTip(1000)
            $form.WindowState = "minimized"
            $cancelEventArgs.cancel = $true
        }
        else {
            #Dispose of all variables
            Get-Variable -exclude Runspace | Where-Object {
                $_.Value -is [System.IDisposable]
            } | Foreach-Object {
                try {
                    $_.Value.Dispose()
                    Remove-Variable $_.Name -ErrorAction Stop
                }
                catch {
                }
            }
        }
    }
        
    #Create the sytem tray icon
    Function Initialize-SystemTrayIcon {
        #Configure the icon
        $script:systemTrayIcon = New-Object System.Windows.Forms.NotifyIcon 
        $systemTrayIcon.Icon = "$($env:windir)\syswow64\OneDrive.ico"
        $systemTrayIcon.BalloonTipText = "Outlook Monitor $((Get-InstalledScript "OutlookMonitor").version) is running in your system tray" 
        $systemTrayIcon.Visible = $True 
        $systemTrayIcon.ShowBalloonTip(1000)

        #enable it pop up the form on click
        $systemTrayIcon.add_click({$form.WindowState = "Normal"; $form.Activate()})

        #Create context menu with option to exit the form
        $contextMenu = New-Object System.Windows.Forms.ContextMenu
        $exitMenuItem = New-Object System.Windows.Forms.MenuItem
        $exitMenuItem.text = "Exit"
        $exitMenuItem.add_Click({Close-Form -sender $this -cancelEventArgs $_})
        [void]$contextMenu.MenuItems.Add($exitMenuItem)
        $systemTrayIcon.contextMenu = $contextMenu    
    }
        
    #Run the process that gets the Outlook state and updates the GUI using a timer
    Function New-OutlookMonitor {
    Param(
        [int]$checkIntervalMilliseconds,
        [int]$sendLogFileIntervalMinutes
    )
   
        #Set up timer to check usage state
        $script:timer = New-Object System.Windows.Forms.Timer
        $timer.Interval = $checkIntervalMilliseconds
        $timer.Add_Tick({Get-OutlookUsageState})
        $timer.Enabled = $True  
            
        #Set up stopwatch to send log files on regular intervals
        $script:sendLogFileTimeSpan = new-timespan -minutes $sendLogFileIntervalMinutes
        $script:stopwatch = [diagnostics.stopwatch]::StartNew()
        Write-Debug "Setting up stopwatch to send log files every $sendLogFileIntervalMinutes minutes"
    }
       
    #Update the GUI
    Function Update-OutlookMonitorGUI {
        Param(
            $OutlookState
        )
        if($OutlookState.OutlookInFocus) {            
            $OutlookInFocus.BackColor = "#00FF00"
            if($OutlookState.UserIsReading)
            {
                $UserIsReading.BackColor = "#00FF00"
            }    
            else {
                $UserIsReading.BackColor = "#d0021b"
            }
            if($OutlookState.UserIsEditing) {
                $UserIsEditing.BackColor = "#00FF00"
            }
            else {
                $UserIsEditing.BackColor = "#d0021b"
            }
            
            #Fill the datagrid with the item info
            $ItemInfoDataGridView.rows.clear()
            if(!$OutlookState.UserIsEditing -and !$OutlookState.UserIsReading)
            {
                $ItemInfoDataGridView.rows.Add("No item selected")
            } 
            else {         
                foreach($property in $OutlookState.psobject.Properties) {
                    $ItemInfoDataGridView.Rows.Add($property.name,$property.value)
                }
            }
        }
        else {
            $OutlookInFocus.BackColor = "#d0021b"
            $UserIsReading.BackColor = "#d0021b"
            $UserIsEditing.BackColor = "#d0021b"
            $ItemInfoDataGridView.rows.clear()
            $ItemInfoDataGridView.rows.Add("No item selected")
        }
        $form.Refresh()
    }

    ##########Functions concerning manipulating Outlook COM objects########

    #Checks if Outlook is running and returns a COM object if so, otherwise null.
    Function Get-OutlookComObject {
        if(Get-Process -Name OUTLOOK -ErrorAction SilentlyContinue) {
            Write-Debug "Retrieving Outlook COM object"
            new-object -comobject outlook.application
        }
        else {
            $null
        }
    }

    #Release Outlook COM object
    Function Release-OutlookComObject {
        Param (
            [object]$comObject
        )

        try {
            [System.Runtime.Interopservices.Marshal]::ReleaseComObject($comObject) | out-null
        }
        catch {
            Write-Debug "Encountered exception releasing COM object $($_.exception.mesage). This can happen if Outlook is closed while the monitor is running."
        }
    }

    #########Functions involving creating and sending the log file############

    #set up log file
    Function Initialize-Log {
        $folderPath = "$($env:LOCALAPPDATA)\OutlookMonitorLogs"
        if(!(Test-Path $folderPath)) {
            $null = New-Item $folderPath -ItemType Directory
        }
        "$($folderPath)$($MyInvocation.ScriptName.Replace((Split-Path $MyInvocation.ScriptName),'').TrimStart(''))_$($env:computername)_$($env:username).csv"
    }

    #Check if a log file needs to be sent on a regular schedule
    Function Send-LogFileOnSchedule {      
        Write-Debug "Log file configured to be sent every $($sendLogFileTimeSpan.TotalMinutes) minutes, $($stopwatch.Elapsed.TotalMinutes) minutes have passed since last send"
        if($stopwatch.elapsed -gt $sendLogFileTimeSpan) {
            Write-Debug "Sending log file"
            Send-LogFile -emailAddress 'outlookmonitor@service.microsoft.com' -logFilePath $logFilePath
            $stopwatch.Restart()
            Update-OutlookMonitor
        } 
        else {
            Write-Debug "Not sending log file"
        }
    }

    #Email the log file to a modern group
    Function Send-LogFile {
        Param (
            [string]$emailAddress,
            [string]$logFilePath
        )

        Write-Debug "Compressing log file $logFilePath in preparation to send"
        $compressedLogFilePath = "$($logFilePath).zip"
        Compress-Archive -LiteralPath $logFilePath -Update -DestinationPath $compressedLogFilePath
        if((Get-Item $compressedLogFilePath).Length -gt 104857600) {
            Write-Debug "Compressed file is > 100MB, log will not be sent"
        } 
        else {          
            write-debug "Creating message to send log file"
            try {
                $message = $outlook.CreateItem([Microsoft.Office.Interop.Outlook.OlItemType]::olMailItem)
            }
            catch {
                Write-Debug "Couldn't create message to send log file due to exception $($_.exception.mesage). This may occur because Outlook is closed." 
                $message = $null
            }
            if($message) {
                $message.To = $emailAddress
                $message.Subject = "$($MyInvocation.ScriptName.Replace((Split-Path $MyInvocation.ScriptName),'').TrimStart(''))_$($env:computername)_$($env:USERNAME)"
                $message.Attachments.Add($compressedLogFilePath)
                Write-Debug "Sending log file to $emailAddress"
                $null = $message.send()
                Release-OutlookComObject($message)
            }
            else {
                Write-Debug "Message object does not exist, log file will not be sent. It will try again next time"
            }
        }
    }

    #########Functions involving installation, uninstallation, and updating############
    
    #Copies script, and creates scheduled task, then runs it
    Function Install-OutlookMonitor {
        Write-Debug "Installing Nuget package provider"
        Install-PackageProvider Nuget -Force
        Write-Debug "Installing PowerShellGet"
        Install-Module -Name PowerShellGet -Force
        #Write-Debug "Copying script to $logFilePath"
        #Copy-Item -path $MyInvocation.ScriptName -Destination "$($env:LOCALAPPDATA)\OutlookMonitorLogs"
        Write-Debug "Installing script"
        Install-script "OutlookMonitor" -Force -Scope CurrentUser
        $scriptFilePath = "$((Get-InstalledScript "OutlookMonitor").installedlocation)\outlookmonitor.ps1"
        Write-Debug "Creating scheduled task"
        Add-OutlookMonitorScheduledTask -filePath $scriptFilePath
        Write-Debug "Starting scheduled task"
        Start-ScheduledTask -TaskName "Outlook Monitor"
    }
      
    #Create Outlook Monitor scheduled tasks
    Function Add-OutlookMonitorScheduledTask {
        Param(
            [string]$filePath
        )
        
        if(Get-ScheduledTask "Outlook Monitor" -ErrorAction SilentlyContinue) {    
           Write-Debug "Outlook Monitor scheduled task exists, not creating it again"
        }
        else {
            Write-Debug "Outlook Monitor scheduled task does not exist, creating it"
            $argument = "-executionpolicy bypass -file `"$filePath`""
            $action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument $argument
            $trigger =  New-ScheduledTaskTrigger -AtLogOn 
            $settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -DontStopOnIdleEnd -MultipleInstances IgnoreNew
            Register-ScheduledTask -Action $action -Trigger $trigger -TaskName "Outlook Monitor" -Description "Runs Outlook activity monitor" -Settings $settings #-User "System" #-CimSession $CIMSession -RunLevel Highest
        }
    }

    #Uninstall Outlook Monitor
    Function Uninstall-OutlookMonitor {
        if(Get-ScheduledTask "Outlook Monitor" -ErrorAction SilentlyContinue) {    
            Write-Debug "Removing Outlook monitor task"
            Stop-ScheduledTask -taskname "Outlook Monitor" 
            Unregister-ScheduledTask "Outlook Monitor" -Confirm:$false
        }
        Write-Debug "Removing Outlook Monitoring Log Folder"
        Remove-Item "$($env:LOCALAPPDATA)\OutlookMonitorLogs" -Force -Confirm:$false -Recurse
        uninstall-script "OutlookMonitor" -Force
    }  

    #Update script file and restart script
    Function Update-OutlookMonitor {
        $onlineScriptVersion = (Find-Script "OutlookMonitor").version
        $currentScriptVersion = (Get-InstalledScript "OutlookMonitor").version
        Write-Debug "Checking for script update, current version is $currentScriptVersion"
        if($onlineScriptVersion -gt $currentScriptVersion) {
            Write-Debug "Script update found, online version is $onlineScriptVersion, installing update"
            Update-Script -Name "OutlookMonitor" -Force
            Write-Debug "Restarting with new script version and exiting old one"
            start-process "PowerShell.exe" -argumentList "-WindowStyle hidden -command Start-ScheduledTask -TaskName `'Outlook Monitor`'"
            Close-Form
        }
        else {
            write-debug "No script update found, online version is $onlineScriptVersion"
        }
    }
}

Process {
    ########Script Body########
    $logFilePath = Initialize-Log
    if($install) {
        Write-Debug "Install parameter used, will create scheduled task"
        Install-OutlookMonitor
    }
    elseif($uninstall) {
        Write-Debug "Uninstall parameter used, will remove scheduled task"
        Uninstall-OutlookMonitor
    }
    else {
        #initialize the GUI
        $script:Outlook = Get-OutlookComObject        
        New-OutlookMonitorGUI
    }
}

##TODO
#maybe make sure no other version of the same script is running.

<#
    #Check if a recipient is a DG
    Function Get-OutlookItemRecipientIsDG {
        Param(
            $outlookItemRecipient
        )
 
        $GAL = $outlook.Session.AddressLists("All Distribution Lists")
        $outlookItemRecipientAddressEntryFromGAL = $gal.AddressEntries($outlookItemRecipient.name)
        if($outlookItemRecipient.address -eq $outlookItemRecipientAddressEntryFromGAL.address) {
            $outlookItemRecipientAddressEntryFromGAL
        }
        else {
            $null
        }
    }
 
#>


<#
    #Count number of recipients, 1 for regular user and more for a DG
    Function Get-OutlookItemRecipientCount {
        Param(
            $outlookItemRecipient
        )
     
        #Doesn't work for all microsoft DG, also doesn't work for Nimrod since there are 2 GAL entreis with same name...
        #Search for the recipient by name in the "All Distribution lists" address list then check to see if the search result address matches your recipient
        #If not, it found the closest user name, which means there was no exact match for your recipient and it's in the list of DGs.
        if($outlookItemRecipientAddressEntryFromGAL = Get-OutlookItemRecipientIsDG -outlookItemRecipient $outlookItemRecipient) {
            write-host "Recipient name: $($outlookItemRecipient.name) is a DG and PR_display_type is $($outlookitemrecipient.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x39050003"))"#>

            <#foreach($outlookItemRecipientAddressEntryFromGALL2 in $outlookItemRecipientAddressEntryFromGAL.members) {
                if($outlookItemRecipientAddressEntryFromGALL3 = Get-OutlookItemRecipientIsDG -outlookItemRecipient $outlookItemRecipientAddressEntryFromGALL2) {
                    write-host "Recipient name: $($outlookItemRecipientAddressEntryFromGALL2.name) is a DG"
                }
                else
                {
                    write-host "Recipient name: $($outlookItemRecipientAddressEntryFromGALL2.name) is not a DG "
                    $memberCount++
                }
 
            }#>

           <# $memberCount
        }
        else
        {
            write-host "Recipient name: $($outlookItemRecipient.name) is a not DG and PR_display_type is $($outlookitemrecipient.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x39050003"))"
            1
        }
    }
 
    #>