While working on a program I recently found that hostnames in .net (or at least in the ping class) are not supposed to have more than 126 characters. The ping class throws an exception if a hostname is longer.
However Wikipedia states that up to 255 characters are allowed.
And it looks that indeed there are machines with a hostname longer than 126 chars out there, so the question is: can this limit be changed, who is right and how to resolve names if it cannot?
The .NET Dns class has a hard upper limit of 126 characters for hostnames (checked for .NET4).
However, you can use the lower-level Win32 DnsQuery method using P/Invoke to translate host names into IP addresses and then use those raw addresses with the .NET networking classes.
Here is a sample DnsAddr class using this approach:
public static class DnsAddr
{
[DllImport("dnsapi", EntryPoint = "DnsQuery_W", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
private static extern int DnsQuery([MarshalAs(UnmanagedType.VBByRefStr)]ref string pszName, QueryTypes wType, QueryOptions options, int aipServers, ref IntPtr ppQueryResults, int pReserved);
[DllImport("dnsapi", CharSet = CharSet.Auto, SetLastError = true)]
private static extern void DnsRecordListFree(IntPtr pRecordList, int FreeType);
public static IEnumerable<IPAddress> GetAddress(string domain)
{
IntPtr ptr1 = IntPtr.Zero;
IntPtr ptr2 = IntPtr.Zero;
List<IPAddress> list = new List<IPAddress>();
DnsRecord record = new DnsRecord();
int num1 = DnsAddr.DnsQuery(ref domain, QueryTypes.DNS_TYPE_A, QueryOptions.DNS_QUERY_NONE, 0, ref ptr1, 0);
if (num1 != 0)
throw new Win32Exception(num1);
for (ptr2 = ptr1; !ptr2.Equals(IntPtr.Zero); ptr2 = record.pNext)
{
record = (DnsRecord)Marshal.PtrToStructure(ptr2, typeof(DnsRecord));
list.Add(new IPAddress(record.ipAddress));
}
DnsAddr.DnsRecordListFree(ptr1, 0);
return list;
}
private enum QueryOptions
{
DNS_QUERY_NONE = 0,
}
private enum QueryTypes
{
DNS_TYPE_A = 1,
}
[StructLayout(LayoutKind.Sequential)]
private struct DnsRecord
{
public IntPtr pNext;
public string pName;
public short wType;
public short wDataLength;
public int flags;
public int dwTtl;
public int dwReserved;
public uint ipAddress;
}
}
Here is a sample test program:
class Program
{
static void Main(string[] args)
{
var addresses = DnsAddr.GetAddress("google.com");
foreach (var address in addresses)
Console.WriteLine(address.ToString());
}
}
which on my machine produces this output:
173.194.33.51
173.194.33.50
173.194.33.49
173.194.33.52
173.194.33.48
Call gethostbyname, then pass an IP address (which is never more than a couple dozen characters, even for IPv6) to the ping class.
Both informations are right.
The 255 character limit refers to the entire hostname (e.g. some.thing.example.com).
In turn, each label (e.g. example or com) is limited to 63 characters. So top-level domains have a theoretical limit of 126 non-dot characters.
Apparently it is like Joel and Dennis explained. .Net is not capable of resolving names longer than 126 chars.
However if somebody has the same problem, take a look here at DNS Plus:
http://www.simpledns.com/dns-client-lib.aspx#download
Related
from within my C#-application I use functions of an external DLL. This DLL itself uses network functions. So it is necessary to initialise Winsock out of my C#-application to let the network sockets work for this DLL. That's how I try to execute WSAStartup for initialisation, but it does not seem to work:
class Program
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct WSAData
{
internal Int16 version;
internal Int16 highVersion;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)]
internal String description;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 129)]
internal String systemStatus;
internal Int16 maxSockets;
internal Int16 maxUdpDg;
internal IntPtr vendorInfo;
}
[DllImport("ws2_32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern Int32 WSAStartup(Int16 wVersionRequested, out WSAData wsaData);
static void Main(string[] args)
{
WSAData wsaData;
wsaData.version = 0;
wsaData.highVersion = 0;
WSAStartup(0,out wsaData); // to initialise windows socket
... // calling external DLL functions
}
}
WSAStartup() seems to be called successfully but the used DLL is still not able to access network. So it seems WSAStartup() did not work for some reason. Any ideas what I'm doing wrong here?
Kind regards
Michael
You must pass 2 << 8 | 2 as the fist parameter (it is the version requested from the WSA)
Note that there is a small bug in the signature Microsoft produced (see Is the .NET use of WSAStartup safe for 64-bit apps?), but it isn't a problem, so you can ignore it.
You should always check the return values of the API functions you call:
int resp = WSAStartup(2 << 8 | 2, out wsaData); // to initialise windows socket
if (resp != 0)
{
// Error
}
Technically the check suggested by Microsoft in https://msdn.microsoft.com/library/windows/desktop/ms742213.aspx is even more complex, because they do:
if (resp != 0 || wsaData.version != (2 << 8 | 2))
{
// Error
}
I want to get user-friendly names of sound inputs with this code, but it can give me only first 32 chars of name, but I want it whole.
[DllImport("winmm.dll", SetLastError = true)]
static extern uint waveInGetNumDevs();
[DllImport("winmm.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern uint waveInGetDevCaps(uint hwo, ref WAVEOUTCAPS pwoc, uint cbwoc);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct WAVEOUTCAPS
{
public ushort wMid;
public ushort wPid;
public uint vDriverVersion;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string szPname;
public uint dwFormats;
public ushort wChannels;
public ushort wReserved1;
public uint dwSupport;
}
public static string[] GetSoundDevices()
{
uint devices = waveInGetNumDevs();
string[] result = new string[devices];
WAVEOUTCAPS caps = new WAVEOUTCAPS();
using (StreamWriter sw = new StreamWriter("appdata/audio/name"))
{
for (uint i = 0; i < devices; i++)
{
waveInGetDevCaps(i, ref caps, (uint)Marshal.SizeOf(caps));
result[i] = caps.szPname;
sw.WriteLine(caps.szPname);
}
return result;
}
}
I need this names of sound inputs:
but this code give me only this:
Thank you guys!
You may have to use the management interface:
ManagementObjectSearcher objSearcher = new ManagementObjectSearcher(
"SELECT * FROM Win32_SoundDevice");
ManagementObjectCollection objCollection = objSearcher.Get();
foreach (ManagementObject obj in objCollection)
{
foreach (PropertyData property in obj.Properties)
{
Console.Out.WriteLine(String.Format("{0}:{1}", property.Name, property.Value));
}
}
Source: How to enumerate audio out devices in c#
You can use WASAPI to get full name of sound devices. It's only limit is that it is not available in Windows XP or older OS.
You need to use IMMDeviceEnumerator::EnumAudioEndpoints method to achieve the goal, but since the library is COM, you need to wrap it to be able to use it in C#.
A sample project is available on CodeProject.
I also couldn't find a way to get full name using WinMM library and finally end up writing a C++ wrapper around WASAPI and used that wrapper in C# through pinvoke!
Why screenshots in foreign and totally not understandable language?
There you go. English screenshot 1
You can copy it, that is what the second one means.
The number stands for the usb bus that is used.
When changing the plug keep that in mind.
At work we make our own tablets. Some of the tablets have fingerprint biometrics, some don't. Sometimes a tech forgets to plug it in. I have yet to find a way to check if that device (or any for that matter) is present.
My first approach was to use the GUID for a biometric which is {53D29EF7-377C-4D14-864B-EB3A85769359}. I would search in the registry at hklm\system\currontcontrolset\control\class and check to see if that key is present.
That doesn't work because it seems that Windows 7 has that key present even if you've never had a biometric installed. It worked in XP, but I just tried again on a unit that used to have a biometric but I took it out and that key is still present.
The hardest part about this problem is that I have to work with Windows 7, 7 embedded, xp, and xp embedded.
Next idea was to use WMI, but I couldn't find the correct class to call to check if it is present.
I then found a biometric.dll but that only works in Windows 7.
Sometimes finding a common solution to a problem is not always easy. I'm doing this project in C# but iI'm willing to convert it to any language.
Any ideas on were I should start looking?
With the help of Joshua Drake who gave me an awesome link on how to solve my problem, those are my results:
The code that I am fixing to post is kind of specialized in that it looks for a specific GUID and only looks for the first one. I adapted it from the article about how to disable a device, although this code does not disable anything it merely checks for presence.
public static bool IsDevicePresent(string guid)
{
var info = IntPtr.Zero;
var NullGuid = new Guid(guid);
try
{
info = SetupDiGetClassDevsW(ref NullGuid,null,IntPtr.Zero,DIGCF_PRESENT);
CheckError("SetupDiGetClassDevs");
var devdata = new SP_DEVINFO_DATA();
devdata.cbSize = (UInt32)Marshal.SizeOf(devdata);
// Get first device matching device criterion.
SetupDiEnumDeviceInfo(info,0,out devdata);
// if no items match filter, throw
if (Marshal.GetLastWin32Error() == ERROR_NO_MORE_ITEMS)
CheckError("No device found matching filter.", 0xcffff);
CheckError("SetupDiEnumDeviceInfo");
}
catch
{
return false;
}
finally
{
if (info != IntPtr.Zero)
SetupDiDestroyDeviceInfoList(info);
}
return true;
}
private static void CheckError(string message, int lasterror = -1)
{
int code = lasterror == -1 ? Marshal.GetLastWin32Error() : lasterror;
if (code != 0)
throw new ApplicationException(String.Format("Error disabling hardware device (Code {0}): {1}",code, message));
}
[DllImport("setupapi.dll", SetLastError = true)]
private static extern IntPtr SetupDiGetClassDevsW([In] ref Guid ClassGuid,[MarshalAs(UnmanagedType.LPWStr)]string Enumerator,IntPtr parent,UInt32 flags);
[DllImport("setupapi.dll", SetLastError = true)]
private static extern bool SetupDiDestroyDeviceInfoList(IntPtr handle);
[DllImport("setupapi.dll", SetLastError = true)]
private static extern bool SetupDiEnumDeviceInfo(IntPtr deviceInfoSet,UInt32 memberIndex,[Out] out SP_DEVINFO_DATA deviceInfoData);
//used to find device info from device manager
[StructLayout(LayoutKind.Sequential)]
private struct SP_DEVINFO_DATA
{
public UInt32 cbSize;
public Guid classGuid;
public UInt32 devInst;
public IntPtr reserved;
}
private const uint DIGCF_PRESENT = 2;
private const uint ERROR_INVALID_DATA = 13;
private const uint ERROR_NO_MORE_ITEMS = 259;
private const uint ERROR_ELEMENT_NOT_FOUND = 1168;
And here is a simple unit test to prove it works for first device
[Test]
public void TestDevicePresent()
{
var bluetoothClassGuid = "e0cbf06c-cd8b-4647-bb8a-263b43f0f974";
var biometricClassGuid = "53D29EF7-377C-4D14-864B-EB3A85769359";
var cdromdrivClassGiud = "4d36e965-e325-11ce-bfc1-08002be10318";
Assert.False(Native.IsDevicePresent(bluetoothClassGuid));
Assert.False(Native.IsDevicePresent(biometricClassGuid));
Assert.True(Native.IsDevicePresent(cdromdrivClassGiud));
}
I'm trying to retrieve the privileges and their current state associated with a token in C# but i can't figure out how to adjust the size of the LUID_AND_ATTRIBUTES array that is returned to fit the actual number of elements.
From MSDN
When MarshalAsAttribute.Value is set to ByValArray, the SizeConst must be set to indicate the number of elements in the array.
I was able to watch the TOKEN_PRIVILEGES.PrivilegeCount property after the call to GetTokenInformation and see that the token I was working with had 24 of the 35 privileges listed on the Privilege Constants reference page. Changing SizeConst = 24 would then give me the ability to see all of them instead of just the first one
(I had initially set SizeConst = 1 following a usage example from PInvoke)
Is there a way to specify the depth of the incoming array as it is being created or will I need to know how many privileges there are going to be before writing the code?
Code Snippet
[DllImport("advapi32.dll", SetLastError = true)]
protected static extern bool GetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, IntPtr TokenInformation, int TokenInformationLength, ref int ReturnLength);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)][return: MarshalAs(UnmanagedType.Bool)]
protected static extern bool LookupPrivilegeName(string lpSystemName, IntPtr lpLuid,System.Text.StringBuilder lpName, ref int cchName);
protected struct TOKEN_PRIVILEGES {
public UInt32 PrivilegeCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public LUID_AND_ATTRIBUTES[] Privileges;
}//TOKEN_PRIVILEGES
[StructLayout(LayoutKind.Sequential)]
protected struct LUID_AND_ATTRIBUTES {
public LUID Luid;
public UInt32 Attributes;
}//LUID_AND_ATTRIBUTES
[StructLayout(LayoutKind.Sequential)]
protected struct LUID {
public uint LowPart;
public int HighPart;
}//LUID
int TokenInfLength = 0;
IntPtr ThisHandle = WindowsIdentity.GetCurrent().Token;
GetTokenInformation(ThisHandle, TOKEN_INFORMATION_CLASS.TokenPrivileges, IntPtr.Zero, TokenInfLength, ref TokenInfLength); //Get the TokenInformation length (returns false)
IntPtr TokenInformation = Marshal.AllocHGlobal(TokenInfLength);
if(GetTokenInformation(WindowsIdentity.GetCurrent().Token, TOKEN_INFORMATION_CLASS.TokenPrivileges, TokenInformation, TokenInfLength, ref TokenInfLength)){
TOKEN_PRIVILEGES ThisPrivilegeSet = (TOKEN_PRIVILEGES)Marshal.PtrToStructure(TokenInformation, typeof(TOKEN_PRIVILEGES));
//ThisPrivilegeSet now holds all of the LUID's i need to check out
foreach(LUID_AND_ATTRIBUTES laa in ThisPrivilegeSet.Privileges){ //ThisPrivilegeSet.Privileges is only as deep as SizeConst will allow
System.Text.StringBuilder StrBuilder = new System.Text.StringBuilder();
int LuidNameLen = 0;
IntPtr LuidPointer = Marshal.AllocHGlobal(Marshal.SizeOf(laa.Luid));
Marshal.StructureToPtr(laa.Luid, LuidPointer, true);
LookupPrivilegeName(null, LuidPointer, null, ref LuidNameLen); //Get the PrivilageName length (returns false)
StrBuilder.EnsureCapacity(LuidNameLen + 1);
if(LookupPrivilegeName(null, LuidPointer, StrBuilder, ref LuidNameLen)){ //StrBuilder gets the name this time
Console.WriteLine("[{0}] : {1}", laa.Attributes.ToString(), StrBuilder.ToString());
}//end if
Marshal.FreeHGlobal(LuidPointer);
}//next
}//end if
This is my first post, so sorry if I did it wrong and TIA for the help
I wanted to follow up on this question because of what i learned by following the link provided by VVS.
If you're like me and have never learned or used a programming language that has to access memory directly you might find this interesting as well (BTW I've marked SwDevman81's answer as the correct one as it works perfectly well).
In the blog post provided by VVS the writer talks about using the value (memory address) of the pointer returned by a call + the size of the objects being returned to marshall the returned data in chuncks to dynamically retrieve a variable number of objects. In this situation and the one outlined in the blog post we benefit from the first value being returned containing the number of elements in the following array, We also have an integer returned from the function telling us how large the returned structure will be which could also be used to check if we have reached the end of our structure should we not have the benefit of a value telling us the number of elements.
I'm providing the below code example for anyone interested in how the above blog post would be applied to this scenario.
Code Example:
using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
namespace MarshallingExample {
class Program {
protected struct TOKEN_PRIVILEGES {
public UInt32 PrivilegeCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public LUID_AND_ATTRIBUTES[] Privileges;
}
[StructLayout(LayoutKind.Sequential)]
protected struct LUID_AND_ATTRIBUTES {
public LUID Luid;
public UInt32 Attributes;
}
[StructLayout(LayoutKind.Sequential)]
protected struct LUID {
public uint LowPart;
public int HighPart;
}
//This enum was huge, I cut it down to save space
protected enum TOKEN_INFORMATION_CLASS {
/// <summary>
/// The buffer receives a TOKEN_PRIVILEGES structure that contains the privileges of the token.
/// </summary>
TokenPrivileges = 3
}
[DllImport("advapi32.dll", SetLastError = true)]
protected static extern bool GetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, IntPtr TokenInformation, int TokenInformationLength, ref int ReturnLength);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool LookupPrivilegeName(string lpSystemName, IntPtr lpLuid, System.Text.StringBuilder lpName, ref int cchName);
static void Main(string[] args) {
//Check and print the privileges this token has
CheckPrivileges(WindowsIdentity.GetCurrent().Token);
}
//test function to output privileges for an account
private static bool CheckPrivileges(IntPtr thisHandle) {
int iTokenInfLength = 0; //holds the length of the TOKEN_PRIVILEGES structure that will be returned by GetTokenInformation
//First call to GetTokenInformation returns the length of the structure that will be returned on the next call
GetTokenInformation(thisHandle, TOKEN_INFORMATION_CLASS.TokenPrivileges, IntPtr.Zero, iTokenInfLength, ref iTokenInfLength);
//Allocate a block of memory large enough to hold the expected structure
IntPtr ipTokenInformation = Marshal.AllocHGlobal(iTokenInfLength);
//ipTokenInformation holds the starting location readable as an integer
//you can view the memory location using Ctrl-Alt-M,1 or Debug->Windows->Memory->Memory1 in Visual Studio
//and pasting the value of ipTokenInformation into the search box (it's still empty right now though)
if(GetTokenInformation(thisHandle, TOKEN_INFORMATION_CLASS.TokenPrivileges, ipTokenInformation, iTokenInfLength, ref iTokenInfLength)) {
//If GetTokenInformation doesn't return false then the structure should be sitting in the space reserved by ipTokenInformation
//at this point
//What was returned is a structure of type TOKEN_PRIVILEGES which has two values, a UInt32 followed by an array
//of LUID_AND_ATTRIBUTES structures. Because we know what to expect and we know the order to expect it we can section
//off the memory into marshalled structures and do some math to figure out where to start our next marshal
uint uiPrivilegeCount = (uint)Marshal.PtrToStructure(ipTokenInformation, typeof(uint)); //Get the count
//lets create the structure we should have had in the first place
LUID_AND_ATTRIBUTES[] aLuidAa = new LUID_AND_ATTRIBUTES[uiPrivilegeCount]; //initialize an array to the right size
LUID_AND_ATTRIBUTES cLuidAa = new LUID_AND_ATTRIBUTES();
//ipPointer will hold our new location to read from by taking the last pointer plus the size of the last structure read
IntPtr ipPointer = new IntPtr(ipTokenInformation.ToInt32() + sizeof(uint)); //first laa pointer
cLuidAa = (LUID_AND_ATTRIBUTES)Marshal.PtrToStructure(ipPointer, typeof(LUID_AND_ATTRIBUTES)); //Read the memory location
aLuidAa[0] = cLuidAa; //Add it to the array
//After getting our first structure we can loop through the rest since they will all be the same
for(int i = 1; i < uiPrivilegeCount; ++i) {
ipPointer = new IntPtr(ipPointer.ToInt32() + Marshal.SizeOf(cLuidAa)); //Update the starting point in ipPointer
cLuidAa = (LUID_AND_ATTRIBUTES)Marshal.PtrToStructure(ipPointer, typeof(LUID_AND_ATTRIBUTES)); //Read the memory location
aLuidAa[i] = cLuidAa; //Add it to the array
}//next
TOKEN_PRIVILEGES cPrivilegeSet = new TOKEN_PRIVILEGES();
cPrivilegeSet.PrivilegeCount = uiPrivilegeCount;
cPrivilegeSet.Privileges = aLuidAa;
//now we have what we should have had to begin with
Console.WriteLine("Privilege Count: {0}", cPrivilegeSet.PrivilegeCount.ToString());
//This loops through the LUID_AND_ATTRIBUTES array and resolves the LUID names with a
//call to LookupPrivilegeName which requires us to first convert our managed structure into an unmanaged one
//so we get to see what it looks like to do it backwards
foreach(LUID_AND_ATTRIBUTES cLaa in cPrivilegeSet.Privileges) {
System.Text.StringBuilder sb = new System.Text.StringBuilder();
int iLuidNameLen = 0; //Holds the length of structure we will be receiving LookupPrivilagename
IntPtr ipLuid = Marshal.AllocHGlobal(Marshal.SizeOf(cLaa.Luid)); //Allocate a block of memory large enough to hold the structure
Marshal.StructureToPtr(cLaa.Luid, ipLuid, true); //Write the structure into the reserved space in unmanaged memory
LookupPrivilegeName(null, ipLuid, null, ref iLuidNameLen); // call once to get the name length we will be receiving
sb.EnsureCapacity(iLuidNameLen + 1); //Make sure there is enough room for it
if(LookupPrivilegeName(null, ipLuid, sb, ref iLuidNameLen)) { // call again to get the name
Console.WriteLine("[{0}] : {1}", cLaa.Attributes.ToString(), sb.ToString());
}//end if
Marshal.FreeHGlobal(ipLuid); //Free up the reserved space in unmanaged memory (Should be done any time AllocHGlobal is used)
}//next
Marshal.FreeHGlobal(ipTokenInformation); //Free up the reserved space in unmanaged memory (Should be done any time AllocHGlobal is used)
}//end if GetTokenInformation
return true;
}//CheckPrivileges
}//Program
}//MarshallingExample
You will not be able to change SizeConst at runtime, so I think your best bet will be to retrieve as many as possible and only use the ones you need. This way you wouldnt need to change the code later if you require additional information.
So for example, if the maximum number of possible privileges is 35, set the SizeConst to 35. Then change the foreach loop to a for loop and go from i = 0 to ThisPrivilegeSet.PrivilegeCount.
Heres an example (For this, I set the SizeConst to 8000):
public void RunPrivileges()
{
int TokenInfLength = 0;
IntPtr ThisHandle = WindowsIdentity.GetCurrent().Token;
GetTokenInformation(ThisHandle, TOKEN_INFORMATION_CLASS.TokenPrivileges, IntPtr.Zero, TokenInfLength, ref TokenInfLength);
IntPtr TokenInformation = Marshal.AllocHGlobal(TokenInfLength);
if (GetTokenInformation(WindowsIdentity.GetCurrent().Token, TOKEN_INFORMATION_CLASS.TokenPrivileges, TokenInformation, TokenInfLength, ref TokenInfLength))
{
TOKEN_PRIVILEGES ThisPrivilegeSet = (TOKEN_PRIVILEGES)Marshal.PtrToStructure(TokenInformation, typeof(TOKEN_PRIVILEGES));
for (int index = 0; index < ThisPrivilegeSet.PrivilegeCount; index++ )
{
LUID_AND_ATTRIBUTES laa = ThisPrivilegeSet.Privileges[index];
System.Text.StringBuilder StrBuilder = new System.Text.StringBuilder();
int LuidNameLen = 0;
IntPtr LuidPointer = Marshal.AllocHGlobal(Marshal.SizeOf(laa.Luid));
Marshal.StructureToPtr(laa.Luid, LuidPointer, true);
LookupPrivilegeName(null, LuidPointer, null, ref LuidNameLen);
StrBuilder.EnsureCapacity(LuidNameLen + 1);
if (LookupPrivilegeName(null, LuidPointer, StrBuilder, ref LuidNameLen))
{
Console.WriteLine("[{0}] : {1}", laa.Attributes.ToString(), StrBuilder.ToString());
}
Marshal.FreeHGlobal(LuidPointer);
}
}
}
Is there a way to find out all the available baud rates that a particular
system supports via C#? This is available through Device Manager-->Ports
but I want to list these programmatically.
I have found a couple of ways to do this. The following two documents were a starting point
http://support.microsoft.com/default.aspx/kb/99026
http://msdn.microsoft.com/en-us/library/aa363189(VS.85).aspx
The clue is in the following paragraph from the first document
The simplest way to determine what baud rates are available on a particular serial port is to call the GetCommProperties() application programming interface (API) and examine the COMMPROP.dwSettableBaud bitmask to determine what baud rates are supported on that serial port.
At this stage there are two choices to do this in C#:
1.0 Use interop (P/Invoke) as follows:
Define the following data structure
[StructLayout(LayoutKind.Sequential)]
struct COMMPROP
{
short wPacketLength;
short wPacketVersion;
int dwServiceMask;
int dwReserved1;
int dwMaxTxQueue;
int dwMaxRxQueue;
int dwMaxBaud;
int dwProvSubType;
int dwProvCapabilities;
int dwSettableParams;
int dwSettableBaud;
short wSettableData;
short wSettableStopParity;
int dwCurrentTxQueue;
int dwCurrentRxQueue;
int dwProvSpec1;
int dwProvSpec2;
string wcProvChar;
}
Then define the following signatures
[DllImport("kernel32.dll")]
static extern bool GetCommProperties(IntPtr hFile, ref COMMPROP lpCommProp);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr CreateFile(string lpFileName, int dwDesiredAccess,
int dwShareMode, IntPtr securityAttrs, int dwCreationDisposition,
int dwFlagsAndAttributes, IntPtr hTemplateFile);
Now make the following calls (refer to http://msdn.microsoft.com/en-us/library/aa363858(VS.85).aspx)
COMMPROP _commProp = new COMMPROP();
IntPtr hFile = CreateFile(#"\\.\" + portName, 0, 0, IntPtr.Zero, 3, 0x80, IntPtr.Zero);
GetCommProperties(hFile, ref commProp);
Where portName is something like COM?? (COM1, COM2, etc). commProp.dwSettableBaud should now contain the desired information.
2.0 Use C# reflection
Reflection can be used to access the SerialPort BaseStream and thence the required data as follows:
_port = new SerialPort(portName);
_port.Open();
object p = _port.BaseStream.GetType().GetField("commProp", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(_port.BaseStream);
Int32 bv = (Int32)p.GetType().GetField("dwSettableBaud", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).GetValue(p);
Note that in both the methods above the port(s) has to be opened at least once to get this data.
I don't think you can.
I recently had this problem, and ended up hard coding the baud rates I wanted to use.
MSDN simply states, "The baud rate must be supported by the user's serial driver".
dwSettableBaud gives 268894207 int (0x1006ffff)
while dwMaxBaud gives 268435456 int (0x10000000)
Obviously, this doesn't help me.
So this is what I am currently relying upon:
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
public static readonly List<string> SupportedBaudRates = new List<string>
{
"300",
"600",
"1200",
"2400",
"4800",
"9600",
"19200",
"38400",
"57600",
"115200",
"230400",
"460800",
"921600"
};
public static int MaxBaudRate(string portName)
{
var maxBaudRate = 0;
try
{
//SupportedBaudRates has the commonly used baudRate rates in it
//flavor to taste
foreach (var baudRate in ConstantsType.SupportedBaudRates)
{
var intBaud = Convert.ToInt32(baudRate);
using (var port = new SerialPort(portName))
{
port.BaudRate = intBaud;
port.Open();
}
maxBaudRate = intBaud;
}
}
catch
{
//ignored - traps exception generated by
//baudRate rate not supported
}
return maxBaudRate;
}
The baud rates are in strings because they are destined for a combo box.
private void CommPorts_SelectedIndexChanged(object sender, EventArgs e)
{
var combo = sender as ComboBox;
if (combo != null)
{
var port = combo.Items[combo.SelectedIndex].ToString();
var maxBaud = AsyncSerialPortType.MaxBaudRate(port);
var baudRates = ConstantsType.SupportedBaudRates;
var f = (SerialPortOpenFormType)(combo.Parent);
f.Baud.Items.Clear();
f.Baud.Items.AddRange(baudRates.Where(baud => Convert.ToInt32(baud) <= maxBaud).ToArray());
}
}
You can improve on performance if you know the minimum baud rate supported by all of the serial ports you plan to open. For instance, starting with 115,200 seems like a safe lower limit for serial ports manufactured in this century.