anzeigename = Der Name, der im "Geräte-Manager" unter "Anschlüsse (COM & LPT)" angezeigt wird.
EDIT: zwei Lösungen zur Verfügung gestellt unten. Eines mit WMI und eines mit SetupAPI.
Versuchen Sie, eine WMI-Abfrage für die Klasse Win32 SerialPort auszuführen. Laden Sie WmiCodeCreator herunter, um den C # -Code zu testen und automatisch zu generieren.
Code für den heutigen Abend zum Vergnügen für alle:
public class SetupDiWrap
{
static public string ComPortNameFromFriendlyNamePrefix(string friendlyNamePrefix)
{
const string className = "Ports";
Guid[] guids = GetClassGUIDs(className);
System.Text.RegularExpressions.Regex friendlyNameToComPort =
new System.Text.RegularExpressions.Regex(@".? \((COM\d+)\)$"); // "..... (COMxxx)" -> COMxxxx
foreach (Guid guid in guids)
{
// We start at the "root" of the device tree and look for all
// devices that match the interface GUID of a disk
Guid guidClone = guid;
IntPtr h = SetupDiGetClassDevs(ref guidClone, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_PROFILE);
if (h.ToInt32() != INVALID_HANDLE_VALUE)
{
int nDevice = 0;
while (true)
{
SP_DEVINFO_DATA da = new SP_DEVINFO_DATA();
da.cbSize = (uint)Marshal.SizeOf(da);
if (0 == SetupDiEnumDeviceInfo(h, nDevice++, ref da))
break;
uint RegType;
byte[] ptrBuf = new byte[BUFFER_SIZE];
uint RequiredSize;
if (SetupDiGetDeviceRegistryProperty(h, ref da,
(uint)SPDRP.FRIENDLYNAME, out RegType, ptrBuf,
BUFFER_SIZE, out RequiredSize))
{
const int utf16terminatorSize_bytes = 2;
string friendlyName = System.Text.UnicodeEncoding.Unicode.GetString(ptrBuf, 0, (int)RequiredSize - utf16terminatorSize_bytes);
if (!friendlyName.StartsWith(friendlyNamePrefix))
continue;
if (!friendlyNameToComPort.IsMatch(friendlyName))
continue;
return friendlyNameToComPort.Match(friendlyName).Groups[1].Value;
}
} // devices
SetupDiDestroyDeviceInfoList(h);
}
} // class guids
return null;
}
/// <summary>
/// The SP_DEVINFO_DATA structure defines a device instance that is a member of a device information set.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct SP_DEVINFO_DATA
{
/// <summary>Size of the structure, in bytes.</summary>
public uint cbSize;
/// <summary>GUID of the device interface class.</summary>
public Guid ClassGuid;
/// <summary>Handle to this device instance.</summary>
public uint DevInst;
/// <summary>Reserved; do not use.</summary>
public uint Reserved;
}
[StructLayout(LayoutKind.Sequential)]
private struct SP_DEVICE_INTERFACE_DATA
{
public Int32 cbSize;
public Guid interfaceClassGuid;
public Int32 flags;
private UIntPtr reserved;
}
const int BUFFER_SIZE = 1024;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct SP_DEVICE_INTERFACE_DETAIL_DATA
{
public int cbSize;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = BUFFER_SIZE)]
public string DevicePath;
}
private enum SPDRP
{
DEVICEDESC = 0x00000000,
HARDWAREID = 0x00000001,
COMPATIBLEIDS = 0x00000002,
NTDEVICEPATHS = 0x00000003,
SERVICE = 0x00000004,
CONFIGURATION = 0x00000005,
CONFIGURATIONVECTOR = 0x00000006,
CLASS = 0x00000007,
CLASSGUID = 0x00000008,
DRIVER = 0x00000009,
CONFIGFLAGS = 0x0000000A,
MFG = 0x0000000B,
FRIENDLYNAME = 0x0000000C,
LOCATION_INFORMATION = 0x0000000D,
PHYSICAL_DEVICE_OBJECT_NAME = 0x0000000E,
CAPABILITIES = 0x0000000F,
UI_NUMBER = 0x00000010,
UPPERFILTERS = 0x00000011,
LOWERFILTERS = 0x00000012,
MAXIMUM_PROPERTY = 0x00000013,
}
[DllImport("setupapi.dll", SetLastError = true)]
static extern bool SetupDiClassGuidsFromName(string ClassName,
ref Guid ClassGuidArray1stItem, UInt32 ClassGuidArraySize,
out UInt32 RequiredSize);
[DllImport("setupapi.dll")]
internal static extern IntPtr SetupDiGetClassDevsEx(IntPtr ClassGuid,
[MarshalAs(UnmanagedType.LPStr)]String enumerator,
IntPtr hwndParent, Int32 Flags, IntPtr DeviceInfoSet,
[MarshalAs(UnmanagedType.LPStr)]String MachineName, IntPtr Reserved);
[DllImport("setupapi.dll")]
internal static extern Int32 SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);
[DllImport(@"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern Boolean SetupDiEnumDeviceInterfaces(
IntPtr hDevInfo,
IntPtr optionalCrap, //ref SP_DEVINFO_DATA devInfo,
ref Guid interfaceClassGuid,
UInt32 memberIndex,
ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData
);
[DllImport("setupapi.dll")]
private static extern Int32 SetupDiEnumDeviceInfo(IntPtr DeviceInfoSet,
Int32 MemberIndex, ref SP_DEVINFO_DATA DeviceInterfaceData);
[DllImport("setupapi.dll")]
private static extern Int32 SetupDiClassNameFromGuid(ref Guid ClassGuid,
StringBuilder className, Int32 ClassNameSize, ref Int32 RequiredSize);
[DllImport("setupapi.dll")]
private static extern Int32 SetupDiGetClassDescription(ref Guid ClassGuid,
StringBuilder classDescription, Int32 ClassDescriptionSize, ref Int32 RequiredSize);
[DllImport("setupapi.dll")]
private static extern Int32 SetupDiGetDeviceInstanceId(IntPtr DeviceInfoSet,
ref SP_DEVINFO_DATA DeviceInfoData,
StringBuilder DeviceInstanceId, Int32 DeviceInstanceIdSize, ref Int32 RequiredSize);
[DllImport("setupapi.dll", CharSet = CharSet.Auto)]
static extern IntPtr SetupDiGetClassDevs( // 1st form using a ClassGUID only, with null Enumerator
ref Guid ClassGuid,
IntPtr Enumerator,
IntPtr hwndParent,
int Flags
);
[DllImport(@"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern Boolean SetupDiGetDeviceInterfaceDetail(
IntPtr hDevInfo,
ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData,
ref SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData,
UInt32 deviceInterfaceDetailDataSize,
out UInt32 requiredSize,
ref SP_DEVINFO_DATA deviceInfoData
);
/// <summary>
/// The SetupDiGetDeviceRegistryProperty function retrieves the specified device property.
/// This handle is typically returned by the SetupDiGetClassDevs or SetupDiGetClassDevsEx function.
/// </summary>
/// <param Name="DeviceInfoSet">Handle to the device information set that contains the interface and its underlying device.</param>
/// <param Name="DeviceInfoData">Pointer to an SP_DEVINFO_DATA structure that defines the device instance.</param>
/// <param Name="Property">Device property to be retrieved. SEE MSDN</param>
/// <param Name="PropertyRegDataType">Pointer to a variable that receives the registry data Type. This parameter can be NULL.</param>
/// <param Name="PropertyBuffer">Pointer to a buffer that receives the requested device property.</param>
/// <param Name="PropertyBufferSize">Size of the buffer, in bytes.</param>
/// <param Name="RequiredSize">Pointer to a variable that receives the required buffer size, in bytes. This parameter can be NULL.</param>
/// <returns>If the function succeeds, the return value is nonzero.</returns>
[DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool SetupDiGetDeviceRegistryProperty(
IntPtr DeviceInfoSet,
ref SP_DEVINFO_DATA DeviceInfoData,
uint Property,
out UInt32 PropertyRegDataType,
byte[] PropertyBuffer,
uint PropertyBufferSize,
out UInt32 RequiredSize);
const int DIGCF_DEFAULT = 0x1;
const int DIGCF_PRESENT = 0x2;
const int DIGCF_ALLCLASSES = 0x4;
const int DIGCF_PROFILE = 0x8;
const int DIGCF_DEVICEINTERFACE = 0x10;
const int INVALID_HANDLE_VALUE = -1;
private static Guid[] GetClassGUIDs(string className)
{
UInt32 requiredSize = 0;
Guid[] guidArray = new Guid[1];
bool status = SetupDiClassGuidsFromName(className, ref guidArray[0], 1, out requiredSize);
if (true == status)
{
if (1 < requiredSize)
{
guidArray = new Guid[requiredSize];
SetupDiClassGuidsFromName(className, ref guidArray[0], requiredSize, out requiredSize);
}
}
else
throw new System.ComponentModel.Win32Exception();
return guidArray;
}
}
Der Code dieses Artikels hat die Aufgabe für mich erledigt (er verlinkt auf diesen Beitrag, scheint aber selbst keine Antwort darauf gegeben zu haben). Hier ist der Code des Autors:
using System.Management;
internal class ProcessConnection {
public static ConnectionOptions ProcessConnectionOptions()
{
ConnectionOptions options = new ConnectionOptions();
options.Impersonation = ImpersonationLevel.Impersonate;
options.Authentication = AuthenticationLevel.Default;
options.EnablePrivileges = true;
return options;
}
public static ManagementScope ConnectionScope(string machineName, ConnectionOptions options, string path)
{
ManagementScope connectScope = new ManagementScope();
connectScope.Path = new ManagementPath(@"\\" + machineName + path);
connectScope.Options = options;
connectScope.Connect();
return connectScope;
}
}
public class COMPortInfo
{
public string Name { get; set; }
public string Description { get; set; }
public COMPortInfo() { }
public static List<COMPortInfo> GetCOMPortsInfo()
{
List<COMPortInfo> comPortInfoList = new List<COMPortInfo>();
ConnectionOptions options = ProcessConnection.ProcessConnectionOptions();
ManagementScope connectionScope = ProcessConnection.ConnectionScope(Environment.MachineName, options, @"\root\CIMV2");
ObjectQuery objectQuery = new ObjectQuery("SELECT * FROM Win32_PnPEntity WHERE ConfigManagerErrorCode = 0");
ManagementObjectSearcher comPortSearcher = new ManagementObjectSearcher(connectionScope, objectQuery);
using (comPortSearcher)
{
string caption = null;
foreach (ManagementObject obj in comPortSearcher.Get())
{
if (obj != null)
{
object captionObj = obj["Caption"];
if (captionObj != null)
{
caption = captionObj.ToString();
if (caption.Contains("(COM"))
{
COMPortInfo comPortInfo = new COMPortInfo();
comPortInfo.Name = caption.Substring(caption.LastIndexOf("(COM")).Replace("(", string.Empty).Replace(")",
string.Empty);
comPortInfo.Description = caption;
comPortInfoList.Add(comPortInfo);
}
}
}
}
}
return comPortInfoList;
}
}
Verwendungszweck:
foreach (COMPortInfo comPort in COMPortInfo.GetCOMPortsInfo())
{
Console.WriteLine(string.Format("{0} – {1}", comPort.Name, comPort.Description));
}
Ich weiß, dass dies in C # veröffentlicht wurde, aber ich bin sicher, dass dies leicht konvertiert werden kann ...
Public Function foo() As Integer
Try
Dim searcher As New ManagementObjectSearcher( _
"root\CIMV2", _
"SELECT * FROM Win32_SerialPort")
For Each queryObj As ManagementObject In searcher.Get()
Debug.WriteLine(queryObj("Caption"))
Debug.WriteLine(queryObj("Description"))
Debug.WriteLine(queryObj("DeviceID"))
Debug.WriteLine(queryObj("Name"))
Debug.WriteLine(queryObj("PNPDeviceID"))
Next
Catch err As ManagementException
Stop
End Try
End Function
Public Function bar() As Integer
Try
Dim searcher As New ManagementObjectSearcher( _
"root\CIMV2", _
"SELECT * FROM Win32_PnPEntity WHERE ConfigManagerErrorCode = 0")
For Each queryObj As ManagementObject In searcher.Get()
If queryObj("Caption").ToString.Contains("(COM") Then
Debug.WriteLine(queryObj("Caption"))
Debug.WriteLine(queryObj("Description"))
Debug.WriteLine(queryObj("DeviceID"))
Debug.WriteLine(queryObj("Name"))
Debug.WriteLine(queryObj("PNPDeviceID"))
End If
Next
Catch err As ManagementException
Stop
End Try
End Function
Es findet alle meine Anschlüsse, Modem, seriell, USB und Bluetooth.
Pavel's Klasse SetupDiWrap
ist großartig und braucht nur ein paar kleine Änderungen für Windows 7.
Hoffentlich hilft dieses Update anderen Leuten, die (wie ich) zu kämpfen haben, um COM-Port-Nummern von VCP-Namen in Windows 7 zu erhalten.
1) Der SP_DEVINFO_DATA
hat sich in Windows 7 geändert, die Gesamtlänge beträgt nicht mehr 28 Byte, sondern 32 Byte. Das funktioniert für mich:
private struct SP_DEVINFO_DATA
{
/// <summary>Size of the structure, in bytes.</summary>
public int cbSize;
/// <summary>GUID of the device interface class.</summary>
public Guid ClassGuid;
/// <summary>Handle to this device instance.</summary>
public int DevInst;
/// <summary>Reserved; do not use.</summary>
public ulong Reserved;
}
Beachten Sie ulong für Reserved statt eines Int. Wenn Sie uint cbSize
in int cbSize
ändern, haben Sie mir später einen Cast gerettet, ansonsten können Sie ihn als uint belassen.
2) Ich schrieb auch die Zeile:
da.cbSize = (uint)Marshal.SizeOf(da);
ein bisschen anders, um die cbSize auf 32 Bit zu bringen:
da.cbSize = Marshal.SizeOf(typeof(SP_DEVINFO_DATA));
3) Ich habe mich verändert
[DllImport("setupapi.dll", CharSet = CharSet.Auto)]
static extern IntPtr SetupDiGetClassDevs(
ref Guid ClassGuid,
IntPtr Enumerator,
IntPtr hwndParent,
int Flags
);
zu
[DllImport("setupapi.dll", CharSet = CharSet.Auto)]
private static extern IntPtr SetupDiGetClassDevs(
ref Guid ClassGuid,
UInt32 Enumerator,
IntPtr hwndParent,
UInt32 Flags
);
Der Enumerator ist keine IntPtr
mehr, daher müssen Sie SetupDiGetClassDevs
folgendermaßen aufrufen:
IntPtr h = SetupDiGetClassDevs(ref guidClone, 0, IntPtr.Zero, DIGCF_PRESENT | DIGCF_PROFILE);
Beachten Sie "0" anstelle von IntPtr.Zero
, wenn Sie den Enumerator übergeben.
Der Code läuft jetzt wie ein Zauber in Windows 7!
Wenn Sie speziell USB-Geräte und keine andere Art von COM-Port verwenden, verfügt die plattformübergreifende USB-Bibliothek libusbp über ein Beispiel , das Sie ausführen können, um anzuzeigen, wie der Name des COM-Ports anhand des USB-Anschlusses ermittelt wird Produkt-ID und Hersteller-ID des COM-Ports.
Dies ist eine andere, aber wahrscheinlich bessere Wahl als der Versuch, den Anzeigenamen im Geräte-Manager zu verwenden. Vielleicht könnte libusbp erweitert werden, um auf den Anzeigenamen zugreifen zu können, wenn Sie das wirklich wollen.
Möglicherweise möchten Sie auch die Verwendung der Registrierung in Betracht ziehen, da ich WMI ziemlich langsam gefunden habe (5 oder 6 Sekunden).
In meinem Fall wollte ich den COM-Anschlussnamen eines Geräts mit einem bekannten Anzeigenamen identifizieren. Mit regedit habe ich die Registrierung nach dem Anzeigenamen in einem Schlüssel durchsucht, der auch den COM-Port enthielt. Da es sich bei dem Schlüssel, den ich fand, um eine Art zufällige ID zusammen mit 10 anderen Personen handelte, stieg ich einige Stufen hinauf, um einen Schlüssel zu finden, der für die Suche darin geeignet war.
Der Code, den ich mir ausgedacht habe, lautet wie folgt:
Dim searchFriendlyName = "Your Device Name".ToLower
Dim k0 = Registry.LocalMachine.OpenSubKey("SYSTEM\CurrentControlSet\Enum\USB\", False)
For Each k1Name In k0.GetSubKeyNames
Dim k1 = k0.OpenSubKey(k1Name, False)
For Each k2name In k1.GetSubKeyNames
Dim k2 = k1.OpenSubKey(k2name, False)
If k2.GetValueNames.Contains("FriendlyName") AndAlso k2.GetValue("FriendlyName").ToString.ToLower.Contains(searchFriendlyName) Then
If k2.GetSubKeyNames.Contains("Device Parameters") Then
Dim k3 = k2.OpenSubKey("Device Parameters", False)
If k3.GetValueNames.Contains("PortName") Then
For Each s In SerialPort.GetPortNames
If k3.GetValue("PortName").ToString.ToLower = s.ToLower Then
Return s
End If
Next
End If
End If
End If
Next
Next
Dies muss natürlich geändert werden, je nachdem, wie und wo Ihr Gerät in der Registrierung angezeigt wird. Wenn Sie jedoch versuchen, den Com-Port einer bestimmten Art von Gerät automatisch zu erkennen, sollten Sie dies in der Lage sein.
Denken Sie daran, wenn Sie rekursiv viele Schlüssel suchen müssen. Dadurch wird die oben genannte Lösung verlangsamt. Versuchen Sie daher, den richtigen Ort in der Registrierung zu suchen.
Ich habe auch WMI-Code nach der Registersuche eingefügt, falls die Registersuche leer war:
Dim mg As New System.Management.ManagementClass("Win32_SerialPort")
Try
For Each i In mg.GetInstances()
Dim name = i.GetPropertyValue("Name")
If name IsNot Nothing AndAlso name.ToString.ToLower.Contains(searchFriendlyName.ToLower) Then
Return i.GetPropertyValue("DeviceId").ToString
End If
Next
Finally
mg.Dispose()
End Try