I know there are a number of similar questions in stackoverflow such as the followings:
- What's a good way to uniquely identify a computer?
- What is a good unique PC identifier?
- Unique computer id C#
- WIN32_Processor::Is ProcessorId Unique for all computers
- How to uniquely identify computer using C#?
... and dozens more and I have studied them all.
The problem is that some of the accepted answers have suggested MAC address as an unique identifier which is entirely incorrect. Some other answers have suggested to use a combination of various components which seems more logical. However, in case of using a combination it should be considered which component is naturally unlikely to be changed frequently. A few days ago we developed a key generator for a software licensing issue where we used the combination of CPUID and MAC to identify a windows pc uniquely and till practical testing we thought our approach was good enough. Ironically when we went testing it we found three computers returning the same id with our key generator!
So, is there really any way to uniquely identify any computer at all? Right now we just need to make our key generator to work on windows pc. Some way (if possible at all) using c# would be great as our system is developed on .net.
Update:
Sorry for creating some confusions and an apparently false alarm. We found out some incorrectness in our method of retrieving HW info. Primarily I thought of deleting this question as now my own confusion has gone and I do believe that a combination of two or more components is good enough to identify a computer. However, then I decided to keep it because I think I should clarify what was causing the problem as the same thing might hurt some other guy in future.
This is what we were doing (excluding other codes):
We were using a getManagementInfo
function to retrieve MAC and Processor ID
private String getManagementInfo(String StrKey_String, String strIndex)
{
String strHwInfo = null;
try
{
ManagementObjectSearcher searcher = new ManagementObjectSearcher("select * from " + StrKey_String);
foreach (ManagementObject share in searcher.Get())
{
strHwInfo += share[strIndex];
}
}
catch (Exception ex)
{
// show some error message
}
return strHwInfo;
}
Then where needed we used that function to retrieve MAC Address
string strMAC = getManagementInfo("Win32_NetworkAdapterConfiguration", "MacAddress");
and to retrieve ProcessorID
string strProcessorId = getManagementInfo("Win32_Processor", "ProcessorId");
At this point, strMAC
would contain more than one MAC address if there are more than one. To take only one we just took the first 17 characters (12 MAC digits and 5 colons in between).
strMAC = strMAC.Length > 17 ? strMAC.Remove(17) : strMAC;
This is where we made the mistake. Because getManagementInfo("Win32_NetworkAdapterConfiguration", "MacAddress")
was returning a number of extra MAC addresses that were really in use. For example, when we searched for MAC addresses in the command prompt by getmac
command then it showed one or two MAC addresses for each pc which were all different. But getManagementInfo("Win32_NetworkAdapterConfiguration", "MacAddress")
returned four to five MAC addresses some of which were identical for all computers. As we just took the first MAC address that our function returned instead of checking anything else, the identical MAC addresses were taken in strMAC
incidently.
The following code by Sowkot Osman does the trick by returning only the first active/ enabled MAC address:
private static string macId()
{
return identifier("Win32_NetworkAdapterConfiguration", "MACAddress", "IPEnabled");
}
private static string identifier(string wmiClass, string wmiProperty, string wmiMustBeTrue)
{
string result = "";
System.Management.ManagementClass mc = new System.Management.ManagementClass(wmiClass);
System.Management.ManagementObjectCollection moc = mc.GetInstances();
foreach (System.Management.ManagementObject mo in moc)
{
if (mo[wmiMustBeTrue].ToString() == "True")
{
//Only get the first one
if (result == "")
{
try
{
result = mo[wmiProperty].ToString();
break;
}
catch
{
}
}
}
}
return result;
}
//Return a hardware identifier
private static string identifier(string wmiClass, string wmiProperty)
{
string result = "";
System.Management.ManagementClass mc = new System.Management.ManagementClass(wmiClass);
System.Management.ManagementObjectCollection moc = mc.GetInstances();
foreach (System.Management.ManagementObject mo in moc)
{
//Only get the first one
if (result == "")
{
try
{
result = mo[wmiProperty].ToString();
break;
}
catch
{
}
}
}
return result;
}
However, I was absolutely right about the identical Processor ID issue. All three returned the same Processor ID when we put wmic cpu get ProcessorId
command in their command prompts.
Now we have decided to use Motherboard serial number instead of Processor ID to make a combination with MAC address. I think our purpose will be served with this way and if it doesn't in some cases then we should let it go in those few cases.