SystemExtensions.psm1

function Suspend-Process 
{

    param
    (
        [Parameter(ValueFromPipeline=$true,Mandatory=$true)]
        [System.Diagnostics.Process]$InputObject
    )

    process 
    {
        #Open Process
        try
        {
            Write-Verbose "Open Process started"
            
            $pProc = [SystemUtilities.ProcessHelper]::OpenProcess("SuspendResume", $false, $InputObject.Id)
            if ($pProc -eq  [IntPtr]::Zero)
            {
                throw "Unable to open process(ID: $($InputObject.Id))"
            }

            Write-Verbose "Open Process completed"
        }
        catch
        {
            Write-Error "Open Process failed. Details: $_" -ErrorAction 'Stop'
        }

        #Suspend Process
        try
        {
            Write-Verbose "Suspend Process started"
            
            $result = [SystemUtilities.ProcessHelper]::SuspendProcess($pProc)
            if ($result -ne 0)
            {
                throw "Failed with ErrorCode: $result"
            }

            Write-Verbose "Suspend Process completed"
        }
        catch
        {
            Write-Error "Suspend Process failed. Details: $_" -ErrorAction 'Stop'
        }
        finally
        {
            $null = [SystemUtilities.ProcessHelper]::CloseHandle($pProc)
        }
    }

}

function Resume-Process 
{

    param
    (
        [Parameter(ValueFromPipeline=$true,Mandatory=$true)]
        [System.Diagnostics.Process]$InputObject
    )

    process 
    {
        #Open Process
        try
        {
            Write-Verbose "Open Process started"
            
            $pProc = [SystemUtilities.ProcessHelper]::OpenProcess("SuspendResume", $false, $InputObject.Id)
            if ($pProc -eq  [IntPtr]::Zero)
            {
                throw "Unable to open process(ID: $($InputObject.Id))"
            }

            Write-Verbose "Open Process completed"
        }
        catch
        {
            Write-Error "Open Process failed. Details: $_" -ErrorAction 'Stop'
        }

        #Resume Process
        try
        {
            Write-Verbose "Resume Process started"
            
            $result = [SystemUtilities.ProcessHelper]::ResumeProcess($pProc)
            if ($result -ne 0)
            {
                throw "Failed with ErrorCode: $result"
            }

            Write-Verbose "Resume Process completed"
        }
        catch
        {
            Write-Error "Resume Process failed. Details: $_" -ErrorAction 'Stop'
        }
        finally
        {
            $null = [SystemUtilities.ProcessHelper]::CloseHandle($pProc)
        }
    }

}

function Suspend-Thread 
{

    param
    (
        [Parameter(ValueFromPipeline=$true,Mandatory=$true)]
        [System.Diagnostics.ProcessThread]$InputObject
    )

    process 
    {
        #Open Thread
        try
        {
            Write-Verbose "Open Thread started"
            
            $pThread = [SystemUtilities.ProcessHelper]::OpenThread("SuspendResume", $false, $InputObject.Id)
            if ($pThread -eq  [IntPtr]::Zero)
            {
                throw "Unable to open Thread(ID: $($InputObject.Id))"
            }

            Write-Verbose "Open Thread completed"
        }
        catch
        {
            Write-Error "Open Thread failed. Details: $_" -ErrorAction 'Stop'
        }

        #Suspend Thread
        try
        {
            Write-Verbose "Suspend Thread started"
            
            $result = [SystemUtilities.ProcessHelper]::SuspendThread($pThread)
            if ($result -ne 0)
            {
                throw "Failed with ErrorCode: $result"
            }

            Write-Verbose "Suspend Thread completed"
        }
        catch
        {
            Write-Error "Suspend Thread failed. Details: $_" -ErrorAction 'Stop'
        }
        finally
        {
            $null = [SystemUtilities.ProcessHelper]::CloseHandle($pThread)
        }
    }

}

function Resume-Thread 
{

    param
    (
        [Parameter(ValueFromPipeline=$true,Mandatory=$true)]
        [System.Diagnostics.ProcessThread]$InputObject
    )

    process 
    {
        #Open Thread
        try
        {
            Write-Verbose "Open Thread started"
            
            $pThread = [SystemUtilities.ProcessHelper]::OpenThread("SuspendResume", $false, $InputObject.Id)
            if ($pThread -eq  [IntPtr]::Zero)
            {
                throw "Unable to open Thread(ID: $($InputObject.Id))"
            }

            Write-Verbose "Open Thread completed"
        }
        catch
        {
            Write-Error "Open Thread failed. Details: $_" -ErrorAction 'Stop'
        }

        #Resume Thread
        try
        {
            Write-Verbose "Resume Thread started"
            
            $result = [SystemUtilities.ProcessHelper]::ResumeThread($pThread)
            if ($result -ne 0)
            {
                throw "Failed with ErrorCode: $result"
            }

            Write-Verbose "Resume Thread completed"
        }
        catch
        {
            Write-Error "Resume Thread failed. Details: $_" -ErrorAction 'Stop'
        }
        finally
        {
            $null = [SystemUtilities.ProcessHelper]::CloseHandle($pThread)
        }
    }

}

function Start-NewProcess
{
    [CmdletBinding(DefaultParameterSetName='Default')]
    [OutputType([System.Diagnostics.Process], ParameterSetName="Default")]
    [OutputType([string], ParameterSetName="Wait")]
    param
    (
        #FilePath
        [Parameter(Mandatory=$true)]
        [string]$FilePath,

        #Arguments
        [Parameter(Mandatory=$false)]
        [string]$Arguments,

        #WorkingDirectory
        [Parameter(Mandatory=$false)]
        [string]$WorkingDirectory,

        #PassThru
        [Parameter(Mandatory=$false,ParameterSetName='Default')]
        [switch]$PassThru = $false,

        #WaitTimeout
        [Parameter(Mandatory=$false,ParameterSetName='Wait')]
        [int]$WaitTimeout = 60,

        #ReturnResult
        [Parameter(Mandatory=$false,ParameterSetName='Wait')]
        [switch]$ReturnResult = $false
    )

    Process
    {
        try
        {
            ### Start the process
            try
            {
                ## Initializing the new process StartInfo object
                Write-Verbose -Message "Initializing the new process StartInfo preferences"

                $ProcessStartInfo = New-Object -TypeName System.Diagnostics.ProcessStartInfo -ArgumentList $FilePath
                $ProcessStartInfo.RedirectStandardOutput = $true
                $ProcessStartInfo.RedirectStandardError = $true
                $ProcessStartInfo.UseShellExecute = $false
                $ProcessStartInfo.CreateNoWindow = $true
                if ($PSBoundParameters.ContainsKey('WorkingDirectory'))
                {
                    $ProcessStartInfo.WorkingDirectory = $WorkingDirectory
                }
                   if ($PSBoundParameters.ContainsKey('Arguments'))
                {
                    $ProcessStartInfo.Arguments = $Arguments
                }
     
                ## Start the process
                $Process = [System.Diagnostics.Process]::Start($ProcessStartInfo)

                Write-Verbose -Message "The Process was started successfully"
            }
            catch
            {
                # Rethrow with custom message
                throw "The process failed to start. Details: $_" 
            }

            switch ($PSCmdlet.ParameterSetName)
            {
                'Default' {
                    if ($PassThru.IsPresent)
                    {
                        # Write the Process object in the Output Stream
                        $Process
                    }
                }

                'Wait' {
                    ## Start a Timer to monitor the WaitTimeout
                    $Timer = [System.Diagnostics.Stopwatch]::StartNew()

                    ## Wait for the process to complete
                    Write-Verbose -Message "Waiting for the process to complete (max $WaitTimeout seconds)"
                    $WaitCompleted = $false
                    while (-not $WaitCompleted)
                    {
                        # Check the process state
                        if ($Process.HasExited)
                        {
                            $WaitCompleted = $true
                            $Timer.Stop()
                        }
                        else
                        {
                            # Check if a Timeout is reached
                            if ($Timer.Elapsed.TotalSeconds -gt $WaitTimeout)
                            {
                                $Timer.Stop()
                                throw "Timeout of $WaitTimeout reached."
                            }

                            Start-Sleep -Milliseconds 250
                        }
                    }

                    Write-Verbose -Message "The process completed successfully"


                    ## Check exitcode
                    if ($Process.exitcode -ne 0)
                    {
                        # Throw errorcode + errormessage
                        try
                        {
                            Write-Verbose -Message "Reading data from Output and Error streams"

                            $errorMsg = "The process failed with exitcode $($Process.ExitCode)."
                            $ProcessOutput_Error = $Process.StandardError.ReadToEnd()
                            $ProcessOutput_Standard = $Process.StandardOutput.ReadToEnd()
                            if ($ProcessOutput_Error)
                            {
                                $errorMsg += " Details: $ProcessOutput_Error"
                            }
                            elseif ($ProcessOutput_Standard)
                            {
                                $errorMsg += "Details: $ProcessOutput_Standard"
                            }
                        }
                        catch
                        {
                            $errorMsg += " Failed to read data from Output and Error streams. Details: $_"
                        }
                        finally
                        {
                            throw $errorMsg
                        }
                    }
                    elseif ($ReturnResult.IsPresent)
                    {
                        # Return output
                        $Process.StandardOutput.ReadToEnd()
                    }
                }

                default
                {
                    throw "Unknown ParameterSetName $($PSCmdlet.ParameterSetName)"
                }
            }
        }
        catch
        {
            Write-Error -Exception ($_.Exception) -Message "$_" -Category NotSpecified -ErrorId 0 -TargetObject $MyInvocation.MyCommand.Name
        }
        finally
        {
            # Dispose
            if($Process)
            {
                $Process.Dispose()
            }
        }
    }
}

function Get-ServiceConfig
{
    [CmdletBinding()]
    [OutputType('System.Service')]
    param 
    (
        #Name
        [parameter(Mandatory=$false, ValueFromPipelineByPropertyName=$True)]
        [string[]]$Name,
        
        #ComputerName
        [parameter(Mandatory=$false, ValueFromPipelineByPropertyName=$True)]
        [Alias('CN','__Server','PSComputername')]
        [string[]]$Computername = $env:COMPUTERNAME,

        #ServiceType
        [parameter(Mandatory=$false)]
        [ValidateSet('Win32','Win32OwnProcess','Win32ShareProcess','FileSystemDriver','KernelDriver','All','Interactive')]
        [string]$ServiceType = 'Win32'
    )
    
    Begin {
        If ($PSBoundParameters.ContainsKey('Debug')) {
            $DebugPreference = 'Continue'
        }
        #region Reflection
        Try {
            [void][Service.Trigger]
        }
        Catch {
            Write-Verbose 'Building pinvoke via reflection'
            #region Module Builder
            $Domain = [AppDomain]::CurrentDomain
            $DynAssembly = New-Object System.Reflection.AssemblyName('ServiceTrigger')
            $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run) # Only run in memory
            $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('ServiceTrigger', $False)
            #endregion Module Builder

            #region Enums

            #region SC_ENUM_TYPE
            $EnumBuilder = $ModuleBuilder.DefineEnum('SC_ENUM_TYPE', 'Public', [uint32])
            [void]$EnumBuilder.DefineLiteral('SC_ENUM_PROCESS_INFO', [uint32] 0x00000000)
            [void]$EnumBuilder.CreateType()
            #endregion SC_ENUM_TYPE

            #region ERROR_CONTROL
            $EnumBuilder = $ModuleBuilder.DefineEnum('ERROR_CONTROL', 'Public', [uint32])
            [void]$EnumBuilder.DefineLiteral('Ignore', [uint32] 0x00000000)
            [void]$EnumBuilder.DefineLiteral('Normal', [uint32] 0x00000001)
            [void]$EnumBuilder.DefineLiteral('Severe', [uint32] 0x00000002)
            [void]$EnumBuilder.DefineLiteral('Critical', [uint32] 0x00000003)
            [void]$EnumBuilder.CreateType()
            #endregion ERROR_CONTROL

            #region SERVICE_START_TYPE
            $EnumBuilder = $ModuleBuilder.DefineEnum('SERVICE_START_TYPE', 'Public', [uint32])
            [void]$EnumBuilder.DefineLiteral('BootStart', [uint32] 0x00000000)
            [void]$EnumBuilder.DefineLiteral('SystemStart', [uint32] 0x00000001)
            [void]$EnumBuilder.DefineLiteral('Auto', [uint32] 0x00000002)
            [void]$EnumBuilder.DefineLiteral('Manual', [uint32] 0x00000003)
            [void]$EnumBuilder.DefineLiteral('Disabled', [uint32] 0x00000004)
            [void]$EnumBuilder.CreateType()
            #endregion SERVICE_START_TYPE

            #region SERVICE_CONTROL_CODE
            $EnumBuilder = $ModuleBuilder.DefineEnum('SERVICE_CONTROL_CODE', 'Public', [uint32])
            [void]$EnumBuilder.DefineLiteral('Unknown', [uint32] 0x00000000)
            [void]$EnumBuilder.DefineLiteral('AcceptStop', [uint32] 0x00000001)
            [void]$EnumBuilder.DefineLiteral('AcceptPauseContinue', [uint32] 0x00000002)
            [void]$EnumBuilder.DefineLiteral('AcceptShutdown', [uint32] 0x00000004)
            [void]$EnumBuilder.DefineLiteral('AcceptParamChange', [uint32] 0x00000008)
            [void]$EnumBuilder.DefineLiteral('AcceptNetBindingChange', [uint32] 0x00000010)
            [void]$EnumBuilder.DefineLiteral('AcceptHardwareProfileChange', [uint32] 0x00000020)
            [void]$EnumBuilder.DefineLiteral('AcceptPowerEvent', [uint32] 0x00000040)
            [void]$EnumBuilder.DefineLiteral('AcceptSessionChange', [uint32] 0x00000080)
            [void]$EnumBuilder.DefineLiteral('AcceptPreShutdown', [uint32] 0x00000100)
            [void]$EnumBuilder.DefineLiteral('AcceptTimeChange', [uint32] 0x00000200)
            [void]$EnumBuilder.DefineLiteral('AcceptTriggerEvent', [uint32] 0x00000400)
            [void]$EnumBuilder.DefineLiteral('AcceptUserModeReboot', [uint32] 0x00000800)
            $EnumBuilder.SetCustomAttribute(
                [FlagsAttribute].GetConstructor([Type]::EmptyTypes),
                @()
            )
            [void]$EnumBuilder.CreateType()
            #endregion SERVICE_CONTROL_CODE

            #region SERVICE_FLAGS
            $EnumBuilder = $ModuleBuilder.DefineEnum('SERVICE_FLAGS', 'Public', [uint32])
            [void]$EnumBuilder.DefineLiteral('NotProcessOrNotRunning', [uint32] 0x00000000)
            [void]$EnumBuilder.DefineLiteral('ServiceRunsInSystemProcess', [uint32] 0x00000001)
            $EnumBuilder.SetCustomAttribute(
                [FlagsAttribute].GetConstructor([Type]::EmptyTypes),
                @()
            )
            [void]$EnumBuilder.CreateType()
            #endregion SERVICE_FLAGS

            #region SERVICE_STATES
            $EnumBuilder = $ModuleBuilder.DefineEnum('SERVICE_STATES', 'Public', [uint32])
            [void]$EnumBuilder.DefineLiteral('SERVICE_ACTIVE', [uint32] 0x00000001)
            [void]$EnumBuilder.DefineLiteral('SERVICE_INACTIVE', [uint32] 0x00000002)
            [void]$EnumBuilder.DefineLiteral('SERVICE_ALL', [uint32] 0x00000003)
            $EnumBuilder.SetCustomAttribute(
                [FlagsAttribute].GetConstructor([Type]::EmptyTypes),
                @()
            )
            [void]$EnumBuilder.CreateType()
            #endregion SERVICE_STATES

            #region SERVICE_STATE
            $EnumBuilder = $ModuleBuilder.DefineEnum('SERVICE_STATE', 'Public', [uint32])
            [void]$EnumBuilder.DefineLiteral('Stopped', [uint32] 0x00000001)
            [void]$EnumBuilder.DefineLiteral('StartPending', [uint32] 0x00000002)
            [void]$EnumBuilder.DefineLiteral('StopPending', [uint32] 0x00000003)
            [void]$EnumBuilder.DefineLiteral('Running', [uint32] 0x00000004)
            [void]$EnumBuilder.DefineLiteral('ContinuePending', [uint32] 0x00000005)
            [void]$EnumBuilder.DefineLiteral('PausePending', [uint32] 0x00000006)
            [void]$EnumBuilder.DefineLiteral('Paused', [uint32] 0x00000007)
                        $EnumBuilder.SetCustomAttribute(
                [FlagsAttribute].GetConstructor([Type]::EmptyTypes),
                @()
            )
            [void]$EnumBuilder.CreateType()
            #endregion SERVICE_STATE

            #region SERVICE_TYPE
            $EnumBuilder = $ModuleBuilder.DefineEnum('SERVICE_TYPE', 'Public', [uint32])
            [void]$EnumBuilder.DefineLiteral('KernelDriver', [uint32] 0x00000001)
            [void]$EnumBuilder.DefineLiteral('FileSystemDriver', [uint32] 0x00000002)
            [void]$EnumBuilder.DefineLiteral('Driver', [uint32] 0x0000000B)
            [void]$EnumBuilder.DefineLiteral('Win32OwnProcess', [uint32] 0x00000010)
            [void]$EnumBuilder.DefineLiteral('Win32ShareProcess', [uint32] 0x00000020)
            [void]$EnumBuilder.DefineLiteral('Win32', [uint32] 0x00000030)
            [void]$EnumBuilder.DefineLiteral('Interactive', [uint32] 0x00000100)
            [void]$EnumBuilder.DefineLiteral('All', [uint32] 0x0000013B)
                        $EnumBuilder.SetCustomAttribute(
                [FlagsAttribute].GetConstructor([Type]::EmptyTypes),
                @()
            )
            [void]$EnumBuilder.CreateType()
            #endregion SERVICE_TYPE

            #region SERVICE_SID_TYPE
            $EnumBuilder = $ModuleBuilder.DefineEnum('SERVICE_SID_TYPE', 'Public', [uint32])
            [void]$EnumBuilder.DefineLiteral('None', [uint32] 0x00000000)
            [void]$EnumBuilder.DefineLiteral('Unrestricted', [uint32] 0x00000001)
            [void]$EnumBuilder.DefineLiteral('Restricted', [uint32] 0x00000003)            
            [void]$EnumBuilder.CreateType()
            #endregion SERVICE_SID_TYPE

            #region SCM_ACCESS
            $EnumBuilder = $ModuleBuilder.DefineEnum('SCM_ACCESS', 'Public', [uint32])
            [void]$EnumBuilder.DefineLiteral('STANDARD_RIGHTS_REQUIRED', [uint32] 0x000F0000)
            [void]$EnumBuilder.DefineLiteral('SC_MANAGER_CONNECT', [uint32] 0x00001)
            [void]$EnumBuilder.DefineLiteral('SC_MANAGER_CREATE_SERVICE', [uint32] 0x00002)
            [void]$EnumBuilder.DefineLiteral('SC_MANAGER_ENUMERATE_SERVICE', [uint32] 0x00004)
            [void]$EnumBuilder.DefineLiteral('SC_MANAGER_LOCK', [uint32] 0x00008)
            [void]$EnumBuilder.DefineLiteral('SC_MANAGER_QUERY_LOCK_STATUS', [uint32] 0x00010)
            [void]$EnumBuilder.DefineLiteral('SC_MANAGER_MODIFY_BOOT_CONFIG', [uint32] 0x00020)
            [void]$EnumBuilder.DefineLiteral('SC_MANAGER_ALL_ACCESS', [uint32] 0xf003f)
                        $EnumBuilder.SetCustomAttribute(
                [FlagsAttribute].GetConstructor([Type]::EmptyTypes),
                @()
            )
            [void]$EnumBuilder.CreateType()
            #endregion SCM_ACCESS

            #region SERVICE_ACCESS
            $EnumBuilder = $ModuleBuilder.DefineEnum('SERVICE_ACCESS', 'Public', [uint32])
            [void]$EnumBuilder.DefineLiteral('STANDARD_RIGHTS_REQUIRED', [uint32] 0x000F0000)
            [void]$EnumBuilder.DefineLiteral('SERVICE_QUERY_CONFIG', [uint32] 0x00001)
            [void]$EnumBuilder.DefineLiteral('SERVICE_CHANGE_CONFIG', [uint32] 0x00002)
            [void]$EnumBuilder.DefineLiteral('SERVICE_QUERY_STATUS', [uint32] 0x00004)
            [void]$EnumBuilder.DefineLiteral('SERVICE_ENUMERATE_DEPENDENTS', [uint32] 0x00008)
            [void]$EnumBuilder.DefineLiteral('SERVICE_START', [uint32] 0x00010)
            [void]$EnumBuilder.DefineLiteral('SERVICE_STOP', [uint32] 0x00020)
            [void]$EnumBuilder.DefineLiteral('SERVICE_PAUSE_CONTINUE', [uint32] 0x00040)
            [void]$EnumBuilder.DefineLiteral('SERVICE_INTERROGATE', [uint32] 0x00080)
            [void]$EnumBuilder.DefineLiteral('SERVICE_USER_DEFINED_CONTROL', [uint32] 0x00100)
            [void]$EnumBuilder.DefineLiteral('SERVICE_ALL_ACCESS', [uint32] 0xf01ff)
                        $EnumBuilder.SetCustomAttribute(
                [FlagsAttribute].GetConstructor([Type]::EmptyTypes),
                @()
            )
            [void]$EnumBuilder.CreateType()
            #endregion SERVICE_ACCESS

            #region SERVICE_INFO_LEVEL
            $EnumBuilder = $ModuleBuilder.DefineEnum('SERVICE_INFO_LEVEL', 'Public', [uint32])
            [void]$EnumBuilder.DefineLiteral('SERVICE_CONFIG_DELAYED_AUTO_START_INFO', [uint32] 0x00000003)
            [void]$EnumBuilder.DefineLiteral('SERVICE_CONFIG_DESCRIPTION', [uint32] 0x00000001)
            [void]$EnumBuilder.DefineLiteral('SERVICE_CONFIG_FAILURE_ACTIONS', [uint32] 0x00000002)
            [void]$EnumBuilder.DefineLiteral('SERVICE_CONFIG_FAILURE_ACTIONS_FLAG', [uint32] 0x00000004)
            [void]$EnumBuilder.DefineLiteral('SERVICE_CONFIG_PREFERRED_NODE', [uint32] 0x00000009)
            [void]$EnumBuilder.DefineLiteral('SERVICE_CONFIG_PRESHUTDOWN_INFO', [uint32] 0x00000007)
            [void]$EnumBuilder.DefineLiteral('SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO', [uint32] 0x00000006)
            [void]$EnumBuilder.DefineLiteral('SERVICE_CONFIG_SERVICE_SID_INFO', [uint32] 0x00000005)
            [void]$EnumBuilder.DefineLiteral('SERVICE_CONFIG_TRIGGER_INFO', [uint32] 0x00000008)
            [void]$EnumBuilder.DefineLiteral('SERVICE_CONFIG_LAUNCH_PROTECTED', [uint32] 0x0000000c)
                        $EnumBuilder.SetCustomAttribute(
                [FlagsAttribute].GetConstructor([Type]::EmptyTypes),
                @()
            )
            [void]$EnumBuilder.CreateType()
            #endregion SERVICE_INFO_LEVEL

            #region TRIGGER_DATA_TYPE
            $EnumBuilder = $ModuleBuilder.DefineEnum('TRIGGER_DATA_TYPE', 'Public', [uint32])
            [void]$EnumBuilder.DefineLiteral('BINARY', [uint32] 0x00000001)
            [void]$EnumBuilder.DefineLiteral('STRING', [uint32] 0x00000002)
            [void]$EnumBuilder.DefineLiteral('LEVEL', [uint32] 0x00000003)
            [void]$EnumBuilder.DefineLiteral('KEYWORD_ANY', [uint32] 0x00000004)
            [void]$EnumBuilder.DefineLiteral('KEYWORD_ALL', [uint32] 0x00000005)
                        $EnumBuilder.SetCustomAttribute(
                [FlagsAttribute].GetConstructor([Type]::EmptyTypes),
                @()
            )
            [void]$EnumBuilder.CreateType()
            #endregion TRIGGER_DATA_TYPE

            #region TRIGGER_TYPE
            $EnumBuilder = $ModuleBuilder.DefineEnum('TRIGGER_TYPE', 'Public', [uint32])
            [void]$EnumBuilder.DefineLiteral('DeviceInterfaceArrival', [uint32] 0x00000001)
            [void]$EnumBuilder.DefineLiteral('IPAddressAvailability', [uint32] 0x00000002)
            [void]$EnumBuilder.DefineLiteral('DomainJoin', [uint32] 0x00000003)
            [void]$EnumBuilder.DefineLiteral('FirewallPortEvent', [uint32] 0x00000004)
            [void]$EnumBuilder.DefineLiteral('GroupPolicy', [uint32] 0x00000005)
            [void]$EnumBuilder.DefineLiteral('NetworkEndpoint', [uint32] 0x00000006)
            [void]$EnumBuilder.DefineLiteral('Custom', [uint32] 0x00000014)
                        $EnumBuilder.SetCustomAttribute(
                [FlagsAttribute].GetConstructor([Type]::EmptyTypes),
                @()
            )
            [void]$EnumBuilder.CreateType()
            #endregion TRIGGER_TYPE

            #region TRIGGER_ACTION
            $EnumBuilder = $ModuleBuilder.DefineEnum('TRIGGER_ACTION', 'Public', [uint32])
            [void]$EnumBuilder.DefineLiteral('ServiceStart', [uint32] 0x00000001)
            [void]$EnumBuilder.DefineLiteral('ServiceStop', [uint32] 0x00000002)
            [void]$EnumBuilder.CreateType()
            #endregion TRIGGER_ACTION

            #region SC_ACTION_TYPE
            $EnumBuilder = $ModuleBuilder.DefineEnum('SC_ACTION_TYPE', 'Public', [uint32])
            [void]$EnumBuilder.DefineLiteral('None', [uint32] 0x00000000)
            [void]$EnumBuilder.DefineLiteral('Restart', [uint32] 0x00000001)
            [void]$EnumBuilder.DefineLiteral('Reboot', [uint32] 0x00000002)
            [void]$EnumBuilder.DefineLiteral('RunCommand', [uint32] 0x00000003)
                        $EnumBuilder.SetCustomAttribute(
                [FlagsAttribute].GetConstructor([Type]::EmptyTypes),
                @()
            )
            [void]$EnumBuilder.CreateType()
            #endregion SC_ACTION_TYPE

            #endregion Enums

            #region Struct

            #region SERVICE_STATUS_PROCESS
            $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit'
            $STRUCT_TypeBuilder = $ModuleBuilder.DefineType('SERVICE_STATUS_PROCESS', $Attributes, [System.ValueType], 8, 0x0)
            [void]$STRUCT_TypeBuilder.DefineField('dwServiceType', [int32], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('dwCurrentState', [int32], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('dwControlsAccepted', [int32], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('dwWin32ExitCode', [int32], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('dwServiceSpecificExitCode', [int32], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('dwCheckPoint', [int32], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('dwWaitHint', [int32], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('dwProcessId', [int32], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('dwServiceFlags', [int32], @('Public'))
            [void]$STRUCT_TypeBuilder.CreateType()
            #endregion SERVICE_STATUS_PROCESS

            #region SERVICE_STATUS
            $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit'
            $STRUCT_TypeBuilder = $ModuleBuilder.DefineType('SERVICE_STATUS', $Attributes, [System.ValueType], 8, 0x0)
            [void]$STRUCT_TypeBuilder.DefineField('dwServiceType', [SERVICE_TYPE], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('dwCurrentState', [SERVICE_STATE], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('dwControlsAccepted', [int32], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('dwWin32ExitCode', [int32], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('dwServiceSpecificExitCode', [int32], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('dwCheckPoint', [int32], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('dwWaitHint', [int32], @('Public'))
            [void]$STRUCT_TypeBuilder.CreateType()
            #endregion SERVICE_STATUS

            #region QUERY_SERVICE_CONFIG
            $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit'
            $STRUCT_TypeBuilder = $ModuleBuilder.DefineType('QUERY_SERVICE_CONFIG', $Attributes, [System.ValueType], 8, 0x0)
            [void]$STRUCT_TypeBuilder.DefineField('dwServiceType', [int32], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('dwStartType', [int32], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('dwErrorControl', [int32], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('lpBinaryPathName', [string], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('lpLoadOrderGroup', [string], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('dwTagId', [int32], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('lpDependencies', [string], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('lpServiceStartName', [string], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('lpDisplayName', [string], @('Public'))
            [void]$STRUCT_TypeBuilder.CreateType()
            #endregion QUERY_SERVICE_CONFIG

            If ([intptr]::Size -eq 8) {
                #region ENUM_SERVICE_STATUS_PROCESS
                $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit'
                $STRUCT_TypeBuilder = $ModuleBuilder.DefineType('ENUM_SERVICE_STATUS_PROCESS', $Attributes, [System.ValueType], 8, 0x0)
                [void]$STRUCT_TypeBuilder.DefineField('lpServiceName', [string], @('Public'))
                [void]$STRUCT_TypeBuilder.DefineField('lpDisplayName', [string], @('Public'))
                [void]$STRUCT_TypeBuilder.DefineField('ServiceStatusProcess', [SERVICE_STATUS_PROCESS], @('Public'))
                [void]$STRUCT_TypeBuilder.CreateType()
                #endregion ENUM_SERVICE_STATUS_PROCESS
            } 
            Else {
                #region ENUM_SERVICE_STATUS_PROCESS
                $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit'
                $STRUCT_TypeBuilder = $ModuleBuilder.DefineType('ENUM_SERVICE_STATUS_PROCESS', $Attributes, [System.ValueType], 4, 0x0)
                [void]$STRUCT_TypeBuilder.DefineField('lpServiceName', [string], @('Public'))
                [void]$STRUCT_TypeBuilder.DefineField('lpDisplayName', [string], @('Public'))
                [void]$STRUCT_TypeBuilder.DefineField('ServiceStatusProcess', [SERVICE_STATUS_PROCESS], @('Public'))
                [void]$STRUCT_TypeBuilder.CreateType()
                #endregion ENUM_SERVICE_STATUS_PROCESS
            }

            If ([intptr]::Size -eq 8) {
                #region ENUM_SERVICE_STATUS
                $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit'
                $STRUCT_TypeBuilder = $ModuleBuilder.DefineType('ENUM_SERVICE_STATUS', $Attributes, [System.ValueType], 8, 0x0)
                [void]$STRUCT_TypeBuilder.DefineField('lpServiceName', [string], @('Public'))
                [void]$STRUCT_TypeBuilder.DefineField('lpDisplayName', [string], @('Public'))
                [void]$STRUCT_TypeBuilder.DefineField('ServiceStatus', [SERVICE_STATUS], @('Public'))
                [void]$STRUCT_TypeBuilder.CreateType()
                #endregion ENUM_SERVICE_STATUS
            }
            Else {
                #region ENUM_SERVICE_STATUS
                $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit'
                $STRUCT_TypeBuilder = $ModuleBuilder.DefineType('ENUM_SERVICE_STATUS', $Attributes, [System.ValueType], 4, 0x0)
                [void]$STRUCT_TypeBuilder.DefineField('lpServiceName', [string], @('Public'))
                [void]$STRUCT_TypeBuilder.DefineField('lpDisplayName', [string], @('Public'))
                [void]$STRUCT_TypeBuilder.DefineField('ServiceStatus', [SERVICE_STATUS], @('Public'))
                [void]$STRUCT_TypeBuilder.CreateType()
                #endregion ENUM_SERVICE_STATUS
            } 

            #region TRIGGER
            $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit'
            $STRUCT_TypeBuilder = $ModuleBuilder.DefineType('TRIGGER', $Attributes, [System.ValueType], 8, 0x0)
            [void]$STRUCT_TypeBuilder.DefineField('TriggerType', [TRIGGER_TYPE], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('Action', [TRIGGER_ACTION], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('SubType', [string], @('Public'))
            [void]$STRUCT_TypeBuilder.CreateType()
            #endregion TRIGGER

            #region SERVICE_REQUIRED_PRIVILEGES_INFO
            $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit'
            $STRUCT_TypeBuilder = $ModuleBuilder.DefineType('SERVICE_REQUIRED_PRIVILEGES_INFO', $Attributes, [System.ValueType], 8, 0x0)
            [void]$STRUCT_TypeBuilder.DefineField('pmszRequiredPrivileges', [intptr], @('Public'))
            [void]$STRUCT_TypeBuilder.CreateType()
            #endregion SERVICE_REQUIRED_PRIVILEGES_INFO

            #region SC_ACTION
            $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit'
            $STRUCT_TypeBuilder = $ModuleBuilder.DefineType('SC_ACTION', $Attributes, [System.ValueType], 8, 0x0)
            [void]$STRUCT_TypeBuilder.DefineField('type', [SC_ACTION_TYPE], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('delay', [uint32], @('Public'))
            [void]$STRUCT_TypeBuilder.CreateType()
            #endregion SC_ACTION

            #region SERVICE_FAILURE_ACTIONS
            $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit'
            $STRUCT_TypeBuilder = $ModuleBuilder.DefineType('SERVICE_FAILURE_ACTIONS', $Attributes, [System.ValueType], 8, 0x0)
            [void]$STRUCT_TypeBuilder.DefineField('dwResetPeriod', [uint32], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('lpRebootMsg', [string], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('lpCommand', [string], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('cActions', [uint32], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('lpsaActions', [intptr], @('Public'))
            [void]$STRUCT_TypeBuilder.CreateType()
            #endregion SERVICE_FAILURE_ACTIONS

            #region SERVICE_FAILURE_ACTIONS_FLAG
            $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit'
            $STRUCT_TypeBuilder = $ModuleBuilder.DefineType('SERVICE_FAILURE_ACTIONS_FLAG', $Attributes, [System.ValueType], 8, 0x0)
            [void]$STRUCT_TypeBuilder.DefineField('fFailureActionsOnNonCrashFailures', [bool], @('Public'))
            [void]$STRUCT_TypeBuilder.CreateType()
            #endregion SERVICE_FAILURE_ACTIONS_FLAG

            #region FAILURE_ACTIONS
            $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit'
            $STRUCT_TypeBuilder = $ModuleBuilder.DefineType('FAILURE_ACTIONS', $Attributes, [System.ValueType], 8, 0x0)
            [void]$STRUCT_TypeBuilder.DefineField('ResetPeriod', [uint32], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('RebootMessage', [string], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('Command', [string], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('Actions', [SC_ACTION[]], @('Public'))
            [void]$STRUCT_TypeBuilder.CreateType()
            #endregion FAILURE_ACTIONS

            #region SERVICE_PREFERRED_NODE_INFO
            $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit'
            $STRUCT_TypeBuilder = $ModuleBuilder.DefineType('SERVICE_PREFERRED_NODE_INFO', $Attributes, [System.ValueType], 8, 0x0)
            [void]$STRUCT_TypeBuilder.DefineField('usPreferredNode', [int16], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('fDelete', [bool], @('Public'))
            [void]$STRUCT_TypeBuilder.CreateType()
            #endregion SERVICE_PREFERRED_NODE_INFO

            #region SERVICE_PRESHUTDOWN_INFO
            $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit'
            $STRUCT_TypeBuilder = $ModuleBuilder.DefineType('SERVICE_PRESHUTDOWN_INFO', $Attributes, [System.ValueType], 8, 0x0)
            [void]$STRUCT_TypeBuilder.DefineField('dwPreshutdownTimeout', [uint32], @('Public'))
            [void]$STRUCT_TypeBuilder.CreateType()
            #endregion SERVICE_PRESHUTDOWN_INFO

            #region SERVICE_SID_INFO
            $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit'
            $STRUCT_TypeBuilder = $ModuleBuilder.DefineType('SERVICE_SID_INFO', $Attributes, [System.ValueType], 8, 0x0)
            [void]$STRUCT_TypeBuilder.DefineField('dwServiceSidType', [uint32], @('Public'))
            [void]$STRUCT_TypeBuilder.CreateType()
            #endregion SERVICE_SID_INFO

            #region SERVICE_DELAYED_AUTO_START_INFO
            $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit'
            $STRUCT_TypeBuilder = $ModuleBuilder.DefineType('SERVICE_DELAYED_AUTO_START_INFO', $Attributes, [System.ValueType], 8, 0x0)
            [void]$STRUCT_TypeBuilder.DefineField('fDelayedAutostart', [bool], @('Public'))
            [void]$STRUCT_TypeBuilder.CreateType()
            #endregion SERVICE_DELAYED_AUTO_START_INFO

            #region SERVICE_DESCRIPTION
            $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit'
            $STRUCT_TypeBuilder = $ModuleBuilder.DefineType('SERVICE_DESCRIPTION', $Attributes, [System.ValueType], 8, 0x0)
            $ctor = [System.Runtime.InteropServices.MarshalAsAttribute].GetConstructor(@([System.Runtime.InteropServices.UnmanagedType]))
            $CustomAttribute = [System.Runtime.InteropServices.UnmanagedType]::LPStr
            $CustomAttributeBuilder = New-Object System.Reflection.Emit.CustomAttributeBuilder -ArgumentList $ctor, $CustomAttribute 
            $Field = $STRUCT_TypeBuilder.DefineField('lpDescription', [string], @('Public'))
            $Field.SetCustomAttribute($CustomAttributeBuilder)
            [void]$STRUCT_TypeBuilder.CreateType()
            #endregion SERVICE_DESCRIPTION

            #region SERVICE_TRIGGER_SPECIFIC_DATA_ITEM
            $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit'
            $STRUCT_TypeBuilder = $ModuleBuilder.DefineType('SERVICE_TRIGGER_SPECIFIC_DATA_ITEM', $Attributes, [System.ValueType], 8, 0x0)
            [void]$STRUCT_TypeBuilder.DefineField('dwDataType', [int32], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('cbData', [uint32], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('pData', [intptr], @('Public'))
            [void]$STRUCT_TypeBuilder.CreateType()
            #endregion SERVICE_TRIGGER_SPECIFIC_DATA_ITEM

            #region SERVICE_TRIGGER
            $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit'
            $STRUCT_TypeBuilder = $ModuleBuilder.DefineType('SERVICE_TRIGGER', $Attributes, [System.ValueType], 8, 0x0)
            [void]$STRUCT_TypeBuilder.DefineField('dwTriggerType', [TRIGGER_TYPE], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('dwAction', [TRIGGER_ACTION], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('pTriggerSubType', [IntPtr], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('cDataItems', [uint32], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('pDataItems', [IntPtr], @('Public'))
            [void]$STRUCT_TypeBuilder.CreateType()
            #endregion SERVICE_TRIGGER

            #region SERVICE_TRIGGER_INFO
            $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit'
            $STRUCT_TypeBuilder = $ModuleBuilder.DefineType('SERVICE_TRIGGER_INFO', $Attributes, [System.ValueType], 8, 0x0)
            [void]$STRUCT_TypeBuilder.DefineField('cTriggers', [int32], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('pTriggers', [Intptr], @('Public'))
            [void]$STRUCT_TypeBuilder.DefineField('pReserved', [intptr], @('Public'))
            [void]$STRUCT_TypeBuilder.CreateType()
            #endregion SERVICE_TRIGGER_INFO

            #endregion Struct

            $TypeBuilder = $ModuleBuilder.DefineType('Service.Trigger', 'Public, Class')

            #region Methods

            #region OpenSCManager Method
            $PInvokeMethod = $TypeBuilder.DefineMethod(
                'OpenSCManager', #Method Name
                [Reflection.MethodAttributes] 'PrivateScope, Public, Static, HideBySig, PinvokeImpl', #Method Attributes
                [intptr], #Method Return Type
                [Type[]] @(
                    [string],
                    [string],  
                    [uint32]   
                ) #Method Parameters
            )

            $DllImportConstructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String]))
            $FieldArray = [Reflection.FieldInfo[]] @(
                [Runtime.InteropServices.DllImportAttribute].GetField('EntryPoint'),
                [Runtime.InteropServices.DllImportAttribute].GetField('SetLastError')
                [Runtime.InteropServices.DllImportAttribute].GetField('ExactSpelling')
                [Runtime.InteropServices.DllImportAttribute].GetField('PreserveSig')
            )

            $FieldValueArray = [Object[]] @(
                'OpenSCManagerA', #CASE SENSITIVE!!
                $True,
                $True,
                $True
            )

            $CustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder(
                $DllImportConstructor,
                @('advapi32.dll'),
                $FieldArray,
                $FieldValueArray
            )

            $PInvokeMethod.SetCustomAttribute($CustomAttribute)
            #endregion OpenSCManager Method

            #region OpenService Method
            $PInvokeMethod = $TypeBuilder.DefineMethod(
                'OpenService', #Method Name
                [Reflection.MethodAttributes] 'PrivateScope, Public, Static, HideBySig, PinvokeImpl', #Method Attributes
                [intptr], #Method Return Type
                [Type[]] @(
                    [intptr],
                    [string],  
                    [SERVICE_ACCESS]   
                ) #Method Parameters
            )

            $DllImportConstructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String]))
            $FieldArray = [Reflection.FieldInfo[]] @(
                [Runtime.InteropServices.DllImportAttribute].GetField('EntryPoint'),
                [Runtime.InteropServices.DllImportAttribute].GetField('SetLastError')
                [Runtime.InteropServices.DllImportAttribute].GetField('ExactSpelling')
                [Runtime.InteropServices.DllImportAttribute].GetField('PreserveSig')
                [Runtime.InteropServices.DllImportAttribute].GetField('CharSet')
            )

            $FieldValueArray = [Object[]] @(
                'OpenService', #CASE SENSITIVE!!
                $True,
                $False,
                $True,
                [System.Runtime.InteropServices.CharSet]::Auto
            )

            $CustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder(
                $DllImportConstructor,
                @('advapi32.dll'),
                $FieldArray,
                $FieldValueArray
            )

            $PInvokeMethod.SetCustomAttribute($CustomAttribute)
            #endregion OpenService Method

            #region QueryServiceConfig2 Method
            $PInvokeMethod = $TypeBuilder.DefineMethod(
                'QueryServiceConfig2', #Method Name
                [Reflection.MethodAttributes] 'PrivateScope, Public, Static, HideBySig, PinvokeImpl', #Method Attributes
                [bool], #Method Return Type
                [Type[]] @(
                    [intptr],
                    [uint32],  
                    [intptr] ,
                    [uint32],
                    [uint32].MakeByRefType()  
                ) #Method Parameters
            )

            $DllImportConstructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String]))
            $FieldArray = [Reflection.FieldInfo[]] @(
                [Runtime.InteropServices.DllImportAttribute].GetField('EntryPoint'),
                [Runtime.InteropServices.DllImportAttribute].GetField('SetLastError')
                [Runtime.InteropServices.DllImportAttribute].GetField('ExactSpelling')
                [Runtime.InteropServices.DllImportAttribute].GetField('PreserveSig')            
            )

            $FieldValueArray = [Object[]] @(
                'QueryServiceConfig2A', #CASE SENSITIVE!!
                $True,
                $False,
                $True
            )

            $CustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder(
                $DllImportConstructor,
                @('advapi32.dll'),
                $FieldArray,
                $FieldValueArray
            )

            $PInvokeMethod.SetCustomAttribute($CustomAttribute)
            #endregion QueryServiceConfig2 Method

            #region QueryServiceConfig Method
            $PInvokeMethod = $TypeBuilder.DefineMethod(
                'QueryServiceConfig', #Method Name
                [Reflection.MethodAttributes] 'PrivateScope, Public, Static, HideBySig, PinvokeImpl', #Method Attributes
                [bool], #Method Return Type
                [Type[]] @(
                    [intptr],
                    [intptr],  
                    [uint32] ,
                    [uint32].MakeByRefType()  
                ) #Method Parameters
            )

            $DllImportConstructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String]))
            $FieldArray = [Reflection.FieldInfo[]] @(
                [Runtime.InteropServices.DllImportAttribute].GetField('EntryPoint'),
                [Runtime.InteropServices.DllImportAttribute].GetField('SetLastError')
                [Runtime.InteropServices.DllImportAttribute].GetField('ExactSpelling')
                [Runtime.InteropServices.DllImportAttribute].GetField('PreserveSig')            
            )

            $FieldValueArray = [Object[]] @(
                'QueryServiceConfigA', #CASE SENSITIVE!!
                $True,
                $False,
                $True
            )

            $CustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder(
                $DllImportConstructor,
                @('advapi32.dll'),
                $FieldArray,
                $FieldValueArray
            )

            $PInvokeMethod.SetCustomAttribute($CustomAttribute)
            #endregion QueryServiceConfig Method

            #region CloseServiceHandle Method
            $PInvokeMethod = $TypeBuilder.DefineMethod(
                'CloseServiceHandle', #Method Name
                [Reflection.MethodAttributes] 'PrivateScope, Public, Static, HideBySig, PinvokeImpl', #Method Attributes
                [bool], #Method Return Type
                [Type[]] @(
                    [intptr] 
                ) #Method Parameters
            )

            $DllImportConstructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String]))
            $FieldArray = [Reflection.FieldInfo[]] @(
                [Runtime.InteropServices.DllImportAttribute].GetField('EntryPoint'),
                [Runtime.InteropServices.DllImportAttribute].GetField('SetLastError')
                [Runtime.InteropServices.DllImportAttribute].GetField('ExactSpelling')
                [Runtime.InteropServices.DllImportAttribute].GetField('PreserveSig')              
            )

            $FieldValueArray = [Object[]] @(
                'CloseServiceHandle', #CASE SENSITIVE!!
                $True,
                $False,
                $True
            )

            $CustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder(
                $DllImportConstructor,
                @('advapi32.dll'),
                $FieldArray,
                $FieldValueArray
            )

            $PInvokeMethod.SetCustomAttribute($CustomAttribute)
            #endregion CloseServiceHandle Method

            #region EnumServicesStatusEx Method
            $PInvokeMethod = $TypeBuilder.DefineMethod(
                'EnumServicesStatusEx', #Method Name
                [Reflection.MethodAttributes] 'PrivateScope, Public, Static, HideBySig, PinvokeImpl', #Method Attributes
                [bool], #Method Return Type
                [Type[]] @(
                    [intptr],
                    [uint32],  
                    [uint32],
                    [uint32],
                    [intptr],
                    [uint32],
                    [uint32].MakeByRefType(),
                    [int].MakeByRefType(),
                    [uint32].MakeByRefType(),
                    [string]
                ) #Method Parameters
            )

            $DllImportConstructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String]))
            $FieldArray = [Reflection.FieldInfo[]] @(
                [Runtime.InteropServices.DllImportAttribute].GetField('EntryPoint'),
                [Runtime.InteropServices.DllImportAttribute].GetField('SetLastError')
                [Runtime.InteropServices.DllImportAttribute].GetField('ExactSpelling')
                [Runtime.InteropServices.DllImportAttribute].GetField('PreserveSig')
            )

            $FieldValueArray = [Object[]] @(
                'EnumServicesStatusExA', #CASE SENSITIVE!!
                $True,
                $True,
                $True
            )

            $CustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder(
                $DllImportConstructor,
                @('advapi32.dll'),
                $FieldArray,
                $FieldValueArray
            )

            $PInvokeMethod.SetCustomAttribute($CustomAttribute)
            #endregion EnumServicesStatusEx Method

            #region QueryServiceStatusEx Method
            $PInvokeMethod = $TypeBuilder.DefineMethod(
                'QueryServiceStatusEx', #Method Name
                [Reflection.MethodAttributes] 'PrivateScope, Public, Static, HideBySig, PinvokeImpl', #Method Attributes
                [bool], #Method Return Type
                [Type[]] @(
                    [intptr],
                    [uint32],  
                    [intptr],
                    [uint32],
                    [uint32].MakeByRefType()
                ) #Method Parameters
            )

            $DllImportConstructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String]))
            $FieldArray = [Reflection.FieldInfo[]] @(
                [Runtime.InteropServices.DllImportAttribute].GetField('EntryPoint'),
                [Runtime.InteropServices.DllImportAttribute].GetField('SetLastError')
                [Runtime.InteropServices.DllImportAttribute].GetField('ExactSpelling')
                [Runtime.InteropServices.DllImportAttribute].GetField('PreserveSig')
            )

            $FieldValueArray = [Object[]] @(
                'QueryServiceStatusEx', #CASE SENSITIVE!!
                $True,
                $True,
                $True
            )

            $CustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder(
                $DllImportConstructor,
                @('advapi32.dll'),
                $FieldArray,
                $FieldValueArray
            )

            $PInvokeMethod.SetCustomAttribute($CustomAttribute)
            #endregion QueryServiceStatusEx Method

            #region EnumDependentServices Method
            $PInvokeMethod = $TypeBuilder.DefineMethod(
                'EnumDependentServices', #Method Name
                [Reflection.MethodAttributes] 'PrivateScope, Public, Static, HideBySig, PinvokeImpl', #Method Attributes
                [bool], #Method Return Type
                [Type[]] @(
                    [intptr],
                    [uint32],  
                    [intptr],
                    [uint32],
                    [uint32].MakeByRefType(),
                    [uint32].MakeByRefType()
                ) #Method Parameters
            )

            $DllImportConstructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String]))
            $FieldArray = [Reflection.FieldInfo[]] @(
                [Runtime.InteropServices.DllImportAttribute].GetField('EntryPoint'),
                [Runtime.InteropServices.DllImportAttribute].GetField('SetLastError')
                [Runtime.InteropServices.DllImportAttribute].GetField('ExactSpelling')
                [Runtime.InteropServices.DllImportAttribute].GetField('PreserveSig')
            )

            $FieldValueArray = [Object[]] @(
                'EnumDependentServicesA', #CASE SENSITIVE!!
                $True,
                $True,
                $True
            )

            $CustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder(
                $DllImportConstructor,
                @('advapi32.dll'),
                $FieldArray,
                $FieldValueArray
            )

            $PInvokeMethod.SetCustomAttribute($CustomAttribute)
            #endregion EnumDependentServices Method

            #endregion Methods

            [void]$TypeBuilder.CreateType()
        }
        #endregion Reflection

        #region Lookups
        $TRIGGER_SUBTYPE = @{
            NAMED_PIPE_EVENT_GUID = [guid]'1F81D131-3FAC-4537-9E0C-7E7B0C2F4B55'
            RPC_INTERFACE_EVENT_GUID = [guid]'BC90D167-9470-4139-A9BA-BE0BBBF5B74D'
            DOMAIN_JOIN_GUID = [guid]'1ce20aba-9851-4421-9430-1ddeb766e809'
            DOMAIN_LEAVE_GUID = [guid]'ddaf516e-58c2-4866-9574-c3b615d42ea1'
            FIREWALL_PORT_OPEN_GUID = [guid]'b7569e07-8421-4ee0-ad10-86915afdad09'
            FIREWALL_PORT_CLOSE_GUID = [guid]'a144ed38-8e12-4de4-9d96-e64740b1a524'
            MACHINE_POLICY_PRESENT_GUID = [guid]'659FCAE6-5BDB-4DA9-B1FF-CA2A178D46E0'
            NETWORK_MANAGER_FIRST_IP_ADDRESS_ARRIVAL_GUID = [guid]'4f27f2de-14e2-430b-a549-7cd48cbc8245'
            NETWORK_MANAGER_LAST_IP_ADDRESS_REMOVAL_GUID = [guid]'cc4ba62a-162e-4648-847a-b6bdf993e335'
            USER_POLICY_PRESENT_GUID = [guid]'54FB46C8-F089-464C-B1FD-59D1B62C3B50'
            ETW_PROVIDER_UUID = [guid]'d02a9c27-79b8-40d6-9b97-cf3f8b7b5d60'
            [guid]'1F81D131-3FAC-4537-9E0C-7E7B0C2F4B55' = 'NAMED_PIPE_EVENT'
            [guid]'BC90D167-9470-4139-A9BA-BE0BBBF5B74D' = 'RPC_INTERFACE_EVENT'
            [guid]'1ce20aba-9851-4421-9430-1ddeb766e809' = 'DOMAIN_JOIN'
            [guid]'ddaf516e-58c2-4866-9574-c3b615d42ea1' = 'DOMAIN_LEAVE'
            [guid]'b7569e07-8421-4ee0-ad10-86915afdad09' = 'FIREWALL_PORT_OPEN'
            [guid]'a144ed38-8e12-4de4-9d96-e64740b1a524' = 'FIREWALL_PORT_CLOSE'
            [guid]'659FCAE6-5BDB-4DA9-B1FF-CA2A178D46E0' = 'MACHINE_POLICY_PRESENT'
            [guid]'4f27f2de-14e2-430b-a549-7cd48cbc8245' = 'NETWORK_MANAGER_FIRST_IP_ADDRESS_ARRIVAL'
            [guid]'cc4ba62a-162e-4648-847a-b6bdf993e335' = 'NETWORK_MANAGER_LAST_IP_ADDRESS_REMOVAL'
            [guid]'54FB46C8-F089-464C-B1FD-59D1B62C3B50' = 'USER_POLICY_PRESENT'
            [guid]'d02a9c27-79b8-40d6-9b97-cf3f8b7b5d60' = 'ETW_PROVIDER'
        }
        #endregion Lookups
    }

    Process {
        If (-NOT $PSBoundParameters.ContainsKey('Computername')) {
            $Computername = $env:COMPUTERNAME            
        } 
        $Computername | ForEach {
            $Computer = $_
            #region Open SCManager
            Write-Verbose 'Opening Service Manager'
            $SCMHandle = [Service.Trigger]::OpenSCManager(
                $Computer, 
                [NullString]::Value, 
                ([SCM_ACCESS]::SC_MANAGER_CONNECT -BOR [SCM_ACCESS]::SC_MANAGER_ENUMERATE_SERVICE -BOR [SCM_ACCESS]::SC_MANAGER_QUERY_LOCK_STATUS)
            )
            $LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
            If ($SCMHandle -eq [intptr]::Zero) {
                Write-Warning ("{0} ({1})" -f $LastError.Message,$LastError.NativeErrorCode)
                Break    
            }
            Write-Debug "SCManager Handle: $SCMHandle"
            #endregion Open SCManager

            $List = New-Object System.Collections.ArrayList

            If (-NOT $PSBoundParameters.ContainsKey('Name')) {
                #region Enum Services
                Write-Verbose 'Get all services'
                $BytesNeeded = 0
                $ServicesReturned = 0
                $ResumeHandle = 0
                $Return = [Service.Trigger]::EnumServicesStatusEx(
                    $SCMHandle,
                    [SC_ENUM_TYPE]::SC_ENUM_PROCESS_INFO,
                    [SERVICE_TYPE]$ServiceType,
                    [SERVICE_STATES]::SERVICE_ALL,
                    [IntPtr]::Zero,
                    0, # Current Buffer
                    [ref]$BytesNeeded,
                    [ref]$ServicesReturned,
                    [ref]$ResumeHandle,
                    [NullString]::Value
                )
                $LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
                Write-Debug "BytesNeeded: $BytesNeeded"
                If ($LastError.NativeErrorCode -eq 234) { #More data is available - Expected result
                    $Buffer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($BytesNeeded)
                    $Return = [Service.Trigger]::EnumServicesStatusEx(
                        $SCMHandle,
                        [SC_ENUM_TYPE]::SC_ENUM_PROCESS_INFO,
                        [SERVICE_TYPE]$ServiceType,
                        [SERVICE_STATES]::SERVICE_ALL,
                        $Buffer,
                        $BytesNeeded, # Current Buffer
                        [ref]$BytesNeeded,
                        [ref]$ServicesReturned,
                        [ref]$ResumeHandle,
                        [NullString]::Value
                    )
                    $LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error() 
                    Write-Debug "ServicesReturned: $ServicesReturned"
                    If ($Return) {                    
                        $tempPointer = $Buffer
                        For ($i=0;$i -lt $ServicesReturned;$i++) {
                            #Write-Progress -Status 'Gathering Services' -PercentComplete (($i/$ServicesReturned)*100) -Activity "Pointer: $tempPointer"
                            If ([intptr]::Size -eq 8) {
                                # 64 bit
                                $Object = ([System.Runtime.InteropServices.Marshal]::PtrToStructure($tempPointer,[type][ENUM_SERVICE_STATUS_PROCESS]))
                                [intptr]$tempPointer = $tempPointer.ToInt64() + [System.Runtime.InteropServices.Marshal]::SizeOf([type][ENUM_SERVICE_STATUS_PROCESS])
                            } 
                            Else {
                                #32 bit
                                $Object = ([System.Runtime.InteropServices.Marshal]::PtrToStructure($tempPointer,[type][ENUM_SERVICE_STATUS_PROCESS]))
                                [intptr]$tempPointer = $tempPointer.ToInt32() + [System.Runtime.InteropServices.Marshal]::SizeOf([type][ENUM_SERVICE_STATUS_PROCESS])
                            }
                            Try {
                                $Controls = [SERVICE_CONTROL_CODE]$Object.ServiceStatusProcess.dwControlsAccepted
                            } 
                            Catch {
                                $Controls = $Object.ServiceStatusProcess.dwControlsAccepted
                            }
                            Try {
                                $Service_Type = [SERVICE_TYPE]$Object.ServiceStatusProcess.dwServiceType
                            } 
                            Catch {
                                $Service_Type = $Object.ServiceStatusProcess.dwServiceType
                            }
                            [void]$List.Add([pscustomobject]@{
                                Name = $Object.lpServiceName
                                Type = $Service_Type
                                State = [SERVICE_STATE]$Object.ServiceStatusProcess.dwCurrentState
                                Controls = $Controls
                                Win32ExitCode = $Object.ServiceStatusProcess.dwWin32ExitCode
                                ServiceExitCode = $Object.ServiceStatusProcess.dwServiceSpecificExitCode
                                CheckPoint = $Object.ServiceStatusProcess.dwCheckPoint
                                WaitHint = $Object.ServiceStatusProcess.dwWaitHint
                                ProcessID = $Object.ServiceStatusProcess.dwProcessId
                                ServiceFlags = [SERVICE_FLAGS]$Object.ServiceStatusProcess.dwServiceFlags
                            })
                        }
                    }         
                }
                #endregion Enum Services
            } 
            Else {
                $Name | ForEach {
                    $_Name = $_
                    Write-Verbose "Query single service: $_Name"

                    #region Open Service
                    $ServiceHandle = [Service.Trigger]::OpenService(
                        $SCMHandle,
                        $_Name,
                        [SERVICE_ACCESS]::SERVICE_QUERY_STATUS
                    )
                    #endregion Open Service

                    Write-Debug "Service Handle: $ServiceHandle"

                    #region Query Service
                    $BytesNeeded = 0
                    $Return = [Service.Trigger]::QueryServiceStatusEx(
                        $ServiceHandle,
                        0,
                        [intptr]::Zero,
                        $BytesNeeded,
                        [ref]$BytesNeeded
                    )
                    $LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
                    Write-Debug "Bytes Needed: $BytesNeeded"
                    If ($LastError.NativeErrorCode -eq 122) { #The data area passed to a system call is too small - This is expected!
                        Write-Verbose "Querying service"
                        $Buffer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($BytesNeeded)
                        $Return = [Service.Trigger]::QueryServiceStatusEx(
                            $ServiceHandle,
                            0,
                            $Buffer,
                            $BytesNeeded,
                            [ref]$BytesNeeded
                        )
                        $LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
                    } 
                    #endregion Query Service
                    If ($Return) {
                        $Object = [System.Runtime.InteropServices.Marshal]::PtrToStructure($Buffer, [type][SERVICE_STATUS_PROCESS])
                        Try {
                            $Controls = [SERVICE_CONTROL_CODE]$Object.dwControlsAccepted
                        } 
                        Catch {
                            $Controls = $Object.dwControlsAccepted
                        }
                        Try {
                            $Service_Type = [SERVICE_TYPE]$Object.dwServiceType
                        } 
                        Catch {
                            $Service_Type = $Object.dwServiceType
                        }
                        [void]$List.Add([pscustomobject]@{
                            Name = $_Name
                            Type = $Service_Type
                            State = [SERVICE_STATE]$Object.dwCurrentState
                            Controls = $Controls
                            Win32ExitCode = $Object.dwWin32ExitCode
                            ServiceExitCode = $Object.dwServiceSpecificExitCode
                            CheckPoint = $Object.dwCheckPoint
                            WaitHint = $Object.dwWaitHint
                            ProcessID = $Object.dwProcessId
                            ServiceFlags = [SERVICE_FLAGS]$Object.dwServiceFlags
                        })
                    }
                    [void][Service.Trigger]::CloseServiceHandle($ServiceHandle)
                }
            }
            Write-Verbose "Beginning iteration through services for more data"
            ForEach ($Service in $List) {
                #region Open Service
                $ServiceHandle = [Service.Trigger]::OpenService(
                    $SCMHandle,
                    $Service.Name,
                    [SERVICE_ACCESS]::SERVICE_QUERY_CONFIG
                )
                Write-Debug "ServiceHandle: $ServiceHandle"
                #endregion Open Service

                #region Query Service Required Privileges
                #Determine bytes needed
                [uint32]$BytesNeeded = $Null
                $Return = [Service.Trigger]::QueryServiceConfig2(
                    $ServiceHandle,
                    [SERVICE_INFO_LEVEL]::SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO,
                    [intptr]::Zero,
                    0,
                    [ref]$BytesNeeded
                )
                $LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
                If ($LastError.NativeErrorCode -eq 122) { 
                    #Buffer too small which is expected so we rerun this again with the expected buffer
                    $Pointer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($BytesNeeded)    
                    $Return = [Service.Trigger]::QueryServiceConfig2(
                        $ServiceHandle,
                        [SERVICE_INFO_LEVEL]::SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO,
                        $Pointer,
                        $BytesNeeded,
                        [ref]$BytesNeeded
                    )
                    $LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
                    If (-NOT $Return) {
                        Write-Warning ("{0} ({1})" -f $LastError.Message,$LastError.NativeErrorCode)
                        Break    
                    }
                    #Build Struct from Pointer
                    $ServicePrivileges = [System.Runtime.InteropServices.Marshal]::PtrToStructure($Pointer,[type][SERVICE_REQUIRED_PRIVILEGES_INFO])
                    $P_Pointer = $ServicePrivileges.pmszRequiredPrivileges
                    $Remaining = $BytesNeeded
                    $Privileges = New-Object System.Collections.ArrayList
                    While ($Remaining -gt 0) {
                        $string = [System.Runtime.InteropServices.Marshal]::PtrToStringAnsi($P_Pointer)
                        If ($string.Length -gt 1) {
                            [void]$Privileges.Add($String)
                        } 
                        Else {
                            BREAK
                        } 
                        [intptr]$P_Pointer = $P_Pointer.ToInt64() + ($string.Length + 1) * ([System.Runtime.InteropServices.Marshal]::SizeOf([type][char]))
                        $Remaining = $Remaining = $string.Length
                    }

                    [System.Runtime.InteropServices.Marshal]::FreeHGlobal($Pointer)  
                }
                #endregion Query Service Required Privileges

                #region Query Service Failure Actions
                #Determine bytes needed
                [uint32]$BytesNeeded = $Null
                $Return = [Service.Trigger]::QueryServiceConfig2(
                    $ServiceHandle,
                    [SERVICE_INFO_LEVEL]::SERVICE_CONFIG_FAILURE_ACTIONS,
                    [intptr]::Zero,
                    0,
                    [ref]$BytesNeeded
                )
                $LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
                If ($LastError.NativeErrorCode -eq 122) { 
                    #Buffer too small which is expected so we rerun this again with the expected buffer
                    $Pointer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($BytesNeeded)    
                    $Return = [Service.Trigger]::QueryServiceConfig2(
                        $ServiceHandle,
                        [SERVICE_INFO_LEVEL]::SERVICE_CONFIG_FAILURE_ACTIONS,
                        $Pointer,
                        $BytesNeeded,
                        [ref]$BytesNeeded
                    )
                    $LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
                    If (-NOT $Return) {
                        Write-Warning ("{0} ({1})" -f $LastError.Message,$LastError.NativeErrorCode)
                        Break    
                    }

                    #Build Struct from Pointer
                    $FailureActions = [System.Runtime.InteropServices.Marshal]::PtrToStructure($Pointer,[type][SERVICE_FAILURE_ACTIONS])
                    $TPointer = $FailureActions.lpsaActions
                    $Actions = For ($i=0; $i -lt $FailureActions.cActions; $i++) {    
                        [System.Runtime.InteropServices.Marshal]::PtrToStructure($TPointer,[type][SC_ACTION])
                        [IntPtr]$TPointer = $TPointer.ToInt64() + [System.Runtime.InteropServices.Marshal]::SizeOf([type][SC_ACTION])
                    }        
                    $_FailureAction = New-Object FAILURE_ACTIONS 
                    $_FailureAction.ResetPeriod = $FailureActions.dwResetPeriod
                    $_FailureAction.RebootMessage = $FailureActions.lpRebootMsg
                    $_FailureAction.Command = $FailureActions.lpCommand
                    $_FailureAction.Actions = [SC_ACTION[]]$Actions
                    
                    [System.Runtime.InteropServices.Marshal]::FreeHGlobal($Pointer)  
                }
                #endregion Query Service Failure Actions

                #region Query Service Failure Actions Flag
                #Determine bytes needed
                [uint32]$BytesNeeded = $Null
                $Return = [Service.Trigger]::QueryServiceConfig2(
                    $ServiceHandle,
                    [SERVICE_INFO_LEVEL]::SERVICE_CONFIG_FAILURE_ACTIONS_FLAG,
                    [intptr]::Zero,
                    0,
                    [ref]$BytesNeeded
                )
                $LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
                If ($LastError.NativeErrorCode -eq 122) { 
                    #Buffer too small which is expected so we rerun this again with the expected buffer
                    $Pointer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($BytesNeeded)    
                    $Return = [Service.Trigger]::QueryServiceConfig2(
                        $ServiceHandle,
                        [SERVICE_INFO_LEVEL]::SERVICE_CONFIG_FAILURE_ACTIONS_FLAG,
                        $Pointer,
                        $BytesNeeded,
                        [ref]$BytesNeeded
                    )
                    $LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
                    If (-NOT $Return) {
                        Write-Warning ("{0} ({1})" -f $LastError.Message,$LastError.NativeErrorCode)
                        Break    
                    }
                    #Build Struct from Pointer
                    $FailureActionsFlag = [System.Runtime.InteropServices.Marshal]::PtrToStructure($Pointer,[type][SERVICE_FAILURE_ACTIONS_FLAG])
                    [System.Runtime.InteropServices.Marshal]::FreeHGlobal($Pointer)  
                }
                #endregion Query Service Failure Actions Flag

                #region Query Service Preferred Node
                #Determine bytes needed
                [uint32]$BytesNeeded = $Null
                $Return = [Service.Trigger]::QueryServiceConfig2(
                    $ServiceHandle,
                    [SERVICE_INFO_LEVEL]::SERVICE_CONFIG_PREFERRED_NODE,
                    [intptr]::Zero,
                    0,
                    [ref]$BytesNeeded
                )
                $LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
                If ($LastError.NativeErrorCode -eq 122) { 
                    #Buffer too small which is expected so we rerun this again with the expected buffer
                    $Pointer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($BytesNeeded)    
                    $Return = [Service.Trigger]::QueryServiceConfig2(
                        $ServiceHandle,
                        [SERVICE_INFO_LEVEL]::SERVICE_CONFIG_PREFERRED_NODE,
                        $Pointer,
                        $BytesNeeded,
                        [ref]$BytesNeeded
                    )
                    $LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
                    If (-NOT $Return) {
                        Write-Warning ("{0} ({1})" -f $LastError.Message,$LastError.NativeErrorCode)
                        Break    
                    }
                    #Build Struct from Pointer
                    $PreferredNode = [System.Runtime.InteropServices.Marshal]::PtrToStructure($Pointer,[type][SERVICE_PREFERRED_NODE_INFO])
                    [System.Runtime.InteropServices.Marshal]::FreeHGlobal($Pointer)  
                }
                #endregion Query Service Preferred Node

                #region Query Service Pre-Shutdown Timeout
                #Determine bytes needed
                [uint32]$BytesNeeded = $Null
                $Return = [Service.Trigger]::QueryServiceConfig2(
                    $ServiceHandle,
                    [SERVICE_INFO_LEVEL]::SERVICE_CONFIG_PRESHUTDOWN_INFO,
                    [intptr]::Zero,
                    0,
                    [ref]$BytesNeeded
                )
                $LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
                If ($LastError.NativeErrorCode -eq 122) { 
                    #Buffer too small which is expected so we rerun this again with the expected buffer
                    $Pointer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($BytesNeeded)    
                    $Return = [Service.Trigger]::QueryServiceConfig2(
                        $ServiceHandle,
                        [SERVICE_INFO_LEVEL]::SERVICE_CONFIG_PRESHUTDOWN_INFO,
                        $Pointer,
                        $BytesNeeded,
                        [ref]$BytesNeeded
                    )
                    $LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
                    If (-NOT $Return) {
                        Write-Warning ("{0} ({1})" -f $LastError.Message,$LastError.NativeErrorCode)
                        Break    
                    }
                    #Build Struct from Pointer
                    $ShutdownTimeout = [System.Runtime.InteropServices.Marshal]::PtrToStructure($Pointer,[type][SERVICE_PRESHUTDOWN_INFO])
                    [System.Runtime.InteropServices.Marshal]::FreeHGlobal($Pointer)  
                }
                #endregion Query Service Pre-Shutdown Timeout

                #region Query Service SID Type
                #Determine bytes needed
                [uint32]$BytesNeeded = $Null
                $Return = [Service.Trigger]::QueryServiceConfig2(
                    $ServiceHandle,
                    [SERVICE_INFO_LEVEL]::SERVICE_CONFIG_SERVICE_SID_INFO,
                    [intptr]::Zero,
                    0,
                    [ref]$BytesNeeded
                )
                $LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
                If ($LastError.NativeErrorCode -eq 122) { 
                    #Buffer too small which is expected so we rerun this again with the expected buffer
                    $Pointer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($BytesNeeded)    
                    $Return = [Service.Trigger]::QueryServiceConfig2(
                        $ServiceHandle,
                        [SERVICE_INFO_LEVEL]::SERVICE_CONFIG_SERVICE_SID_INFO,
                        $Pointer,
                        $BytesNeeded,
                        [ref]$BytesNeeded
                    )
                    $LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
                    If (-NOT $Return) {
                        Write-Warning ("{0} ({1})" -f $LastError.Message,$LastError.NativeErrorCode)
                        Break    
                    }
                    #Build Struct from Pointer
                    $ServiceSID = [System.Runtime.InteropServices.Marshal]::PtrToStructure($Pointer,[type][SERVICE_SID_INFO])
                    [System.Runtime.InteropServices.Marshal]::FreeHGlobal($Pointer)  
                }
                #endregion Query Service SID Type

                #region Query Service Description
                #Determine bytes needed
                [uint32]$BytesNeeded = $Null
                $Return = [Service.Trigger]::QueryServiceConfig2(
                    $ServiceHandle,
                    [SERVICE_INFO_LEVEL]::SERVICE_CONFIG_DESCRIPTION,
                    [intptr]::Zero,
                    0,
                    [ref]$BytesNeeded
                )
                $LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
                If ($LastError.NativeErrorCode -eq 122) { 
                    #Buffer too small which is expected so we rerun this again with the expected buffer
                    $Pointer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($BytesNeeded)    
                    $Return = [Service.Trigger]::QueryServiceConfig2(
                        $ServiceHandle,
                        [SERVICE_INFO_LEVEL]::SERVICE_CONFIG_DESCRIPTION,
                        $Pointer,
                        $BytesNeeded,
                        [ref]$BytesNeeded
                    )
                    $LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
                    If (-NOT $Return) {
                        Write-Warning ("{0} ({1})" -f $LastError.Message,$LastError.NativeErrorCode)
                        Break    
                    }
                    #Build Struct from Pointer
                    $Description = [System.Runtime.InteropServices.Marshal]::PtrToStructure($Pointer,[type][SERVICE_DESCRIPTION])
                    [System.Runtime.InteropServices.Marshal]::FreeHGlobal($Pointer)  
                }
                #endregion Query Service Description

                #region Query Service Configuration
                #Determine bytes needed
                [uint32]$BytesNeeded = $Null
                $Return = [Service.Trigger]::QueryServiceConfig(
                    $ServiceHandle,
                    [intptr]::Zero,
                    0,
                    [ref]$BytesNeeded
                )
                $LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
                If ($LastError.NativeErrorCode -eq 122) { 
                    #Buffer too small which is expected so we rerun this again with the expected buffer
                    $Pointer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($BytesNeeded)    
                    $Return = [Service.Trigger]::QueryServiceConfig(
                        $ServiceHandle,
                        $Pointer,
                        $BytesNeeded,
                        [ref]$BytesNeeded
                    )
                    $LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
                    If (-NOT $Return) {
                        Write-Warning ("{0} ({1})" -f $LastError.Message,$LastError.NativeErrorCode)
                        Break    
                    }
                    #Build Struct from Pointer
                    $ServiceConfiguration = [System.Runtime.InteropServices.Marshal]::PtrToStructure($Pointer,[type][QUERY_SERVICE_CONFIG])
                    [System.Runtime.InteropServices.Marshal]::FreeHGlobal($Pointer)  
                }
                #endregion Query Service Configuration

                #region Query Service Delayed AutoStart
                #Determine bytes needed
                [uint32]$BytesNeeded = $Null
                $Return = [Service.Trigger]::QueryServiceConfig2(
                    $ServiceHandle,
                    [SERVICE_INFO_LEVEL]::SERVICE_CONFIG_DELAYED_AUTO_START_INFO,
                    [intptr]::Zero,
                    0,
                    [ref]$BytesNeeded
                )
                $LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
                If ($LastError.NativeErrorCode -eq 122) { 
                    #Buffer too small which is expected so we rerun this again with the expected buffer
                    $Pointer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($BytesNeeded)    
                    $Return = [Service.Trigger]::QueryServiceConfig2(
                        $ServiceHandle,
                        [SERVICE_INFO_LEVEL]::SERVICE_CONFIG_DELAYED_AUTO_START_INFO,
                        $Pointer,
                        $BytesNeeded,
                        [ref]$BytesNeeded
                    )
                    $LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
                    If (-NOT $Return) {
                        Write-Warning ("{0} ({1})" -f $LastError.Message,$LastError.NativeErrorCode)
                        Break    
                    }
                    #Build Struct from Pointer
                    $AutoStart = [System.Runtime.InteropServices.Marshal]::PtrToStructure($Pointer,[type][SERVICE_DELAYED_AUTO_START_INFO])
                    [System.Runtime.InteropServices.Marshal]::FreeHGlobal($Pointer)  
                }
                #endregion Query Service Delayed AutoStart

                #region Query Service Triggers
                [uint32]$BytesNeeded = $Null
                $Return = [Service.Trigger]::QueryServiceConfig2(
                    $ServiceHandle,
                    [SERVICE_INFO_LEVEL]::SERVICE_CONFIG_TRIGGER_INFO,
                    [intptr]::Zero,
                    0,
                    [ref]$BytesNeeded
                )
                $LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
                If ($LastError.NativeErrorCode -eq 122) { 
                    #Buffer too small which is expected so we rerun this again with the expected buffer
                    $Pointer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($BytesNeeded)    
                    $Return = [Service.Trigger]::QueryServiceConfig2(
                        $ServiceHandle,
                        [SERVICE_INFO_LEVEL]::SERVICE_CONFIG_TRIGGER_INFO,
                        $Pointer,
                        $BytesNeeded,
                        [ref]$BytesNeeded
                    )
                    $LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
                    If (-NOT $Return) {
                        Write-Warning ("{0} ({1})" -f $LastError.Message,$LastError.NativeErrorCode)
                        Break    
                    }
                    #Build Struct from Pointer
                    $TriggerInfo = [System.Runtime.InteropServices.Marshal]::PtrToStructure($Pointer,[type][SERVICE_TRIGGER_INFO])
                    $TPointer = $TriggerInfo.pTriggers
                    $Triggers = For ($i=0; $i -lt $TriggerInfo.cTriggers; $i++) {    
                        $ServiceTrigger = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TPointer,[type][SERVICE_TRIGGER])
                        [IntPtr]$TPointer = $TPointer.ToInt64() + [System.Runtime.InteropServices.Marshal]::SizeOf([type][SERVICE_TRIGGER])
                        $GUID = [System.Runtime.InteropServices.Marshal]::PtrToStructure($ServiceTrigger.pTriggerSubType,[type][guid])
                        $Trigger = New-Object TRIGGER
                        $Trigger.TriggerType = $ServiceTrigger.dwTriggerType
                        $Trigger.Action = $ServiceTrigger.dwAction
                        $Trigger.SubType = $TRIGGER_SUBTYPE[$GUID]     
                        $Trigger
                    }
                }
                #endregion Query Service Triggers

                [void][Service.Trigger]::CloseServiceHandle($ServiceHandle)

                #region Open Service
                $ServiceHandle = [Service.Trigger]::OpenService(
                    $SCMHandle,
                    $Service.Name,
                    [SERVICE_ACCESS]::SERVICE_ENUMERATE_DEPENDENTS
                )
                $LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
                # TODO - Determine why certain services will not let you open handle with this access type without being admin; ie: RpcEptMapper
                Write-Debug "ServiceHandle: $ServiceHandle"
                #endregion Open Service

                #region Enum Dependant Services
                $D_Services = New-Object System.Collections.ArrayList
                Write-Verbose 'Get all dependant services'
                $BytesNeeded = 0
                $ServicesReturned = 0
                $Return = [Service.Trigger]::EnumDependentServices(
                    $ServiceHandle,
                    [SERVICE_STATES]::SERVICE_ALL,
                    [IntPtr]::Zero,
                    0, # Current Buffer
                    [ref]$BytesNeeded,
                    [ref]$ServicesReturned
                )
                $LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
                Write-Debug "BytesNeeded<Dependant Services>: $BytesNeeded"
                Write-Debug "<Dependant Services>: $($LastError|select *|out-string)"
                If ($LastError.NativeErrorCode -eq 234) {
                    $Buffer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($BytesNeeded)
                    $Return = [Service.Trigger]::EnumDependentServices(
                        $ServiceHandle,
                        [SERVICE_STATES]::SERVICE_ALL,
                        $Buffer,
                        $BytesNeeded, # Current Buffer
                        [ref]$BytesNeeded,
                        [ref]$ServicesReturned
                    )
                    $LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error() 
                    Write-Debug "ServicesReturned: $ServicesReturned"
                    If ($Return) {                    
                        $tempPointer = $Buffer
                        For ($i=0;$i -lt $ServicesReturned;$i++) {
                            If ([intptr]::Size -eq 8) {
                                # 64 bit
                                $Object = ([System.Runtime.InteropServices.Marshal]::PtrToStructure($tempPointer,[type][ENUM_SERVICE_STATUS]))
                                [intptr]$tempPointer = $tempPointer.ToInt64() + [System.Runtime.InteropServices.Marshal]::SizeOf([type][ENUM_SERVICE_STATUS])
                                [void]$D_Services.Add($Object)
                            } 
                            Else {
                                #32 bit
                                $Object = ([System.Runtime.InteropServices.Marshal]::PtrToStructure($tempPointer,[type][ENUM_SERVICE_STATUS]))
                                [intptr]$tempPointer = $tempPointer.ToInt32() + [System.Runtime.InteropServices.Marshal]::SizeOf([type][ENUM_SERVICE_STATUS])
                                [void]$D_Services.Add($Object)
                            }
                        }
                    }         
                }
                #endregion Enum Dependant Services

                #region Display Object and Perform Cleanup
                Try {
                    $ServiceSIDType = [SERVICE_SID_TYPE]$ServiceSID.dwServiceSidType
                } 
                Catch {
                    $ServiceSIDType = $Null
                }
                Try {
                    $ServiceStartType = [SERVICE_START_TYPE]$ServiceConfiguration.dwStartType
                } 
                Catch {
                    $ServiceStartType = $Null
                }
                Try {
                    $ErrorControl = [ERROR_CONTROL]$ServiceConfiguration.dwErrorControl
                } 
                Catch {
                    $ErrorControl = $Null
                }
                $Object = [pscustomobject]@{
                    Name = $Service.Name
                    DisplayName = $ServiceConfiguration.lpDisplayName
                    Description = $Description.lpDescription
                    Triggers = $Triggers
                    IsDelayedAutoStart = $AutoStart.fDelayedAutostart
                    SIDType = $ServiceSIDType
                    Privileges = $Privileges
                    ShutdownTimeout = $ShutdownTimeout.dwPreshutdownTimeout
                    PreferredNode = $PreferredNode
                    FailureActionsFlag = $FailureActionsFlag.fFailureActionsOnNonCrashFailures
                    FailureActions = $_FailureAction
                    Type = $Service.Type
                    State = $Service.State
                    Controls = $Service.Controls
                    Win32ExitCode = $Service.Win32ExitCode
                    ServiceExitCode = $Service.ServiceExitCode
                    CheckPoint = $Service.CheckPoint
                    WaitHint = $Service.WaitHint
                    ProcessID = $Service.ProcessID
                    ServiceFlags = $Service.ServiceFlags
                    StartMode = $ServiceStartType
                    ErrorControl = $ErrorControl
                    FilePath = $ServiceConfiguration.lpBinaryPathName
                    LoadOrderGroup = $ServiceConfiguration.lpLoadOrderGroup
                    TagID = $ServiceConfiguration.dwTagId
                    Dependancies = $ServiceConfiguration.lpDependencies
                    StartName = $ServiceConfiguration.lpServiceStartName
                    DependantServices = $D_Services
                    Computername = $Computer
                }
                $Object.pstypenames.insert(0,'System.Service')
                $Object
                [void][Service.Trigger]::CloseServiceHandle($ServiceHandle)
                #endregion Display Object and Perform Cleanup
            }
        }
    }
}

function Set-ServiceConfig
{
    [CmdletBinding()]
    [OutputType([void])]
    param
    (
        #Name
        [Parameter(Mandatory=$true,ParameterSetName='NoRemoting_Default')]
        [string]$Name,

        #SIDType
        [Parameter(Mandatory=$false,ParameterSetName='NoRemoting_Default')]
        [ValidateSet('none','unrestricted','restricted')]
        [string]$SIDType,

        #StartType
        [Parameter(Mandatory=$false,ParameterSetName='NoRemoting_Default')]
        [ValidateSet('auto','demand','disabled')]
        [string]$StartType,

        #DelayedStart
        [Parameter(Mandatory=$false,ParameterSetName='NoRemoting_Default')]
        [ValidateSet($true,$false)]
        [bool]$IsDelayedAutoStart
    )
    
    Process
    {
        #Get Service
        try
        {
            Write-Verbose "Get Service started"
            
            $Service = Get-Service -Name $Name -ErrorAction Stop
      
            Write-Verbose "Get Service completed"
        }
        catch
        {
            Write-Error "Get Service failed. Details: $_" -ErrorAction 'Stop'
        }

        #Set Process
        try
        {
            Write-Verbose "Set Process started"
            
            if ($PSBoundParameters.ContainsKey('SIDType'))
            {
                Write-Verbose "Set Process in proress. Setting SIDType"
                $SCPath = Join-Path -Path ([environment]::GetFolderPath('System')) -ChildPath sc.exe
                Start-NewProcess -FilePath $SCPath -Arguments "sidtype $($Service.Name) $SIDType" -WaitTimeout 10 -ErrorAction Stop
            }
      
            Write-Verbose "Set Process completed" 
        }
        catch
        {
            Write-Error "Set Process failed. Details: $_" -ErrorAction 'Stop'
        }

        #Set Process StartType
        try
        {
            Write-Verbose "Set Process StartType started"
            
            if ($PSBoundParameters.ContainsKey('StartType') -or $PSBoundParameters.ContainsKey('IsDelayedAutoStart'))
            {
                Write-Verbose "Set Process StartType in proress. Setting startup type $StartType"
                $SCPath = Join-Path -Path ([environment]::GetFolderPath('System')) -ChildPath sc.exe
                if ($PSBoundParameters.ContainsKey('IsDelayedAutoStart')) 
                {
                    $startParam = "delayed-auto"
                }
                else 
                {
                    $startParam = $StartType
                }
                Start-NewProcess -FilePath $SCPath -Arguments "config $($Service.Name) start= $startParam" -WaitTimeout 10 -ErrorAction Stop
            }
      
            Write-Verbose "Set Process StartType completed"
        }
        catch
        {
            Write-Error "Set Process StartType failed. Details: $_" -ErrorAction 'Stop'
        }


    }
}