What is the context :
Set DNS IPv6 addresses via netsh
Processes
What I Did :
public static bool StartProcessAsync(
string filename,
string arguments,
string verb = null,
bool useShell = false,
bool noWindow = false,
bool redirectOut = true,
bool redirectErr = true,
string workingDir = null,
int? timeoutInMs = null,
EventHandler startHandler = null,
EventHandler exitHandler = null
)
{
if (string.IsNullOrWhiteSpace(filename))
return false;
Process newProcess = CreateProcess(filename, arguments, verb, useShell, noWindow, redirectOut, redirectErr, workingDir);
if (newProcess == null)
return false;
newProcess.EnableRaisingEvents = true;
newProcess.Exited += new EventHandler((s, e) =>
{
Debug.WriteLine(filename + " Exited with " + newProcess.ExitCode.ToString() + " exit code.");
string errors = newProcess.StandardError.ReadToEnd();
string output = newProcess.StandardOutput.ReadToEnd();
if (newProcess.ExitCode != 0)
Debug.WriteLine(filename + " exit code: " + newProcess.ExitCode.ToString() + " " + (!string.IsNullOrEmpty(errors) ? " " + errors : "") + " " + (!string.IsNullOrEmpty(output) ? " " + output : ""));
}
);
if (exitHandler != null)
newProcess.Exited += exitHandler;
if (startHandler != null)
startHandler.Invoke(null, null);
return newProcess.Start();
}
How I am calling it :
//set new IPv6 DNS addresses
if (ipv6 != null && ipv6.Length > 0)
{
//primary
address = ipv6[0];
try
{
bool res = NetshDNSManaging(ni.Name, true, true, address, startHandler, exitHandler);
}
catch (Exception e)
{ Debug.WriteLine(e.Message); }
if (ipv6.Length > 1)
{
//secondary
address = ipv6[1];
try
{
bool res = NetshDNSManaging(ni.Name, true, false, address, startHandler, exitHandler);
}
catch (Exception e)
{ Debug.WriteLine(e.Message); }
}
}
with those intermediate convenient method :
private static bool NetshDNSManaging(
string interfaceName,
bool isIPv6,
bool isPrimary = true,
string address = null,
EventHandler startHandler = null,
EventHandler exittHandler =null
)
{
if (string.IsNullOrWhiteSpace(interfaceName))
return false;
bool noAddress = (string.IsNullOrWhiteSpace(address));
string args = string.Format("interface {0} {1} dnsservers \"{2}\"{3} {4} {5}",
isIPv6 ? "ipv6" : "ipv4",//{0}
noAddress?"delete":(isPrimary ? "set" : "add"),//{1}
interfaceName,//{2}
(isPrimary && !noAddress) ? " static" : "",//{3}
noAddress?"all":address,//{4}
noAddress?"":(isPrimary ? "primary" : "index=2")//{5}
);
return StartProcessAsAdminAsync("netsh", args, startHandler, exittHandler);
}
and :
private static bool StartProcessAsAdminAsync(
string filename,
string arguments,
EventHandler startHandler = null,
EventHandler exitHandler = null,
string verb = "runas",
bool useShell = false,
bool noWindow = true,
bool redirectOut = true,
bool redirectErr = true,
string workingDir = null,
int? timeoutInMs = null
)
{
return ProcessUtils.StartProcessAsync(filename, arguments, verb, useShell, noWindow, redirectOut, redirectErr, workingDir, timeoutInMs, startHandler, exitHandler);
}
What is my issue :
It happen from time to time that due to non sequential Process code execution, that the second call NetshDNSManaging(ni.Name, true, false, address, startHandler, exitHandler);
(witch execute for example : netsh interface ipv6 add dnsservers "Ethernet" 2001:4860:4860::8844 index=2
) finish before the first one NetshDNSManaging(ni.Name, true, true, address, startHandler, exitHandler);
(e.g. netsh interface ipv6 set dnsservers "Ethernet" static 2001:4860:4860::8888 primary
).
It result of having only the primary DNS set, as the secondary one is overwritten (witch was meanwhile transformed as a primary, by netsh
I guess, since it was the first to finish)
What I end up :
EventHandler startHandler = new EventHandler(ProcessStarted);
EventHandler exitHandler = new EventHandler(ProcessExited);
with
void ProcessExited(object? sender, EventArgs e)
{
_procNumber = Math.Max(_procNumber - 1, 0);
if (_procNumber == 0)
{
IsWaiting = false;
}
}
void ProcessStarted(object? sender, EventArgs e)
{
while (_procNumber > 0) ; //Kinda ugly synchronization !!!
_procNumber++;
IsWaiting = true;
}
and IsWaiting
Property notifying UI to display or not the waiting wheel animation.
So that work but I'm unhappy with what I think isn't a good practice here.
What I want : Make the second call starting only when the first one finish (so sequential call to the 2 created Process), but without blocking main WPF thread (to have my UI display a waiting animated wheel).
So something like : Main UI thread having a List<Process>
(my example have only 2 process, but could be generalized to several more), and when needed, it should start the Process in the list one after one, in order, the second one waiting for the first one to finish before starting, and so on until end of the List.
But without blocking the main WPF UI thread !
Parallelism is one of my nemesis, I struggle with this.