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?

Youngjae
  • 24,352
  • 18
  • 113
  • 198
StarlitSkies
  • 1,144
  • 7
  • 14
  • 1
    Quote: `However, the ASHWID changes if the hardware profile of the device changes, such as when the user unplugs a USB Bluetooth adapter`. http://msdn.microsoft.com/en-us/library/windows/apps/jj553431.aspx – Hans Passant Sep 21 '12 at 10:29
  • 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! :) – StarlitSkies Sep 21 '12 at 13:53

4 Answers4

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

StarlitSkies
  • 1,144
  • 7
  • 14
  • 6
    Note that this id is **not** constant! If you turn airplane mode on, you get a different id. – Thomas Levesque Aug 01 '14 at 13:56
  • @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. – ruffin Mar 25 '16 at 17:33
  • @ruffin, I noticed that in WinRT, it might have change in UWP – Thomas Levesque Mar 29 '16 at 08:59
  • 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.1 – Heshan Jul 25 '16 at 06:32
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()));
Chris Gessler
  • 22,727
  • 7
  • 57
  • 83
  • Alas, "Windows.System.Profile.HardwareIdentification.GetPackageSpecificToken(null).Id.ToArray()" doesn't work - there's no "ToArray()" method on the IBuffer in Windows 8. – StarlitSkies Sep 21 '12 at 10:30
  • 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. – StarlitSkies Sep 21 '12 at 12:51
  • 1
    You need to add the appropriate using statement - `using System.Runtime.InteropServices.WindowsRuntime` and you must have .NET 4.5 – Chris Gessler Sep 21 '12 at 18:36
  • 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! – StarlitSkies Sep 23 '12 at 13:52
  • @StarlitSkies - I have found that extensions are not suggested in VS2010, for example the Linq extensions. – Chris Gessler Sep 23 '12 at 14:04
  • 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. – Kris Vandermotten Jan 13 '16 at 17:06
  • @KrisVandermotten - good to note, however, the question is how to turn the result into a string. – Chris Gessler Jan 14 '16 at 15:31
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.

Kris Vandermotten
  • 10,111
  • 38
  • 49
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);
    }
Clinton Ward
  • 2,441
  • 1
  • 22
  • 26
  • 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 https://msdn.microsoft.com/en-us/library/system.guid.newguid.aspx – Kris Vandermotten Apr 03 '15 at 10:10