GPUServiceCall

Background

Important: Before proceeding to install the service application, refer to Using ADLX in a Windows system service application to determine if this procedure is required.

  • ADLX v1.4 and above can be used directly in a Windows system service application, providing access to a limited set of ADLX functionality when ADLX runs in Session 0.
    If the Windows system service application requires additional ADLX functionality, it can be accessed through a user mode process.
  • ADLX v1.3 and below cannot be used directly in a Windows system service application, and can be accessed through a user mode process.

This sample demonstrates how to create a user process from within a Windows system service application to access full ADLX functionality, provided through a communication mechanism between the user mode process that uses ADLX, and the system service.

This process uses shared memory to obtain ADLX information from the user process into the system service. Every three seconds, the sample service will output debug information – including vendor id and name – for the first GPU enumerated.

SamplePath

/Samples/C/ServiceCall/GPUService

The sample builds GPUService.exe, and this application must be installed and executed as a Windows service application.

The sample must be compiled with debug information in order to debug the service.

Installing the ADLX GPU Service application

  1. Run the Windows Command Processor application (cmd.exe) as administrator.
  2. In Windows Command Processor, change the working directory to the path where GPUService.exe is built.
  3. In Windows Command Processor, execute: GPUService.exe -install.
  4. Press Win + R to launch Windows Services, and enter: services.msc.
  5. In Windows Services, select ADLX GPU Service.

Debugging the ADLX GPU Service application

  1. Launch DebugView as administrator.
  2. In DebugView, click Capture in the top menu and select the following options:
    • Capture Win32
    • Capture Global Win32
    • Pass-Through
    • Capture Events
  3. Press Win + R and enter: services.msc to launch Windows Services.
  4. Right-click on Windows Services > ADLX GPU Service, and select Start from the menu.
  5. In DebugView, note that every 3 seconds the ADLX GPU Service outputs debug information with the vendor id and and name for the first GPU enumerated, prefixed with ADLX Call Service:.

Uninstalling the ADLX GPU Service application

  1. Run the Windows Command Processor application (cmd.exe) as administrator.
  2. In Windows Command Processor, execute: GPUService.exe –remove.

Code

Copied!

//
// Copyright (c) 2021 - 2025 Advanced Micro Devices, Inc. All rights reserved.
//
//-------------------------------------------------------------------------------------------------


#pragma warning(suppress: 6248)

#include <Windows.h>
#include <TlHelp32.h>
#include <wchar.h>
#include "SDK/ADLXHelper/Windows/C/ADLXHelper.h"
#include "SDK/Include/ISystem.h"
#include "GlobalDefs.h"
#include <stdio.h>

void XTrace (wchar_t* lpszFormat, ...)
{
    va_list args;
    va_start (args, lpszFormat);
    int nBuf;
    WCHAR szBuffer[MAX_DEBUG_STR_LEN];
    nBuf = _vsnwprintf_s (szBuffer, MAX_DEBUG_STR_LEN, _TRUNCATE, lpszFormat, args);
    OutputDebugStringW (szBuffer);
    va_end (args);
}

// Service name
#define ADLX_SERVICE_NAME  L"ADLX GPU Service"

// Global variables for service routine
SERVICE_STATUS_HANDLE g_StatusHandle = NULL;
SERVICE_STATUS        g_ServiceStatus = { 0 };
HANDLE                g_ServiceStopEvent = INVALID_HANDLE_VALUE;

// Service routine
VOID WINAPI  ServiceMain (DWORD argc, LPTSTR* argv);
VOID WINAPI  ServiceCtrlHandler (DWORD);
DWORD WINAPI ServiceWorkerThread (LPVOID lpParam);

// Install/unistall this service
// N.B: Need admin permission
// User can also install/unistall this service by windows command line
// Usage: SC CREATE "ADLX GPU Service" "GPUService.exe"
//        SC DELETE "ADLX GPU Service"
void InstallService ();
void RemoveService ();

// Invoker GPU information with ADLX in user process by share memory communication
extern BOOL GetUserProcessData (Messager* messager, ResponseData* responseData);

// ADLX executor to be run in user process
// Retrieve the first available GPU vender id and name
BOOL GetAdlxGpuInfo (WCHAR* resultBuffer)
{
    if (resultBuffer == NULL)
    {
        XTrace (L"ADLX Call Service: GetAdlxGpuInfo: invalid argument");
        return FALSE;
    }

    if (ADLX_FAILED  (ADLXHelper_Initialize ()))
    {
        XTrace (L"ADLX Call Service: GetAdlxGpuInfo: ADLX helper initalize failed.");
        return FALSE;
    }

    BOOL ret = FALSE;

    IADLXSystem* sys = ADLXHelper_GetSystemServices ();
    IADLXGPUList* gpus = NULL;
    ADLX_RESULT  res = sys->pVtbl->GetGPUs (sys, &gpus);
    if (ADLX_SUCCEEDED  (res) && !gpus->pVtbl->Empty(gpus))
    {
        IADLXGPU* gpu = NULL;
        res = gpus->pVtbl->At_GPUList (gpus, 0, &gpu);
        if (ADLX_SUCCEEDED  (res) && gpu)
        {
            const char* vendorId = NULL;
            res = gpu->pVtbl->VendorId (gpu, &vendorId);
            if (ADLX_SUCCEEDED  (res))
            {
                swprintf_s (resultBuffer, MAX_RESULT_LEN, L"VenderId=%hs ", vendorId);
            }

            const char* gpuName = NULL;
            res = gpu->pVtbl->Name (gpu, &gpuName);
            if (ADLX_SUCCEEDED  (res))
            {
                swprintf_s (resultBuffer + wcslen(resultBuffer), MAX_RESULT_LEN - wcslen (resultBuffer), L"GpuName=%hs", gpuName);
            }

            gpu->pVtbl->Release (gpu);
            ret = TRUE;
        }

        if (gpus != NULL)
        {
            gpus->pVtbl->Release (gpus);
        }
    }

    return ret;
}

// Get and dump GPU vender id and name
void ShowGPUInfo ()
{
    ResponseData result;
    Messager oneMessager;
    oneMessager.adlxEntityName = ADLX_FUNCTION_NAME;
    oneMessager.exector = GetAdlxGpuInfo;

    if (GetUserProcessData (&oneMessager, &result))
    {
        // Dump results
        XTrace (L"ADLX Call Service:ShowGPUInfo: %s", result.executedResults);
    }
}

// Usage
void Usage ()
{
    printf ("\\n");
    printf ("\\tThis exe is a service executable, you can install/unistall it with:\\n");
    printf ("\\t    SC CREATE 'ADLX GPU Service' 'GPUService.exe'\\n");
    printf ("\\t    SC DELETE 'ADLX GPU Service'\\n");
    printf ("\\n");
    printf ("\\tOr use the executable itself to install/uninstall:\\n");
    printf ("\\t    GPUService.exe -install or GPUService.exe /install\\n");
    printf ("\\t    GPUService.exe -remove or GPUService.exe /remove\\n");
    printf ("\\n");
    printf ("\\t    Use GPUService.exe -help, GPUService.exe /help to show this usage, or GPUService.exe ?\\n");
    printf ("\\n");
}

int wmain (int argc, wchar_t* argv[])
{
    XTrace (L"ADLX Call Service: Main: Entry");
    XTrace (L"ADLX Call Service: Main: Args:%s", GetCommandLineW ());

    if ((argc > 1))
    {
        if (((*argv[1] == '-') || (*argv[1] == '/')))
        {
            if (_wcsicmp (L"install", argv[1] + 1) == 0)
            {
                InstallService ();
                return 0;
            }
            else if (_wcsicmp (L"remove", argv[1] + 1) == 0)
            {
                RemoveService ();
                return 0;
            }
            else if (_wcsicmp (L"help", argv[1] + 1) == 0)
            {
                Usage ();
                return 0;
            }
        }
        else if (_wcsicmp (L"?", argv[1]) == 0)
        {
            Usage ();
            return 0;
        }
    }
    else if (_wcsicmp (L"GPUInfo", GetCommandLineW ()) == 0)
    {
        WCHAR eventPath[MAX_PATH] = { 0 };
        WCHAR shmPath[MAX_PATH] = { 0 };

        swprintf_s (eventPath, MAX_PATH, ADXL_EVENT_NAME, ADLX_FUNCTION_NAME);
        swprintf_s (shmPath, MAX_PATH, ADXL_SHAREMENORY_NAME, ADLX_FUNCTION_NAME);

        XTrace (L"ADLX Call Service: Main: GPUInfo evt: %s, shmpath: %s\\n", eventPath, shmPath);

        if (ShmCreate (shmPath, SHARE_MEMORY_MAX_SIZE))
        {
            Messager messager;
            if (ShmReadBuffer (shmPath, (PVOID)&messager, sizeof (Messager)))
            {
                ResponseData result;
                if (messager.exector (result.executedResults))
                {
                    result.size = (int)wcslen (result.executedResults);
                    ShmWriteBuffer (shmPath, (PVOID*)&result, (ULONG)sizeof (result));

                    HANDLE hEvent = OpenEventW (EVENT_ALL_ACCESS, FALSE, eventPath);
                    if (hEvent)
                    {
                        SetEvent (hEvent);
                        CloseHandle (hEvent);
                        XTrace (L"ADLX Call Service: Main: GPUInfo, user app finished.");
                    }
                }
            }
        }

        return 0;
    }

    SERVICE_TABLE_ENTRYW ServiceTable[] =
    {
        { ADLX_SERVICE_NAME, (LPSERVICE_MAIN_FUNCTIONW)ServiceMain },
        { NULL, NULL }
    };

    if (StartServiceCtrlDispatcherW (ServiceTable) == FALSE)
    {
        XTrace (L"ADLX Call Service: Main: StartServiceCtrlDispatcher returned error.");
        return GetLastError ();
    }

    XTrace (L"ADLX Call Service: Main: Exit");

    return 0;
}

VOID WINAPI ServiceMain (DWORD argc, LPTSTR* argv)
{
    DWORD Status = E_FAIL;

    XTrace (L"ADLX Call Service: ServiceMain: Entry");

    g_StatusHandle = RegisterServiceCtrlHandlerW (ADLX_SERVICE_NAME, ServiceCtrlHandler);
    if (g_StatusHandle == NULL)
    {
        XTrace (L"ADLX Call Service: ServiceMain: RegisterServiceCtrlHandler returned error");
        goto EXIT;
    }

    // Tell the service controller we are starting
    ZeroMemory (&g_ServiceStatus, sizeof (g_ServiceStatus));
    g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    g_ServiceStatus.dwControlsAccepted = 0;
    g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
    g_ServiceStatus.dwWin32ExitCode = 0;
    g_ServiceStatus.dwServiceSpecificExitCode = 0;
    g_ServiceStatus.dwCheckPoint = 0;

    if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE)
    {
        XTrace (L"ADLX Call Service: ServiceMain: SetServiceStatus returned error");
    }

    /*
     * Perform tasks neccesary to start the service here
     */
    XTrace (L"ADLX Call Service: ServiceMain: Performing Service Start Operations");

    // Create stop event to wait on later.
    g_ServiceStopEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
    if (g_ServiceStopEvent == NULL)
    {
        XTrace (L"ADLX Call Service: ServiceMain: CreateEvent(g_ServiceStopEvent) returned error");

        g_ServiceStatus.dwControlsAccepted = 0;
        g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
        g_ServiceStatus.dwWin32ExitCode = GetLastError ();
        g_ServiceStatus.dwCheckPoint = 1;

        if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE)
        {
            XTrace (L"ADLX Call Service: ServiceMain: SetServiceStatus returned error");
        }
        goto EXIT;
    }

    // Tell the service controller we are started
    g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
    g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
    g_ServiceStatus.dwWin32ExitCode = 0;
    g_ServiceStatus.dwCheckPoint = 0;

    if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE)
    {
        XTrace (L"ADLX Call Service: ServiceMain: SetServiceStatus returned error");
    }

    // Start the thread that will perform the main task of the service
    HANDLE hThread = CreateThread (NULL, 0, ServiceWorkerThread, NULL, 0, NULL);

    XTrace (L"ADLX Call Service: ServiceMain: Waiting for Worker Thread to complete");

    // Wait until our worker thread exits effectively signaling that the service needs to stop
    WaitForSingleObject (hThread, INFINITE);

    XTrace (L"ADLX Call Service: ServiceMain: Worker Thread Stop Event signaled");

    /*
     * Perform any cleanup tasks
     */
    XTrace (L"ADLX Call Service: ServiceMain: Performing Cleanup Operations");

    CloseHandle (g_ServiceStopEvent);

    g_ServiceStatus.dwControlsAccepted = 0;
    g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
    g_ServiceStatus.dwWin32ExitCode = 0;
    g_ServiceStatus.dwCheckPoint = 3;

    if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE)
    {
        XTrace (L"ADLX Call Service: ServiceMain: SetServiceStatus returned error");
    }

EXIT:
    XTrace (L"ADLX Call Service: ServiceMain: Exit");

    return;
}

VOID WINAPI ServiceCtrlHandler (DWORD CtrlCode)
{
    XTrace (L"ADLX Call Service: ServiceCtrlHandler: Entry");

    switch (CtrlCode)
    {
    case SERVICE_CONTROL_STOP:

        XTrace (L"ADLX Call Service: ServiceCtrlHandler: SERVICE_CONTROL_STOP Request");

        if (g_ServiceStatus.dwCurrentState != SERVICE_RUNNING)
            break;

        /*
         * Perform tasks neccesary to stop the service here
         */

        g_ServiceStatus.dwControlsAccepted = 0;
        g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
        g_ServiceStatus.dwWin32ExitCode = 0;
        g_ServiceStatus.dwCheckPoint = 4;

        if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE)
        {
            XTrace (L"ADLX Call Service: ServiceCtrlHandler: SetServiceStatus returned error");
        }

        // This will signal the worker thread to shut down
        SetEvent (g_ServiceStopEvent);
        break;

    default:
        break;
    }

    XTrace (L"ADLX Call Service: ServiceCtrlHandler: Exit");
}

DWORD WINAPI ServiceWorkerThread (LPVOID lpParam)
{
    XTrace (L"ADLX Call Service: ServiceWorkerThread: Entry");

    // Periodically check if the service has been requested to stop
    while (WaitForSingleObject (g_ServiceStopEvent, 0) != WAIT_OBJECT_0)
    {
        ShowGPUInfo ();
        Sleep (3 * 1000);
    }

    XTrace (L"ADLX Call Service: ServiceWorkerThread: Exit");

    return ERROR_SUCCESS;
}

void InstallService ()
{
    WCHAR path[512] = { 0 };
    if (GetModuleFileNameW (NULL, path, 512) == 0)
    {
        XTrace (L"ADLX Call Service: InstallService: Cannot get g_SharedFile path.");
        return;
    }

    SC_HANDLE serviceManager = OpenSCManager (NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
    if (serviceManager)
    {
        SC_HANDLE serviceHandle = CreateServiceW (
            serviceManager, ADLX_SERVICE_NAME, ADLX_SERVICE_NAME, SERVICE_QUERY_STATUS,
            SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
            SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, path,
            NULL, NULL, L"", NULL, NULL);

        if (serviceHandle)
        {
            CloseServiceHandle (serviceHandle);
        }
        else
        {
            XTrace (L"ADLX Call Service: InstallService: Cannot create service.");
        }

        CloseServiceHandle (serviceManager);
    }
    else
    {
        XTrace (L"ADLX Call Service: InstallService: Cannot open service.");
    }
}

void RemoveService ()
{
    SC_HANDLE serviceManager = OpenSCManager (NULL, NULL, SC_MANAGER_CONNECT);
    if (!serviceManager)
    {
        XTrace (L"ADLX Call Service: RemoveService: Cannot open service manager.");
        return;
    }

    SC_HANDLE serviceHandle = OpenServiceW (serviceManager, ADLX_SERVICE_NAME, DELETE | SERVICE_STOP);
    if (!serviceHandle)
    {
        CloseServiceHandle (serviceManager);
        XTrace (L"ADLX Call Service: RemoveService: Cannot open service.");
        return;
    }

    if (!DeleteService (serviceHandle))
    {
        CloseServiceHandle (serviceHandle);
        CloseServiceHandle (serviceManager);
        XTrace (L"ADLX Call Service: RemoveService: Delete service failed.");
        return;
    }

    XTrace (L"ADLX Call Service: RemoveService: Deleted service.");

    SERVICE_STATUS serviceStatus = { 0 };
    ControlService (serviceHandle, SERVICE_CONTROL_STOP, (LPSERVICE_STATUS)&serviceStatus);

    CloseServiceHandle (serviceHandle);
    CloseServiceHandle (serviceManager);
}

Related pages

  • Visit the Adlx product page for download links and more information.

Looking for more documentation on GPUOpen?

AMD GPUOpen software blogs

Our handy software release blogs will help you make good use of our tools, SDKs, and effects, as well as sharing the latest features with new releases.

GPUOpen Manuals

Don’t miss our manual documentation! And if slide decks are what you’re after, you’ll find 100+ of our finest presentations here.

AMD GPUOpen Performance Guides

The home of great performance and optimization advice for AMD RDNA™ 2 GPUs, AMD Ryzen™ CPUs, and so much more.

Getting started: AMD GPUOpen software

New or fairly new to AMD’s tools, libraries, and effects? This is the best place to get started on GPUOpen!

AMD GPUOpen Getting Started Development and Performance

Looking for tips on getting started with developing and/or optimizing your game, whether on AMD hardware or generally? We’ve got you covered!

AMD GPUOpen Technical blogs

Browse our technical blogs, and find valuable advice on developing with AMD hardware, ray tracing, Vulkan®, DirectX®, Unreal Engine, and lots more.

Find out more about our software!

AMD GPUOpen Effects - AMD FidelityFX technologies

Create wonder. No black boxes. Meet the AMD FidelityFX SDK!

AMD GPUOpen Samples

Browse all our useful samples. Perfect for when you’re needing to get started, want to integrate one of our libraries, and much more.

AMD GPUOpen developer SDKs

Discover what our SDK technologies can offer you. Query hardware or software, manage memory, create rendering applications or machine learning, and much more!

AMD GPUOpen Developer Tools

Analyze, Optimize, Profile, Benchmark. We provide you with the developer tools you need to make sure your game is the best it can be!