如何在 C# 中更改 DNS IP 地址
How to change DNS IP addresses in C#
为了在我的 WPF 应用程序中以编程方式更改我的适配器 (NetworkInterface) DNS IP 地址,我遵循了这个问题: 回答并添加如下:
/// <summary>
/// Method to set the DNS IP addresses of a given <see cref="NetworkInterface"/>
/// Note : using "Win32_NetworkAdapterConfiguration" library, so only on Windows OS
/// </summary>
/// <param name="ni">The <see cref="NetworkInterface"/> adapter to modify its DNS IP addresses along given <paramref name="addresses"/></param>
/// <param name="addresses">The IP addresses to store in <paramref name="ni"/> adapter as its new DNS IP addresses</param>
public static void SetDNS(this NetworkInterface ni, string[] addresses)
{
if (ni == null) return;
ManagementClass objMC = new ManagementClass("Win32_NetworkAdapterConfiguration");
ManagementObjectCollection objMOC = objMC.GetInstances();
foreach (ManagementObject objMO in objMOC)
{
if ((bool)objMO["IPEnabled"])
{
if (objMO["Caption"].ToString().Contains(ni.Description))
{
ManagementBaseObject objdns = objMO.GetMethodParameters("SetDNSServerSearchOrder");
if (objdns != null)
{
objdns["DNSServerSearchOrder"] = addresses;
objMO.InvokeMethod("SetDNSServerSearchOrder", objdns, null);
}
}
}
}
}
进入调试模式显示它正确调用了这个函数,循环遍历 ManagementObjectCollection
,结束其中一项以正确匹配我的 2 if
语句,最后调用
objdns["DNSServerSearchOrder"] = addresses;
objMO.InvokeMethod("SetDNSServerSearchOrder", objdns, null);
但是当我使用 ipconfig /all
CLI 检查时,我的 DNS IP 地址没有改变!
如何通过 C# 直接在 windows 中有效更改我的 DNS 地址,而不是通过调用后面的 cmd.exe 进程。
谢谢。
这就是我更改系统 DNS 地址的结尾。
感谢@jimi 帮助我更好地理解这一点。
所以,正如@jimi 指出的那样,毕竟我正在静默使用 netsh 来设置新的 DNS 地址,因为我尝试的原始解决方案(通过 WMI)不支持 IPv6
到目前为止(没有全面测试)它有效!
这是我所做的:
/// <summary>
/// Start a <see cref="Process"/>
/// </summary>
/// <param name="processName">The <see cref="ProcessStartInfo.FileName"/> (usually name of the exe / command to start)</param>
/// <param name="args">The <see cref="ProcessStartInfo.Arguments"/> (the argument of the command)</param>
/// <param name="verb">The <see cref="ProcessStartInfo.Verb"/>. Use "runas" to start the process with admin priviledge (default is null)</param>
/// <param name="useShell">The <see cref="ProcessStartInfo.UseShellExecute"/>. Does the process run silently or not (silent by default)</param>
/// <param name="redirectErros">The <see cref="ProcessStartInfo.RedirectStandardError"/>. Do we redirect standard error ? (true by default)</param>
/// <param name="redirectOutput">The <see cref="ProcessStartInfo.RedirectStandardOutput"/>. Do we redirect standard output ? (true by default)</param>
/// <param name="noWindow">The <see cref="ProcessStartInfo.CreateNoWindow"/>. Do we prevent the creation of CMD window to run silently ? (silent by default)</param>
/// <returns>True if <paramref name="processName"/> isn't null and process execution succeeded. False if <paramref name="processName"/> is null or empty.
/// Throw an <see cref="Exception"/> if execution failed</returns>
public static bool StartProcess(string processName, string args, string verb = null, bool useShell = false, bool redirectErros = true, bool redirectOutput = true, bool noWindow = true)
{
if (string.IsNullOrWhiteSpace(processName))
return false;
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = processName;
psi.Arguments = args;
psi.UseShellExecute = useShell;
psi.RedirectStandardOutput = redirectOutput;
psi.RedirectStandardError = redirectErros;
psi.CreateNoWindow = noWindow;
if (verb != null)
psi.Verb = verb;
Process proc = Process.Start(psi);
proc.WaitForExit();
string errors = proc.StandardError.ReadToEnd();
string output = proc.StandardOutput.ReadToEnd();
if (proc.ExitCode != 0)
throw new Exception(processName + " exit code: " + proc.ExitCode.ToString() + " " + (!string.IsNullOrEmpty(errors) ? " " + errors : "") + " " + (!string.IsNullOrEmpty(output) ? " " + output : ""));
return true;
}
/// <summary>
/// Convinient method to start a "netsh" process as admin to set a new DNS IP address calling <see cref="StartProcess(string, string, string, bool, bool, bool)"/>
/// </summary>
/// <param name="interfaceName">The name of the interface to set its new <paramref name="address"/> IP ddress</param>
/// <param name="address">The new IP address to set of the <paramref name="interfaceName"/> DNS</param>
/// <param name="isPrimary">Is this new DNS IP address is a primary one ?</param>
/// <returns><see cref="StartProcess(string, string, string, bool, bool, bool)"/> return value,
/// or false if <paramref name="address"/> isn't a correct IP address</returns>
public static bool netshSetNewDNS(string interfaceName, string address, bool isPrimary)
{
var ipVer = IPversion(address, RGXVX);
if (!(ipVer[0] || ipVer[1]))
return false;
return netshSetNewDNS(interfaceName, address, isPrimary, ipVer[0]);
}
/// <summary>
/// Convinient method to start a "netsh" process as admin to set a new DNS IP address calling <see cref="netshSetNewDNS(string, string, bool)"/>
/// </summary>
/// <param name="interfaceName">The name of the interface to set its new <paramref name="address"/> IP ddress</param>
/// <param name="address">The new IP address to set of the <paramref name="interfaceName"/> DNS</param>
/// <param name="isPrimary">Is this new DNS IP address is a primary one ?</param>
/// <param name="isIPv6">Does <paramref name="address"/> is IPv6 ?</param>
/// <returns><see cref="netshSetNewDNS(string, string, bool)"/> return value</returns>
public static bool netshSetNewDNS(string interfaceName, string address, bool isPrimary, bool isIPv6)
{
string arg = string.Format("interface {0} {1} dnsservers \"{2}\"{3} {4} {5}", isIPv6 ? "ipv6" : "ipv4", isPrimary ? "set" : "add", interfaceName, isPrimary ? " static" : "", address, isPrimary ? "primary" : "index=2");
return StartProcess("netsh", arg, "runas");
}
/// <summary>
/// Method to set the DNS IP addresses of a given <see cref="NetworkInterface"/>
/// note : we use netsh silently.
/// see : https://www.tenforums.com/tutorials/77444-change-ipv4-ipv6-dns-server-address-windows.html
/// </summary>
/// <param name="ni">The <see cref="NetworkInterface"/> adapter to modify its DNS IP addresses along given <paramref name="addresses"/></param>
/// <param name="ipv4">The IPv4 addresses to store in <paramref name="ni"/> adapter as its new DNS IP addresses</param>
/// <param name="ipv6">The IPv6 addresses to store in <paramref name="ni"/> adapter as its new DNS IP addresses</param>
public static void SetDNS(this NetworkInterface ni, string[] ipv4, string[] ipv6)
{
if (!(ipv4.Any(add => IsIP(add, RGXV4)) || ipv6.Any(add => IsIP(add, RGXV6))))
{
Debug.WriteLine("None of the suplied addresses are IPv4/6.\nCan not update DNS IP addresses.");
return;
}
// delete current IPv4 DNS
StartProcess("netsh", "interface ipv4 delete dnsservers \"" + ni.Name + "\" all", "runas");
// delete current IPv6 DNS
StartProcess("netsh", "interface ipv6 delete dnsservers \"" + ni.Name + "\" all", "runas");
string address;
//set new IPv4 DNS addresses
if (ipv4 != null && ipv4.Length>0)
{
//primary
address = ipv4[0];
try
{ bool res = netshSetNewDNS(ni.Name, address, true, false); }
catch(Exception e)
{ Debug.WriteLine(e.Message); }
if (ipv4.Length>1)
{
//secondary
address = ipv4[1];
try
{ bool res = netshSetNewDNS(ni.Name, address, false, false); }
catch (Exception e)
{ Debug.WriteLine(e.Message); }
}
}
//set new IPv6 DNS addresses
if (ipv6 != null && ipv6.Length > 0)
{
//primary
address = ipv6[0];
try
{ bool res = netshSetNewDNS(ni.Name, address, true, true); }
catch (Exception e)
{ Debug.WriteLine(e.Message); }
if (ipv6.Length > 1)
{
//secondary
address = ipv6[1];
try
{ bool res = netshSetNewDNS(ni.Name, address, false, true); }
catch (Exception e)
{ Debug.WriteLine(e.Message); }
}
}
}
我有 1 个“小”问题:设置 ipv6(+ipv4?)地址会遇到性能问题。
而只有新的 (2) 个 ipv4 IP 地址几乎是即时的(最多 1 秒),使用 2 ipv6 + 2 ipv4 设置,实际(正确)更改我的 DNS IP 地址需要最多 10 秒。
调试显示它在 StartProcess(...)
调用辅助 IPv4 DNS 地址设置(在 SetDNS(...) bool res = netshSetNewDNS(ni.Name, address, false, false);
中)的函数的 proc.WaitForExit();
时冻结!
奇怪的是它总是在这里冻结,因为当我只使用 2 个新的 IPv4 DNS 地址设置调用时它这次没有冻结...
为了在我的 WPF 应用程序中以编程方式更改我的适配器 (NetworkInterface) DNS IP 地址,我遵循了这个问题:
/// <summary>
/// Method to set the DNS IP addresses of a given <see cref="NetworkInterface"/>
/// Note : using "Win32_NetworkAdapterConfiguration" library, so only on Windows OS
/// </summary>
/// <param name="ni">The <see cref="NetworkInterface"/> adapter to modify its DNS IP addresses along given <paramref name="addresses"/></param>
/// <param name="addresses">The IP addresses to store in <paramref name="ni"/> adapter as its new DNS IP addresses</param>
public static void SetDNS(this NetworkInterface ni, string[] addresses)
{
if (ni == null) return;
ManagementClass objMC = new ManagementClass("Win32_NetworkAdapterConfiguration");
ManagementObjectCollection objMOC = objMC.GetInstances();
foreach (ManagementObject objMO in objMOC)
{
if ((bool)objMO["IPEnabled"])
{
if (objMO["Caption"].ToString().Contains(ni.Description))
{
ManagementBaseObject objdns = objMO.GetMethodParameters("SetDNSServerSearchOrder");
if (objdns != null)
{
objdns["DNSServerSearchOrder"] = addresses;
objMO.InvokeMethod("SetDNSServerSearchOrder", objdns, null);
}
}
}
}
}
进入调试模式显示它正确调用了这个函数,循环遍历 ManagementObjectCollection
,结束其中一项以正确匹配我的 2 if
语句,最后调用
objdns["DNSServerSearchOrder"] = addresses;
objMO.InvokeMethod("SetDNSServerSearchOrder", objdns, null);
但是当我使用 ipconfig /all
CLI 检查时,我的 DNS IP 地址没有改变!
如何通过 C# 直接在 windows 中有效更改我的 DNS 地址,而不是通过调用后面的 cmd.exe 进程。
谢谢。
这就是我更改系统 DNS 地址的结尾。 感谢@jimi 帮助我更好地理解这一点。
所以,正如@jimi 指出的那样,毕竟我正在静默使用 netsh 来设置新的 DNS 地址,因为我尝试的原始解决方案(通过 WMI)不支持 IPv6
到目前为止(没有全面测试)它有效!
这是我所做的:
/// <summary>
/// Start a <see cref="Process"/>
/// </summary>
/// <param name="processName">The <see cref="ProcessStartInfo.FileName"/> (usually name of the exe / command to start)</param>
/// <param name="args">The <see cref="ProcessStartInfo.Arguments"/> (the argument of the command)</param>
/// <param name="verb">The <see cref="ProcessStartInfo.Verb"/>. Use "runas" to start the process with admin priviledge (default is null)</param>
/// <param name="useShell">The <see cref="ProcessStartInfo.UseShellExecute"/>. Does the process run silently or not (silent by default)</param>
/// <param name="redirectErros">The <see cref="ProcessStartInfo.RedirectStandardError"/>. Do we redirect standard error ? (true by default)</param>
/// <param name="redirectOutput">The <see cref="ProcessStartInfo.RedirectStandardOutput"/>. Do we redirect standard output ? (true by default)</param>
/// <param name="noWindow">The <see cref="ProcessStartInfo.CreateNoWindow"/>. Do we prevent the creation of CMD window to run silently ? (silent by default)</param>
/// <returns>True if <paramref name="processName"/> isn't null and process execution succeeded. False if <paramref name="processName"/> is null or empty.
/// Throw an <see cref="Exception"/> if execution failed</returns>
public static bool StartProcess(string processName, string args, string verb = null, bool useShell = false, bool redirectErros = true, bool redirectOutput = true, bool noWindow = true)
{
if (string.IsNullOrWhiteSpace(processName))
return false;
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = processName;
psi.Arguments = args;
psi.UseShellExecute = useShell;
psi.RedirectStandardOutput = redirectOutput;
psi.RedirectStandardError = redirectErros;
psi.CreateNoWindow = noWindow;
if (verb != null)
psi.Verb = verb;
Process proc = Process.Start(psi);
proc.WaitForExit();
string errors = proc.StandardError.ReadToEnd();
string output = proc.StandardOutput.ReadToEnd();
if (proc.ExitCode != 0)
throw new Exception(processName + " exit code: " + proc.ExitCode.ToString() + " " + (!string.IsNullOrEmpty(errors) ? " " + errors : "") + " " + (!string.IsNullOrEmpty(output) ? " " + output : ""));
return true;
}
/// <summary>
/// Convinient method to start a "netsh" process as admin to set a new DNS IP address calling <see cref="StartProcess(string, string, string, bool, bool, bool)"/>
/// </summary>
/// <param name="interfaceName">The name of the interface to set its new <paramref name="address"/> IP ddress</param>
/// <param name="address">The new IP address to set of the <paramref name="interfaceName"/> DNS</param>
/// <param name="isPrimary">Is this new DNS IP address is a primary one ?</param>
/// <returns><see cref="StartProcess(string, string, string, bool, bool, bool)"/> return value,
/// or false if <paramref name="address"/> isn't a correct IP address</returns>
public static bool netshSetNewDNS(string interfaceName, string address, bool isPrimary)
{
var ipVer = IPversion(address, RGXVX);
if (!(ipVer[0] || ipVer[1]))
return false;
return netshSetNewDNS(interfaceName, address, isPrimary, ipVer[0]);
}
/// <summary>
/// Convinient method to start a "netsh" process as admin to set a new DNS IP address calling <see cref="netshSetNewDNS(string, string, bool)"/>
/// </summary>
/// <param name="interfaceName">The name of the interface to set its new <paramref name="address"/> IP ddress</param>
/// <param name="address">The new IP address to set of the <paramref name="interfaceName"/> DNS</param>
/// <param name="isPrimary">Is this new DNS IP address is a primary one ?</param>
/// <param name="isIPv6">Does <paramref name="address"/> is IPv6 ?</param>
/// <returns><see cref="netshSetNewDNS(string, string, bool)"/> return value</returns>
public static bool netshSetNewDNS(string interfaceName, string address, bool isPrimary, bool isIPv6)
{
string arg = string.Format("interface {0} {1} dnsservers \"{2}\"{3} {4} {5}", isIPv6 ? "ipv6" : "ipv4", isPrimary ? "set" : "add", interfaceName, isPrimary ? " static" : "", address, isPrimary ? "primary" : "index=2");
return StartProcess("netsh", arg, "runas");
}
/// <summary>
/// Method to set the DNS IP addresses of a given <see cref="NetworkInterface"/>
/// note : we use netsh silently.
/// see : https://www.tenforums.com/tutorials/77444-change-ipv4-ipv6-dns-server-address-windows.html
/// </summary>
/// <param name="ni">The <see cref="NetworkInterface"/> adapter to modify its DNS IP addresses along given <paramref name="addresses"/></param>
/// <param name="ipv4">The IPv4 addresses to store in <paramref name="ni"/> adapter as its new DNS IP addresses</param>
/// <param name="ipv6">The IPv6 addresses to store in <paramref name="ni"/> adapter as its new DNS IP addresses</param>
public static void SetDNS(this NetworkInterface ni, string[] ipv4, string[] ipv6)
{
if (!(ipv4.Any(add => IsIP(add, RGXV4)) || ipv6.Any(add => IsIP(add, RGXV6))))
{
Debug.WriteLine("None of the suplied addresses are IPv4/6.\nCan not update DNS IP addresses.");
return;
}
// delete current IPv4 DNS
StartProcess("netsh", "interface ipv4 delete dnsservers \"" + ni.Name + "\" all", "runas");
// delete current IPv6 DNS
StartProcess("netsh", "interface ipv6 delete dnsservers \"" + ni.Name + "\" all", "runas");
string address;
//set new IPv4 DNS addresses
if (ipv4 != null && ipv4.Length>0)
{
//primary
address = ipv4[0];
try
{ bool res = netshSetNewDNS(ni.Name, address, true, false); }
catch(Exception e)
{ Debug.WriteLine(e.Message); }
if (ipv4.Length>1)
{
//secondary
address = ipv4[1];
try
{ bool res = netshSetNewDNS(ni.Name, address, false, false); }
catch (Exception e)
{ Debug.WriteLine(e.Message); }
}
}
//set new IPv6 DNS addresses
if (ipv6 != null && ipv6.Length > 0)
{
//primary
address = ipv6[0];
try
{ bool res = netshSetNewDNS(ni.Name, address, true, true); }
catch (Exception e)
{ Debug.WriteLine(e.Message); }
if (ipv6.Length > 1)
{
//secondary
address = ipv6[1];
try
{ bool res = netshSetNewDNS(ni.Name, address, false, true); }
catch (Exception e)
{ Debug.WriteLine(e.Message); }
}
}
}
我有 1 个“小”问题:设置 ipv6(+ipv4?)地址会遇到性能问题。
而只有新的 (2) 个 ipv4 IP 地址几乎是即时的(最多 1 秒),使用 2 ipv6 + 2 ipv4 设置,实际(正确)更改我的 DNS IP 地址需要最多 10 秒。
调试显示它在 StartProcess(...)
调用辅助 IPv4 DNS 地址设置(在 SetDNS(...) bool res = netshSetNewDNS(ni.Name, address, false, false);
中)的函数的 proc.WaitForExit();
时冻结!
奇怪的是它总是在这里冻结,因为当我只使用 2 个新的 IPv4 DNS 地址设置调用时它这次没有冻结...