I've needed to solve the same issue before.
Done the MAC address thing, that broke horribly as we quickly found several duplicates on just 10k users. We had problems to the day that product died.
Another project needed a similar solution and we tried the NTFS Id for C:\ drive. We threw out that idea and then considered the Win32 machine id, that of course also got thrown out.
Finally we settled onto the following: We created a Guid, encrypted it with the RSA machine key, and stuck it in the Registry.
I really believe that is your best bet for uniqueness even if you then need to incorporate some hardware information as well. So here is something like the code we used to store & fetch:
static class MachineKey
{
const byte[] ENTROPY = null;
const string KEY_PATH = @"HKEY_LOCAL_MACHINE\SOFTWARE\company-name-goes-here";
public static Guid Get()
{
try
{
string base64 = Microsoft.Win32.Registry.GetValue(KEY_PATH, "HostId", null) as string;
if (base64 == null)
return Create();
byte[] cypher = System.Convert.FromBase64String(base64);
byte[] bytes = System.Security.Cryptography.ProtectedData.Unprotect(cypher, ENTROPY,
System.Security.Cryptography.DataProtectionScope.LocalMachine);
if (bytes.Length != 20 || bytes[0] != 'H' || bytes[1] != 'o' || bytes[2] != 's' || bytes[3] != 't')
return Create();
byte[] guid = new byte[16];
Array.Copy(bytes, 4, guid, 0, 16);
return new Guid(guid);
}
catch
{
return Create();
}
}
static Guid Create()
{
byte[] prefix = new byte[] { (byte)'H', (byte)'o', (byte)'s', (byte)'t' };
Guid id = Guid.NewGuid();
byte[] store = new byte[20];
Array.Copy(prefix, store, 4);
Array.Copy(id.ToByteArray(), 0, store, 4, 16);
store = System.Security.Cryptography.ProtectedData.Protect(store, ENTROPY,
System.Security.Cryptography.DataProtectionScope.LocalMachine);
Microsoft.Win32.Registry.SetValue(KEY_PATH, "HostId", System.Convert.ToBase64String(store));
return id;
}
}