What's the best way to reset network connections using C#/.NET?
My company has several machines out with customers that connect by various means (3G, wifi, ethernet cable) and sometimes (especially with 3G) are reporting to Windows that they're still connected when they're not. I have a way to check if the connection is really live, but I'm struggling to reset them. Here's one problem:
var searcher = new ManagementObjectSearcher("select * from Win32_NetworkAdapter");
var managementObject = searcher.Get();
foreach (ManagementObject obj in managementObject)
{
var name = obj.Properties["Name"].Value.ToString();
Console.WriteLine(name);
obj.InvokeMethod("Disable", null);
obj.InvokeMethod("Enable", null);
}
As you can see, that will go through ALL network adapters and reset them, which I don't want to do. Furthermore, some adapters won't accept the null parameter.
I can get the NetworkInterface objects I want with this:
var interfaces = NetworkInterface.GetAllNetworkInterfaces().Where(ni => ni.IsReceiveOnly == false &&
ni.OperationalStatus == OperationalStatus.Up && ni.NetworkInterfaceType != NetworkInterfaceType.Loopback);
But the NetworkInterface class seems to have no Start(), Stop(), Reset() etc methods. Where do I go from here?
After some searching and experimentation, I found this blog post:
Disable/Enable Network Connections Under Vista
It is a much better approach.
The just of it, is to use a utility called mgmtclassgen.exe to generate a wrapper class around the WMI Win32_NetworkAdapter class. Use the following command in a developer command prompt at the folder of your choosing:
mgmtclassgen Win32_NetworkAdapter -p NetworkAdapter.cs
After you've generated NetworkAdapter.cs you can import it into a new project, add System.Management.dll to your project references, and use the following code to disable or enable an adapter of your choosing:
SelectQuery query = new SelectQuery("Win32_NetworkAdapter", "NetConnectionStatus=2");
ManagementObjectSearcher search = new ManagementObjectSearcher(query);
foreach(ManagementObject result in search.Get())
{
NetworkAdapter adapter = new NetworkAdapter(result);
// Identify the adapter you wish to disable here.
// In particular, check the AdapterType and
// Description properties.
// Here, we're selecting the LAN adapters.
if (adapter.AdapterType.Equals("Ethernet 802.3"))
{
adapter.Disable();
}
}
Also keep in mind, your program will have to be run as an administrator on any systems where UAC is enabled - to do this it is recommended to create an application manifest. You can do that by changing the requestedExecutionLevel entry in your manifest file to this:
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
Might be a bit simplistic, but could you call ipconfig /renew from a new process?
Process.Start("ipconfig", "/renew");
Alternatively, this similar question talks about using netsh, which I guess you could call from a process too How to disable (or reset) a network adapter programmatically in C#
You need Win32 API :
GetExtendedTcpTable : Get all exists tcp connection.
SetTcpEntry : Change tcp connection state.
Example code:
MIB_TCPROW row = new MIB_TCPROW();
row.dwState = TcpState.DeleteTcb;
row.dwLocalAddr = ...;
row.dwLocalPort = ...;
row.dwRemoteAddr = ...;
row.dwRemotePort = ...;
SetTcpEntry(ref row);
Related
I'm coding a windows service and in one point I need to know if there is an active interactive session.
I tried using OnSessionChange() and keep in a variable the last SessionChangeReason. When I reach to the mentioned point I compare if it's equal to SessionChangeReason.SessionLogOn. This works with the inconvenient that the service has a delayed start so if the user logs on before the service starts running this information is lost.
I have also seen the System.Environment.Interactive property but as I understand this refers to the process of the current service which is not interactive, so it wouldn't give me the information I need (I might misunderstood this, though).
Is there a way to get this info 'on demand' without having to keep register of the SessionChangeReason?
Edit: Maybe I wasn't clear about this point. Aside from knowing that there is an interactive session I also need to know that it isn't locked.
P/Invoke WTSEnumerateSessions to see if there are additional sessions and what their connected states are. You obviously have to ignore session 0 on Vista+.
You should only do this when your service is started, the session change notification should be used to detect further changes.
Finally I've resigned to knowing specifically that there is a session and isn't locked so we'll work with whether there is an active session or not.
If only knowing there is an active session works for you and you don't want to use pInvoke you can either:
a) Search for the explorer process
Process[] ps = Process.GetProcessesByName("explorer");
bool explorerActive = (ps.Length > 0);
b) Use the following WMI query to get the UserName of the active session:
using System.Management;
ConnectionOptions oConn = new ConnectionOptions();
ManagementScope oMs = new ManagementScope("\\\\localhost", oConn);
ObjectQuery oQuery = new ObjectQuery("select * from Win32_ComputerSystem");
ManagementObjectSearcher oSearcher = new ManagementObjectSearcher(oMs, oQuery);
ManagementObjectCollection oReturnCollection = oSearcher.Get();
foreach (ManagementObject oReturn in oReturnCollection)
{
if (oReturn["UserName"] == null)
{
// No active session
Console.Write("UserName: null");
}
else
{
// Active session
Console.Write("UserName: " + oReturn["UserName"].ToString());
}
}
If you want to use pInvoke see Anders' answer.
I am programming a little script to change the IPv4 address of a specific Wireless80211 or Ethernet network interface. So far everything is working fine. The script sets the IPv4 with the command prompt and netsh (to control it with C# I use System.Diagnostics).
I want to add the feature, that the script disables or enables all Wireless80211 and Ethernet network interfaces (without a specific one) which you can find in "Control Panel>Network and Internet>Network Connections".
The script is mainly used for ArtNet to DMX to automatically prepare everything to use ArtNet (for people, which do not know anything of the Control Panel and to automate the workflow).
I have already tried it with the System.Net.NetworkInformation namespace, but I have only found a way to get enabled network interfaces. As soon as I disable an interface System.Net.NetworkInformation does not show this interface.
I wasn't aware that NetworkInterface.GetAllNetworkInterfaces() didn't return disabled interfaces.
Anyway, you could try using the WMI api via System.Management.dll that's available in the .NET framework (you must add this reference to your project), I did a test and it allows you to interact even with disabled network interfaces.
The following example give you an idea of how to work with WMI via this api, I pretty much extracted it from the documentation:
using System;
using System.Management;
...
void ListNetworkAdapters()
{
var query = new ObjectQuery("SELECT * FROM Win32_NetworkAdapter");
using (var searcher = new ManagementObjectSearcher(query))
{
var queryCollection = searcher.Get();
foreach (var m in queryCollection)
{
Console.WriteLine("ServiceName : {0}", m["Name"]);
Console.WriteLine("MACAddress : {0}", m["Description"]);
Console.WriteLine();
}
Console.ReadLine();
}
}
The documentation can be found here:
https://learn.microsoft.com/en-us/windows/desktop/cimwin32prov/win32-networkadapter
NetworkInterface.GetAllNetworkInterfaces()
Then check the OperationalStatus property
https://learn.microsoft.com/en-us/dotnet/api/system.net.networkinformation.networkinterface?view=netframework-4.7.2
NetworkInterface.GetAllNetworkInterfaces() doesn't return a complete list,
when I go to A network device, and using properties I disable internet protocol version 4(TCP/IPv4) check box, GetAllNetworkInterfaces stop recognizing the device.
I find this odd as although the device is not internet capable after this(we still may have IPv6 but for the sake of the argument) its still a network device
can someone explain this, or show how this can be solved?
After some time searching I found that a complete list can be accessed by using the following:
SelectQuery wmiQuery = new SelectQuery("SELECT * FROM Win32_NetworkAdapter");
ManagementObjectSearcher searchProcedure = new ManagementObjectSearcher(wmiQuery);
foreach (ManagementObject networkAdapter in searchProcedure.Get())
{
//here do whatever you want to do to the adapter
}
A more general description can be found at(not C# specific):
http://msdn.microsoft.com/en-us/library/aa394216(v=vs.85).aspx
Also in C# make sure to reference System.Management, or you wan't be able to use the code above
Ideally I want to do this inside of C# so I'm including the C# tag.
I have several Window Communication Foundation Services that are running and open up TCP ports (one each) using System.ServiceModel.ServiceHost. I have a listing of the port numbers that are being using but I want to use the running service to map from the port number to the executable.
I have tried to use netstat, TCPView, and few other similar tools I've found trying to search this solution but nothing display my process, the closest I can get is the System (PID 4).
All of these are Windows WCF Services, operate as intended, and do show up in netstat and TCPView (by port number) but can only supply the "System" as the process.
The code is being managed by several different departments, so I'm not considering an common interface approach as a valid solution. I do have full admin rights to the machine.
The WCF provides a WMI interface for diagnostics which you should be able to use in order to associate a WCF service port with a particular process. In particular the Service class looks promising.
(Please feel free to attach your example code here as discussed, or post it as another answer.)
Adding my sample code to #HarryJohnston's answer:
String wcfNamespace = String.Format(#"\\{0}\Root\ServiceModel", "MachineName");
ConnectionOptions connection = new ConnectionOptions();
connection.Authentication = AuthenticationLevel.PacketPrivacy;
ManagementScope scope = new ManagementScope(wcfNamespace, connection);
scope.Connect();
ObjectQuery query = new ObjectQuery("Select * From Service");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
ManagementObjectCollection queryCollection = searcher.Get();
ManagementObject[] listing = queryCollection.OfType<ManagementObject>().ToArray();
Dictionary<int, int> portToPID = new Dictionary<int, int>();
foreach (ManagementObject mo in queryCollection)
{
//each of services only have one base address in my example
Uri baseAddress = new Uri(((Array)mo.Properties["BaseAddresses"].Value).GetValue(0).ToString());
int pid = Int32.Parse(mo.Properties["ProcessId"].Value.ToString());
portToPID.Add(baseAddress.Port, pid);
}
Also requires add this to each service .config, not WMI Client
<system.serviceModel>
…
<diagnostics wmiProviderEnabled="true" />
…
</system.serviceModel>
I am currently developing an application in C# (.NET 4.0) that should have as a part of its functionality the ability to determine the percentage of fragmentation on a particular volume. All the other features have been tested and are working fine but I’ve hit a snag trying to access this data. I would ideally prefer to use WMI as this matches the format I’m using for the other features but at this point I’m willing to use anything that can be efficiently integrated into the application, even if I have to use RegEx to filter the data. I am currently doing the development on a Windows 7 Professional (x64) machine. I have tested the following Powershell snippet using Administrator rights and it works flawlessly.
$drive = Get-WmiObject -Class Win32_Volume -Namespace root\CIMV2 -ComputerName . | Where-Object { $_.DriveLetter -eq 'D:' }
$drive.DefragAnalysis().DefragAnalysis
This is the method I’m using in C# to accomplish the same thing, but the InvokeMethod keeps returning 11 (0xB).
public static Fragmentation GetVolumeFragmentationAnalysis(string drive)
{
//Fragmenation object initialization removed for simplicity
try
{
ConnectionOptions mgmtConnOptions = new ConnectionOptions { EnablePrivileges = true };
ManagementScope scope = new ManagementScope(new ManagementPath(string.Format(#"\\{0}\root\CIMV2", Environment.MachineName)), mgmtConnOptions);
ObjectQuery query = new ObjectQuery(string.Format(#"SELECT * FROM Win32_Volume WHERE Name = '{0}\\'", drive));
scope.Connect();
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query))
{
object[] outputArgs = new object[2];
foreach (ManagementObject moVolume in searcher.Get())
{
// Execution stops at this line as the result is always 11
UInt32 result = (UInt32)moVolume.InvokeMethod("DefragAnalysis", outputArgs);
if (result == 0)
{
Console.WriteLine("Defrag Needed: = {0}\n", outputArgs[0]);
ManagementBaseObject mboDefragAnalysis = outputArgs[1] as ManagementBaseObject;
if (null != mboDefragAnalysis)
{
Console.WriteLine(mboDefragAnalysis["TotalPercentFragmentation"].ToString());
}
}
else
{
Console.WriteLine("Return Code: = {0}", result);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("Could not acquire fragmentation data.\n" + ex);
}
return result;
}
I have even added the following line to the app.manifest but still nothing.
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
Could somebody please tell me what I’m overlooking? Failure is not an option for me on this, so if it cannot be done using C# I don’t mind creating a DLL in another language (even if I have to learn it), that will give me the results I need. Ideally the application should be able work on any OS from XP upwards and must be totally transparent to the user.
These are the resources I have already used. I wanted to add the jeffrey_wall blog on msdn as well but as a new user I can only add 2 hyperlinks at a time. Thanks again.
http://www.codeproject.com/Messages/2901324/Re-the-result-of-DefragAnalysis-method-in-csharp.aspx
http://social.technet.microsoft.com/Forums/vi-VN/winserverfiles/thread/9d56bfad-dcf5-4258-90cf-4ba9247200da
Try building your application targeting 'Any CPU' - on the Build tab of the project properties. I suspect you're using a target of x86. I get the same error code on my Win7 x64 machine if I do that.
In fact, running your PowerShell snippet in the x86 version of PowerShell gives an empty set of results, too.
You get the same error if you run either piece of code without full Administrator privileges, as you've found, so also ensure your app.manifest is correct. A UAC prompt is a handy hint that it's taking effect!
No idea why this WMI query doesn't like running under WoW64, I'm afraid, but hopefully this will give you a head-start.
You could simply call the PowerShell command you mentioned in your post, since you said the PowerShell code works. From C# you would want to follow this workflow:
Instantiate a PowerShell RunSpace
Open the RunSpace
Add a script to the Commands property
Invoke the command list
Here is an example of how to achieve this, and process the resulting object output.
http://www.codeproject.com/Articles/18229/How-to-run-PowerShell-scripts-from-C
For Windows XP and Windows Vista, you would have to ensure that PowerShell was installed on each of the systems you want to run your program on. Not a bad prerequisite to have, but something to keep in mind as a dependency.
Hope this helps.
The 32-bit WMI provider for Win32_Volume doesn't seem to be able to start the defragsvc for whatever reason. You can force the 64-bit WMI provider even in a 32-bit client running under WOW64 by changing your code to add an additional WMI connection option:
ConnectionOptions mgmtConnOptions = new ConnectionOptions {
EnablePrivileges = true,
Context = new ManagementNamedValueCollection() {
{ "__ProviderArchitecture", 64 }
}
};