C#でVPN接続（L2TP over IPsec　事前共有キーあり）する

需要があるかわからないですが、C#でVPN接続する忘備録。
時間が経っているのでうろ覚え。。。

C#って、こういう処理はすっごい簡単にできそうなんだけど
このVPNに関して言えば、それっぽいWIN　APIとかもなくて
とにかく困りました。

[softether](https://ja.softether.org/)　でVPNサーバーをたててWINアプリからそこにVPN接続したいと考えたんだけど
これがなかなかうまくいかない。
接続方式とか指定しなければもっと簡単に接続できるはずだが、
L2TP over IPsecの事前共有キーありの接続をするのが結構難しかった。


参照で[DotRas](https://dotras.codeplex.com/)が必要だった気がする。
結構調べたが情報が古いものが多く、かなり調べると中華系のコミュニティで情報を発見。
で、色んな情報を組み合わせて試行錯誤したけどVISTAぐらいの時代に書かれたソースなので
Win8、Win10で動かないっぽかった。
そっからアレコレ更に試行錯誤してコード調整した記憶がある。

下記はソース（クラス）。クッソ長い。

```csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;


/// <summary>
/// 
/// </summary>
namespace VPNClient
{
    /// <summary>
    /// 
    /// </summary>
    class VPNAPI
    {
        /// <summary>
        /// 
        /// </summary>
        public static int CURRENT_VPN_CONN  = 0;

        /// <summary>
        /// 
        /// </summary>
        public enum RASCredMask
        {
            RASCM_UserName = 0x1,
            RASCM_password = 0x2,
            RASCM_Domain = 0x4,
            RASCM_DefaultCreds = 0x8,
            RASCM_PreSharedKey = 0x10,
            RASCM_ServerPreSharedKey = 0x20,
            RASCM_DDMPreSharedKey = 0x40
        }

        /// <summary>
        /// 
        /// </summary>
        public enum RasEntryOptions
        {
            RASEO_UseCountrAndAreaCodes = 0x00000001,
            RASEO_SpecificIpAddr = 0x00000002,
            RASEO_SpecificNameServers = 0x00000004,
            RASEO_IpHeaderCompression = 0x00000008,
            RASEO_RemoteDefaultGateway = 0x00000010,
            RASEO_DisableLcpExtensions = 0x00000020,
            RASEO_TerminalBeforeDial = 0x00000040,
            RASEO_TerminalAfterDial = 0x00000080,
            RASEO_ModemLights = 0x00000100,
            RASEO_SwCompression = 0x00000200,
            RASEO_RequireEncrptedPw = 0x00000400,
            RASEO_RequireMsEncrptedPw = 0x00000800,
            RASEO_RequireDataEncrption = 0x00001000,
            RASEO_NetworkLogon = 0x00002000,
            RASEO_UseLogonCredentials = 0x00004000,
            RASEO_PromoteAlternates = 0x00008000,
            RASEO_SecureLocalFiles = 0x00010000,
            RASEO_RequireEAP = 0x00020000,
            RASEO_RequirePAP = 0x00040000,
            RASEO_RequireSPAP = 0x00080000,
            RASEO_Custom = 0x00100000,
            RASEO_PreviewPhoneNumber = 0x00200000,
            RASEO_SharedPhoneNumbers = 0x00800000,
            RASEO_PreviewUserPw = 0x01000000,
            RASEO_PreviewDomain = 0x02000000,
            RASEO_ShowDialingProgress = 0x04000000,
            RASEO_RequireCHAP = 0x08000000,
            RASEO_RequireMsCHAP = 0x10000000,
            RASEO_RequireMsCHAP2 = 0x20000000,
            RASEO_RequireW95MSCHAP = 0x40000000

        }

        /// <summary>
        /// 
        /// </summary>
        public enum RasEntryOptions2
        {
            RASEO2_SecureFileAndPrint = 0x1,
            RASEO2_SecureClientForMSNet = 0x2,
            RASEO2_DontNegotiateMultilink = 0x4,
            RASEO2_DontUseRasCredentials = 0x8,
            RASEO2_UsePreSharedKey = 0x10,
            RASEO2_Internet = 0x20,
            RASEO2_DisableNbtOverIP = 0x40,
            RASEO2_UseGlobalDeviceSettings = 0x80,
            RASEO2_ReconnectIfDropped = 0x100,
            RASEO2_SharePhoneNumbers = 0x200,
            //winver > 0x600
            RASEO2_SecureRoutingCompartment = 0x400,
            RASEO2_UseTypicalSettings = 0x800,
            RASEO2_IPv6SpecificNameServer = 0x1000,
            RASEO2_IPv6RemoteDefaultGateway = 0x2000,
            RASEO2_RegisterIpWithDNS = 0x4000,
            RASEO2_UseDNSSuffixForRegistration = 0x8000,
            RASEO2_IPv4ExplicitMetric = 0x10000,
            RASEO2_IPv6ExplicitMetric = 0x20000,
            RASEO2_DisableIKENameEkuCheck = 0x40000,
            //winver > 0x601
            RASEO2_DisableClassBasedStaticRoute = 0x00080000,
            RASEO2_SpecificIPv6Addr = 0x00100000,
            RASEO2_DisableMobility = 0x00200000,
            RASEO2_RequireMachineCertificates = 0x00400000,
            //winver > 0x602
            RASEO2_UsePreSharedKeyForIkev2Initiator = 0x00800000,
            RASEO2_UsePreSharedKeyForIkev2Responder = 0x01000000,
            RASEO2_CacheCredentials = 0x02000000,
            //winver > 0x603
            RASEO2_AutoTriggerCapable = 0x04000000,
            RASEO2_IsThirdPartyProfile = 0x08000000,
            RASEO2_AuthTypeIsOtp = 0x10000000,
            //winver > 0x604
            RASEO2_IsAlwaysOn = 0x20000000,
            RASEO2_IsPrivateNetwork = 0x40000000
        }

        /// <summary>
        /// 
        /// </summary>
        public enum RASNP
        {
            RASNP_NetBEUI = 0x1,
            RASNP_Ipx = 0x2,
            RASNP_Ip = 0x4,
            RASNP_Ipv6 = 0x8
        }


        /// <summary>
        /// 
        /// </summary>
        private const long ET_None = 0;               //   No   encryption

        /// <summary>
        /// 
        /// </summary>
        private const long ET_Require = 1;            //   Require   Encryption

        /// <summary>
        /// 
        /// </summary>
        private const long ET_RequireMax = 2;         //   Require   max   encryption

        /// <summary>
        /// 
        /// </summary>
        private const long ET_Optional = 3;           //   Do   encryption   if   possible.   None   Ok.

        /// <summary>
        /// 
        /// </summary>
        private const long VS_Default = 0;            //   default   (PPTP   for   now)

        /// <summary>
        /// 
        /// </summary>
        private const long VS_PptpOnly = 1;           //   Only   PPTP   is   attempted.

        /// <summary>
        /// 
        /// </summary>
        private const long VS_PptpFirst = 2;          //   PPTP   is   tried   first.

        /// <summary>
        /// 
        /// </summary>
        private const long VS_L2tpOnly = 3;           //  Only   L2TP   is   attempted.

        /// <summary>
        /// 
        /// </summary>
        private const long VS_L2tpFirst = 4;          //   L2TP   is   tried   first.

        /// <summary>
        /// 
        /// </summary>
        private const long RASET_Phone = 1;           //   Phone   lines:   modem,   ISDN,   X.25,   etc

        /// <summary>
        /// 
        /// </summary>
        private const long RASET_Vpn = 2;             //   Virtual   private   network

        /// <summary>
        /// 
        /// </summary>
        private const long RASET_Direct = 3;          //   Direct   connect:   serial,   parallel

        /// <summary>
        /// 
        /// </summary>
        private const long RASET_Internet = 4;        //   BaseCamp   internet

        /// <summary>
        /// 
        /// </summary>
        private const long RASET_Broadband = 5;       //   Broadband

        /// <summary>
        /// 
        /// </summary>
        private const int MAX_PATH = 260;

        /// <summary>
        /// 
        /// </summary>
        private const int RAS_MaxAreaCode = 10;

        /// <summary>
        /// 
        /// </summary>
        private const int RAS_MaxPhoneNumber = 128;

        /// <summary>
        /// 
        /// </summary>
        private const int RAS_MaxDeviceType = 16;

        /// <summary>
        /// 
        /// </summary>
        private const int RAS_MaxDeviceName = 128;

        /// <summary>
        /// 
        /// </summary>
        private const int RAS_MaxPadType = 32;

        /// <summary>
        /// 
        /// </summary>
        private const int RAS_MaxX25Address = 200;

        /// <summary>
        /// 
        /// </summary>
        private const int RAS_MaxFacilities = 200;

        /// <summary>
        /// 
        /// </summary>
        private const int RAS_MaxUserData = 200;

        /// <summary>
        /// 
        /// </summary>
        private const int RAS_MaxDnsSuffix = 256;

        /// <summary>
        /// 
        /// </summary>
        private const int RAS_MaxEntryName = 256;

        /// <summary>
        /// 
        /// </summary>
        private const int RAS_MaxCallbackNumber = RAS_MaxPhoneNumber;

        /// <summary>
        /// 
        /// </summary>
        private const int UNLEN = 256;

        /// <summary>
        /// 
        /// </summary>
        private const int PWLEN = 256;

        /// <summary>
        /// 
        /// </summary>
        private const int DNLEN = 16;

        /// <summary>
        /// 
        /// </summary>
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        private struct GUID
        {
            uint data1;
            UInt16 data2;
            UInt16 data3;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
            byte[] data4;
        }

        /// <summary>
        /// 
        /// </summary>
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        public struct RASIPADDR
        {
            byte a;
            byte b;
            byte c;
            byte d;
        }

        /// <summary>
        /// 
        /// </summary>
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        public struct RASIPV6ADDR
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
            byte[] a;
        }

        /// <summary>
        /// 
        /// </summary>
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        private struct RASENTRY
        {
            /// <summary>
            /// 
            /// </summary>
            public uint dwSize;

            /// <summary>
            /// 
            /// </summary>
            public uint dwfOptions;

            //
            // Location/phone number.
            //
            public uint dwCountryID;

            /// <summary>
            /// 
            /// </summary>
            public uint dwCountryCode;

            /// <summary>
            /// 
            /// </summary>
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)(RAS_MaxAreaCode + 1))]
            public string szAreaCode;

            /// <summary>
            /// 
            /// </summary>
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)(RAS_MaxPhoneNumber + 1))]
            public string szLocalPhoneNumber;

            /// <summary>
            /// 
            /// </summary>
            public uint dwAlternateOffset;

            //
            // PPP/Ip
            //
            public RASIPADDR ipaddr;

            /// <summary>
            /// 
            /// </summary>
            public RASIPADDR ipaddrDns;

            /// <summary>
            /// 
            /// </summary>
            public RASIPADDR ipaddrDnsAlt;

            /// <summary>
            /// 
            /// </summary>
            public RASIPADDR ipaddrWins;

            /// <summary>
            /// 
            /// </summary>
            public RASIPADDR ipaddrWinsAlt;

            //
            // Framing
            //
            public uint dwFrameSize;

            /// <summary>
            /// 
            /// </summary>
            public uint dwfNetProtocols;

            /// <summary>
            /// 
            /// </summary>
            public uint dwFramingProtocol;

            //
            // Scripting
            //
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)MAX_PATH)]
            public string szScript;

            //
            // AutoDial
            //
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)MAX_PATH)]
            public string szAutodialDll;

            /// <summary>
            /// 
            /// </summary>
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)MAX_PATH)]
            public string szAutodialFunc;

            //
            // Device
            //
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)(RAS_MaxDeviceType + 1))]
            public string szDeviceType;

            /// <summary>
            /// 
            /// </summary>
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)(RAS_MaxDeviceName + 1))]
            public string szDeviceName;

            //
            // X.25
            //
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)(RAS_MaxPadType + 1))]
            public string szX25PadType;

            /// <summary>
            /// 
            /// </summary>
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)(RAS_MaxX25Address + 1))]
            public string szX25Address;

            /// <summary>
            /// 
            /// </summary>
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)(RAS_MaxFacilities + 1))]
            public string szX25Facilities;

            /// <summary>
            /// 
            /// </summary>
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)(RAS_MaxUserData + 1))]
            public string szX25UserData;

            /// <summary>
            /// 
            /// </summary>
            public uint dwChannels;

            //
            // Reserved
            //
            public uint dwReserved1;

            /// <summary>
            /// 
            /// </summary>
            public uint dwReserved2;

            /// <summary>
            /// 
            /// </summary>
            public uint dwSubEntries;

            /// <summary>
            /// 
            /// </summary>
            public uint dwDialMode;

            /// <summary>
            /// 
            /// </summary>
            public uint dwDialExtraPercent;

            /// <summary>
            /// 
            /// </summary>
            public uint dwDialExtraSampleSeconds;

            /// <summary>
            /// 
            /// </summary>
            public uint dwHangUpExtraPercent;

            /// <summary>
            /// 
            /// </summary>
            public uint dwHangUpExtraSampleSeconds;

            /// <summary>
            /// 
            /// </summary>
            public uint dwIdleDisconnectSeconds;

            /// <summary>
            /// 
            /// </summary>
            public uint dwType;

            /// <summary>
            /// 
            /// </summary>
            public uint dwEncryptionType;

            /// <summary>
            /// 
            /// </summary>
            public uint dwCustomAuthKey;

            /// <summary>
            /// 
            /// </summary>
            public GUID guid;

            /// <summary>
            /// 
            /// </summary>
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)MAX_PATH)] //259
            public string szCustomDialDll;

            /// <summary>
            /// 
            /// </summary>
            public uint dwVpnStrategy;

            /// <summary>
            /// 
            /// </summary>
            public uint dwfOptions2;

            /// <summary>
            /// 
            /// </summary>
            public uint dwfOptions3;

            /// <summary>
            /// 
            /// </summary>
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)RAS_MaxDnsSuffix)] //255
            public string szDnsSuffix;

            /// <summary>
            /// 
            /// </summary>
            public uint dwTcpWindowSize;

            /// <summary>
            /// 
            /// </summary>
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)MAX_PATH)]   //259
            public string szPrerequisitePbk;

            /// <summary>
            /// 
            /// </summary>
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)(RAS_MaxEntryName + 1))] //RAS_MaxEntryName=256
            public string szPrerequisiteEntry;

            /// <summary>
            /// 
            /// </summary>
            public uint dwRedialCount;

            /// <summary>
            /// 
            /// </summary>
            public uint dwRedialPause;

            /// <summary>
            /// 
            /// </summary>
            public RASIPV6ADDR ipv6addrDns;

            /// <summary>
            /// 
            /// </summary>
            public RASIPV6ADDR ipv6addrDnsAlt;

            /// <summary>
            /// 
            /// </summary>
            public uint dwIPv4InterfaceMetric;

            /// <summary>
            /// 
            /// </summary>
            public uint dwIPv6InterfaceMetric;

            /// <summary>
            /// 
            /// </summary>
            public RASIPV6ADDR ipv6addr;

            /// <summary>
            /// 
            /// </summary>
            public uint dwIPv6PrefixLength;

            /// <summary>
            /// 
            /// </summary>
            public uint dwNetworkOutageTime;
        }

        /// <summary>
        /// 
        /// </summary>
        [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Auto)]
        public struct RASDIALPARAMS
        {
            /// <summary>
            /// 
            /// </summary>
            public int dwSize;

            /// <summary>
            /// 
            /// </summary>
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = RAS_MaxEntryName + 1)]
            public string szEntryName;

            /// <summary>
            /// 
            /// </summary>
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = RAS_MaxPhoneNumber + 1)]
            public string szPhoneNumber;

            /// <summary>
            /// 
            /// </summary>
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = RAS_MaxCallbackNumber + 1)]
            public string szCallbackNumber;

            /// <summary>
            /// 
            /// </summary>
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = UNLEN + 1)]
            public string szUserName;

            /// <summary>
            /// 
            /// </summary>
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = PWLEN + 1)]
            public string szPassword;

            /// <summary>
            /// 
            /// </summary>
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = DNLEN + 1)]
            public string szDomain;

            /// <summary>
            /// 
            /// </summary>
            public int dwSubEntry;

            /// <summary>
            /// 
            /// </summary>
            public int dwCallbackId;
        }

        /// <summary>
        /// 
        /// </summary>
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        private struct RASCREDENTIALS
        {
            /// <summary>
            /// 
            /// </summary>
            public int dwSize;

            /// <summary>
            /// 
            /// </summary>
            public int dwMask;

            /// <summary>
            /// 
            /// </summary>
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)UNLEN + 1)]
            public string szUserName;

            /// <summary>
            /// 
            /// </summary>
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)PWLEN + 1)]
            public string szPassword;

            /// <summary>
            /// 
            /// </summary>
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)DNLEN + 1)]
            public string szDomain;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="unMsg"></param>
        /// <param name="rasconnstate"></param>
        /// <param name="dwError"></param>
        public delegate void Callback(uint unMsg, int rasconnstate, int dwError);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="lpRasDialExtensions"></param>
        /// <param name="lpszPhonebook"></param>
        /// <param name="lprasdialparams"></param>
        /// <param name="dwNotifierType"></param>
        /// <param name="lpvNotifier"></param>
        /// <param name="lphRasConn"></param>
        /// <returns></returns>
        [DllImport("rasapi32.dll ", CharSet = CharSet.Auto)]
        public static extern int RasDial(IntPtr lpRasDialExtensions, string lpszPhonebook, ref RASDIALPARAMS lprasdialparams, int dwNotifierType, Callback lpvNotifier, ref int lphRasConn);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="hrasconn"></param>
        /// <returns></returns>
        [DllImport("rasapi32.dll", CharSet = CharSet.Auto)]
        public extern static int RasHangUp(int hrasconn); // handle to the RAS connection to hang up );

        /// <summary>
        /// 
        /// </summary>
        /// <param name="lpszPhonebook"></param>
        /// <param name="lpszEntry"></param>
        /// <param name="lpRasEntry"></param>
        /// <param name="dwEntryInfoSize"></param>
        /// <param name="lpbDeviceInfo"></param>
        /// <param name="dwDeviceInfoSize"></param>
        /// <returns></returns>
        [DllImport("rasapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern int RasSetEntryProperties(string lpszPhonebook, string lpszEntry, ref RASENTRY lpRasEntry, uint dwEntryInfoSize, IntPtr lpbDeviceInfo, int dwDeviceInfoSize);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="lpszPhonebook"></param>
        /// <param name="lpszEntry"></param>
        /// <param name="lpCredentials"></param>
        /// <param name="fClearCredentials"></param>
        /// <returns></returns>
        [DllImport("rasapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern int RasSetCredentials(string lpszPhonebook, string lpszEntry, ref RASCREDENTIALS lpCredentials, int fClearCredentials);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="serverIP"></param>
        /// <param name="entryName"></param>
        /// <param name="user"></param>
        /// <param name="pw"></param>
        /// <param name="PreSharedKey"></param>
        /// <returns></returns>
        public static bool rasConnectVPN(string serverIP, string entryName, string user, string pw, string PreSharedKey)
        {
            bool result = false;
            try
            {
                RASENTRY rasEntry = new RASENTRY();
                RASDIALPARAMS RasDialParams = new RASDIALPARAMS();
                RASCREDENTIALS Rascredentials = new RASCREDENTIALS();
                //  uint dwSize = (uint)Marshal.SizeOf(rasEntry);
                uint dwSize = (uint)Marshal.SizeOf(typeof(RASENTRY));

                // System.Runtime.InteropServices.Marshal.SizeOf(typeof(RasDialParams));
                //byte[] lpb = new byte[436];
                //int lpbSize = lpb.Length;
                int ret = -1;
                rasEntry.dwSize = dwSize;
                rasEntry.dwCountryCode = 0;
                rasEntry.dwCountryID = 0;
                rasEntry.dwDialExtraPercent = 75;
                rasEntry.dwDialExtraSampleSeconds = 120;
                rasEntry.dwDialMode = 0;
                rasEntry.dwEncryptionType = (uint)ET_Optional;
                rasEntry.dwfNetProtocols = (uint)(RASNP.RASNP_Ip | RASNP.RASNP_Ipv6);
                rasEntry.dwfOptions = (uint)(RasEntryOptions.RASEO_RemoteDefaultGateway
                | RasEntryOptions.RASEO_SecureLocalFiles
                | RasEntryOptions.RASEO_RequireSPAP
                | RasEntryOptions.RASEO_PreviewUserPw
                | RasEntryOptions.RASEO_PreviewDomain
                //| RasEntryOptions.RASEO_RequireCHAP 
                //| RasEntryOptions.RASEO_RequireMsCHAP 
                | RasEntryOptions.RASEO_RequireMsCHAP2
                | RasEntryOptions.RASEO_ShowDialingProgress
                );
                //rasEntry.dwfOptions2 = 367;
                rasEntry.dwFramingProtocol = 1;
                rasEntry.dwHangUpExtraPercent = 10;
                rasEntry.dwHangUpExtraSampleSeconds = 120;
                rasEntry.dwRedialCount = 3;
                rasEntry.dwRedialPause = 60;
                rasEntry.dwType = (uint)RASET_Vpn;
                rasEntry.dwVpnStrategy = (uint)VS_L2tpOnly;
                rasEntry.szDeviceName = "WAN Miniport (L2TP)";
                rasEntry.szDeviceType = "Vpn";
                rasEntry.szLocalPhoneNumber = serverIP;

                rasEntry.dwfOptions2 = (uint)(RasEntryOptions2.RASEO2_SecureFileAndPrint
                        | RasEntryOptions2.RASEO2_SecureClientForMSNet
                        | RasEntryOptions2.RASEO2_DontNegotiateMultilink
                        //| RasEntryOptions2.RASEO2_DontUseRasCredentials
                        | RasEntryOptions2.RASEO2_Internet
                        | RasEntryOptions2.RASEO2_ReconnectIfDropped
                        | RasEntryOptions2.RASEO2_CacheCredentials
                        );

                if (PreSharedKey != string.Empty)
                {
                    rasEntry.dwfOptions2 += (uint)RasEntryOptions2.RASEO2_UsePreSharedKey;
                }

                ret = RasSetEntryProperties(null, entryName, ref rasEntry, rasEntry.dwSize, IntPtr.Zero, 0);
                if (ret != 0)
                {
                    throw new Exception("FailedToRasSetEntryProperties");
                }

                if (!string.IsNullOrEmpty(PreSharedKey))
                {
                    Rascredentials.dwSize = Marshal.SizeOf(Rascredentials);
                    Rascredentials.dwMask = (int)RASCredMask.RASCM_PreSharedKey;
                    Rascredentials.szPassword = PreSharedKey;
                    ret = RasSetCredentials(null, entryName, ref Rascredentials, 0);
                    if (ret >= 1)
                    {
                        throw new Exception("FailedToRasSetCredentials");
                    }
                }

                RasDialParams.dwSize = Marshal.SizeOf(RasDialParams);
                RasDialParams.szEntryName = entryName;
                RasDialParams.szUserName = user;
                RasDialParams.szPassword = pw;
                int nn = 0;
                ret = RasDial(IntPtr.Zero, null, ref RasDialParams, -1, null, ref nn);
                
                if (ret >= 1)
                {
                    if (nn > 0)
                        RasHangUp(nn);

                    throw new Exception("FailedToRasDial");
                }

                CURRENT_VPN_CONN = nn;

                result = true;
            }
            catch (Exception ex)
            {
                result = false;
            }
            finally
            {

            }

            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="entryName"></param>
        /// <returns></returns>
        public static bool rasDisConnectVPN(string entryName)
        {
            bool result = false;
            if (string.IsNullOrEmpty(entryName))
            {
                return result;
            }

            try
            {
                if (CURRENT_VPN_CONN > 0)
                {
                    RasHangUp(CURRENT_VPN_CONN );
                }
                else
                {
                    //System.Diagnostics.Process.Start("rasdial.exe", entryName + " /d");
                    Process process = new Process();
                    process.StartInfo.FileName = "rasdial.exe ";
                    process.StartInfo.Verb = entryName + " /d runas";
                    process.StartInfo.UseShellExecute = false;
                    process.StartInfo.CreateNoWindow = true;//not show cmd window
                    process.Start();
                }

                result = true;
            }
            catch (Exception ex)
            {
                result = false;
            }
            finally
            {

            }

            return result;
        }
    }
}

```

呼び出すときは

```
if (!VPNAPI.rasConnectVPN('VPNのホスト', '接続名', 'VPNユーザー', 'パスフレーズ', '事前共有キー'))
{
        //"接続ができませんでした";
        return;
}
```

で、アプリ終了タイミングなど適切なタイミングでVPNを切断する場合は下記のようにする。
```
VPNAPI.rasDisConnectVPN('接続名'); 
```

正直よくわからん。