51Degrees Device Detection C/C++  4.4

A device detection library that is used natively or by 51Degrees products


This example shows how to use 51Degrees on-premise device detection to determine details about a device based on its User-Agent and User-Agent Client Hint HTTP header values.You will learn:

  1. How to initialise a resource manager and a place holder for the results
  2. How to pass input data (evidence) to the detection APIs
  3. How to retrieve the results

This example is available in full on GitHub.

/* *********************************************************************
* This Original Work is copyright of 51 Degrees Mobile Experts Limited.
* Copyright 2022 51 Degrees Mobile Experts Limited, Davidson House,
* Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU.
* This Original Work is licensed under the European Union Public Licence
* (EUPL) v.1.2 and is subject to its terms as set out below.
* If a copy of the EUPL was not distributed with this file, You can obtain
* one at https://opensource.org/licenses/EUPL-1.2.
* The 'Compatible Licences' set out in the Appendix to the EUPL (as may be
* amended by the European Commission) shall be deemed incompatible for
* the purposes of the Work and the provisions of the compatibility
* clause in Article 5 of the EUPL shall not apply.
* If using the Work as, or as part of, a network application, by
* including the attribution notice(s) required under Article 5 of the EUPL
* in the end user terms of the application under an appropriate heading,
* such notice(s) shall fulfill the requirements of that article.
* ********************************************************************* */
#include <stdio.h>
#include <time.h>
// Include ExmapleBase.h before others as it includes Windows 'crtdbg.h'
// which requires to be included before 'malloc.h'.
#include "ExampleBase.h"
#include "../../../src/hash/hash.h"
#include "../../../src/hash/fiftyone.h"
#define MAX_EVIDENCE 5
static const char *dataDir = "device-detection-data";
// In this example, by default, the 51degrees "Lite" file needs to be in the
// device-detection-data,
// or you may specify another file as a command line parameter.
// Note that the Lite data file is only used for illustration, and has
// limited accuracy and capabilities.
// Find out about the Enterprise data file on our pricing page:
// https://51degrees.com/pricing
static const char *dataFileName = "51Degrees-LiteV4.1.hash";
static char valueBuffer[1024] = "";
typedef struct {
uint32_t count;
struct {
const char* key;
const char* value;
} items[MAX_EVIDENCE];
// A User-Agent from a mobile device.
static evidence mobileDevice = {
{ { FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "user-agent", ("Mozilla/5.0 (Linux; Android 9; SAMSUNG SM-G960U) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"SamsungBrowser/10.1 Chrome/71.0.3578.99 Mobile "
"Safari/537.36") } }
// A User-Agent from a desktop device.
static evidence desktopDevice = {
{ { FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "user-agent", ("Mozilla / 5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/78.0.3904.108 Safari/537.36") } }
// Evidence values from a windows 11 device using a browser
// that supports User-Agent Client Hints.
static evidence userAgentClientHints = {
{ {FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/98.0.4758.102 Safari/537.36"},
{FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "sec-ch-ua", ("\" Not A; Brand\";v=\"99\", \"Chromium\";v=\"98\", "
"\"Google Chrome\";v=\"98\"")},
{FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "sec-ch-ua-platform", "Windows"},
{FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "sec-ch-ua-platform-version", "\"14.0.0\""} }
// This collection contains the various input values that will
// be passed to the device detection algorithm.
static evidence* evidenceValues[3] = {
static void outputValue(
ResultsHash *results,
const char *name,
const char* propertyName,
FILE *output) {
DataSetHash* dataset = (DataSetHash*)results->b.b.dataSet;
int propertyIndex = PropertiesGetPropertyIndexFromName(dataset->b.b.available, "IsMobile");
// If a value has not been set then trying to access the value will
// result in an exception.
results, propertyIndex, exception)) {
else {
// A no value message can also be obtained. This message describes why
// the value has not been set.
ResultsHashGetNoValueReason(results, propertyIndex, exception);
sprintf(valueBuffer, "Unknown (%s)", ResultsHashGetNoValueReasonMessage(reason));
fprintf(output, "\n\t%s: %s", name, valueBuffer);
static void reportStatus(StatusCode status,
const char* fileName) {
const char *message = StatusGetMessage(status, fileName);
printf("%s\n", message);
static void analyse(
ResultsHash* results,
FILE* output) {
// Information required for detection is called "evidence"
// and usually consists of a number of HTTP Header field
// values, in this case represented by a
// Object of header name/value entries.
// list the evidence
fprintf(output, "Input values:");
for (uint32_t i = 0; i < evidence->count; i++) {
"\n\t%s%s: %s",
fprintf(output, "\n");
ResultsHashFromEvidence(results, evidence, exception);
fprintf(output, "Results:");
outputValue(results, "Mobile Device", "IsMobile", output);
outputValue(results, "Platform Name", "PlatformName", output);
outputValue(results, "Platform Version", "PlatformVersion", output);
outputValue(results, "Browser Name", "BrowserName", output);
outputValue(results, "Browser Version", "BrowserVersion", output);
fprintf(output, "\n\n");
void fiftyoneDegreesHashGettingStarted(
const char *dataFilePath,
ConfigHash *config,
FILE* output) {
ResourceManager manager;
// Set the properties to be returned for each User-Agent.
// Initialise the manager for device detection.
if (status != SUCCESS) {
reportStatus(status, dataFilePath);
// Create a results instance to store and process evidence.
// The capacity of the results should be the same as the maximum potential
// evidence that can be provided.
ResultsHash *results = ResultsHashCreate(&manager, MAX_EVIDENCE, MAX_EVIDENCE);
for (int i = 0; i < (int)(sizeof(evidenceValues)/sizeof(evidence *)); i++) {
// Create an evidence collection and add the evidence to the collection
EvidenceKeyValuePairArray* evidenceArray = EvidenceCreate(MAX_EVIDENCE);
evidence *evs = evidenceValues[i];
for (uint32_t j = 0; j < evs->count; j++) {
// Add the evidence as string
analyse(results, evidenceArray, output);
// Ensure the evidence collection is freed after used
// Check data file
DataSetHash* dataset = (DataSetHash *)results->b.b.dataSet;
// Ensure the results are freed to avoid memory leaks.
// Free the resources used by the manager.
void fiftyoneDegreesExampleCGettingStartedRun(ExampleParameters *params) {
// Call the actual function.
#ifndef TEST
int main(int argc, char* argv[]) {
StatusCode status = SUCCESS;
// We use the low memory profile as its performance is
// sufficient for this example. See the documentation for
// more detail on this and other configuration options:
// http://51degrees.com/documentation/_device_detection__features__performance_options.html
// http://51degrees.com/documentation/_features__automatic_datafile_updates.html
// http://51degrees.com/documentation/_features__usage_sharing.html
char dataFilePath[FILE_MAX_PATH];
// Use the supplied path for the data file or find the lite data that
// is included in the repository
if (argc > 1) {
strcpy(dataFilePath, argv[1]);
else {
status = FileGetPath(
if (status != SUCCESS) {
printf(("Failed to find a device detection "
"data file. Make sure the device-detection-data "
"submodule has been updated by running "
"`git submodule update --recursive`"));
return 1;
// Check if the example is compiled for memory usage only
ExampleParameters params;
params.dataFilePath = dataFilePath;
params.config = &config;
params.output = stdout;
// Run the example
// Wait for a character to be pressed.
return 0;