2

Consider following figure.

Path to Executable

I have written following code that is supposed to get the path to the executable of a service, highlighted in figure. Basically the code snippet I gave below will be written in the another cpp file that will generate the lpa.exe and is supposed to run as service. I tried GetModuleFileName and the function finds correct path if executable(lpa.exe) is run normally(not as service). But once it runs as service, it does not give correct path and points to system32/.... To solve the issue, I believe following snippet of code might be the solution:

#include <Windows.h>
#include <iostream>

int main()
{
    SC_HANDLE sHandle;
    LPQUERY_SERVICE_CONFIG lpServiceConfig = NULL;
    DWORD cbBufSize = 100;
    LPDWORD bytesNeeded = NULL;
    sHandle = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
    sHandle = OpenService(sHandle, "LogPointAgent",SERVICE_ALL_ACCESS);
    QueryServiceConfig(sHandle,lpServiceConfig,cbBufSize,bytesNeeded);
    std::cout << lpServiceConfig->lpBinaryPathName << std::endl;
}

The code compiles but I get exception Unhandled exception at 0x00ad1103 in LearningCpp.exe: 0xC0000005: Access violation reading location 0x0000000c. This means I am having issues related to pointer. What is wrong in code I gave above?

dandan78
  • 13,328
  • 13
  • 64
  • 78
Mahadeva
  • 1,584
  • 4
  • 23
  • 56

1 Answers1

2

QueryServiceConfig requires a pointer that points to allocated memory as the second parameter if you specify a size with the third parameter. You specify the size, but the pointer is NULL, which causes the crash.

lpServiceConfig [out, optional]

A pointer to a buffer that receives the service configuration information. The format of the data is a QUERY_SERVICE_CONFIG structure.

The maximum size of this array is 8K bytes. To determine the required size, specify NULL for this parameter and 0 for the cbBufSize parameter. The function will fail and GetLastError will return ERROR_INSUFFICIENT_BUFFER. The pcbBytesNeeded parameter will receive the required size.*

You will have to allocate memory for lpServiceConfig parameter.

Here is an official example:

Querying a Service's Configuration

//
// Purpose: 
//   Retrieves and displays the current service configuration.
//
// Parameters:
//   None
// 
// Return value:
//   None
//
VOID __stdcall DoQuerySvc()
{
    SC_HANDLE schSCManager;
    SC_HANDLE schService;
    LPQUERY_SERVICE_CONFIG lpsc; 
    LPSERVICE_DESCRIPTION lpsd;
    DWORD dwBytesNeeded, cbBufSize, dwError; 

    // Get a handle to the SCM database. 

    schSCManager = OpenSCManager( 
        NULL,                    // local computer
        NULL,                    // ServicesActive database 
        SC_MANAGER_ALL_ACCESS);  // full access rights 

    if (NULL == schSCManager) 
    {
        printf("OpenSCManager failed (%d)\n", GetLastError());
        return;
    }

    // Get a handle to the service.

    schService = OpenService( 
        schSCManager,          // SCM database 
        szSvcName,             // name of service 
        SERVICE_QUERY_CONFIG); // need query config access 

    if (schService == NULL)
    { 
        printf("OpenService failed (%d)\n", GetLastError()); 
        CloseServiceHandle(schSCManager);
        return;
    }

    // Get the configuration information.

    if( !QueryServiceConfig( 
        schService, 
        NULL, 
        0, 
        &dwBytesNeeded))
    {
        dwError = GetLastError();
        if( ERROR_INSUFFICIENT_BUFFER == dwError )
        {
            cbBufSize = dwBytesNeeded;
            lpsc = (LPQUERY_SERVICE_CONFIG) LocalAlloc(LMEM_FIXED, cbBufSize);
        }
        else
        {
            printf("QueryServiceConfig failed (%d)", dwError);
            goto cleanup; 
        }
    }

    if( !QueryServiceConfig( 
        schService, 
        lpsc, 
        cbBufSize, 
        &dwBytesNeeded) ) 
    {
        printf("QueryServiceConfig failed (%d)", GetLastError());
        goto cleanup;
    }

    if( !QueryServiceConfig2( 
        schService, 
        SERVICE_CONFIG_DESCRIPTION,
        NULL, 
        0, 
        &dwBytesNeeded))
    {
        dwError = GetLastError();
        if( ERROR_INSUFFICIENT_BUFFER == dwError )
        {
            cbBufSize = dwBytesNeeded;
            lpsd = (LPSERVICE_DESCRIPTION) LocalAlloc(LMEM_FIXED, cbBufSize);
        }
        else
        {
            printf("QueryServiceConfig2 failed (%d)", dwError);
            goto cleanup; 
        }
    }

    if (! QueryServiceConfig2( 
        schService, 
        SERVICE_CONFIG_DESCRIPTION,
        (LPBYTE) lpsd, 
        cbBufSize, 
        &dwBytesNeeded) ) 
    {
        printf("QueryServiceConfig2 failed (%d)", GetLastError());
        goto cleanup;
    }

    // Print the configuration information.

    _tprintf(TEXT("%s configuration: \n"), szSvcName);
    _tprintf(TEXT("  Type: 0x%x\n"), lpsc->dwServiceType);
    _tprintf(TEXT("  Start Type: 0x%x\n"), lpsc->dwStartType);
    _tprintf(TEXT("  Error Control: 0x%x\n"), lpsc->dwErrorControl);
    _tprintf(TEXT("  Binary path: %s\n"), lpsc->lpBinaryPathName);
    _tprintf(TEXT("  Account: %s\n"), lpsc->lpServiceStartName);

    if (lpsd->lpDescription != NULL && lstrcmp(lpsd->lpDescription, TEXT("")) != 0)
        _tprintf(TEXT("  Description: %s\n"), lpsd->lpDescription);
    if (lpsc->lpLoadOrderGroup != NULL && lstrcmp(lpsc->lpLoadOrderGroup, TEXT("")) != 0)
        _tprintf(TEXT("  Load order group: %s\n"), lpsc->lpLoadOrderGroup);
    if (lpsc->dwTagId != 0)
        _tprintf(TEXT("  Tag ID: %d\n"), lpsc->dwTagId);
    if (lpsc->lpDependencies != NULL && lstrcmp(lpsc->lpDependencies, TEXT("")) != 0)
        _tprintf(TEXT("  Dependencies: %s\n"), lpsc->lpDependencies);

    LocalFree(lpsc); 
    LocalFree(lpsd);

cleanup:
    CloseServiceHandle(schService); 
    CloseServiceHandle(schSCManager);
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
2501
  • 25,460
  • 4
  • 47
  • 87
  • I am passing LPQUERY_SERVICE_CONFIG lpServiceConfig in the parameter. Isn't that a correct way? – Mahadeva Jan 06 '15 at 14:22
  • Yes QueryServiceConfig worked but why am I supposed to call the same function twice. I could also use QueryServiceConfig2 as well. Thanks for the links. – Mahadeva Jan 06 '15 at 14:37
  • @Sarvagya: It's a common pattern with Windows API programming: Services that return information of varying size can be called with an invalid buffer to query the required size. This size can then be used to provide a properly sized buffer in a second call. – IInspectable Jan 06 '15 at 15:31
  • It might be confusing (as it was initially for me) why would one want to determine the size of the buffer when it's for a fixed structure. Obviously it has to store the variable length stuff (like path name, display name etc) somewhere - and it must be storing that at the end of the fixed structure, and the variable length fields within it point to memory after it. If that makes sense (btw, not checked any of this :D) – TByte Sep 01 '21 at 21:12