|
1 | 1 | using System;
|
2 | 2 | using System.Diagnostics;
|
3 | 3 | using System.IO;
|
| 4 | +using System.Text; |
| 5 | +using System.Threading; |
4 | 6 | using Shadowsocks.Controller;
|
5 | 7 | using Shadowsocks.Properties;
|
6 | 8 | using Shadowsocks.Model;
|
7 |
| -using System.Text; |
8 | 9 | using Newtonsoft.Json;
|
9 | 10 |
|
10 | 11 | namespace Shadowsocks.Util.SystemProxy
|
@@ -83,43 +84,78 @@ public static void SetIEProxy(bool enable, bool global, string proxyServer, stri
|
83 | 84 |
|
84 | 85 | private static void ExecSysproxy(string arguments)
|
85 | 86 | {
|
86 |
| - using (var process = new Process()) |
| 87 | + // using event to avoid hanging when redirect standard output/error |
| 88 | + // ref: https://stackoverflow.com/questions/139593/processstartinfo-hanging-on-waitforexit-why |
| 89 | + // and http://blog.csdn.net/zhangweixing0/article/details/7356841 |
| 90 | + using (AutoResetEvent outputWaitHandle = new AutoResetEvent(false)) |
| 91 | + using (AutoResetEvent errorWaitHandle = new AutoResetEvent(false)) |
87 | 92 | {
|
88 |
| - // Configure the process using the StartInfo properties. |
89 |
| - process.StartInfo.FileName = Utils.GetTempPath("sysproxy.exe"); |
90 |
| - process.StartInfo.Arguments = arguments; |
91 |
| - process.StartInfo.WorkingDirectory = Utils.GetTempPath(); |
92 |
| - process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; |
93 |
| - process.StartInfo.UseShellExecute = false; |
94 |
| - process.StartInfo.RedirectStandardError = true; |
95 |
| - process.StartInfo.RedirectStandardOutput = true; |
| 93 | + using (var process = new Process()) |
| 94 | + { |
| 95 | + // Configure the process using the StartInfo properties. |
| 96 | + process.StartInfo.FileName = Utils.GetTempPath("sysproxy.exe"); |
| 97 | + process.StartInfo.Arguments = arguments; |
| 98 | + process.StartInfo.WorkingDirectory = Utils.GetTempPath(); |
| 99 | + process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; |
| 100 | + process.StartInfo.UseShellExecute = false; |
| 101 | + process.StartInfo.RedirectStandardError = true; |
| 102 | + process.StartInfo.RedirectStandardOutput = true; |
96 | 103 |
|
97 |
| - // Need to provide encoding info, or output/error strings we got will be wrong. |
98 |
| - process.StartInfo.StandardOutputEncoding = Encoding.Unicode; |
99 |
| - process.StartInfo.StandardErrorEncoding = Encoding.Unicode; |
| 104 | + // Need to provide encoding info, or output/error strings we got will be wrong. |
| 105 | + process.StartInfo.StandardOutputEncoding = Encoding.Unicode; |
| 106 | + process.StartInfo.StandardErrorEncoding = Encoding.Unicode; |
100 | 107 |
|
101 |
| - process.StartInfo.CreateNoWindow = true; |
102 |
| - process.Start(); |
| 108 | + process.StartInfo.CreateNoWindow = true; |
103 | 109 |
|
104 |
| - var stderr = process.StandardError.ReadToEnd(); |
105 |
| - var stdout = process.StandardOutput.ReadToEnd(); |
| 110 | + StringBuilder output = new StringBuilder(); |
| 111 | + StringBuilder error = new StringBuilder(); |
106 | 112 |
|
107 |
| - process.WaitForExit(); |
| 113 | + process.OutputDataReceived += (sender, e) => |
| 114 | + { |
| 115 | + if (e.Data == null) |
| 116 | + { |
| 117 | + outputWaitHandle.Set(); |
| 118 | + } |
| 119 | + else |
| 120 | + { |
| 121 | + output.AppendLine(e.Data); |
| 122 | + } |
| 123 | + }; |
| 124 | + process.ErrorDataReceived += (sender, e) => |
| 125 | + { |
| 126 | + if (e.Data == null) |
| 127 | + { |
| 128 | + errorWaitHandle.Set(); |
| 129 | + } |
| 130 | + else |
| 131 | + { |
| 132 | + error.AppendLine(e.Data); |
| 133 | + } |
| 134 | + }; |
108 | 135 |
|
109 |
| - var exitCode = process.ExitCode; |
110 |
| - if (exitCode != (int)RET_ERRORS.RET_NO_ERROR) |
111 |
| - { |
112 |
| - throw new ProxyException(stderr); |
113 |
| - } |
| 136 | + process.Start(); |
114 | 137 |
|
115 |
| - if (arguments == "query") |
116 |
| - { |
117 |
| - if (stdout.IsNullOrWhiteSpace() || stdout.IsNullOrEmpty()) |
| 138 | + process.BeginErrorReadLine(); |
| 139 | + process.BeginOutputReadLine(); |
| 140 | + |
| 141 | + process.WaitForExit(); |
| 142 | + |
| 143 | + var stderr = error.ToString(); |
| 144 | + var stdout = output.ToString(); |
| 145 | + |
| 146 | + var exitCode = process.ExitCode; |
| 147 | + if (exitCode != (int)RET_ERRORS.RET_NO_ERROR) |
118 | 148 | {
|
119 |
| - // we cannot get user settings |
120 |
| - throw new ProxyException("failed to query wininet settings"); |
| 149 | + throw new ProxyException(stderr); |
| 150 | + } |
| 151 | + |
| 152 | + if (arguments == "query") { |
| 153 | + if (stdout.IsNullOrWhiteSpace() || stdout.IsNullOrEmpty()) { |
| 154 | + // we cannot get user settings |
| 155 | + throw new ProxyException("failed to query wininet settings"); |
| 156 | + } |
| 157 | + _queryStr = stdout; |
121 | 158 | }
|
122 |
| - _queryStr = stdout; |
123 | 159 | }
|
124 | 160 | }
|
125 | 161 | }
|
@@ -147,13 +183,9 @@ private static void Read()
|
147 | 183 | {
|
148 | 184 | string configContent = File.ReadAllText(Utils.GetTempPath(_userWininetConfigFile));
|
149 | 185 | _userSettings = JsonConvert.DeserializeObject<SysproxyConfig>(configContent);
|
150 |
| - } |
151 |
| - catch (Exception) |
152 |
| - { |
| 186 | + } catch(Exception) { |
153 | 187 | // Suppress all exceptions. finally block will initialize new user config settings.
|
154 |
| - } |
155 |
| - finally |
156 |
| - { |
| 188 | + } finally { |
157 | 189 | if (_userSettings == null) _userSettings = new SysproxyConfig();
|
158 | 190 | }
|
159 | 191 | }
|
|
0 commit comments