OSDN Git Service

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