How do I get a unique identifier for a machine running Windows 8 in C#?
Asked Answered
E

4

8

I'm working on a Metro app written in C# and need a way to uniquely identify a device. I found the ASHWID in the documentation which looks great. The code suggested is as follows:

HardwareToken token = HardwareIdentification.GetPackageSpecificToken(null);
IBuffer hardwareId = token.Id;
IBuffer signature = token.Signature;
IBuffer certificate = token.Certificate;

The problem is, how do I turn that IBuffer into a string which I can use?

Epiphany answered 21/9, 2012 at 9:50 Comment(2)
Quote: However, the ASHWID changes if the hardware profile of the device changes, such as when the user unplugs a USB Bluetooth adapter. msdn.microsoft.com/en-us/library/windows/apps/jj553431.aspxAmmamaria
I did notice that, but it seems to be about the best option available in Windows 8. For our uses it will suffice even if it's not quite as good as we're used to! :)Epiphany
E
15

After a lot of hunting through suggestions which were actually in JS or C++ I finally found an answer!

private string GetHardwareId()  
{  
    var token = HardwareIdentification.GetPackageSpecificToken(null);  
    var hardwareId = token.Id;  
    var dataReader = Windows.Storage.Streams.DataReader.FromBuffer(hardwareId);  

    byte[] bytes = new byte[hardwareId.Length];  
    dataReader.ReadBytes(bytes);  

    return BitConverter.ToString(bytes);  
}  

Thanks go to this blog - http://bartwullems.blogspot.co.uk/2012/09/windows-8-uniquely-identifying-device.html

Epiphany answered 21/9, 2012 at 9:50 Comment(4)
Note that this id is not constant! If you turn airplane mode on, you get a different id.Aegis
@ThomasLevesque I believe you're correct about the id shifting when changes are made to hardware, but I just tried (UWP in Win10) and am getting the same value with Airplane Mode on or off & when I unplug a mouse, so it doesn't seem quite as sensitive as you claim here, at least on this platform.Disendow
@ruffin, I noticed that in WinRT, it might have change in UWPAegis
This token.Id value changes if you switch your internet connection from Wi-Fi to Ethernet or the opposite. So, it's not unique. At least in Win 8.1Derringdo
I
4

This should work as well, but I don't have Windows 8 to test with...

private string GetHardwareId()   
{ 
  return BitConverter.ToString(Windows.System.Profile.HardwareIdentification.GetPackageSpecificToken(null).Id.ToArray());
}

And if you call it more than once, you might want to stick it in a Lazy<T>

private static Lazy<string> _hardwareId = new Lazy<string>(() => BitConverter.ToString(Windows.System.Profile.HardwareIdentification.GetPackageSpecificToken(null).Id.ToArray()), true);

public string HardwareId()   
{ 
     return _hardwareId.Value;
}

Or just make it static if you know it will always be called:

public static readonly string HardwareId = BitConverter.ToString(Windows.System.Profile.HardwareIdentification.GetPackageSpecificToken(null).Id.ToArray()));
Introvert answered 21/9, 2012 at 10:2 Comment(7)
Alas, "Windows.System.Profile.HardwareIdentification.GetPackageSpecificToken(null).Id.ToArray()" doesn't work - there's no "ToArray()" method on the IBuffer in Windows 8.Epiphany
Well, if I copy/paste your first suggestion into my project I get this error: 'Windows.Storage.Streams.IBuffer' does not contain a definition for 'ToArray' and no extension method 'ToArray' accepting a first argument of type 'Windows.Storage.Streams.IBuffer' could be found.Epiphany
You need to add the appropriate using statement - using System.Runtime.InteropServices.WindowsRuntime and you must have .NET 4.5Introvert
Adding that using statement does seem to work, but it's a bit weird that Visual Studio didn't want to suggest that to Resolve the issue like it usually does when I type things which need a new using statement!Epiphany
@Epiphany - I have found that extensions are not suggested in VS2010, for example the Linq extensions.Introvert
If you use this, the HardwareId may change when a device is added using USB or BlueTooth. Filter the information as I described in my answer.Palingenesis
@KrisVandermotten - good to note, however, the question is how to turn the result into a string.Introvert
P
3

You can use HardwareIdentification.GetPackageSpecificToken(null), see http://msdn.microsoft.com/en-us/library/windows/apps/jj553431.aspx

That function gives you a lot of information, that you can filter as you like. For example:

public static string GetMachineId()
{
    var hardwareToken = 
        HardwareIdentification.GetPackageSpecificToken(null).Id.ToArray();
    var count = hardwareToken.Length / 4;
    ulong id = 0ul;
    for (int i = 0; i < count; i++)
    {
        switch (BitConverter.ToUInt16(hardwareToken, i * 4))
        {
            case 1:
                // processor
            case 2:
                // memory
            case 9:
                // system BIOS
                id = (id << 12) ^ BitConverter.ToUInt16(hardwareToken, i * 4 + 2);
                break;
        }
    }
    return Convert.ToBase64String(BitConverter.GetBytes(id));
}

However, bear in mind that this function, and the underlying API, cannot guarantee absolute uniqueness across all the machines connected to the internet. You would typically combine this with information about the user.

Another option is to generate and store a GUID in local (non-roaming) storage, and use that as your machine id. Depending in you exact needs, this may be a better solution.

Palingenesis answered 30/9, 2014 at 8:38 Comment(0)
C
0

For a guid id you can do the following as an extension to the above answer

    private Guid GetHardwareId()
    {
        var token = HardwareIdentification.GetPackageSpecificToken(null);  
        var hardwareId = token.Id;  
        var dataReader = Windows.Storage.Streams.DataReader.FromBuffer(hardwareId);  

        byte[] bytes = new byte[hardwareId.Length];  
        dataReader.ReadBytes(bytes);  

        byte[] deviceId = new byte[16];
        Array.Copy((byte[])bytes, deviceId, deviceId.Length);

        return new Guid(deviceId);
    }
Claribelclarice answered 6/2, 2015 at 8:12 Comment(1)
That sounds like a bad idea. What I meant was the following: look for a file in local storage. If (and only if) it isn't there, create it and place a new Guid in there, created using Guid.NewGuid(). Use that Guid as your machine identifier. On the next run, take it from the file. See msdn.microsoft.com/en-us/library/system.guid.newguid.aspxPalingenesis

© 2022 - 2024 — McMap. All rights reserved.