Unpacking Software Livestream

Join our monthly Unpacking Software livestream to hear about the latest news, chat and opinion on packaging, software deployment and lifecycle management!

Learn More

Chocolatey Product Spotlight

Join the Chocolatey Team on our regular monthly stream where we put a spotlight on the most recent Chocolatey product releases. You'll have a chance to have your questions answered in a live Ask Me Anything format.

Learn More

Chocolatey Coding Livestream

Join us for the Chocolatey Coding Livestream, where members of our team dive into the heart of open source development by coding live on various Chocolatey projects. Tune in to witness real-time coding, ask questions, and gain insights into the world of package management. Don't miss this opportunity to engage with our team and contribute to the future of Chocolatey!

Learn More

Calling All Chocolatiers! Whipping Up Windows Automation with Chocolatey Central Management

Webinar from
Wednesday, 17 January 2024

We are delighted to announce the release of Chocolatey Central Management v0.12.0, featuring seamless Deployment Plan creation, time-saving duplications, insightful Group Details, an upgraded Dashboard, bug fixes, user interface polishing, and refined documentation. As an added bonus we'll have members of our Solutions Engineering team on-hand to dive into some interesting ways you can leverage the new features available!

Watch On-Demand
Chocolatey Community Coffee Break

Join the Chocolatey Team as we discuss all things Community, what we do, how you can get involved and answer your Chocolatey questions.

Watch The Replays
Chocolatey and Intune Overview

Webinar Replay from
Wednesday, 30 March 2022

At Chocolatey Software we strive for simple, and teaching others. Let us teach you just how simple it could be to keep your 3rd party applications updated across your devices, all with Intune!

Watch On-Demand
Chocolatey For Business. In Azure. In One Click.

Livestream from
Thursday, 9 June 2022

Join James and Josh to show you how you can get the Chocolatey For Business recommended infrastructure and workflow, created, in Azure, in around 20 minutes.

Watch On-Demand
The Future of Chocolatey CLI

Livestream from
Thursday, 04 August 2022

Join Paul and Gary to hear more about the plans for the Chocolatey CLI in the not so distant future. We'll talk about some cool new features, long term asks from Customers and Community and how you can get involved!

Watch On-Demand
Hacktoberfest Tuesdays 2022

Livestreams from
October 2022

For Hacktoberfest, Chocolatey ran a livestream every Tuesday! Re-watch Cory, James, Gary, and Rain as they share knowledge on how to contribute to open-source projects such as Chocolatey CLI.

Watch On-Demand

Downloads:

937,171

Downloads of v 2.4.3:

7,820

Last Update:

22 Jun 2017

Package Maintainer(s):

Software Author(s):

  • OpenVPN Technologies
  • Inc

Tags:

openvpn community tunnel ssl admin

OpenVPN

This is not the latest version of OpenVPN available.

  • 1
  • 2
  • 3

2.4.3 | Updated: 22 Jun 2017

Downloads:

937,171

Downloads of v 2.4.3:

7,820

Maintainer(s):

Software Author(s):

  • OpenVPN Technologies
  • Inc

OpenVPN 2.4.3

This is not the latest version of OpenVPN available.

  • 1
  • 2
  • 3

Some Checks Have Failed or Are Not Yet Complete

Not All Tests Have Passed


Validation Testing Passed


Verification Testing Passed

Details

Scan Testing Resulted in Flagged:

This package was submitted (and approved) prior to automated virus scanning integration into the package moderation processs.

We recommend clicking the "Details" link to make your own decision on installing this package.

Details
Learn More

Deployment Method: Individual Install, Upgrade, & Uninstall

To install OpenVPN, run the following command from the command line or from PowerShell:

>

To upgrade OpenVPN, run the following command from the command line or from PowerShell:

>

To uninstall OpenVPN, run the following command from the command line or from PowerShell:

>

Deployment Method:

NOTE

This applies to both open source and commercial editions of Chocolatey.

1. Enter Your Internal Repository Url

(this should look similar to https://community.chocolatey.org/api/v2/)


2. Setup Your Environment

1. Ensure you are set for organizational deployment

Please see the organizational deployment guide

2. Get the package into your environment

  • Open Source or Commercial:
    • Proxy Repository - Create a proxy nuget repository on Nexus, Artifactory Pro, or a proxy Chocolatey repository on ProGet. Point your upstream to https://community.chocolatey.org/api/v2/. Packages cache on first access automatically. Make sure your choco clients are using your proxy repository as a source and NOT the default community repository. See source command for more information.
    • You can also just download the package and push it to a repository Download

3. Copy Your Script

choco upgrade openvpn -y --source="'INTERNAL REPO URL'" --version="'2.4.3'" [other options]

See options you can pass to upgrade.

See best practices for scripting.

Add this to a PowerShell script or use a Batch script with tools and in places where you are calling directly to Chocolatey. If you are integrating, keep in mind enhanced exit codes.

If you do use a PowerShell script, use the following to ensure bad exit codes are shown as failures:


choco upgrade openvpn -y --source="'INTERNAL REPO URL'" --version="'2.4.3'" 
$exitCode = $LASTEXITCODE

Write-Verbose "Exit code was $exitCode"
$validExitCodes = @(0, 1605, 1614, 1641, 3010)
if ($validExitCodes -contains $exitCode) {
  Exit 0
}

Exit $exitCode

- name: Install openvpn
  win_chocolatey:
    name: openvpn
    version: '2.4.3'
    source: INTERNAL REPO URL
    state: present

See docs at https://docs.ansible.com/ansible/latest/modules/win_chocolatey_module.html.


chocolatey_package 'openvpn' do
  action    :install
  source   'INTERNAL REPO URL'
  version  '2.4.3'
end

See docs at https://docs.chef.io/resource_chocolatey_package.html.


cChocoPackageInstaller openvpn
{
    Name     = "openvpn"
    Version  = "2.4.3"
    Source   = "INTERNAL REPO URL"
}

Requires cChoco DSC Resource. See docs at https://github.com/chocolatey/cChoco.


package { 'openvpn':
  ensure   => '2.4.3',
  provider => 'chocolatey',
  source   => 'INTERNAL REPO URL',
}

Requires Puppet Chocolatey Provider module. See docs at https://forge.puppet.com/puppetlabs/chocolatey.


4. If applicable - Chocolatey configuration/installation

See infrastructure management matrix for Chocolatey configuration elements and examples.

Package Approved

This package was approved as a trusted package on 22 Jun 2017.

Description

OpenVPN is a full-featured open source SSL VPN solution that accommodates a wide range of configurations, including remote access, site-to-site VPNs, Wi-Fi security, and enterprise-scale remote access solutions with load balancing, failover, and fine-grained access-controls. Starting with the fundamental premise that complexity is the enemy of security, OpenVPN offers a cost-effective, lightweight alternative to other VPN technologies that is well-targeted for the SME and enterprise markets.

This package will be installed with the following options:
* /SELECT_OPENVPN=1 : Install OpenVPN user-space components, including openvpn.exe
* /SELECT_SERVICE=1 : Install the OpenVPN service wrappers
* /SELECT_TAP=1 : Install/upgrade the TAP virtual device driver
* /SELECT_OPENVPNGUI=1 : Install OpenVPN GUI by Mathias Sundman
* /SELECT_ASSOCIATIONS=1 : Register OpenVPN config file association (*.ovpn)
* /SELECT_OPENSSL_UTILITIES=1 : Install the OpenSSL Utilities (used for generating public/private key pairs)
* /SELECT_EASYRSA=1 : Install OpenVPN RSA scripts for X509 certificate management
* /SELECT_PATH=1 : Add OpenVPN executable directory to the current user's PATH
* /SELECT_SHORTCUTS=1 : Add OpenVPN shortcuts to the current user's Start Menu
* /SELECT_LAUNCH=1 : Launch OpenVPN GUI on user logon
* /SELECT_OPENSSLDLLS=1 : Install OpenSSL DLLs locally (may be omitted if DLLs are already installed globally)
* /SELECT_LZODLLS=1 : Install LZO DLLs locally (may be omitted if DLLs are already installed globally)
* /SELECT_PKCS11DLLS=1 : Install PKCS#11 helper DLLs locally (may be omitted if DLLs are already installed globally)

The only difference with the default package configuration is the EASYRSA option enabled while the default install disables it.

In order to be started automatically, your configuration still needs to reside in C:\Program Files\OpenVPN\config.
By design, OpenVPN-GUI does not show connections started automatically; it only shows connections started by the current user.
For more info, read: https://github.com/OpenVPN/openvpn-gui/blob/master/README.rst#adding-an-openvpn-configuration-file


tools\chocolateyInstall.ps1
$packageName = 'openvpn'
# By default: C:\ProgramData\chocolatey\lib\openvpn\tools
$toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
$fileType = 'exe'
# For a list of all silent arguments used
# https://github.com/OpenVPN/openvpn-build/blob/c92af79befec86f21b257b5defba0becb3d7641f/windows-nsis/openvpn.nsi#L551
# For their description
# https://github.com/OpenVPN/openvpn-build/blob/c92af79befec86f21b257b5defba0becb3d7641f/windows-nsis/openvpn.nsi#L107
$silentArgs = '/S /SELECT_EASYRSA=1'
$validExitCodes = @(0)

$packageFileName = "$toolsDir\$($packageName)Install.$fileType"
$sigFileName = "$toolsDir\$($packageName)Install.$fileType.asc"
$pgpKeyFileName = "$toolsDir\openvpn_public_key.asc"
$certFileName = "$toolsDir\openvpn.cer"

$packageChecksum = 'A0DA5281A38C2445AF1C89F3153BE6CED9D419B2E2C94C0326CD0821C6DAD682808ADA2BBA5643754C5C9971B84940F4020163AF4053D83FF13E605748CB13F0'
$sigChecksum = 'CF44F472D7F12F35C20F1E8197170D5DE79CDAE03AB5C37E77D664983CEF78D66372C4B1408B537E79B2218170F7589EE41BF07BD16C0D7015C26B5C05EF95D3'
$pgpKeyChecksum = '7205EB2A23DF08313255FA4A75CF1E8D00F8777BEDEC48FE3B31FFD9EC297AB683193DA585AF3FA9E35D9F17390A3E2C0BBD30AAB0524F44F0EFC69EDE02A6F3'
$certChecksum = '8F53ADB36F1C61C50E11B8BDBEF8D0FFB9B26665A69D81246551A0B455E72EC0B26A34DC9B65CB3750BAF5D8A6D19896C3B4A31B578B15AB7086377955509FAD'

# Load custom functions
. "$toolsDir\utils\utils.ps1"

# If GPG has been just added, need to refresh to access to it from this session
Update-SessionEnvironment

Get-ChecksumValid `
    -File "$packageFileName" `
    -Checksum "$packageChecksum" `
    -ChecksumType 'sha512'
Get-ChecksumValid `
    -File "$sigFileName" `
    -Checksum "$sigChecksum" `
    -ChecksumType 'sha512'
Get-ChecksumValid `
    -File "$pgpKeyFileName" `
    -Checksum "$pgpKeyChecksum" `
    -ChecksumType 'sha512'
Get-ChecksumValid `
    -File "$certFileName" `
    -Checksum "$certChecksum" `
    -ChecksumType 'sha512'

# The GPG signature needs to have the same filename as the file checked but
# with the .asc suffix, otherwise gpg reports it cannot verify the file with
# the following message:
# gpg: no signed data
# gpg: can't hash datafile: No data
CheckPGPSignature `
    -pgpKey "$pgpKeyFileName" `
    -signatureFile "$sigFileName" `
    -file "$packageFileName"

Write-Host "Adding OpenVPN to the Trusted Publishers (needed to have a silent install of the TAP driver)..."
AddTrustedPublisherCertificate -file "$certFileName"

Write-Host "Getting the state of the current OpenVPN service (if any)..."
# Needed to reset the state of the Interactive service if upgrading from a
# branch 2.4 and onwards or reinstalling a build from the branch 2.4
try {
    $previousInteractiveService = GetServiceProperties "OpenVPNServiceInteractive"
} catch {
    Write-Host "No previous OpenVPN interactive service detected."
}
# Even if 2.4.1 fixes reset of services. This is still needed for all cases 2.3
# to 2.4 or 2.4 to 2.4.x and onwards.
try {
    $previousService = GetServiceProperties "OpenVpnService"
} catch {
    Write-Host "No previous OpenVPN service detected."
}

Install-ChocolateyInstallPackage `
    -PackageName $packageName `
    -FileType $fileType `
    -SilentArgs $silentArgs `
    -File $packageFileName `
    -ValidExitCodes $validExitCodes

if ($previousInteractiveService) {
    Write-Host "Resetting previous OpenVPN interactive service to " `
        "'$($previousInteractiveService.status)' and " `
        "'$($previousInteractiveService.startupType)'..."
    SetServiceProperties `
        -name "OpenVPNServiceInteractive" `
        -status "$($previousInteractiveService.status)" `
        -startupType "$($previousInteractiveService.startupType)"
}

if ($previousService) {
    Write-Host "Resetting previous OpenVPN service to " `
        "'$($previousService.status)' and "  `
        "'$($previousService.startupType)'..."
    SetServiceProperties `
        -name "OpenVPNService" `
        -status "$($previousService.status)" `
        -startupType "$($previousService.startupType)"
}

Write-Host "Removing OpenVPN from the Trusted Publishers..."
RemoveTrustedPublisherCertificate -file "$certFileName"
tools\chocolateyUninstall.ps1
$packageName = 'openvpn'
$fileType = 'exe'
$silentArgs = '/S'
$validExitCodes = @(0)

# If we specify to Uninstall-ChocolateyPackage a silent argument but without
# a path, the command throws an exception. We cannot thus rely on the
# Chocolatey Auto Uninstaller feature. We will need to do manually what the
# PowerShell command does i.e. looking for the right path in the registry
# manually.


[array]$key = Get-UninstallRegistryKey -SoftwareName "OpenVPN*"

if ($key.Count -eq 1) {
    $key | % {
        $file = $key.UninstallString

        Write-Host "Removing OpenVPN... The OpenVPN service will be automatically stopped and removed."
        Uninstall-ChocolateyPackage `
            -PackageName "$packageName" `
            -FileType "$fileType" `
            -SilentArgs "$silentArgs" `
            -ValidExitCodes "$validExitCodes" `
            -File "$file"
    }
} elseif ($key.Count -eq 0) {
    Write-Warning "$packageName has already been uninstalled by other means."
} elseif ($key.Count -gt 1) {
    Write-Warning "$key.Count matches found!"
    Write-Warning "To prevent accidental data loss, no programs will be uninstalled."
    Write-Warning "Please alert package maintainer the following keys were matched:"
    $key | % {Write-Warning "- $_.DisplayName"}
}

# After the uninstall has performed, choco checks if there are uninstall
# registry keys left and decides to launch or not its auto uninstaller feature.
# However, here, we have a race condition. When choco checks if the following
# registry key is still present, it's already gone.
# SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\OpenVPN
# A fix for this issue is already present in choco 0.10.4
# https://github.com/chocolatey/choco/issues/1035
# Let's sleep. Still failing with only 3 secs. 5 seems to work.
Start-Sleep -s 5

# The uninstaller changes the PATH, apply these changes in the current PowerShell
# session (limited to this script).
Update-SessionEnvironment

# This script does not have to take care of removing the gpg4win-vanilla
# dependency as Chocolatey as a built-in function for that. To notify the user
# that a dependency can be removed is unneccessary. If a user wants to
# uninstall a package and its dependencies (as long as no other package depends
# on it) a user can run choco uninstall -x when uninstalling a package.
tools\openvpn.cer
 
tools\openvpnInstall.exe
md5: 057FFF0436C8B7558B281710D1EFD0A9 | sha1: E325B4A0FC0FF1DCA463FF84D4BB9DD7BAE4CABB | sha256: 36FDFD1B6D2E6C94ADD59E5670F9391162D4D3866CD20B719C018FC18919F54D | sha512: A0DA5281A38C2445AF1C89F3153BE6CED9D419B2E2C94C0326CD0821C6DAD682808ADA2BBA5643754C5C9971B84940F4020163AF4053D83FF13E605748CB13F0
tools\openvpnInstall.exe.asc
 
tools\openvpn_public_key.asc
 
tools\utils\.git
 
tools\utils\README.md
# Chocolatey custom functions

The current development state of Chocolatey and the fact we have to support old Windows versions leads us to require some quirks or to reinvent the wheel at each Chocolatey package we write.

This repository aims at gathering some generic PowerShell 2.0 functions to be used with Chocolatey.

These are intended to work only for Chocolatey, although you can take inspiration from this work to write your own PowerShell functions.

## Features

* CreateTempDirPackageVersion
* PrintWhenVerbose
* GetServiceProperties
* SetServiceProperties
* CheckPGPSignature
* GetCertificateInfo
* AddTrustedPublisherCertificate
* RemoveTrustedPublisherCertificate

To see how these commands/functions work, simply read the documentation in the source file.

## Testing

Some functions can be tested outside Chocolatey only if these are not too tied to Chocolatey internals. To test these functions outside of Chocolatey, just copy them to another file and run the following command:

    powershell -ExecutionPolicy Unrestricted -File .\utils.ps1

Please note these functions come as a bundle and are not intended to be used as a standalone solution.

## Contributions

If you have comments to make or push requests to submit, you are welcome to contribute to this repository.

## License

[As Apache 2 software can be included in GPLv3 projects, but GPLv3 software cannot be included in Apache projects](https://www.apache.org/licenses/GPL-compatibility.html) and in order to comply with [NuGet](https://www.nuget.org/policies/About) and Chocolatey licenses, this software is licensed under the terms of the Apache License 2.0.
tools\utils\test.ps1
$Assem = (
	"System",
	"System.Runtime.InteropServices")

$Source = @"
using System;
using System.Runtime.InteropServices;

// This project must target the .NET 4 framework Client Profile for
// compatibility with choco (Posh 2 and Windows 7).
// To learn the differences between the full framework and the Client Profile:
// http://stackoverflow.com/a/2786831/3514658

public class ServicesManager
{
    
    static void Main(string[] args) {
        // Use the service name and *NOT* the display name.
        ServiceProperties props = GetServiceProperties("Dnscache");
        PrintServiceProperties(props);
        SetServiceProperties("OpenVPNService", ServiceControllerStatus.Running, ServiceStartMode.AutomaticDelayed);
    }

    static public ServiceProperties GetServiceProperties(string serviceName) {

        UInt32 dwBytesNeeded;
        string errMsg;

        IntPtr databaseHandle = OpenSCManager(
            null,
            null,
            NativeConstants.ServiceControlManager.SC_MANAGER_ALL_ACCESS);
        // An error might happen here if we are not running as administrator and the service
        // database is locked.
        if (databaseHandle == IntPtr.Zero) {
            throw new ExternalException("Unable to OpenSCManager. Not enough rights.");
        }

        IntPtr serviceHandle = OpenService(
            databaseHandle,
            serviceName,
            NativeConstants.Service.SERVICE_ALL_ACCESS);
        if (serviceHandle == IntPtr.Zero) {
            Cleanup(databaseHandle, IntPtr.Zero, out errMsg);
            throw new ExternalException("Unable to OpenService '" + serviceName + "':" + errMsg);
        }

        // Take basic info. Everything is taken into account except the
        // delayed autostart.
        //
        // The 'ref' keyword tells the compiler that the object is initialized
        // before entering the function, while 'out' tells the compiler that the
        // object will be initialized inside the function.
        // src.: http://stackoverflow.com/a/388467/3514658
        // Determine the buffer size needed (dwBytesNeeded).
        if (!QueryServiceConfig(
                serviceHandle,
                IntPtr.Zero,
                0,
                out dwBytesNeeded)) {

            if (Marshal.GetLastWin32Error() != NativeConstants.SystemErrorCode.ERROR_INSUFFICIENT_BUFFER) {
                Cleanup(databaseHandle, serviceHandle, out errMsg);
                throw new ExternalException("Unable to get service config for '" + serviceName + "': " + errMsg);
            }
        }

        // Get the main info of the service. See this struct for more info:
        // src.: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684950(v=vs.85).aspx
        IntPtr ptr = Marshal.AllocHGlobal((int)dwBytesNeeded);
        if (!QueryServiceConfig(
                serviceHandle,
                ptr,
                dwBytesNeeded,
                out dwBytesNeeded)) {

            Cleanup(databaseHandle, serviceHandle, out errMsg);
            throw new ExternalException("Unable to get service config for '" + serviceName + "': " + errMsg);
        }
        QUERY_SERVICE_CONFIG serviceConfig = new QUERY_SERVICE_CONFIG();
        Marshal.PtrToStructure(ptr, serviceConfig);
        Marshal.FreeHGlobal(ptr);

        // Determine the buffer size needed (dwBytesNeeded).
        if (!QueryServiceConfig2(
                serviceHandle,
                NativeConstants.Service.SERVICE_CONFIG_DELAYED_AUTO_START_INFO,
                IntPtr.Zero,
                0,
                out dwBytesNeeded)) {

            if (Marshal.GetLastWin32Error() != NativeConstants.SystemErrorCode.ERROR_INSUFFICIENT_BUFFER) {
                Cleanup(databaseHandle, serviceHandle, out errMsg);
                throw new ExternalException("Unable to get service delayed auto start property for '" + serviceName + "': " + errMsg);
            }            
        }

        // Get the info if the service is set in delayed mode or not.
        ptr = Marshal.AllocHGlobal((int)dwBytesNeeded);
        if (!QueryServiceConfig2(
                serviceHandle,
                NativeConstants.Service.SERVICE_CONFIG_DELAYED_AUTO_START_INFO,
                ptr,
                dwBytesNeeded,
                out dwBytesNeeded)) {

            Cleanup(databaseHandle, serviceHandle, out errMsg);
            throw new ExternalException("Unable to get service delayed auto start property for '" + serviceName + "': " + errMsg);
        }
        ServiceDelayedAutoStartInfo serviceDelayed = new ServiceDelayedAutoStartInfo();
        Marshal.PtrToStructure(ptr, serviceDelayed);
        Marshal.FreeHGlobal(ptr);

        ServiceStatus serviceStatus = new ServiceStatus();
        if (!QueryServiceStatus(
                serviceHandle,
                out serviceStatus)) {
            Cleanup(databaseHandle, serviceHandle, out errMsg);
            throw new ExternalException("Unable to get service status for '" + serviceName + "': " + errMsg);
        }

        ServiceProperties props = new ServiceProperties();

        props.QueryServiceConfig.lpServiceConfig.dwServiceType = serviceConfig.dwServiceType;
        props.QueryServiceConfig.lpServiceConfig.dwStartType = serviceConfig.dwStartType;
        props.QueryServiceConfig.lpServiceConfig.dwErrorControl = serviceConfig.dwErrorControl;
        props.QueryServiceConfig.lpServiceConfig.lpBinaryPathName = serviceConfig.lpBinaryPathName;
        props.QueryServiceConfig.lpServiceConfig.lpLoadOrderGroup = serviceConfig.lpLoadOrderGroup;
        props.QueryServiceConfig.lpServiceConfig.dwTagId = serviceConfig.dwTagId;
        props.QueryServiceConfig.lpServiceConfig.lpDependencies = serviceConfig.lpDependencies;
        props.QueryServiceConfig.lpServiceConfig.lpServiceStartName = serviceConfig.lpServiceStartName;
        props.QueryServiceConfig.lpServiceConfig.lpDisplayName = serviceConfig.lpDisplayName;

        props.QueryServiceConfig2.lpBuffer.SERVICE_DELAYED_AUTO_START_INFO.fDelayedAutostart = serviceDelayed.fDelayedAutostart;

        props.QueryServiceStatus.lpServiceStatus.dwServiceType = serviceStatus.dwServiceType;
        props.QueryServiceStatus.lpServiceStatus.dwCurrentState = serviceStatus.dwCurrentState;
        props.QueryServiceStatus.lpServiceStatus.dwControlsAccepted = serviceStatus.dwControlsAccepted;
        props.QueryServiceStatus.lpServiceStatus.dwWin32ExitCode = serviceStatus.dwWin32ExitCode;
        props.QueryServiceStatus.lpServiceStatus.dwServiceSpecificExitCode = serviceStatus.dwServiceSpecificExitCode;
        props.QueryServiceStatus.lpServiceStatus.dwCheckPoint = serviceStatus.dwCheckPoint;
        props.QueryServiceStatus.lpServiceStatus.dwWaitHint = serviceStatus.dwWaitHint;

        props.Name = serviceName;
        props.DisplayName = serviceConfig.lpDisplayName;

        switch (serviceConfig.dwStartType) {
            case NativeConstants.Service.SERVICE_AUTO_START:
                if (serviceDelayed.fDelayedAutostart) {
                    props.StartMode = ServiceStartMode.AutomaticDelayed;
                }
                else {
                    props.StartMode = ServiceStartMode.Automatic;
                }
                break;
            case NativeConstants.Service.SERVICE_BOOT_START:
                props.StartMode = ServiceStartMode.Boot;
                break;
            case NativeConstants.Service.SERVICE_DISABLED:
                props.StartMode = ServiceStartMode.Disabled;
                break;
            case NativeConstants.Service.SERVICE_DEMAND_START:
                props.StartMode = ServiceStartMode.Manual;
                break;
            case NativeConstants.Service.SERVICE_SYSTEM_START:
                props.StartMode = ServiceStartMode.System;
                break;
            default:
                CloseServiceHandle(databaseHandle);
                CloseServiceHandle(serviceHandle);
                throw new ExternalException("The service '" + serviceName + "' has an invalid start type");
        }

        switch (serviceStatus.dwCurrentState) {
            case NativeConstants.Service.SERVICE_CONTINUE_PENDING:
                props.Status = ServiceControllerStatus.ContinuePending;
                break;
            case NativeConstants.Service.SERVICE_PAUSE_PENDING:
                props.Status = ServiceControllerStatus.PausePending;
                break;
            case NativeConstants.Service.SERVICE_PAUSED:
                props.Status = ServiceControllerStatus.Paused;
                break;
            case NativeConstants.Service.SERVICE_RUNNING:
                props.Status = ServiceControllerStatus.Running;
                break;
            case NativeConstants.Service.SERVICE_START_PENDING:
                props.Status = ServiceControllerStatus.StartPending;
                break;
            case NativeConstants.Service.SERVICE_STOP_PENDING:
                props.Status = ServiceControllerStatus.StopPending;
                break;
            case NativeConstants.Service.SERVICE_STOPPED:
                props.Status = ServiceControllerStatus.Stopped;
                break;
            default:
                CloseServiceHandle(databaseHandle);
                CloseServiceHandle(serviceHandle);
                throw new ExternalException("The service '" + serviceName + "' has an invalid status");
        }

        CloseServiceHandle(databaseHandle);
        CloseServiceHandle(serviceHandle);

        return props;
    }

    static public void SetServiceProperties(
        string serviceName,
        ServiceControllerStatus status,
        ServiceStartMode startMode) {

        ServiceProperties props = GetServiceProperties(serviceName);

        string errMsg;

        IntPtr databaseHandle = OpenSCManager(
            null,
            null,
            NativeConstants.ServiceControlManager.SC_MANAGER_ALL_ACCESS);
        // An error might happen here if we are not running as administrator and the service
        // database is locked.
        if (databaseHandle == IntPtr.Zero) {
            throw new ExternalException("Unable to OpenSCManager. Not enough rights.");
        }

        IntPtr serviceHandle = OpenService(
            databaseHandle,
            serviceName,
            NativeConstants.Service.SERVICE_ALL_ACCESS);
        if (serviceHandle == IntPtr.Zero) {
            Cleanup(databaseHandle, IntPtr.Zero, out errMsg);
            throw new ExternalException("Unable to OpenService '" + serviceName + "':" + errMsg);
        }

        // Change service startType config
        Int32 dwStartType;
        SERVICE_DELAYED_AUTO_START_INFO delayed =
            new SERVICE_DELAYED_AUTO_START_INFO();
        delayed.fDelayedAutostart = false;
        switch (startMode) {
            case ServiceStartMode.AutomaticDelayed:
                dwStartType = NativeConstants.Service.SERVICE_AUTO_START;
                delayed.fDelayedAutostart = true;
                break;
            case ServiceStartMode.Automatic:
                dwStartType = NativeConstants.Service.SERVICE_AUTO_START;
                break;
            case ServiceStartMode.Boot:
                dwStartType = NativeConstants.Service.SERVICE_BOOT_START;
                break;
            case ServiceStartMode.Disabled:
                dwStartType = NativeConstants.Service.SERVICE_DISABLED;
                break;
            case ServiceStartMode.Manual:
                dwStartType = NativeConstants.Service.SERVICE_DEMAND_START;
                break;
            case ServiceStartMode.System:
                dwStartType = NativeConstants.Service.SERVICE_SYSTEM_START;
                break;
            default:
                CloseServiceHandle(databaseHandle);
                CloseServiceHandle(serviceHandle);
                throw new ExternalException("The service '" + serviceName + "' has an invalid start type");
        }

        if (!ChangeServiceConfig(
                // handle of service
                serviceHandle,
                // service type: no change
                NativeConstants.Service.SERVICE_NO_CHANGE,
                // service start type
                dwStartType,
                // error control: no change
                NativeConstants.Service.SERVICE_NO_CHANGE,
                // binary path: no change
                IntPtr.Zero,
                // load order group: no change
                IntPtr.Zero,
                // tag ID: no change
                IntPtr.Zero,
                // dependencies: no change
                IntPtr.Zero,
                // account name: no change
                IntPtr.Zero,
                // password: no change
                IntPtr.Zero,
                // display name: no change
                IntPtr.Zero)) {
            Cleanup(databaseHandle, serviceHandle, out errMsg);
            throw new ExternalException("Unable to change configuration for service '" + serviceName + "':" + errMsg);
        }

        IntPtr unmanagedAddr = Marshal.AllocHGlobal(Marshal.SizeOf(delayed));
        Marshal.StructureToPtr(delayed, unmanagedAddr, true);

        if (!ChangeServiceConfig2(
                serviceHandle,
                NativeConstants.Service.SERVICE_CONFIG_DELAYED_AUTO_START_INFO,
                unmanagedAddr)) {
            Cleanup(databaseHandle, serviceHandle, out errMsg);
            Marshal.FreeHGlobal(unmanagedAddr);
            unmanagedAddr = IntPtr.Zero;
            throw new ExternalException("Unable to change configuration for service '" + serviceName + "':" + errMsg);
        }

        Marshal.FreeHGlobal(unmanagedAddr);
        unmanagedAddr = IntPtr.Zero;

        // If the user wants to start the service
        if (status == ServiceControllerStatus.Running ||
            status == ServiceControllerStatus.StartPending) {

            // Try to start the service, only if it is not already started
            if (props.Status == ServiceControllerStatus.Stopped ||
                props.Status == ServiceControllerStatus.StopPending) {

            }
        }
    }

    static private void PrintServiceProperties(ServiceProperties props) {
        
        Console.WriteLine("DisplayName: '" + props.DisplayName + "'");
        Console.WriteLine("StartMode: '" + props.StartMode + "'");
        Console.WriteLine("Status: '" + props.Status + "'");
        Console.ReadLine();
    }

    static private void Cleanup(
        IntPtr databaseHandle,
        IntPtr serviceHandle,
        out string errMsg) {

        int errCode = Marshal.GetLastWin32Error();
        if (serviceHandle != IntPtr.Zero) {
            CloseServiceHandle(serviceHandle);
        }

        if (databaseHandle != IntPtr.Zero) {
            CloseServiceHandle(databaseHandle);
        }
        // We can't get the capitalized error constant programatically. This
        // piss of code is thus needed. Otherwise we need to load a file
        // containing all Win32 error constants.
        // src.: https://stackoverflow.com/a/30204142/3514658
        switch (errCode) {
            case NativeConstants.SystemErrorCode.ERROR_ACCESS_DENIED:
                errMsg = "ERROR_ACCESS_DENIED";
                break;
            case NativeConstants.SystemErrorCode.ERROR_INSUFFICIENT_BUFFER:
                errMsg = "ERROR_INSUFFICIENT_BUFFER";
                break;
            case NativeConstants.SystemErrorCode.ERROR_INVALID_HANDLE:
                errMsg = "ERROR_INVALID_HANDLE";
                break;
            case NativeConstants.SystemErrorCode.ERROR_INVALID_NAME:
                errMsg = "ERROR_INVALID_NAME";
                break;
            case NativeConstants.SystemErrorCode.ERROR_SERVICE_DOES_NOT_EXIST:
                errMsg = "ERROR_SERVICE_DOES_NOT_EXIST";
                break;
            case NativeConstants.SystemErrorCode.ERROR_CIRCULAR_DEPENDENCY:
                errMsg = "ERROR_CIRCULAR_DEPENDENCY";
                break;
            case NativeConstants.SystemErrorCode.ERROR_DUPLICATE_SERVICE_NAME:
                errMsg = "ERROR_DUPLICATE_SERVICE_NAME";
                break;
            case NativeConstants.SystemErrorCode.ERROR_INVALID_PARAMETER:
                errMsg = "ERROR_INVALID_PARAMETER";
                break;
            case NativeConstants.SystemErrorCode.ERROR_INVALID_SERVICE_ACCOUNT:
                errMsg = "ERROR_INVALID_SERVICE_ACCOUNT";
                break;
            case NativeConstants.SystemErrorCode.ERROR_SERVICE_MARKED_FOR_DELETE:
                errMsg = "ERROR_SERVICE_MARKED_FOR_DELETE";
                break;
            default:
                errMsg = errCode.ToString();
                break;
        }
    }

   public class ServiceProperties {
        public QueryServiceConfigClass QueryServiceConfig = new QueryServiceConfigClass();
        public QueryServiceConfig2Class QueryServiceConfig2 = new QueryServiceConfig2Class();
        public QueryServiceStatusClass QueryServiceStatus = new QueryServiceStatusClass();

        public String Name { get; set; }
        public String DisplayName { get; set; }
        public ServiceStartMode StartMode { get; set; }
        public ServiceControllerStatus Status { get; set; }
    }
    
    public class QueryServiceConfigClass {
        public ServiceConfigClass lpServiceConfig = new ServiceConfigClass();
    }

    public class ServiceConfigClass {
        // From the struct QUERY_SERVICE_CONFIG
        // src.: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684950(v=vs.85).aspx
        // used by the function QueryServiceConfig()
        // src.: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684932(v=vs.85).aspx
        public uint dwServiceType { get; set; }
        public uint dwStartType { get; set; }
        public uint dwErrorControl { get; set; }
        public string lpBinaryPathName { get; set; }
        public string lpLoadOrderGroup { get; set; }
        public uint dwTagId { get; set; }
        public string lpDependencies { get; set; }
        public string lpServiceStartName { get; set; }
        public string lpDisplayName { get; set; }
    }

    public class QueryServiceConfig2Class {
        public ServiceConfig2Class lpBuffer = new ServiceConfig2Class();
    }

    public class ServiceConfig2Class {
        public SERVICE_DELAYED_AUTO_START_INFO SERVICE_DELAYED_AUTO_START_INFO = new SERVICE_DELAYED_AUTO_START_INFO();
    }

    // From the struct SERVICE_DELAYED_AUTO_START_INFO 
    // src.: https://msdn.microsoft.com/en-us/library/windows/desktop/ms685155(v=vs.85).aspx
    // used by the function QueryServiceConfig2()
    // src.: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684935(v=vs.85).aspx
    [StructLayout(LayoutKind.Explicit)]
    public struct SERVICE_DELAYED_AUTO_START_INFO {
        [FieldOffset(0)]
        public bool fDelayedAutostart;
    }

    public class QueryServiceStatusClass {
        public ServiceStatus lpServiceStatus = new ServiceStatus();
    }

    // Based on System.ServiceProcess.ServiceControllerStatus
    // src.: https://msdn.microsoft.com/en-us/library/system.serviceprocess.servicecontrollerstatus(v=vs.110).aspx
    public enum ServiceControllerStatus {
        ContinuePending = NativeConstants.Service.SERVICE_CONTINUE_PENDING,
        Paused = NativeConstants.Service.SERVICE_PAUSED,
        PausePending = NativeConstants.Service.SERVICE_PAUSE_PENDING,
        Running = NativeConstants.Service.SERVICE_RUNNING,
        StartPending = NativeConstants.Service.SERVICE_START_PENDING,
        Stopped = NativeConstants.Service.SERVICE_STOPPED,
        StopPending = NativeConstants.Service.SERVICE_STOP_PENDING
    }

    // Based on System.ServiceProcess.ServiceStartMode
    // src.: https://msdn.microsoft.com/en-us/library/system.serviceprocess.servicestartmode(v=vs.110).aspx
    // but we added the AutomaticDelayed mode.
    public enum ServiceStartMode {
        Automatic = NativeConstants.Service.SERVICE_AUTO_START,
        // Enum numeric literals are defined at compilation time and the C#
        // language specification doesn't prevent to have duplicate values
        // for an enum. Here, when we weren't specifying an explicit value
        // for AutomaticDelayed, the latter had the same value (3) as Manual.
        // We had to specify a value not used by other fields. Another solution
        // would be to use a class instead, but this is a bit overkill for this
        // use case.
        // src.: https://stackoverflow.com/a/26827597/3514658
        // src.: https://stackoverflow.com/a/1425791/3514658
        // src.: https://stackoverflow.com/a/26828917/3514658
        AutomaticDelayed = 1000,
        Boot = NativeConstants.Service.SERVICE_BOOT_START,
        Disabled = NativeConstants.Service.SERVICE_DISABLED,
        Manual = NativeConstants.Service.SERVICE_DEMAND_START,
        System = NativeConstants.Service.SERVICE_SYSTEM_START
    }

    #region P/Invoke declarations

    // The LayoutKind.Sequential specifies that the fields of the type should
    // be laid out in memory in the same order they are declared in your
    // source code. That's often important when interoperating with native
    // code. Without the attribute the CLR is free to optimize memory use
    // by rearranging the fields.
    // src.: https://social.msdn.microsoft.com/Forums/vstudio/en-US/2abc6be8-c593-4686-93d2-89785232dacd#0455ea02-7eab-451b-8a83-fbfc4384d654
    [StructLayout(LayoutKind.Sequential)]
    public class SERVICE_DESCRIPTION
    {
        [MarshalAs(UnmanagedType.LPWStr)]
        public String lpDescription;
    }
    
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public class SERVICE_FAILURE_ACTIONS
    {
        public int dwResetPeriod;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string lpRebootMsg;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string lpCommand;
        public int cActions;
        public IntPtr lpsaActions;
    }

    [StructLayout(LayoutKind.Sequential)]
    public class SC_ACTION
    {
        public Int32 type;
        public UInt32 dwDelay;
    }

    // P/Invoke Interop Assistant 1.0 is useful to generate declarations of
    // native code constant in their managed counterparts.
    // http://stackoverflow.com/a/5122534/3514658
    [StructLayoutAttribute(LayoutKind.Sequential)]
    public class QUERY_SERVICE_CONFIG
    {
        /// DWORD->unsigned int
        public uint dwServiceType;

        /// DWORD->unsigned int
        public uint dwStartType;

        /// DWORD->unsigned int
        public uint dwErrorControl;

        /// LPWSTR->WCHAR*
        [MarshalAsAttribute(UnmanagedType.LPWStr)]
        public string lpBinaryPathName;

        /// LPWSTR->WCHAR*
        [MarshalAsAttribute(UnmanagedType.LPWStr)]
        public string lpLoadOrderGroup;

        /// DWORD->unsigned int
        public uint dwTagId;

        /// LPWSTR->WCHAR*
        [MarshalAsAttribute(UnmanagedType.LPWStr)]
        public string lpDependencies;

        /// LPWSTR->WCHAR*
        [MarshalAsAttribute(UnmanagedType.LPWStr)]
        public string lpServiceStartName;

        /// LPWSTR->WCHAR*
        [MarshalAsAttribute(UnmanagedType.LPWStr)]
        public string lpDisplayName;
    }

    [StructLayout(LayoutKind.Sequential)]
    public class ServiceDelayedAutoStartInfo {
        [MarshalAs(UnmanagedType.Bool)]
        public bool fDelayedAutostart;
    }

    // From the struct ServiceStatus
    // src.: https://msdn.microsoft.com/en-us/library/windows/desktop/ms685996(v=vs.85).aspx
    // used by the function QueryServiceStatus()
    // src.: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684939(v=vs.85).aspx
    [StructLayoutAttribute(LayoutKind.Sequential)]
    public struct ServiceStatus {

        /// DWORD->unsigned int
        public uint dwServiceType { get; set; }
        /// DWORD->unsigned int
        public uint dwCurrentState { get; set; }
        /// DWORD->unsigned int
        public uint dwControlsAccepted { get; set; }
        /// DWORD->unsigned int
        public uint dwWin32ExitCode { get; set; }
        /// DWORD->unsigned int
        public uint dwServiceSpecificExitCode { get; set; }
        /// DWORD->unsigned int
        public uint dwCheckPoint { get; set; }
        /// DWORD->unsigned int
        public uint dwWaitHint { get; set; }
    }

    // Some import statements are inspired from some public solutions from
    // Pinvoke.net.
    // https://webcache.googleusercontent.com/search?q=cache:4U7pz3gubesJ:www.pinvoke.net/default.aspx/advapi32.queryserviceconfig2
    // The following page is a great use to find types equivalence between
    // C++ et .NET types.
    // src.: https://www.codeproject.com/Articles/9714/Win-API-C-to-NET
    [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern IntPtr OpenSCManager(String lpMachineName, String lpDatabaseName, UInt32 dwDesiredAccess);

    [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern IntPtr OpenService(IntPtr hSCManager, String lpServiceName, UInt32 dwDesiredAccess);
   
    // The EntryPoint specifies a DLL function by name or ordinal. If the name
    // of the function in our method definition is the same as the entry
    // point in the DLL, we do not have to explicitly identify the function
    // with the EntryPoint field. Here since we are mapping QueryServiceConfig
    // to QueryServiceConfigW, this property is needed.
    // src. https://msdn.microsoft.com/en-us/library/f5xe74x8(v=vs.110).aspx#Anchor_1
    [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "QueryServiceConfigW")]
    public static extern bool QueryServiceConfig(IntPtr hService, IntPtr lpServiceConfig, UInt32 cbBufSize, out UInt32 pcbBytesNeeded);

    [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "QueryServiceConfig2W")]
    public static extern bool QueryServiceConfig2(IntPtr hService, UInt32 dwInfoLevel, IntPtr buffer, UInt32 cbBufSize, out UInt32 pcbBytesNeeded);

    [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "QueryServiceStatus")]
    public static extern bool QueryServiceStatus(IntPtr hService, out ServiceStatus lpServiceStatus);

    [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "ChangeServiceConfigW")]  
    public static extern bool ChangeServiceConfig(
        IntPtr hService,
        Int32 dwServiceType,
        Int32 dwStartType,
        Int32 dwErrorControl,
        IntPtr lpBinaryPathName,
        IntPtr lpLoadOrderGroup,
        IntPtr lpdwTagId,
        IntPtr lpDependencies,
        IntPtr lpServiceStartName,
        IntPtr lpPassword,
        IntPtr lpDisplayName);

    [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "ChangeServiceConfig2W")]
    public static extern bool ChangeServiceConfig2(IntPtr hService, UInt32 dwInfoLevel, IntPtr lpInfo);

    [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "CloseServiceHandle")]
    public static extern bool CloseServiceHandle(IntPtr hSCObject);

    public partial class NativeConstants {

        // From System Error Codes (0-499)
        // src.: https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx
        public class SystemErrorCode {

            // Errors from OpenSCManager

            /// ERROR_ACCESS_DENIED -> 5L
            public const int ERROR_ACCESS_DENIED = 5;
            /// ERROR_DATABASE_DOES_NOT_EXIST -> 1065L
            public const int ERROR_DATABASE_DOES_NOT_EXIST = 1065;


            // Errors from OpenService

            // + ERROR_ACCESS_DENIED
            /// ERROR_INVALID_HANDLE -> 6L
            public const int ERROR_INVALID_HANDLE = 6;
            /// ERROR_INVALID_NAME -> 123L
            public const int ERROR_INVALID_NAME = 123;
            /// ERROR_SERVICE_DOES_NOT_EXIST -> 1060L
            public const int ERROR_SERVICE_DOES_NOT_EXIST = 1060;


            // Errors from QueryServiceConfig

            // + ERROR_ACCESS_DENIED
            /// ERROR_INSUFFICIENT_BUFFER -> 122L
            public const int ERROR_INSUFFICIENT_BUFFER = 122;
            // + ERROR_INVALID_HANDLE


            // Errors from QueryServiceConfig2
            // + ERROR_ACCESS_DENIED
            // + ERROR_INSUFFICIENT_BUFFER
            // + ERROR_INVALID_HANDLE


            // Errors from QueryServiceStatus
            // + ERROR_ACCESS_DENIED
            // + ERROR_INVALID_HANDLE


            // Errors from ChangeServiceConfig

            // + ERROR_ACCESS_DENIED
            /// ERROR_CIRCULAR_DEPENDENCY -> 1059L
            public const int ERROR_CIRCULAR_DEPENDENCY = 1059;
            /// ERROR_DUPLICATE_SERVICE_NAME -> 1078L
            public const int ERROR_DUPLICATE_SERVICE_NAME = 1078;
            // + ERROR_INVALID_HANDLE
            /// ERROR_INVALID_PARAMETER -> 87L
            public const int ERROR_INVALID_PARAMETER = 87;
            /// ERROR_INVALID_SERVICE_ACCOUNT -> 1057L
            public const int ERROR_INVALID_SERVICE_ACCOUNT = 1057;
            /// ERROR_SERVICE_MARKED_FOR_DELETE -> 1072L
            public const int ERROR_SERVICE_MARKED_FOR_DELETE = 1072;
        }

        public class ServiceControlManager {

            // From Service Control Manager access rights
            // src.: https://msdn.microsoft.com/en-us/library/windows/desktop/ms685981(v=vs.85).aspx#access_rights_for_the_service_control_manager

            /// SC_MANAGER_ALL_ACCESS -> (STANDARD_RIGHTS_REQUIRED      |                                         SC_MANAGER_CONNECT            |                                         SC_MANAGER_CREATE_SERVICE     |                                         SC_MANAGER_ENUMERATE_SERVICE  |                                         SC_MANAGER_LOCK               |                                         SC_MANAGER_QUERY_LOCK_STATUS  |                                         SC_MANAGER_MODIFY_BOOT_CONFIG)
            public const int SC_MANAGER_ALL_ACCESS = (ServiceControlManager.STANDARD_RIGHTS_REQUIRED
                        | (ServiceControlManager.SC_MANAGER_CONNECT
                        | (ServiceControlManager.SC_MANAGER_CREATE_SERVICE
                        | (ServiceControlManager.SC_MANAGER_ENUMERATE_SERVICE
                        | (ServiceControlManager.SC_MANAGER_LOCK
                        | (ServiceControlManager.SC_MANAGER_QUERY_LOCK_STATUS | NativeConstants.ServiceControlManager.SC_MANAGER_MODIFY_BOOT_CONFIG))))));
            /// SC_MANAGER_CREATE_SERVICE -> 0x0002
            public const int SC_MANAGER_CREATE_SERVICE = 2;
            /// SC_MANAGER_CONNECT -> 0x0001
            public const int SC_MANAGER_CONNECT = 1;
            /// SC_MANAGER_ENUMERATE_SERVICE -> 0x0004
            public const int SC_MANAGER_ENUMERATE_SERVICE = 4;
            /// SC_MANAGER_LOCK -> 0x0008
            public const int SC_MANAGER_LOCK = 8;
            /// SC_MANAGER_MODIFY_BOOT_CONFIG -> 0x0020
            public const int SC_MANAGER_MODIFY_BOOT_CONFIG = 32;
            /// SC_MANAGER_QUERY_LOCK_STATUS -> 0x0010
            public const int SC_MANAGER_QUERY_LOCK_STATUS = 16;

            /// STANDARD_RIGHTS_REQUIRED -> (0x000F0000L)
            public const int STANDARD_RIGHTS_REQUIRED = 983040;
        }

        public class Service {

            // From Service access rights
            // src.: https://msdn.microsoft.com/en-us/library/windows/desktop/ms685981(v=vs.85).aspx#access_rights_for_a_service

            /// SERVICE_ALL_ACCESS -> (STANDARD_RIGHTS_REQUIRED     |                                         SERVICE_QUERY_CONFIG         |                                         SERVICE_CHANGE_CONFIG        |                                         SERVICE_QUERY_STATUS         |                                         SERVICE_ENUMERATE_DEPENDENTS |                                         SERVICE_START                |                                         SERVICE_STOP                 |                                         SERVICE_PAUSE_CONTINUE       |                                         SERVICE_INTERROGATE          |                                         SERVICE_USER_DEFINED_CONTROL)
            public const int SERVICE_ALL_ACCESS = (Service.STANDARD_RIGHTS_REQUIRED
                        | (Service.SERVICE_QUERY_CONFIG
                        | (Service.SERVICE_CHANGE_CONFIG
                        | (Service.SERVICE_QUERY_STATUS
                        | (Service.SERVICE_ENUMERATE_DEPENDENTS
                        | (Service.SERVICE_START
                        | (Service.SERVICE_STOP
                        | (Service.SERVICE_PAUSE_CONTINUE
                        | (Service.SERVICE_INTERROGATE | NativeConstants.Service.SERVICE_USER_DEFINED_CONTROL)))))))));
            /// SERVICE_CHANGE_CONFIG -> 0x0002
            public const int SERVICE_CHANGE_CONFIG = 2;
            /// SERVICE_ENUMERATE_DEPENDENTS -> 0x0008
            public const int SERVICE_ENUMERATE_DEPENDENTS = 8;
            /// SERVICE_INTERROGATE -> 0x0080
            public const int SERVICE_INTERROGATE = 128;
            /// SERVICE_PAUSE_CONTINUE -> 0x0040
            public const int SERVICE_PAUSE_CONTINUE = 64;
            /// SERVICE_QUERY_CONFIG -> 0x0001
            public const int SERVICE_QUERY_CONFIG = 1;
            /// SERVICE_QUERY_STATUS -> 0x0004
            public const int SERVICE_QUERY_STATUS = 4;
            /// SERVICE_START -> 0x0010
            public const int SERVICE_START = 16;
            /// SERVICE_STOP -> 0x0020
            public const int SERVICE_STOP = 32;
            /// SERVICE_USER_DEFINED_CONTROL -> 0x0100
            public const int SERVICE_USER_DEFINED_CONTROL = 256;

            /// STANDARD_RIGHTS_REQUIRED -> (0x000F0000L)
            public const int STANDARD_RIGHTS_REQUIRED = 983040;


            // QUERY_SERVICE_CONFIG > dwServiceType
            // same as ServiceStatus > dwServiceType


            // QUERY_SERVICE_CONFIG > dwStartType

            /// SERVICE_AUTO_START -> 0x00000002
            public const int SERVICE_AUTO_START = 2;
            /// SERVICE_BOOT_START -> 0x00000000
            public const int SERVICE_BOOT_START = 0;
            /// SERVICE_DEMAND_START -> 0x00000003
            public const int SERVICE_DEMAND_START = 3;
            /// SERVICE_DISABLED -> 0x00000004
            public const int SERVICE_DISABLED = 4;
            /// SERVICE_SYSTEM_START -> 0x00000001
            public const int SERVICE_SYSTEM_START = 1;


            // QUERY_SERVICE_CONFIG > dwErrorControl

            /// SERVICE_ERROR_CRITICAL -> 0x00000003
            public const int SERVICE_ERROR_CRITICAL = 3;
            /// SERVICE_ERROR_IGNORE -> 0x00000000
            public const int SERVICE_ERROR_IGNORE = 0;
            /// SERVICE_ERROR_NORMAL -> 0x00000001
            public const int SERVICE_ERROR_NORMAL = 1;
            /// SERVICE_ERROR_SEVERE -> 0x00000002
            public const int SERVICE_ERROR_SEVERE = 2;


            // ServiceStatus > dwServiceType

            /// SERVICE_FILE_SYSTEM_DRIVER -> 0x00000002
            public const int SERVICE_FILE_SYSTEM_DRIVER = 2;
            /// SERVICE_KERNEL_DRIVER -> 0x00000001
            public const int SERVICE_KERNEL_DRIVER = 1;
            /// SERVICE_WIN32_OWN_PROCESS -> 0x00000010
            public const int SERVICE_WIN32_OWN_PROCESS = 16;
            /// SERVICE_WIN32_SHARE_PROCESS -> 0x00000020
            public const int SERVICE_WIN32_SHARE_PROCESS = 32;
            /// SERVICE_USER_OWN_PROCESS -> 0x00000050
            public const int SERVICE_USER_OWN_PROCESS = 80;
            /// SERVICE_USER_SHARE_PROCESS -> 0x00000060
            public const int SERVICE_USER_SHARE_PROCESS = 96;

            /// SERVICE_INTERACTIVE_PROCESS -> 0x00000100
            public const int SERVICE_INTERACTIVE_PROCESS = 256;


            // ServiceStatus > dwCurrentState

            /// SERVICE_CONTINUE_PENDING -> 0x00000005
            public const int SERVICE_CONTINUE_PENDING = 5;
            /// SERVICE_PAUSE_PENDING -> 0x00000006
            public const int SERVICE_PAUSE_PENDING = 6;
            /// SERVICE_PAUSED -> 0x00000007
            public const int SERVICE_PAUSED = 7;
            /// SERVICE_RUNNING -> 0x00000004
            public const int SERVICE_RUNNING = 4;
            /// SERVICE_START_PENDING -> 0x00000002
            public const int SERVICE_START_PENDING = 2;
            /// SERVICE_STOP_PENDING -> 0x00000003
            public const int SERVICE_STOP_PENDING = 3;
            /// SERVICE_STOPPED -> 0x00000001
            public const int SERVICE_STOPPED = 1;


            // ServiceStatus > dwControlsAccepted

            /// SERVICE_ACCEPT_NETBINDCHANGE -> 0x00000010
            public const int SERVICE_ACCEPT_NETBINDCHANGE = 16;
            /// SERVICE_ACCEPT_PARAMCHANGE -> 0x00000008
            public const int SERVICE_ACCEPT_PARAMCHANGE = 8;
            /// SERVICE_ACCEPT_PAUSE_CONTINUE -> 0x00000002
            public const int SERVICE_ACCEPT_PAUSE_CONTINUE = 2;
            /// SERVICE_ACCEPT_PRESHUTDOWN -> 0x00000100
            public const int SERVICE_ACCEPT_PRESHUTDOWN = 256;
            /// SERVICE_ACCEPT_SHUTDOWN -> 0x00000004
            public const int SERVICE_ACCEPT_SHUTDOWN = 4;
            /// SERVICE_ACCEPT_STOP -> 0x00000001
            public const int SERVICE_ACCEPT_STOP = 1;

            /// SERVICE_ACCEPT_HARDWAREPROFILECHANGE -> 0x00000020
            public const int SERVICE_ACCEPT_HARDWAREPROFILECHANGE = 32;
            /// SERVICE_ACCEPT_POWEREVENT -> 0x00000040
            public const int SERVICE_ACCEPT_POWEREVENT = 64;
            /// SERVICE_ACCEPT_SESSIONCHANGE -> 0x00000080
            public const int SERVICE_ACCEPT_SESSIONCHANGE = 128;
            /// SERVICE_ACCEPT_TIMECHANGE -> 0x00000200
            public const int SERVICE_ACCEPT_TIMECHANGE = 512;
            /// SERVICE_ACCEPT_TRIGGEREVENT -> 0x00000400
            public const int SERVICE_ACCEPT_TRIGGEREVENT = 1024;
            /// SERVICE_ACCEPT_USERMODEREBOOT -> 0x00000800
            public const int SERVICE_ACCEPT_USERMODEREBOOT = 2048;



            // Parameters used by QueryServiceConfig2()
            // src.: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684935(v=vs.85).aspx
            public const int SERVICE_CONFIG_DELAYED_AUTO_START_INFO = 3;


            // ChangeServiceConfig > hService
            // + SERVICE_CHANGE_CONFIG

            // ChangeServiceConfig > dwServiceType
            /// SERVICE_NO_CHANGE -> 0xffffffff
            public const int SERVICE_NO_CHANGE = -1;
            // + SERVICE_FILE_SYSTEM_DRIVER
            // + SERVICE_KERNEL_DRIVER
            // + SERVICE_WIN32_OWN_PROCESS
            // + SERVICE_WIN32_SHARE_PROCESS
            // + SERVICE_INTERACTIVE_PROCESS

            // ChangeServiceConfig > dwStartType
            // + SERVICE_NO_CHANGE
            // + SERVICE_AUTO_START
            // + SERVICE_BOOT_START
            // + SERVICE_DEMAND_START
            // + SERVICE_DISABLED
            // + SERVICE_SYSTEM_START

            // ChangeServiceConfig > dwErrorControl
            // + SERVICE_NO_CHANGE
            // + SERVICE_ERROR_CRITICAL
            // + SERVICE_ERROR_IGNORE
            // + SERVICE_ERROR_NORMAL
            // + SERVICE_ERROR_SEVERE
        }
    }

    #endregion // P/Invoke declarations
}
"@

Add-Type -ReferencedAssemblies $Assem -TypeDefinition $Source -Language CSharp 

#$return = [ServicesManager]::GetServiceProperties("Dnscache")
#$return.QueryServiceConfig.lpServiceConfig.lpDisplayName
tools\utils\utils.ps1
function CreateTempDirPackageVersion {
<#
.DESCRIPTION
Create a temporary folder in current user temporary location. The folder name
has the name of the package name and version (if any).

.OUTPUTS
The location to the created directory

.NOTES
This function is based on part of the code of the command
Install-ChocolateyPackage
src.: https://goo.gl/jUpwOQ
#>
    $chocTempDir = $env:TEMP
    $tempDir = Join-Path $chocTempDir "$($env:chocolateyPackageName)"
    if ($env:chocolateyPackageVersion -ne $null) {
        $tempDir = Join-Path $tempDir "$($env:chocolateyPackageVersion)"
    }
    $tempDir = $tempDir -replace '\\chocolatey\\chocolatey\\', '\chocolatey\'

    if (![System.IO.Directory]::Exists($tempDir)) {
        [System.IO.Directory]::CreateDirectory($tempDir) | Out-Null
    }

    return $tempDir
}

function PrintWhenVerbose {
<#
.DESCRIPTION
Display the string passed as argument if chocolatey has been run in debug or
verbose mode. The string argument is cut automatically and each line is
prefixed by the "VERBOSE: " statement thanks to the call of Write-Verbose
cmdlet.

.PARAMETER string
The string to display in verbose mode
#>
    param (
        [Parameter(Position=0)]
        [string]
        $string
    )

    # Display the output of the executables if chocolatey is run either in debug
    # or in verbose mode.
    if ($env:ChocolateyEnvironmentDebug -eq 'true' -or
        $env:ChocolateyEnvironmentVerbose -eq 'true') {

        $stringReader = New-Object System.IO.StringReader("$string")
        while (($line = $stringReader.ReadLine()) -ne $null) {
           Write-Verbose "$line"
        }
    }
}

function GetServiceProperties {
<#
.DESCRIPTION
Get service properties

.OUTPUTS
An object made of the following fields:
- name (string)
- status (string)
- startupType (string)
- delayedStart (bool)
#>
    param (
        [Parameter(Mandatory=$true)][string]$name
    )

    # Lets return our own object.
    # src.: http://stackoverflow.com/a/12621314
    $properties = "" | Select-Object -Property name,status,startupType,delayedStart

    # Get-Service is not throwing an exception when the service name
    # contains * (asterisks) and the service is not found. Prevent that.
    if ($name -cmatch "\*") {
        Write-Warning "Asterisks have been discarded from the service name '$name'"
        $name = $name -Replace "\*",""
    }

    # The Get-Service Cmdlet returns a System.ServiceProcess.ServiceController
    # Get-Service throws an exception when the exact case insensitive service
    # is not found. Therefore, there is no need to make any further checks.
    $service = Get-Service "$name" -ErrorAction Stop

    # Correct to the exact service name
    if ($name -cnotmatch $service.Name) {
        Write-Debug "The service name '$name' has been corrected to '$($service.Name)'"
    }
    $properties.name = $service.Name

    # Get the service status. The Status property returns an enumeration
    # ServiceControllerStatus src.: https://goo.gl/oq8Bbx
    # This cannot be tested directly from CLI as the .NET assembly is not
    # loaded, we get an exception
    [array]$statusAvailable = [enum]::GetValues([System.ServiceProcess.ServiceControllerStatus])
    if ($statusAvailable -notcontains "$($service.Status)") {
        $errorString = "The status '$service.status' must be '"
        $errorString += $statusAvailable -join "', '"
        $errorString += "'"
        throw "$errorString"
    }

    $properties.status = $service.Status

    # The property StartType of the class System.ServiceProcess.ServiceController
    # might not available in the .NET Framework when used with PowerShell 2.0
    # (cf. https://goo.gl/5NDtZJ). This property has been made available since
    # .NET 4.6.1 (src.: https://goo.gl/ZSvO7B).
    # Since we cannot rely on this property, we need to find another solution.
    # While WMI is widely available and working, let's parse the registry;
    # later we will need an info exclusively storred in it.

    # To list all the properties of an object:
    # $services[0] | Get-ItemProperty
    $service = Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\$name
    if (!$service) {
        throw "The service '$name' was not found using the registry"
    }

    # The values are the ones defined in
    # [enum]::GetValues([System.ServiceProcess.ServiceStartMode])
    switch ($service.Start) {
        2 { $properties.startupType = "Automatic" }
        3 { $properties.startupType = "Manual" }
        4 { $properties.startupType = "Disabled" }
        default { throw "The startup type is invalid" }
    }

    # If the delayed flag is not set, there is no record DelayedAutoStart to the
    # object.
    if ($service.DelayedAutoStart) {
        $properties.delayedStart = $true
    } else {
        $properties.delayedStart = $false
    }

    return $properties
}

function SetServiceProperties {
<#
.DESCRIPTION
Set service properties supporting delayed services

.PARAMETER name
The service name

.PARAMETER status
One of the following service status:
- 'Stopped'
- 'StartPending'
- 'StopPending'
- 'Running'
- 'ContinuePending'
- 'PausePending'
- 'Paused'

.PARAMETER startupType
One of the following service startup type:
- 'Automatic (Delayed Start)'
- 'Automatic'
- 'Manual'
- 'Disabled'
#>
    param (
        # By default parameter are positional, this means the parameter name
        # can be omitted, but needs to repect the order in which the arguments
        # are declared, except if the PositionalBinding is set to false.
        # src.: https://goo.gl/UpOU62
        [Parameter(Mandatory=$true)][string]$name,
        [Parameter(Mandatory=$true)][string]$status,
        [Parameter(Mandatory=$true)][string]$startupType
    )

    try {
        $service = GetServiceProperties "$name"
    } catch {
        throw "The service '$name' cannot be found"
    }

    if ($env:ChocolateyEnvironmentDebug -eq 'true' -or
        $env:ChocolateyEnvironmentVerbose -eq 'true') {
        Write-Verbose "Before SetServicesProperties:"
        if ($service.delayedStart) {
            Write-Verbose "Service '$($service.name)' was '$($service.status)', with '$($service.startupType)' startup type and delayed"
        } else {
            Write-Verbose "Service '$($service.name)' was '$($service.status)', with '$($service.startupType)' startup type"
        }
    }

    # src.: https://goo.gl/oq8Bbx
    [array]$statusAvailable = [enum]::GetValues([System.ServiceProcess.ServiceControllerStatus])
    if ($statusAvailable -notcontains "$status") {
        $errorString = "The status '$status' must be '"
        $errorString += $statusAvailable -join "', '"
        $errorString += "'"
        throw "$errorString"
    }

    if ($startupType -ne "Automatic (Delayed Start)" -and
        $startupType -ne "Automatic" -and
        $startupType -ne "Manual" -and
        $startupType -ne "Disabled") {
        throw "The startupType '$startupType' must either be 'Automatic (Delayed Start)', 'Automatic', 'Manual' or 'Disabled'"
    }

    # Set delayed auto start
    if ($startupType -eq "Automatic (Delayed Start)") {

        # (src.: https://goo.gl/edhCxm and https://goo.gl/NyVXxM)
        # Modifying the registry does not change the value in services.msc,
        # using sc.exe does. sc.exe uses the Windows NT internal functions
        # OpenServiceW and ChangeServiceConfigW. We could use it in PowerShell,
        # but it would requires a C++ wrapper imported in C# code with
        # DllImport, the same C# code imported in PowerShell. While this is
        # doable, this is way slower than calling the sc utility directly.
        # Set-ItemProperty -Path "Registry::HKLM\System\CurrentControlSet\Services\$($service.Name)" -Name DelayedAutostart -Value 1 -Type DWORD
        # An .exe can be called directly but ensuring the exit code and
        # stdout/stderr are properly redirected can only be checked with
        # this code.
        $psi = New-object System.Diagnostics.ProcessStartInfo
        $psi.CreateNoWindow = $true
        $psi.UseShellExecute = $false
        $psi.RedirectStandardInput = $true
        $psi.RedirectStandardOutput = $true
        $psi.RedirectStandardError = $true
        $process = New-Object System.Diagnostics.Process
        $process.StartInfo = $psi
        $psi.FileName = 'sc.exe'
        $psi.Arguments = "Config ""$($service.Name)"" Start= Delayed-Auto"
        # The [void] casting is actually needed to avoid True or False to be displayed
        # on stdout.
        [void]$process.Start()
        #PrintWhenVerbose $process.StandardOutput.ReadToEnd()
        #PrintWhenVerbose $process.StandardError.ReadToEnd()
        $process.WaitForExit()
        if (!($process.ExitCode -eq 0)) {
            throw "Unable to set the service '$($service.Name)' to a delayed autostart."
        }
    } else {
        # Make sure the property DelayedAutostart is reset otherwise
        # GetServiceProperties could report a service as Manual and delayed
        # which is not possible.
        Set-ItemProperty `
        -Path "Registry::HKLM\System\CurrentControlSet\Services\$($service.Name)" `
        -Name DelayedAutostart -Value 1 -Type DWORD -ErrorAction Stop
    }

    # Cast "Automatic (Delayed Start)" to "Automatic" to have a valid name
    if ($startupType -match "Automatic (Delayed Start)") {
        $startupType = "Automatic"
    }

    # Set-Service cannot stop services properly and complains the service is
    # dependent on other services, which seems to be wrong.
    # src.: http://stackoverflow.com/a/39811972/3514658
    if ($status -eq "Stopped") {
        Stop-Service "$($service.Name)" -ErrorAction Stop
    }

    Set-Service -Name "$($service.Name)" -StartupType "$startupType" -Status "$status" -ErrorAction Stop

    if ($env:ChocolateyEnvironmentDebug -eq 'true' -or
        $env:ChocolateyEnvironmentVerbose -eq 'true') {
        $service = GetServiceProperties "$name"
        Write-Verbose "After SetServicesProperties:"
        if ($service.delayedStart) {
            Write-Verbose "Service '$($service.name)' now '$($service.status)', with '$($service.startupType)' startup type and delayed"
        } else {
            Write-Verbose "Service '$($service.name)' now '$($service.status)', with '$($service.startupType)' startup type"
        }
    }
}

function CheckPGPSignature {
<#
.DESCRIPTION
Check the signature of a file using the public key and signatures provided.

.PARAMETER pgpKey
The path and file name to PGP public key to check the signature.

.PARAMETER signatureFile
The path and file name to the signature file. The signature file must keep
its original filename if the argument 'file' is not specified.

.PARAMETER file (optional)
GPG can find the filename of the file to check by itself, only if the
signatureFile has its original file name. What GnuPG does is to retrieve the
filename of the file to check is to remove the .asc suffix from the
signature file.
#>
    param (
        [Parameter(Mandatory=$true)][string]$pgpKey,
        [Parameter(Mandatory=$true)][string]$signatureFile,
        [Parameter(Mandatory=$false)][string]$file
    )

    # Get-Command throws an error message but continues execution, ask to
    # continue without message at all.
    if (!(Get-Command 'gpg.exe' -ErrorAction SilentlyContinue)) {
        throw "Unable to find the GnuPG executable 'gpg.exe'."
    }

    # Check if folder or path exists. Work for files as well.
    if (!(Test-Path "$pgpKey")) {
        throw "Unable to find the PGP key '$pgpKey'."
    }

    if (!(Test-Path "$signatureFile")) {
        throw "Unable to find the PGP signature file '$signatureFile'."
    }

    if ($file -and !(Test-Path "$file")) {
        throw "Unable to find the file '$file'."
    }

    # Get temporary folder for the keyring
    # src.: http://stackoverflow.com/a/34559554/3514658
    $tempDirKeyring = Join-Path $(Split-Path $pgpKey) $([System.Guid]::NewGuid())
    [System.IO.Directory]::CreateDirectory($tempDirKeyring) | Out-Null

    $psi = New-object System.Diagnostics.ProcessStartInfo
    $psi.CreateNoWindow = $true
    $psi.UseShellExecute = $false
    $psi.RedirectStandardInput = $true
    $psi.RedirectStandardOutput = $true
    $psi.RedirectStandardError = $true
    $process = New-Object System.Diagnostics.Process
    $process.StartInfo = $psi

    Write-Debug "Importing PGP key '$pgpKey' in the temporary keyring ($tempDirKeyring\pubring.gpg)..."
    # Simply invoing the command gpg.exe and checking the value of $? was not
    # enough. Using the following method worked and was indeed more reliable.
    # src.: https://goo.gl/Ungugv
    $psi.FileName = 'gpg.exe'
    # Surrounding filenames by 2 double quotes is needed, otherwise of the user
    # folder has a space in it, the space is not taken into account and gpg cannot
    # find the signed data to verify.
    if ($env:ChocolateyEnvironmentDebug -eq 'true' -or
        $env:ChocolateyEnvironmentVerbose -eq 'true') {
        $psi.Arguments = "-v --homedir ""$tempDirKeyring"" --import ""$pgpKey"""
    } else {
        $psi.Arguments = "--homedir ""$tempDirKeyring"" --import ""$pgpKey"""
    }
    # The [void] casting is actually needed to avoid True or False to be displayed
    # on stdout.
    [void]$process.Start()
    PrintWhenVerbose $process.StandardOutput.ReadToEnd()
    PrintWhenVerbose $process.StandardError.ReadToEnd()
    $process.WaitForExit()
    if (!($process.ExitCode -eq 0)) {
        throw "Unable to import PGP key '$pgpKey' in the temporary keyring ($tempDirKeyring\pubring.gpg)."
    }

    # This step is actually facultative. It avoids to have this kind of warning
    # by trusting ultimately the key with the highest level available (level 5,
    # number 6, used for the ultimate/owner trust, a level used for own keys.
    # gpg: WARNING: This key is not certified with a trusted signature!
    # gpg:          There is no indication that the signature belongs to the owner.
    Write-Debug "Getting the fingerprint of the PGP key '$pgpKey'..."
    $psi.FileName = 'gpg.exe'
    if ($env:ChocolateyEnvironmentDebug -eq 'true' -or
        $env:ChocolateyEnvironmentVerbose -eq 'true') {
        $psi.Arguments = "-v --homedir ""$tempDirKeyring"" --with-fingerprint --with-colons ""$pgpKey"""
    } else {
        $psi.Arguments = "--homedir ""$tempDirKeyring"" --with-fingerprint --with-colons ""$pgpKey"""
    }
    # Get the full fingerprint of the key
    [void]$process.Start()
    # src.: http://stackoverflow.com/a/8762068/3514658
    $pgpFingerprint = $process.StandardOutput.ReadToEnd()
    $process.WaitForExit()
    $pgpFingerprint = $pgpFingerprint -split ':'
    $pgpFingerprint = $pgpFingerprint[18]

    Write-Debug "Trusting the PGP key '$pgpKey' ultimately based on its fingerprint '$pgpFingerprint'..."
    $psi.FileName = 'gpg.exe'
    if ($env:ChocolateyEnvironmentDebug -eq 'true' -or
        $env:ChocolateyEnvironmentVerbose -eq 'true') {
        $psi.Arguments = "-v --homedir ""$tempDirKeyring"" --import-ownertrust"
    } else {
        $psi.Arguments = "--homedir ""$tempDirKeyring"" --import-ownertrust"
    }
    [void]$process.Start()
    # Specify the fingerprint and the trust level to stdin
    # e.g.: ABCDEF01234567890ABCDEF01234567890ABCDEF:6:
    $input = $process.StandardInput
    $input.WriteLine($pgpFingerprint + ":6:")
    # Not written until the stream is closed. If not closed, the process will
    # still run and the software will hang.
    # src.: https://goo.gl/5oYgk4
    $input.Close()
    $process.WaitForExit()

    Write-Debug "Checking PGP signature..."
    $psi.FileName = 'gpg.exe'
    if ($env:ChocolateyEnvironmentDebug -eq 'true' -or
        $env:ChocolateyEnvironmentVerbose -eq 'true') {
        if ($file) {
            $psi.Arguments = "-v --homedir ""$tempDirKeyring"" --verify ""$signatureFile"" ""$file"""
        } else {
            $psi.Arguments = "-v --homedir ""$tempDirKeyring"" --verify ""$signatureFile"""
        }
    } else {
        if ($file) {
            $psi.Arguments = "--homedir ""$tempDirKeyring"" --verify ""$signatureFile"" ""$file"""
        } else {
            $psi.Arguments = "--homedir ""$tempDirKeyring"" --verify ""$signatureFile"""
        }
    }
    [void]$process.Start()
    PrintWhenVerbose $process.StandardOutput.ReadToEnd()
    PrintWhenVerbose $process.StandardError.ReadToEnd()
    $process.WaitForExit()
    if (!($process.ExitCode -eq 0)) {
        throw "The signature does not match."
    }
}

function GetCertificateInfo {
<#
.DESCRIPTION
Return a X509Certificate object.
This function has ben implemented in a polymorphic way. Either we specify
a file or we specify a store and a certificate fingerprint.

Usage 1: Specify a file to open as a X509 certificate.

Usage 2: Specify a store and a certificate fingerprint to search for.

.PARAMETER file (usage 1)
The path and file name to the certificate file.

.PARAMETER store (usage 2)
The certificate store (X509Store object) which has been previously opened.

.PARAMETER fingerprint (usage 2)
The fingerprint of the certificate to search for from the certificate store.

.OUTPUTS
A X509Certificate object cf. https://goo.gl/VRuWkL to see the documentation
#>
    param (
        [Parameter(Mandatory=$true, ParameterSetName="file")]
        [string]$file,
        [Parameter(Mandatory=$true, ParameterSetName="fingerprint")]
        [System.Security.Cryptography.X509Certificates.X509Store]$store,
        [Parameter(Mandatory=$true, ParameterSetName="fingerprint")]
        [string]$fingerprint
    )

    switch ($PsCmdlet.ParameterSetName) {
        "file" {
            # New-Object does not respect the rule -ErrorAction
            # src.: https://goo.gl/bzXAL0
            try {
                $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate `
                -ArgumentList "$file"
            } catch {
                throw "Unable to open the X509certificate '$file'"
            }
        }
        "fingerprint" {
            # Sanitize the fingerprint
            if ($fingerprint) {
                $fingerprint = $fingerprint.replace(' ','')
            }

            $certificates = New-Object `
            System.Security.Cryptography.X509Certificates.X509CertificateCollection `
            -ArgumentList $store.Certificates

            $i = 0
            while ($i -lt $certificates.Count) {
                if ("$($certificates.item($i).GetCertHashString())" -eq "$fingerprint") {
                    $cert = $certificates.item($i)
                    break
                }
                $i++
            }
            if ($i -gt $certificates.Count) {
                throw "Unable to find the certificate in the store '$($store.Name)' at location '$($store.Location)'"
            }
        }
    }

    return $cert
}

function AddTrustedPublisherCertificate {
<#
.DESCRIPTION
Adds a X509 certificate to the TrustedPublisher certificate store.

.PARAMETER file (usage 1)
The path and file name to the certificate file.

.NOTES
Sometimes setup executables try to install autosigned drivers. Windows asks us
if we want to trust the certificate from the software publisher. In order to
have a complete silent install, it is needed to add that certificate to the
Windows TrustedPublisher keystore.

In order to recover that certificate for firther use, we have to
- Install the driver accepting the certificate
- Tick the checkbox "Always trust software from "Software Publisher, Inc.""
- As by default, only certificates of the local users are displayed in the
  certificate manager, we need to add the view for the whole computer first.
  For that, we need to run the Microsoft Management Console, run mmc.exe
- Then go to "File -> Add/Remove Snap-in..."
- Select "Certificates" from the left list view then run certmgr.msc,
- Click the "Add >" button at the center of the window
- Select the "Computer account" radio button
- Click the "Next >" button
- Click the "Finish" button
- Click the "OK" button
- Expand "Certificates (Local Computer) -> Trusted Publishers -> Certificates"
- Right click the "OpenVPN Technologies, Inc." certificate
- Select "All Tasks -> Export..."
- Click the "Next >" button
- Select the "Base64 encoded x.509 (.CER)" radio button
- Click the "Next" button
- Select a destination and a filename you wish to save the certificate
- Click the "Next >" button
- Click the "Finish" button
- Click the "OK" button from the confirmation dialog box

The certificate is now in the location specified.
src.: https://goo.gl/o3BVGJ
Next time we install the same piece of software, even if we remove that
certificate, Windows will not ask us to confirm the installation as the
driver is cached in the Drivers Store (C:\Windows\Inf).

To simulate a first install we need to remove the cached drivers as well.
src.: https://goo.gl/Zbcs6T
#>
    param (
        [Parameter(Mandatory=$true)][string]$file
    )

    $cert = GetCertificateInfo -file "$file"

    $store = New-Object System.Security.Cryptography.X509Certificates.X509Store `
    -ArgumentList ([System.Security.Cryptography.X509Certificates.StoreName]::TrustedPublisher,`
    [System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine)

    $store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)

    $store.Add($cert)
    $store.Close()
}

function RemoveTrustedPublisherCertificate {
<#
.DESCRIPTION
Removes a X509 certificate from the TrustedPublisher certificate store.
This function has ben implemented in a polymorphic way. Either we specify
a file or we specify a certificate fingerprint.

Usage 1: Specify a file to remove a X509 certificate from the certificate
         store.

Usage 2: Specify a certificate fingerprint to remove the certificate
         corresponding to that certificate fingerprint.

.PARAMETER file (usage 1)
The path and file name to the certificate file.

.PARAMETER fingerprint (usage 2)
The fingerprint of the certificate to remove from the certificate store.
#>
    param (
        [Parameter(Mandatory=$true, ParameterSetName="file")]
        [string]$file,
        [Parameter(Mandatory=$true, ParameterSetName="fingerprint")]
        [string]$fingerprint
    )

    $store = New-Object System.Security.Cryptography.X509Certificates.X509Store `
    -ArgumentList ([System.Security.Cryptography.X509Certificates.StoreName]::TrustedPublisher,`
    [System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine)

    $store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)

    switch ($PsCmdlet.ParameterSetName) {
        "file" {
            $cert = GetCertificateInfo -file "$file"
        }
        "fingerprint" {
            $cert = GetCertificateInfo -store $store -fingerprint "$fingerprint"
        }
    }

    $store.Remove($cert)
    $store.Close()
}

<#
.DESCRIPTION
Get the childs processes of the process pid passed as argument.

.PARAMETER string
The PID of the process to search for the subprocesses.

.OUTPUTS
An array of Win32_Process objects
#>
function GetChildPid {
    param (
        [Parameter(Mandatory=$true)][Int32]$id
    )

    [array]$result = Get-WmiObject -Class Win32_Process -Filter "ParentProcessID=$id"
    return $result
}

Log in or click on link to see number of positives.

In cases where actual malware is found, the packages are subject to removal. Software sometimes has false positives. Moderators do not necessarily validate the safety of the underlying software, only that a package retrieves software from the official distribution point and/or validate embedded software against official distribution point (where distribution rights allow redistribution).

Chocolatey Pro provides runtime protection from possible malware.

Add to Builder Version Downloads Last Updated Status
OpenVPN - Open Source SSL VPN Solution 2.6.9.001 37232 Tuesday, February 13, 2024 Approved
OpenVPN - Open Source SSL VPN Solution 2.6.8.001 56276 Saturday, November 18, 2023 Approved
OpenVPN - Open Source SSL VPN Solution 2.6.7.001 16168 Friday, November 10, 2023 Approved
OpenVPN - Open Source SSL VPN Solution 2.6.6.001 56997 Friday, August 18, 2023 Approved
OpenVPN - Open Source SSL VPN Solution 2.6.5.001 42029 Friday, June 16, 2023 Approved
OpenVPN - Open Source SSL VPN Solution 2.6.4.001 33974 Saturday, May 13, 2023 Approved
OpenVPN - Open Source SSL VPN Solution 2.6.3.003 15600 Friday, April 28, 2023 Approved
OpenVPN - Open Source SSL VPN Solution 2.6.3.001 16030 Friday, April 14, 2023 Approved
OpenVPN - Open Source SSL VPN Solution 2.6.2.001 17437 Tuesday, March 28, 2023 Approved
OpenVPN - Open Source SSL VPN Solution 2.6.1.001 17196 Friday, March 10, 2023 Approved
OpenVPN - Open Source SSL VPN Solution 2.6.0.005 13426 Tuesday, February 28, 2023 Approved
OpenVPN - Open Source SSL VPN Solution 2.6.0.004 7950 Saturday, February 25, 2023 Approved
OpenVPN - Open Source SSL VPN Solution 2.6.0 23120 Friday, January 27, 2023 Approved
OpenVPN - Open Source SSL VPN Solution 2.5.8 50025 Thursday, November 3, 2022 Approved
OpenVPN - Open Source SSL VPN Solution 2.5.7 73355 Wednesday, June 1, 2022 Approved
OpenVPN - Open Source SSL VPN Solution 2.5.6 37159 Thursday, March 17, 2022 Approved
OpenVPN - Open Source SSL VPN Solution 2.5.5 16774 Thursday, February 17, 2022 Approved
OpenVPN - Open Source SSL VPN Solution 2.5.4 429 Wednesday, February 16, 2022 Approved
OpenVPN 2.4.7 215195 Saturday, March 23, 2019 Approved
OpenVPN 2.4.6.20190116 12948 Sunday, January 20, 2019 Approved
OpenVPN 2.4.6.20180710 93528 Tuesday, July 10, 2018 Approved
OpenVPN 2.4.6 4812 Monday, June 25, 2018 Approved
OpenVPN 2.4.5 495 Monday, June 25, 2018 Approved
OpenVPN 2.4.4 24077 Tuesday, October 3, 2017 Approved
OpenVPN 2.4.3 7820 Thursday, June 22, 2017 Approved
OpenVPN 2.4.2 3521 Friday, May 26, 2017 Approved
OpenVPN 2.4.1 2558 Saturday, May 13, 2017 Approved
OpenVPN Community 2.4.0 6826 Sunday, January 8, 2017 Approved
OpenVPN Community 2.3.13.20161120 3145 Sunday, November 20, 2016 Approved
OpenVPN Community 2.3.13 3252 Monday, November 7, 2016 Approved
OpenVPN Community 2.3.11 5270 Monday, June 20, 2016 Approved
OpenVPN Community for Windows (incl. OpenVPN GUI) 2.3.10 2859 Monday, January 11, 2016 Approved
OpenVPN 2.3.6 4427 Thursday, December 18, 2014 Approved
openvpn 2.3.2 1702 Tuesday, July 16, 2013 Approved
openvpn 2.2.2.20130718 548 Thursday, July 18, 2013 Approved
openvpn 2.2.2 627 Thursday, July 18, 2013 Approved

Discussion for the OpenVPN Package

Ground Rules:

  • This discussion is only about OpenVPN and the OpenVPN package. If you have feedback for Chocolatey, please contact the Google Group.
  • This discussion will carry over multiple versions. If you have a comment about a particular version, please note that in your comments.
  • The maintainers of this Chocolatey Package will be notified about new comments that are posted to this Disqus thread, however, it is NOT a guarantee that you will get a response. If you do not hear back from the maintainers after posting a message below, please follow up by using the link on the left side of this page or follow this link to contact maintainers. If you still hear nothing back, please follow the package triage process.
  • Tell us what you love about the package or OpenVPN, or tell us what needs improvement.
  • Share your experiences with the package, or extra configuration or gotchas that you've found.
  • If you use a url, the comment will be flagged for moderation until you've been whitelisted. Disqus moderated comments are approved on a weekly schedule if not sooner. It could take between 1-5 days for your comment to show up.
comments powered by Disqus