Skip to content

Commit bef3713

Browse files
liyuan-reyceleron533
authored andcommitted
fix: using event to avoid hanging when redirect standard output/error (shadowsocks#1755)
1 parent 2a93461 commit bef3713

File tree

1 file changed

+67
-35
lines changed

1 file changed

+67
-35
lines changed

shadowsocks-csharp/Util/SystemProxy/Sysproxy.cs

Lines changed: 67 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
using System;
22
using System.Diagnostics;
33
using System.IO;
4+
using System.Text;
5+
using System.Threading;
46
using Shadowsocks.Controller;
57
using Shadowsocks.Properties;
68
using Shadowsocks.Model;
7-
using System.Text;
89
using Newtonsoft.Json;
910

1011
namespace Shadowsocks.Util.SystemProxy
@@ -83,43 +84,78 @@ public static void SetIEProxy(bool enable, bool global, string proxyServer, stri
8384

8485
private static void ExecSysproxy(string arguments)
8586
{
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))
8792
{
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;
96103

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;
100107

101-
process.StartInfo.CreateNoWindow = true;
102-
process.Start();
108+
process.StartInfo.CreateNoWindow = true;
103109

104-
var stderr = process.StandardError.ReadToEnd();
105-
var stdout = process.StandardOutput.ReadToEnd();
110+
StringBuilder output = new StringBuilder();
111+
StringBuilder error = new StringBuilder();
106112

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+
};
108135

109-
var exitCode = process.ExitCode;
110-
if (exitCode != (int)RET_ERRORS.RET_NO_ERROR)
111-
{
112-
throw new ProxyException(stderr);
113-
}
136+
process.Start();
114137

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)
118148
{
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;
121158
}
122-
_queryStr = stdout;
123159
}
124160
}
125161
}
@@ -147,13 +183,9 @@ private static void Read()
147183
{
148184
string configContent = File.ReadAllText(Utils.GetTempPath(_userWininetConfigFile));
149185
_userSettings = JsonConvert.DeserializeObject<SysproxyConfig>(configContent);
150-
}
151-
catch (Exception)
152-
{
186+
} catch(Exception) {
153187
// Suppress all exceptions. finally block will initialize new user config settings.
154-
}
155-
finally
156-
{
188+
} finally {
157189
if (_userSettings == null) _userSettings = new SysproxyConfig();
158190
}
159191
}

0 commit comments

Comments
 (0)