Repository navigation
Expand file tree
/
Copy pathSerialInspector.cs
More file actions
181 lines (155 loc) · 6.38 KB
/
Copy pathSerialInspector.cs
File metadata and controls
181 lines (155 loc) · 6.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/*
* FLOSS Serial Port Inspector
* License: MIT
* * A minimalistic, dependency-free C# tool to list COM ports and their status.
* Compatible with legacy .NET Framework 4.x (csc.exe).
*/
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Management; // Requires reference to System.Management.dll
using System.Text.RegularExpressions;
namespace SerialInspector
{
class Program
{
// Structure to hold our port data
struct PortInfo
{
public string Name; // e.g. COM3
public string Status; // Available, Busy, or Access Denied
public string Description; // e.g. USB Serial Port
public string Manufacturer; // e.g. FTDI
}
static void Main(string[] args)
{
Console.WriteLine("Scanning Serial Ports... Please wait.");
Console.WriteLine();
// 1. Get List of all active ports from the OS
string[] rawPorts = SerialPort.GetPortNames();
List<PortInfo> finalContext = new List<PortInfo>();
// 2. Fetch Hardware Info via WMI (Win32_PnPEntity is more reliable than Win32_SerialPort for USB adapters)
var wmiData = GetWmiDeviceData();
foreach (string portName in rawPorts)
{
PortInfo info = new PortInfo();
info.Name = portName;
info.Description = "Generic Serial Device"; // Default
info.Manufacturer = "Unknown";
// Match WMI data to this port name
if (wmiData.ContainsKey(portName))
{
info.Description = wmiData[portName].Description;
info.Manufacturer = wmiData[portName].Manufacturer;
}
// 3. Check Status (The invasive check)
// Note: This attempts to open the port. If it fails with UnauthorizedAccess, it's Busy.
info.Status = CheckPortStatus(portName);
finalContext.Add(info);
}
// 4. Sort (Natural sort logic: COM1, COM2, COM10)
finalContext.Sort((x, y) => {
int nX = ExtractNumber(x.Name);
int nY = ExtractNumber(y.Name);
return nX.CompareTo(nY);
});
// 5. Print Table
PrintTable(finalContext);
}
// Helper to check port status
static string CheckPortStatus(string portName)
{
SerialPort sp = null;
try
{
sp = new SerialPort(portName);
sp.Open();
// If we get here, it opened successfully
sp.Close();
return "Available";
}
catch (UnauthorizedAccessException)
{
// Port exists but is locked by another process
return "BUSY / OCCUPIED";
}
catch (Exception ex)
{
// Other hardware errors
return "Error: " + ex.GetType().Name;
}
finally
{
if (sp != null && sp.IsOpen) sp.Close();
if (sp != null) sp.Dispose();
}
}
// Helper to get WMI data
static Dictionary<string, PortInfo> GetWmiDeviceData()
{
Dictionary<string, PortInfo> data = new Dictionary<string, PortInfo>();
try
{
// Query PnP Entities that are Ports
// ClassGuid for Ports: {4d36e978-e325-11ce-bfc1-08002be10318}
ManagementObjectSearcher searcher = new ManagementObjectSearcher(
"root\\CIMV2",
"SELECT * FROM Win32_PnPEntity WHERE ClassGuid=\"{4d36e978-e325-11ce-bfc1-08002be10318}\""
);
foreach (ManagementObject queryObj in searcher.Get())
{
string caption = queryObj["Caption"] != null ? queryObj["Caption"].ToString() : "";
// Regex to find "COMx" in the caption (e.g. "Intel SOL (COM3)")
Match match = Regex.Match(caption, @"\((COM\d+)\)");
if (match.Success)
{
string comName = match.Groups[1].Value;
PortInfo p = new PortInfo();
p.Description = caption; // Use full caption as description
p.Manufacturer = queryObj["Manufacturer"] != null ? queryObj["Manufacturer"].ToString() : "Unknown";
if (!data.ContainsKey(comName))
{
data.Add(comName, p);
}
}
}
}
catch { /* WMI failure - ignore and just return empty map */ }
return data;
}
// Helper to formatting
static void PrintTable(List<PortInfo> ports)
{
if (ports.Count == 0)
{
Console.WriteLine("No Serial Ports found.");
return;
}
// Headers
string hFormat = "{0,-10} | {1,-15} | {2,-40}";
Console.WriteLine(hFormat, "PORT", "STATUS", "DESCRIPTION");
Console.WriteLine(new String('-', 75));
foreach (var p in ports)
{
// Colorize based on status
ConsoleColor original = Console.ForegroundColor;
if (p.Status.StartsWith("BUSY")) Console.ForegroundColor = ConsoleColor.Red;
else if (p.Status == "Available") Console.ForegroundColor = ConsoleColor.Green;
else Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(hFormat, p.Name, p.Status, Truncate(p.Description, 40));
Console.ForegroundColor = original;
}
Console.WriteLine();
}
static int ExtractNumber(string name)
{
Match m = Regex.Match(name, @"\d+");
return m.Success ? int.Parse(m.Value) : 0;
}
static string Truncate(string val, int max)
{
if (string.IsNullOrEmpty(val)) return "";
return val.Length <= max ? val : val.Substring(0, max - 3) + "...";
}
}
}