/*
 * PROJECT:         ReactOS api tests
 * LICENSE:         GPLv2+ - See COPYING in the top level directory
 * PURPOSE:         Tests for Lock/UnlockServiceDatabase and QueryServiceLockStatusA/W
 * PROGRAMMER:      Hermès BÉLUSCA - MAÏTO
 */

#include <apitest.h>

#include <winsvc.h>
#include <strsafe.h>

#define TESTING_SERVICE     L"Spooler"

static void Test_LockUnlockServiceDatabase(void)
{
    BOOL bError = FALSE;

    SC_HANDLE hScm  = NULL;
    SC_LOCK   hLock = NULL;

    /* First of all, try to lock / unlock the services database with invalid handles */
    SetLastError(0xdeadbeef);
    hScm  = NULL;
    hLock = LockServiceDatabase(hScm);
    ok(hLock == NULL, "hLock = 0x%p, expected 0\n", hLock);
    ok_err(ERROR_INVALID_HANDLE);

    SetLastError(0xdeadbeef);
    hScm  = (SC_HANDLE)0xdeadbeef;
    hLock = LockServiceDatabase(hScm);
    ok(hLock == NULL, "hLock = 0x%p, expected 0\n", hLock);
    ok_err(ERROR_INVALID_HANDLE);

/** This test seems to make this application crash on Windows 7... I do not know why... **/
    SetLastError(0xdeadbeef);
    hLock = NULL;
    bError = UnlockServiceDatabase(hLock);
    ok(bError == FALSE, "bError = %u, expected FALSE\n", bError);
    ok_err(ERROR_INVALID_SERVICE_LOCK);
/*****************************************************************************************/

    SetLastError(0xdeadbeef);
    hLock = (SC_LOCK)0xdeadbeef;
    bError = UnlockServiceDatabase(hLock);
    ok(bError == FALSE, "bError = %u, expected FALSE\n", bError);
    ok_err(ERROR_INVALID_SERVICE_LOCK);


    /* Then, try to lock the services database without having rights */
    SetLastError(0xdeadbeef);
    hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
    ok(hScm != NULL, "Failed to open service manager, error=0x%08lx\n", GetLastError());
    if (!hScm)
    {
        skip("No service control manager; cannot proceed with LockUnlockServiceDatabase test\n");
        goto cleanup;
    }
    ok_err(ERROR_SUCCESS);

    SetLastError(0xdeadbeef);
    hLock = LockServiceDatabase(hScm);
    ok(hLock == NULL, "hLock = 0x%p, expected 0\n", hLock);
    ok_err(ERROR_ACCESS_DENIED);

    if (hLock)
        UnlockServiceDatabase(hLock);
    CloseServiceHandle(hScm);

    /* Try to lock the services database with good rights */
    SetLastError(0xdeadbeef);
    hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_LOCK);
    ok(hScm != NULL, "Failed to open service manager, error=0x%08lx\n", GetLastError());
    if (!hScm)
    {
        skip("No service control manager; cannot proceed with LockUnlockServiceDatabase test\n");
        goto cleanup;
    }
    ok_err(ERROR_SUCCESS);

    SetLastError(0xdeadbeef);
    hLock = LockServiceDatabase(hScm);
    ok(hLock != NULL, "hLock = 0x%p, expected non-zero\n", hLock);
    ok_err(ERROR_SUCCESS);

    /* Now unlock it */
    if (hLock)
    {
        SetLastError(0xdeadbeef);
        bError = UnlockServiceDatabase(hLock);
        ok(bError == TRUE, "bError = %u, expected TRUE\n", bError);
        ok_err(ERROR_SUCCESS);
    }


cleanup:
    if (hScm)
        CloseServiceHandle(hScm);

    return;
}

static void Test_LockUnlockServiceDatabaseWithServiceStart(void)
{
    BOOL bError = FALSE;

    SC_HANDLE hScm  = NULL;
    SC_HANDLE hSvc  = NULL;
    SC_LOCK   hLock = NULL;

    LPQUERY_SERVICE_CONFIGW lpConfig = NULL;
    DWORD dwRequiredSize = 0;
    SERVICE_STATUS status;
    BOOL bWasRunning = FALSE;
    DWORD dwOldStartType = 0;

    /* Open the services database */
    SetLastError(0xdeadbeef);
    hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_LOCK);
    ok(hScm != NULL, "Failed to open service manager, error=0x%08lx\n", GetLastError());
    if (!hScm)
    {
        skip("No service control manager; cannot proceed with LockUnlockServiceDatabaseWithServiceStart test\n");
        goto cleanup;
    }
    ok_err(ERROR_SUCCESS);

    /* Grab a handle to the testing service */
    SetLastError(0xdeadbeef);
    hSvc = OpenServiceW(hScm, TESTING_SERVICE, SERVICE_START | SERVICE_STOP | SERVICE_CHANGE_CONFIG | SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS);
    ok(hSvc != NULL, "hSvc = 0x%p, expected non-null, error=0x%08lx\n", hSvc, GetLastError());
    if (!hSvc)
    {
        skip("Cannot open a handle to service %S; cannot proceed with LockUnlockServiceDatabaseWithServiceStart test\n", TESTING_SERVICE);
        goto cleanup;
    }
    ok_err(ERROR_SUCCESS);

    /* Lock the services database */
    SetLastError(0xdeadbeef);
    hLock = LockServiceDatabase(hScm);
    ok(hLock != NULL, "hLock = 0x%p, expected non-zero, error=0x%08lx\n", hLock, GetLastError());
    if (!hLock)
    {
        skip("Cannot lock the services database; cannot proceed with LockUnlockServiceDatabaseWithServiceStart test\n");
        goto cleanup;
    }
    ok_err(ERROR_SUCCESS);

    /* To proceed further, firstly attempt to stop the testing service */
    QueryServiceConfigW(hSvc, NULL, 0, &dwRequiredSize);
    lpConfig = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwRequiredSize);
    QueryServiceConfigW(hSvc, lpConfig, dwRequiredSize, &dwRequiredSize);
    dwOldStartType = lpConfig->dwStartType;
    HeapFree(GetProcessHeap(), 0, lpConfig);
    if (dwOldStartType == SERVICE_DISABLED)
    {
        ChangeServiceConfigW(hSvc,
                             SERVICE_NO_CHANGE,
                             SERVICE_DEMAND_START,
                             SERVICE_NO_CHANGE,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL);
    }
    QueryServiceStatus(hSvc, &status);
    bWasRunning = (status.dwCurrentState != SERVICE_STOPPED);
    if (bWasRunning)
    {
        ControlService(hSvc, SERVICE_CONTROL_STOP, &status);
        Sleep(1000); /* Wait 1 second for the service to stop */
    }

    /* Now try to start it (this test won't work under Windows Vista / 7 / 8) */
    SetLastError(0xdeadbeef);
    bError = StartServiceW(hSvc, 0, NULL);
    ok(bError == FALSE, "bError = %u, expected FALSE\n", bError);
    ok_err(ERROR_SERVICE_DATABASE_LOCKED);
    Sleep(1000); /* Wait 1 second for the service to start */

    /* Stop the testing service */
    ControlService(hSvc, SERVICE_CONTROL_STOP, &status);
    Sleep(1000); /* Wait 1 second for the service to stop */

    /* Now unlock the services database */
    SetLastError(0xdeadbeef);
    bError = UnlockServiceDatabase(hLock);
    ok(bError == TRUE, "bError = %u, expected TRUE\n", bError);
    ok_err(ERROR_SUCCESS);

    /* Try to start again the service, this time the database unlocked */
    SetLastError(0xdeadbeef);
    bError = StartServiceW(hSvc, 0, NULL);
    ok(bError == TRUE, "bError = %u, expected TRUE\n", bError);
    ok_err(ERROR_SUCCESS);
    Sleep(1000); /* Wait 1 second for the service to start */

    /* Stop the testing service */
    ControlService(hSvc, SERVICE_CONTROL_STOP, &status);
    Sleep(1000); /* Wait 1 second for the service to stop */

    /* Restore its original state */
    if (bWasRunning)
    {
        StartServiceW(hSvc, 0, NULL);
    }

    if (dwOldStartType == SERVICE_DISABLED)
    {
        ChangeServiceConfigW(hSvc,
                             SERVICE_NO_CHANGE,
                             SERVICE_DISABLED,
                             SERVICE_NO_CHANGE,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL);
    }


cleanup:
    if (hSvc)
        CloseServiceHandle(hSvc);

    if (hScm)
        CloseServiceHandle(hScm);

    return;
}

static void Test_QueryLockStatusW(void)
{
    BOOL bError = FALSE;

    SC_HANDLE hScm  = NULL;
    SC_LOCK   hLock = NULL;
    LPQUERY_SERVICE_LOCK_STATUSW lpLockStatus = NULL;
    DWORD dwRequiredSize = 0;

    /* Firstly try to get lock status with invalid handles */
    SetLastError(0xdeadbeef);
    bError = QueryServiceLockStatusW(hScm,
                                     NULL,
                                     0,
                                     &dwRequiredSize);
    ok(bError == FALSE && GetLastError() == ERROR_INVALID_HANDLE, "(bError, GetLastError()) = (%u, 0x%08lx), expected (FALSE, 0x%08lx)\n", bError, GetLastError(), (DWORD)ERROR_INVALID_HANDLE);
    ok(dwRequiredSize == 0, "dwRequiredSize is non-zero, expected zero\n");

    /* Open the services database without having rights */
    SetLastError(0xdeadbeef);
    hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
    ok(hScm != NULL, "Failed to open service manager, error=0x%08lx\n", GetLastError());
    if (!hScm)
    {
        skip("No service control manager; cannot proceed with QueryLockStatusW test\n");
        goto cleanup;
    }
    ok_err(ERROR_SUCCESS);

    /* Try to get lock status */
    SetLastError(0xdeadbeef);
    bError = QueryServiceLockStatusW(hScm,
                                     NULL,
                                     0,
                                     &dwRequiredSize);
    ok(bError == FALSE && GetLastError() == ERROR_ACCESS_DENIED, "(bError, GetLastError()) = (%u, 0x%08lx), expected (FALSE, 0x%08lx)\n", bError, GetLastError(), (DWORD)ERROR_ACCESS_DENIED);
    ok(dwRequiredSize == 0, "dwRequiredSize is non-zero, expected zero\n");

    CloseServiceHandle(hScm);


    /*
     * Query only the lock status.
     */

    SetLastError(0xdeadbeef);
    hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_QUERY_LOCK_STATUS);
    ok(hScm != NULL, "Failed to open service manager, error=0x%08lx\n", GetLastError());
    if (!hScm)
    {
        skip("No service control manager; cannot proceed with QueryLockStatusW test\n");
        goto cleanup;
    }
    ok_err(ERROR_SUCCESS);

    /* Get the needed size */
    SetLastError(0xdeadbeef);
    bError = QueryServiceLockStatusW(hScm,
                                     NULL,
                                     0,
                                     &dwRequiredSize);
    ok(bError == FALSE && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "(bError, GetLastError()) = (%u, 0x%08lx), expected (FALSE, 0x%08lx)\n", bError, GetLastError(), (DWORD)ERROR_INSUFFICIENT_BUFFER);
    ok(dwRequiredSize != 0, "dwRequiredSize is zero, expected non-zero\n");
    if (dwRequiredSize == 0)
    {
        skip("Required size is null; cannot proceed with QueryLockStatusW test\n");
        goto cleanup;
    }

    /* Allocate memory */
    lpLockStatus = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwRequiredSize);
    if (lpLockStatus == NULL)
    {
        skip("Cannot allocate %lu bytes of memory\n", dwRequiredSize);
        goto cleanup;
    }

    /* Get the actual value */
    SetLastError(0xdeadbeef);
    bError = QueryServiceLockStatusW(hScm,
                                     lpLockStatus,
                                     dwRequiredSize,
                                     &dwRequiredSize);
    ok(bError, "bError = %u, expected TRUE\n", bError);

    /* These conditions must be verified iff the services database wasn't previously locked */
    ok(lpLockStatus->fIsLocked == 0, "lpLockStatus->fIsLocked = %lu, expected 0\n", lpLockStatus->fIsLocked);
    ok(lpLockStatus->lpLockOwner != NULL, "lpLockStatus->lpLockOwner is null, expected non-null\n");
    ok(lpLockStatus->lpLockOwner && *lpLockStatus->lpLockOwner == 0, "*lpLockStatus->lpLockOwner != \"\\0\", expected \"\\0\"\n");
    ok(lpLockStatus->dwLockDuration == 0, "lpLockStatus->dwLockDuration = %lu, expected 0\n", lpLockStatus->dwLockDuration);

    HeapFree(GetProcessHeap(), 0, lpLockStatus);

    CloseServiceHandle(hScm);


    /*
     * Now, try to lock the database and check its lock status.
     */

    SetLastError(0xdeadbeef);
    hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_LOCK | SC_MANAGER_QUERY_LOCK_STATUS);
    ok(hScm != NULL, "Failed to open service manager, error=0x%08lx\n", GetLastError());
    if (!hScm)
    {
        skip("No service control manager; cannot proceed with QueryLockStatusW test\n");
        goto cleanup;
    }
    ok_err(ERROR_SUCCESS);

    /* Get the needed size */
    SetLastError(0xdeadbeef);
    bError = QueryServiceLockStatusW(hScm,
                                     NULL,
                                     0,
                                     &dwRequiredSize);
    ok(bError == FALSE && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "(bError, GetLastError()) = (%u, 0x%08lx), expected (FALSE, 0x%08lx)\n", bError, GetLastError(), (DWORD)ERROR_INSUFFICIENT_BUFFER);
    ok(dwRequiredSize != 0, "dwRequiredSize is zero, expected non-zero\n");
    if (dwRequiredSize == 0)
    {
        skip("Required size is null; cannot proceed with QueryLockStatusW test\n");
        goto cleanup;
    }

    /* Allocate memory */
    lpLockStatus = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwRequiredSize);
    if (lpLockStatus == NULL)
    {
        skip("Cannot allocate %lu bytes of memory\n", dwRequiredSize);
        goto cleanup;
    }

    /* Get the actual value */
    SetLastError(0xdeadbeef);
    bError = QueryServiceLockStatusW(hScm,
                                     lpLockStatus,
                                     dwRequiredSize,
                                     &dwRequiredSize);
    ok(bError, "bError = %u, expected TRUE\n", bError);

    /* These conditions must be verified iff the services database wasn't previously locked */
    ok(lpLockStatus->fIsLocked == 0, "lpLockStatus->fIsLocked = %lu, expected 0\n", lpLockStatus->fIsLocked);
    ok(lpLockStatus->lpLockOwner != NULL, "lpLockStatus->lpLockOwner is null, expected non-null\n");
    ok(lpLockStatus->lpLockOwner && *lpLockStatus->lpLockOwner == 0, "*lpLockStatus->lpLockOwner != \"\\0\", expected \"\\0\"\n");
    ok(lpLockStatus->dwLockDuration == 0, "lpLockStatus->dwLockDuration = %lu, expected 0\n", lpLockStatus->dwLockDuration);

    HeapFree(GetProcessHeap(), 0, lpLockStatus);


    /*
     * Try again, this time with the database locked.
     */

    SetLastError(0xdeadbeef);
    hLock = LockServiceDatabase(hScm);
    ok(hLock != NULL, "hLock = 0x%p, expected non-zero\n", hLock);
    ok_err(ERROR_SUCCESS);

    Sleep(1000); /* Wait 1 second to let lpLockStatus->dwLockDuration increment */

    /* Get the needed size */
    SetLastError(0xdeadbeef);
    bError = QueryServiceLockStatusW(hScm,
                                     NULL,
                                     0,
                                     &dwRequiredSize);
    ok(bError == FALSE && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "(bError, GetLastError()) = (%u, 0x%08lx), expected (FALSE, 0x%08lx)\n", bError, GetLastError(), (DWORD)ERROR_INSUFFICIENT_BUFFER);
    ok(dwRequiredSize != 0, "dwRequiredSize is zero, expected non-zero\n");
    if (dwRequiredSize == 0)
    {
        skip("Required size is null; cannot proceed with QueryLockStatusW test\n");
        goto cleanup;
    }

    /* Allocate memory */
    lpLockStatus = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwRequiredSize);
    if (lpLockStatus == NULL)
    {
        skip("Cannot allocate %lu bytes of memory\n", dwRequiredSize);
        goto cleanup;
    }

    /* Get the actual value */
    SetLastError(0xdeadbeef);
    bError = QueryServiceLockStatusW(hScm,
                                     lpLockStatus,
                                     dwRequiredSize,
                                     &dwRequiredSize);
    ok(bError, "bError = %u, expected TRUE\n", bError);

    /* These conditions must be verified iff the services database is locked */
    ok(lpLockStatus->fIsLocked != 0, "lpLockStatus->fIsLocked = %lu, expected non-zero\n", lpLockStatus->fIsLocked);
    ok(lpLockStatus->lpLockOwner != NULL, "lpLockStatus->lpLockOwner is null, expected non-null\n");
    ok(lpLockStatus->lpLockOwner && *lpLockStatus->lpLockOwner != 0, "*lpLockStatus->lpLockOwner = \"\\0\", expected non-zero\n");
    ok(lpLockStatus->dwLockDuration != 0, "lpLockStatus->dwLockDuration = %lu, expected non-zero\n", lpLockStatus->dwLockDuration);

    HeapFree(GetProcessHeap(), 0, lpLockStatus);


    /*
     * Last try, with the database again unlocked.
     */

    SetLastError(0xdeadbeef);
    bError = UnlockServiceDatabase(hLock);
    ok(bError == TRUE, "bError = %u, expected TRUE\n", bError);
    ok_err(ERROR_SUCCESS);
    hLock = NULL;

    /* Get the needed size */
    SetLastError(0xdeadbeef);
    bError = QueryServiceLockStatusW(hScm,
                                     NULL,
                                     0,
                                     &dwRequiredSize);
    ok(bError == FALSE && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "(bError, GetLastError()) = (%u, 0x%08lx), expected (FALSE, 0x%08lx)\n", bError, GetLastError(), (DWORD)ERROR_INSUFFICIENT_BUFFER);
    ok(dwRequiredSize != 0, "dwRequiredSize is zero, expected non-zero\n");
    if (dwRequiredSize == 0)
    {
        skip("Required size is null; cannot proceed with QueryLockStatusW test\n");
        goto cleanup;
    }

    /* Allocate memory */
    lpLockStatus = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwRequiredSize);
    if (lpLockStatus == NULL)
    {
        skip("Cannot allocate %lu bytes of memory\n", dwRequiredSize);
        goto cleanup;
    }

    /* Get the actual value */
    SetLastError(0xdeadbeef);
    bError = QueryServiceLockStatusW(hScm,
                                     lpLockStatus,
                                     dwRequiredSize,
                                     &dwRequiredSize);
    ok(bError, "bError = %u, expected TRUE\n", bError);

    /* These conditions must be verified iff the services database is unlocked */
    ok(lpLockStatus->fIsLocked == 0, "lpLockStatus->fIsLocked = %lu, expected 0\n", lpLockStatus->fIsLocked);
    ok(lpLockStatus->lpLockOwner != NULL, "lpLockStatus->lpLockOwner is null, expected non-null\n");
    ok(lpLockStatus->lpLockOwner && *lpLockStatus->lpLockOwner == 0, "*lpLockStatus->lpLockOwner != \"\\0\", expected \"\\0\"\n");
    ok(lpLockStatus->dwLockDuration == 0, "lpLockStatus->dwLockDuration = %lu, expected 0\n", lpLockStatus->dwLockDuration);

    HeapFree(GetProcessHeap(), 0, lpLockStatus);


cleanup:
    if (hLock)
        UnlockServiceDatabase(hLock);

    if (hScm)
        CloseServiceHandle(hScm);

    return;
}

static void Test_QueryLockStatusA(void)
{
    BOOL bError = FALSE;

    SC_HANDLE hScm  = NULL;
    SC_LOCK   hLock = NULL;
    LPQUERY_SERVICE_LOCK_STATUSA lpLockStatus = NULL;
    DWORD dwRequiredSize = 0;

    /* Firstly try to get lock status with invalid handles */
    SetLastError(0xdeadbeef);
    bError = QueryServiceLockStatusA(hScm,
                                     NULL,
                                     0,
                                     &dwRequiredSize);
    ok(bError == FALSE && GetLastError() == ERROR_INVALID_HANDLE, "(bError, GetLastError()) = (%u, 0x%08lx), expected (FALSE, 0x%08lx)\n", bError, GetLastError(), (DWORD)ERROR_INVALID_HANDLE);
    ok(dwRequiredSize == 0, "dwRequiredSize is non-zero, expected zero\n");

    /* Open the services database without having rights */
    SetLastError(0xdeadbeef);
    hScm = OpenSCManagerA(NULL, NULL, SC_MANAGER_CONNECT);
    ok(hScm != NULL, "Failed to open service manager, error=0x%08lx\n", GetLastError());
    if (!hScm)
    {
        skip("No service control manager; cannot proceed with QueryLockStatusA test\n");
        goto cleanup;
    }
    ok_err(ERROR_SUCCESS);

    /* Try to get lock status */
    SetLastError(0xdeadbeef);
    bError = QueryServiceLockStatusA(hScm,
                                     NULL,
                                     0,
                                     &dwRequiredSize);
    ok(bError == FALSE && GetLastError() == ERROR_ACCESS_DENIED, "(bError, GetLastError()) = (%u, 0x%08lx), expected (FALSE, 0x%08lx)\n", bError, GetLastError(), (DWORD)ERROR_ACCESS_DENIED);
    ok(dwRequiredSize == 0, "dwRequiredSize is non-zero, expected zero\n");

    CloseServiceHandle(hScm);


    /*
     * Query only the lock status.
     */

    SetLastError(0xdeadbeef);
    hScm = OpenSCManagerA(NULL, NULL, SC_MANAGER_QUERY_LOCK_STATUS);
    ok(hScm != NULL, "Failed to open service manager, error=0x%08lx\n", GetLastError());
    if (!hScm)
    {
        skip("No service control manager; cannot proceed with QueryLockStatusA test\n");
        goto cleanup;
    }
    ok_err(ERROR_SUCCESS);

    /* Get the needed size */
    SetLastError(0xdeadbeef);
    bError = QueryServiceLockStatusA(hScm,
                                     NULL,
                                     0,
                                     &dwRequiredSize);
    ok(bError == FALSE && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "(bError, GetLastError()) = (%u, 0x%08lx), expected (FALSE, 0x%08lx)\n", bError, GetLastError(), (DWORD)ERROR_INSUFFICIENT_BUFFER);
    ok(dwRequiredSize != 0, "dwRequiredSize is zero, expected non-zero\n");
    if (dwRequiredSize == 0)
    {
        skip("Required size is null; cannot proceed with QueryLockStatusA test\n");
        goto cleanup;
    }

    /* Allocate memory */
    lpLockStatus = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwRequiredSize);
    if (lpLockStatus == NULL)
    {
        skip("Cannot allocate %lu bytes of memory\n", dwRequiredSize);
        goto cleanup;
    }

    /* Get the actual value */
    SetLastError(0xdeadbeef);
    bError = QueryServiceLockStatusA(hScm,
                                     lpLockStatus,
                                     dwRequiredSize,
                                     &dwRequiredSize);
    ok(bError, "bError = %u, expected TRUE\n", bError);

    /* These conditions must be verified iff the services database wasn't previously locked */
    ok(lpLockStatus->fIsLocked == 0, "lpLockStatus->fIsLocked = %lu, expected 0\n", lpLockStatus->fIsLocked);
    ok(lpLockStatus->lpLockOwner != NULL, "lpLockStatus->lpLockOwner is null, expected non-null\n");
    ok(lpLockStatus->lpLockOwner && *lpLockStatus->lpLockOwner == 0, "*lpLockStatus->lpLockOwner != \"\\0\", expected \"\\0\"\n");
    ok(lpLockStatus->dwLockDuration == 0, "lpLockStatus->dwLockDuration = %lu, expected 0\n", lpLockStatus->dwLockDuration);

    HeapFree(GetProcessHeap(), 0, lpLockStatus);

    CloseServiceHandle(hScm);


    /*
     * Now, try to lock the database and check its lock status.
     */

    SetLastError(0xdeadbeef);
    hScm = OpenSCManagerA(NULL, NULL, SC_MANAGER_LOCK | SC_MANAGER_QUERY_LOCK_STATUS);
    ok(hScm != NULL, "Failed to open service manager, error=0x%08lx\n", GetLastError());
    if (!hScm)
    {
        skip("No service control manager; cannot proceed with QueryLockStatusA test\n");
        goto cleanup;
    }
    ok_err(ERROR_SUCCESS);

    /* Get the needed size */
    SetLastError(0xdeadbeef);
    bError = QueryServiceLockStatusA(hScm,
                                     NULL,
                                     0,
                                     &dwRequiredSize);
    ok(bError == FALSE && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "(bError, GetLastError()) = (%u, 0x%08lx), expected (FALSE, 0x%08lx)\n", bError, GetLastError(), (DWORD)ERROR_INSUFFICIENT_BUFFER);
    ok(dwRequiredSize != 0, "dwRequiredSize is zero, expected non-zero\n");
    if (dwRequiredSize == 0)
    {
        skip("Required size is null; cannot proceed with QueryLockStatusA test\n");
        goto cleanup;
    }

    /* Allocate memory */
    lpLockStatus = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwRequiredSize);
    if (lpLockStatus == NULL)
    {
        skip("Cannot allocate %lu bytes of memory\n", dwRequiredSize);
        goto cleanup;
    }

    /* Get the actual value */
    SetLastError(0xdeadbeef);
    bError = QueryServiceLockStatusA(hScm,
                                     lpLockStatus,
                                     dwRequiredSize,
                                     &dwRequiredSize);
    ok(bError, "bError = %u, expected TRUE\n", bError);

    /* These conditions must be verified iff the services database wasn't previously locked */
    ok(lpLockStatus->fIsLocked == 0, "lpLockStatus->fIsLocked = %lu, expected 0\n", lpLockStatus->fIsLocked);
    ok(lpLockStatus->lpLockOwner != NULL, "lpLockStatus->lpLockOwner is null, expected non-null\n");
    ok(lpLockStatus->lpLockOwner && *lpLockStatus->lpLockOwner == 0, "*lpLockStatus->lpLockOwner != \"\\0\", expected \"\\0\"\n");
    ok(lpLockStatus->dwLockDuration == 0, "lpLockStatus->dwLockDuration = %lu, expected 0\n", lpLockStatus->dwLockDuration);

    HeapFree(GetProcessHeap(), 0, lpLockStatus);


    /*
     * Try again, this time with the database locked.
     */

    SetLastError(0xdeadbeef);
    hLock = LockServiceDatabase(hScm);
    ok(hLock != NULL, "hLock = 0x%p, expected non-zero\n", hLock);
    ok_err(ERROR_SUCCESS);

    Sleep(1000); /* Wait 1 second to let lpLockStatus->dwLockDuration increment */

    /* Get the needed size */
    SetLastError(0xdeadbeef);
    bError = QueryServiceLockStatusA(hScm,
                                     NULL,
                                     0,
                                     &dwRequiredSize);
    ok(bError == FALSE && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "(bError, GetLastError()) = (%u, 0x%08lx), expected (FALSE, 0x%08lx)\n", bError, GetLastError(), (DWORD)ERROR_INSUFFICIENT_BUFFER);
    ok(dwRequiredSize != 0, "dwRequiredSize is zero, expected non-zero\n");
    if (dwRequiredSize == 0)
    {
        skip("Required size is null; cannot proceed with QueryLockStatusA test\n");
        goto cleanup;
    }

    /* Allocate memory */
    lpLockStatus = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwRequiredSize);
    if (lpLockStatus == NULL)
    {
        skip("Cannot allocate %lu bytes of memory\n", dwRequiredSize);
        goto cleanup;
    }

    /* Get the actual value */
    SetLastError(0xdeadbeef);
    bError = QueryServiceLockStatusA(hScm,
                                     lpLockStatus,
                                     dwRequiredSize,
                                     &dwRequiredSize);
    ok(bError, "bError = %u, expected TRUE\n", bError);

    /* These conditions must be verified iff the services database is locked */
    ok(lpLockStatus->fIsLocked != 0, "lpLockStatus->fIsLocked = %lu, expected non-zero\n", lpLockStatus->fIsLocked);
    ok(lpLockStatus->lpLockOwner != NULL, "lpLockStatus->lpLockOwner is null, expected non-null\n");
    ok(lpLockStatus->lpLockOwner && *lpLockStatus->lpLockOwner != 0, "*lpLockStatus->lpLockOwner = \"\\0\", expected non-zero\n");
    ok(lpLockStatus->dwLockDuration != 0, "lpLockStatus->dwLockDuration = %lu, expected non-zero\n", lpLockStatus->dwLockDuration);

    HeapFree(GetProcessHeap(), 0, lpLockStatus);


    /*
     * Last try, with the database again unlocked.
     */

    SetLastError(0xdeadbeef);
    bError = UnlockServiceDatabase(hLock);
    ok(bError == TRUE, "bError = %u, expected TRUE\n", bError);
    ok_err(ERROR_SUCCESS);
    hLock = NULL;

    /* Get the needed size */
    SetLastError(0xdeadbeef);
    bError = QueryServiceLockStatusA(hScm,
                                     NULL,
                                     0,
                                     &dwRequiredSize);
    ok(bError == FALSE && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "(bError, GetLastError()) = (%u, 0x%08lx), expected (FALSE, 0x%08lx)\n", bError, GetLastError(), (DWORD)ERROR_INSUFFICIENT_BUFFER);
    ok(dwRequiredSize != 0, "dwRequiredSize is zero, expected non-zero\n");
    if (dwRequiredSize == 0)
    {
        skip("Required size is null; cannot proceed with QueryLockStatusA test\n");
        goto cleanup;
    }

    /* Allocate memory */
    lpLockStatus = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwRequiredSize);
    if (lpLockStatus == NULL)
    {
        skip("Cannot allocate %lu bytes of memory\n", dwRequiredSize);
        goto cleanup;
    }

    /* Get the actual value */
    SetLastError(0xdeadbeef);
    bError = QueryServiceLockStatusA(hScm,
                                     lpLockStatus,
                                     dwRequiredSize,
                                     &dwRequiredSize);
    ok(bError, "bError = %u, expected TRUE\n", bError);

    /* These conditions must be verified iff the services database is unlocked */
    ok(lpLockStatus->fIsLocked == 0, "lpLockStatus->fIsLocked = %lu, expected 0\n", lpLockStatus->fIsLocked);
    ok(lpLockStatus->lpLockOwner != NULL, "lpLockStatus->lpLockOwner is null, expected non-null\n");
    ok(lpLockStatus->lpLockOwner && *lpLockStatus->lpLockOwner == 0, "*lpLockStatus->lpLockOwner != \"\\0\", expected \"\\0\"\n");
    ok(lpLockStatus->dwLockDuration == 0, "lpLockStatus->dwLockDuration = %lu, expected 0\n", lpLockStatus->dwLockDuration);

    HeapFree(GetProcessHeap(), 0, lpLockStatus);


cleanup:
    if (hLock)
        UnlockServiceDatabase(hLock);

    if (hScm)
        CloseServiceHandle(hScm);

    return;
}


START_TEST(LockDatabase)
{
    Test_LockUnlockServiceDatabase();
    Test_LockUnlockServiceDatabaseWithServiceStart();
    Test_QueryLockStatusW();
    Test_QueryLockStatusA();
}