OSDN Git Service

ReSharperのバージョンアップにともなう変更をマージする
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / Net / SystemProxy.cs
1 // Copyright (C) 2015 Kazuhiro Fujieda <fujieda@users.osdn.me>\r
2 //\r
3 // Licensed under the Apache License, Version 2.0 (the "License");\r
4 // you may not use this file except in compliance with the License.\r
5 // You may obtain a copy of the License at\r
6 //\r
7 //    http://www.apache.org/licenses/LICENSE-2.0\r
8 //\r
9 // Unless required by applicable law or agreed to in writing, software\r
10 // distributed under the License is distributed on an "AS IS" BASIS,\r
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
12 // See the License for the specific language governing permissions and\r
13 // limitations under the License.\r
14 \r
15 using System;\r
16 using System.Runtime.InteropServices;\r
17 using Microsoft.Win32;\r
18 // ReSharper disable once IdentifierTypo\r
19 using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;\r
20 \r
21 namespace KancolleSniffer.Net\r
22 {\r
23     public class SystemProxy\r
24     {\r
25         private InternetPerConnOptionList _orgList;\r
26         private Uri _initialUri;\r
27 \r
28         private void SaveSettings()\r
29         {\r
30             if (_orgList.dwSize != 0)\r
31                 return;\r
32             var opts = new[]\r
33             {\r
34                 new InternetPerConnOption {dwOption = PerConnOption.INTERNET_PER_CONN_FLAGS},\r
35                 new InternetPerConnOption {dwOption = PerConnOption.INTERNET_PER_CONN_AUTOCONFIG_URL}\r
36             };\r
37             var list = new InternetPerConnOptionList\r
38             {\r
39                 pOptions = MarshalOptions(opts),\r
40                 pszConnection = IntPtr.Zero,\r
41                 dwOptionCount = opts.Length,\r
42                 dwOptionError = 0\r
43             };\r
44             var listSize = list.dwSize = Marshal.SizeOf(list);\r
45             if (InternetQueryOption(IntPtr.Zero, InternetOption.INTERNET_OPTION_PER_CONNECTION_OPTION,\r
46                 ref list, ref listSize))\r
47             {\r
48                 _orgList = list;\r
49             }\r
50             AdjustLocalIntranetZoneFlags();\r
51         }\r
52 \r
53         public void SetAutoConfigUrl(string url)\r
54         {\r
55             SaveSettings();\r
56             var flagValue = new InternetPerConnOptionValue {dwValue = (int)PerConnFlags.PROXY_TYPE_AUTO_PROXY_URL};\r
57             var urlValue = new InternetPerConnOptionValue {pszValue = Marshal.StringToHGlobalAuto(url)};\r
58             if (_initialUri == null)\r
59                 Uri.TryCreate(url, UriKind.Absolute, out _initialUri);\r
60             var opts = new[]\r
61             {\r
62                 new InternetPerConnOption {dwOption = PerConnOption.INTERNET_PER_CONN_FLAGS, Value = flagValue},\r
63                 new InternetPerConnOption {dwOption = PerConnOption.INTERNET_PER_CONN_AUTOCONFIG_URL, Value = urlValue}\r
64             };\r
65             var list = new InternetPerConnOptionList\r
66             {\r
67                 pOptions = MarshalOptions(opts),\r
68                 pszConnection = IntPtr.Zero,\r
69                 dwOptionCount = opts.Length,\r
70                 dwOptionError = 0\r
71             };\r
72             var listSize = list.dwSize = Marshal.SizeOf(list);\r
73             var listBuff = Marshal.AllocCoTaskMem(listSize);\r
74             Marshal.StructureToPtr(list, listBuff, false);\r
75             InternetSetOption(IntPtr.Zero, InternetOption.INTERNET_OPTION_PER_CONNECTION_OPTION, listBuff, listSize);\r
76             Refresh();\r
77 \r
78             Marshal.FreeHGlobal(urlValue.pszValue);\r
79             Marshal.FreeCoTaskMem(list.pOptions);\r
80             Marshal.FreeCoTaskMem(listBuff);\r
81         }\r
82 \r
83         public void RestoreSettings()\r
84         {\r
85             if (_orgList.dwSize == 0)\r
86                 return;\r
87             var size = Marshal.SizeOf(typeof(InternetPerConnOption));\r
88             var urlOpt = (InternetPerConnOption)\r
89                 Marshal.PtrToStructure((IntPtr)((long)_orgList.pOptions + size), typeof(InternetPerConnOption));\r
90             Uri.TryCreate(Marshal.PtrToStringUni(urlOpt.Value.pszValue) ?? "", UriKind.Absolute, out var orgUri);\r
91             if (orgUri?.Authority == _initialUri?.Authority) // The restoration was sikipped or failed at last time.\r
92             {\r
93                 // Unselect the Use automatic configration script check box.\r
94                 var flagsOpt =\r
95                     (InternetPerConnOption)Marshal.PtrToStructure(_orgList.pOptions, typeof(InternetPerConnOption));\r
96                 flagsOpt.Value.dwValue &= ~(int)PerConnFlags.PROXY_TYPE_AUTO_PROXY_URL;\r
97                 Marshal.StructureToPtr(flagsOpt, _orgList.pOptions, false);\r
98             }\r
99             var listSize = _orgList.dwSize;\r
100             var listBuff = Marshal.AllocCoTaskMem(listSize);\r
101             Marshal.StructureToPtr(_orgList, listBuff, false);\r
102             InternetSetOption(IntPtr.Zero, InternetOption.INTERNET_OPTION_PER_CONNECTION_OPTION, listBuff, listSize);\r
103             Refresh();\r
104 \r
105             Marshal.FreeCoTaskMem(listBuff);\r
106             Marshal.FreeHGlobal(urlOpt.Value.pszValue);\r
107             Marshal.FreeCoTaskMem(_orgList.pOptions);\r
108             _orgList.dwSize = 0;\r
109         }\r
110 \r
111         private IntPtr MarshalOptions(InternetPerConnOption[] opts)\r
112         {\r
113             var buff = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(InternetPerConnOption)) * opts.Length);\r
114             var size = Marshal.SizeOf(typeof(InternetPerConnOption));\r
115             for (var i = 0; i < opts.Length; i++)\r
116             {\r
117                 var ptr = (IntPtr)((long)buff + (i * size));\r
118                 Marshal.StructureToPtr(opts[i], ptr, false);\r
119             }\r
120             return buff;\r
121         }\r
122 \r
123         public static void Refresh()\r
124         {\r
125             InternetSetOption(IntPtr.Zero, InternetOption.INTERNET_OPTION_SETTINGS_CHANGED, IntPtr.Zero, 0);\r
126             InternetSetOption(IntPtr.Zero, InternetOption.INTERNET_OPTION_PROXY_SETTINGS_CHANGED, IntPtr.Zero, 0);\r
127         }\r
128 \r
129         /// <summary>\r
130         /// PACファイルでDIRECTを指定すると、すべてのサイトがローカルイントラネットになり、\r
131         /// IEが互換表示になるなどの不具合があるので、イントラネットにならないようにする\r
132         /// </summary>\r
133         private void AdjustLocalIntranetZoneFlags()\r
134         {\r
135             var zones = Registry.CurrentUser.OpenSubKey(\r
136                 @"Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\1", true);\r
137             if (zones == null)\r
138                 return;\r
139             if (!(zones.GetValue("Flags") is int flags))\r
140                 return;\r
141             zones.SetValue("Flags", flags & (-1 ^ 0x108));\r
142         }\r
143 \r
144         [DllImport("WinInet.dll", CharSet = CharSet.Unicode)]\r
145         private static extern bool InternetQueryOption(IntPtr hInternet, InternetOption dwOption,\r
146             // ReSharper disable once IdentifierTypo\r
147             ref InternetPerConnOptionList optionList, ref int lpdwBufferLength);\r
148 \r
149         [DllImport("WinInet.dll", CharSet = CharSet.Unicode)]\r
150         private static extern bool InternetSetOption(IntPtr hInternet, InternetOption dwOption,\r
151             IntPtr lpBuffer, int dwBufferLength);\r
152 \r
153         [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]\r
154         private struct InternetPerConnOptionList\r
155         {\r
156             public int dwSize;\r
157             public IntPtr pszConnection;\r
158             public int dwOptionCount;\r
159             public int dwOptionError;\r
160             public IntPtr pOptions;\r
161         }\r
162 \r
163         [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]\r
164         private struct InternetPerConnOption\r
165         {\r
166             public PerConnOption dwOption;\r
167             public InternetPerConnOptionValue Value;\r
168         }\r
169 \r
170         [StructLayout(LayoutKind.Explicit)]\r
171         public struct InternetPerConnOptionValue\r
172         {\r
173             [FieldOffset(0)] public int dwValue;\r
174             [FieldOffset(0)] public IntPtr pszValue;\r
175             [FieldOffset(0)] public FILETIME ftValue;\r
176         }\r
177 \r
178         // ReSharper disable UnusedMember.Global\r
179         // ReSharper disable UnusedMember.Local\r
180         // ReSharper disable InconsistentNaming\r
181         // ReSharper disable IdentifierTypo\r
182         private enum InternetOption : uint\r
183         {\r
184 \r
185             INTERNET_OPTION_REFRESH = 0x00000025,\r
186 \r
187             INTERNET_OPTION_SETTINGS_CHANGED = 0x00000027,\r
188             INTERNET_OPTION_PER_CONNECTION_OPTION = 0x0000004B,\r
189             INTERNET_OPTION_PROXY_SETTINGS_CHANGED = 0x0000005F\r
190         }\r
191 \r
192         private enum PerConnOption\r
193         {\r
194 \r
195             INTERNET_PER_CONN_FLAGS = 1,\r
196 \r
197             INTERNET_PER_CONN_PROXY_SERVER = 2,\r
198             INTERNET_PER_CONN_PROXY_BYPASS = 3,\r
199             INTERNET_PER_CONN_AUTOCONFIG_URL = 4,\r
200 \r
201             INTERNET_PER_CONN_AUTODISCOVERY_FLAGS = 5\r
202         }\r
203 \r
204         [Flags]\r
205         private enum PerConnFlags\r
206         {\r
207             PROXY_TYPE_DIRECT = 0x00000001,\r
208             PROXY_TYPE_PROXY = 0x00000002,\r
209             PROXY_TYPE_AUTO_PROXY_URL = 0x00000004,\r
210             PROXY_TYPE_AUTO_DETECT = 0x00000008\r
211         }\r
212     }\r
213 }