[SERVICES] Simplify the implementation of RGetServiceDisplayNameA/W() and RGetServiceKeyNameA/W().

Also comment about the observed behaviour of the returned number of "characters"
returned by the ANSI versions of these APIs (which is tested by advapi32_winetest:service).

[ADVAPI32:SCM] Add a comment concerning wide characters vs. bytes mismatch.
This commit is contained in:
Hermès Bélusca-Maïto 2018-09-22 15:41:17 +02:00
parent 60e166535a
commit bf164caae8
No known key found for this signature in database
GPG key ID: 3B2539C65E7B93D0
2 changed files with 182 additions and 145 deletions

View file

@ -2947,7 +2947,7 @@ ROpenServiceW(
lpService = ScmGetServiceEntryByName(lpServiceName); lpService = ScmGetServiceEntryByName(lpServiceName);
if (lpService == NULL) if (lpService == NULL)
{ {
DPRINT("Could not find a service!\n"); DPRINT("Could not find the service!\n");
dwError = ERROR_SERVICE_DOES_NOT_EXIST; dwError = ERROR_SERVICE_DOES_NOT_EXIST;
goto Done; goto Done;
} }
@ -3310,8 +3310,9 @@ RGetServiceDisplayNameW(
LPWSTR lpDisplayName, LPWSTR lpDisplayName,
DWORD *lpcchBuffer) DWORD *lpcchBuffer)
{ {
// PMANAGER_HANDLE hManager; // PMANAGER_HANDLE hManager;
PSERVICE lpService; PSERVICE lpService;
LPCWSTR lpSvcDisplayName;
DWORD dwLength; DWORD dwLength;
DWORD dwError; DWORD dwError;
@ -3321,56 +3322,42 @@ RGetServiceDisplayNameW(
DPRINT("lpDisplayName: %p\n", lpDisplayName); DPRINT("lpDisplayName: %p\n", lpDisplayName);
DPRINT("*lpcchBuffer: %lu\n", *lpcchBuffer); DPRINT("*lpcchBuffer: %lu\n", *lpcchBuffer);
// hManager = (PMANAGER_HANDLE)hSCManager; #if 0
// if (hManager->Handle.Tag != MANAGER_TAG) hManager = (PMANAGER_HANDLE)hSCManager;
// { if (hManager->Handle.Tag != MANAGER_TAG)
// DPRINT("Invalid manager handle!\n"); {
// return ERROR_INVALID_HANDLE; DPRINT("Invalid manager handle!\n");
// } return ERROR_INVALID_HANDLE;
}
#endif
/* Get service database entry */ /* Get service database entry */
lpService = ScmGetServiceEntryByName(lpServiceName); lpService = ScmGetServiceEntryByName(lpServiceName);
if (lpService == NULL) if (lpService == NULL)
{ {
DPRINT("Could not find a service!\n"); DPRINT("Could not find the service!\n");
/* If the service could not be found and lpcchBuffer is less than 2, windows
puts null in lpDisplayName and puts 2 in lpcchBuffer */
if (*lpcchBuffer < sizeof(WCHAR))
{
*lpcchBuffer = sizeof(WCHAR);
if (lpDisplayName != NULL)
{
*lpDisplayName = 0;
}
}
return ERROR_SERVICE_DOES_NOT_EXIST; return ERROR_SERVICE_DOES_NOT_EXIST;
} }
if (!lpService->lpDisplayName) if (lpService->lpDisplayName)
{ lpSvcDisplayName = lpService->lpDisplayName;
dwLength = (DWORD)wcslen(lpService->lpServiceName); else
lpSvcDisplayName = lpService->lpServiceName;
if (lpDisplayName != NULL && dwLength = (DWORD)wcslen(lpSvcDisplayName);
*lpcchBuffer > dwLength)
{ if (*lpcchBuffer > dwLength)
wcscpy(lpDisplayName, lpService->lpServiceName); {
} if (lpDisplayName != NULL)
wcscpy(lpDisplayName, lpSvcDisplayName);
dwError = ERROR_SUCCESS;
} }
else else
{ {
dwLength = (DWORD)wcslen(lpService->lpDisplayName); dwError = ERROR_INSUFFICIENT_BUFFER;
if (lpDisplayName != NULL &&
*lpcchBuffer > dwLength)
{
wcscpy(lpDisplayName, lpService->lpDisplayName);
}
} }
dwError = (*lpcchBuffer > dwLength) ? ERROR_SUCCESS : ERROR_INSUFFICIENT_BUFFER;
*lpcchBuffer = dwLength; *lpcchBuffer = dwLength;
return dwError; return dwError;
@ -3386,7 +3373,7 @@ RGetServiceKeyNameW(
LPWSTR lpServiceName, LPWSTR lpServiceName,
DWORD *lpcchBuffer) DWORD *lpcchBuffer)
{ {
// PMANAGER_HANDLE hManager; // PMANAGER_HANDLE hManager;
PSERVICE lpService; PSERVICE lpService;
DWORD dwLength; DWORD dwLength;
DWORD dwError; DWORD dwError;
@ -3397,44 +3384,36 @@ RGetServiceKeyNameW(
DPRINT("lpServiceName: %p\n", lpServiceName); DPRINT("lpServiceName: %p\n", lpServiceName);
DPRINT("*lpcchBuffer: %lu\n", *lpcchBuffer); DPRINT("*lpcchBuffer: %lu\n", *lpcchBuffer);
// hManager = (PMANAGER_HANDLE)hSCManager; #if 0
// if (hManager->Handle.Tag != MANAGER_TAG) hManager = (PMANAGER_HANDLE)hSCManager;
// { if (hManager->Handle.Tag != MANAGER_TAG)
// DPRINT("Invalid manager handle!\n"); {
// return ERROR_INVALID_HANDLE; DPRINT("Invalid manager handle!\n");
// } return ERROR_INVALID_HANDLE;
}
#endif
/* Get service database entry */ /* Get service database entry */
lpService = ScmGetServiceEntryByDisplayName(lpDisplayName); lpService = ScmGetServiceEntryByDisplayName(lpDisplayName);
if (lpService == NULL) if (lpService == NULL)
{ {
DPRINT("Could not find a service!\n"); DPRINT("Could not find the service!\n");
/* If the service could not be found and lpcchBuffer is less than 2, windows
puts null in lpDisplayName and puts 2 in lpcchBuffer */
if (*lpcchBuffer < sizeof(WCHAR))
{
*lpcchBuffer = sizeof(WCHAR);
if (lpServiceName != NULL)
{
*lpServiceName = 0;
}
}
return ERROR_SERVICE_DOES_NOT_EXIST; return ERROR_SERVICE_DOES_NOT_EXIST;
} }
dwLength = (DWORD)wcslen(lpService->lpServiceName); dwLength = (DWORD)wcslen(lpService->lpServiceName);
if (lpServiceName != NULL && if (*lpcchBuffer > dwLength)
*lpcchBuffer > dwLength)
{ {
wcscpy(lpServiceName, lpService->lpServiceName); if (lpServiceName != NULL)
*lpcchBuffer = dwLength; wcscpy(lpServiceName, lpService->lpServiceName);
return ERROR_SUCCESS;
}
dwError = (*lpcchBuffer > dwLength) ? ERROR_SUCCESS : ERROR_INSUFFICIENT_BUFFER; dwError = ERROR_SUCCESS;
}
else
{
dwError = ERROR_INSUFFICIENT_BUFFER;
}
*lpcchBuffer = dwLength; *lpcchBuffer = dwLength;
@ -4474,11 +4453,11 @@ RGetServiceDisplayNameA(
LPSTR lpDisplayName, LPSTR lpDisplayName,
LPBOUNDED_DWORD_4K lpcchBuffer) LPBOUNDED_DWORD_4K lpcchBuffer)
{ {
// PMANAGER_HANDLE hManager; // PMANAGER_HANDLE hManager;
PSERVICE lpService = NULL; PSERVICE lpService = NULL;
DWORD dwLength; LPCWSTR lpSvcDisplayName;
DWORD dwError;
LPWSTR lpServiceNameW; LPWSTR lpServiceNameW;
DWORD dwLength;
DPRINT("RGetServiceDisplayNameA() called\n"); DPRINT("RGetServiceDisplayNameA() called\n");
DPRINT("hSCManager = %p\n", hSCManager); DPRINT("hSCManager = %p\n", hSCManager);
@ -4486,13 +4465,16 @@ RGetServiceDisplayNameA(
DPRINT("lpDisplayName: %p\n", lpDisplayName); DPRINT("lpDisplayName: %p\n", lpDisplayName);
DPRINT("*lpcchBuffer: %lu\n", *lpcchBuffer); DPRINT("*lpcchBuffer: %lu\n", *lpcchBuffer);
// hManager = (PMANAGER_HANDLE)hSCManager; #if 0
// if (hManager->Handle.Tag != MANAGER_TAG) hManager = (PMANAGER_HANDLE)hSCManager;
// { if (hManager->Handle.Tag != MANAGER_TAG)
// DPRINT("Invalid manager handle!\n"); {
// return ERROR_INVALID_HANDLE; DPRINT("Invalid manager handle!\n");
// } return ERROR_INVALID_HANDLE;
}
#endif
/* Get service database entry */
if (lpServiceName != NULL) if (lpServiceName != NULL)
{ {
dwLength = (DWORD)(strlen(lpServiceName) + 1); dwLength = (DWORD)(strlen(lpServiceName) + 1);
@ -4516,61 +4498,71 @@ RGetServiceDisplayNameA(
if (lpService == NULL) if (lpService == NULL)
{ {
DPRINT("Could not find a service!\n"); DPRINT("Could not find the service!\n");
/* If the service could not be found and lpcchBuffer is 0, windows
puts null in lpDisplayName and puts 1 in lpcchBuffer */
if (*lpcchBuffer == 0)
{
*lpcchBuffer = sizeof(CHAR);
if (lpDisplayName != NULL)
{
*lpDisplayName = 0;
}
}
return ERROR_SERVICE_DOES_NOT_EXIST; return ERROR_SERVICE_DOES_NOT_EXIST;
} }
if (!lpService->lpDisplayName) if (lpService->lpDisplayName)
lpSvcDisplayName = lpService->lpDisplayName;
else
lpSvcDisplayName = lpService->lpServiceName;
/*
* NOTE: On Windows the comparison on *lpcchBuffer is made against
* the number of (wide) characters of the UNICODE display name, and
* not against the number of bytes needed to store the ANSI string.
*/
dwLength = (DWORD)wcslen(lpSvcDisplayName);
if (*lpcchBuffer > dwLength)
{ {
dwLength = (DWORD)wcslen(lpService->lpServiceName);
if (lpDisplayName != NULL && if (lpDisplayName != NULL &&
*lpcchBuffer > dwLength)
{
WideCharToMultiByte(CP_ACP, WideCharToMultiByte(CP_ACP,
0, 0,
lpService->lpServiceName, lpSvcDisplayName,
(int)wcslen(lpService->lpServiceName), -1,
lpDisplayName, lpDisplayName,
dwLength + 1, (int)*lpcchBuffer,
NULL, NULL,
NULL); NULL) == 0)
return ERROR_SUCCESS; {
/*
* But then, if *lpcchBuffer was greater than the number of
* (wide) characters of the UNICODE display name, yet smaller
* than the number of bytes needed due to the possible presence
* of DBCS characters, the *exact* number of bytes is returned
* (without the NULL terminator).
*/
dwLength = (DWORD)WideCharToMultiByte(CP_ACP,
0,
lpSvcDisplayName,
(int)dwLength,
NULL,
0,
NULL,
NULL);
*lpDisplayName = 0;
*lpcchBuffer = dwLength;
return ERROR_INSUFFICIENT_BUFFER;
} }
/*
* NOTE: On Windows, RGetServiceDisplayNameA() does not update
* *lpcchBuffer on success, contrary to RGetServiceDisplayNameW().
*/
return ERROR_SUCCESS;
} }
else else
{ {
dwLength = (DWORD)wcslen(lpService->lpDisplayName); /*
if (lpDisplayName != NULL && * NOTE: On Windows, if *lpcchBuffer is smaller than the number of
*lpcchBuffer > dwLength) * (wide) characters of the UNICODE display name, only an upper
{ * estimation is returned by doubling the string length, to account
WideCharToMultiByte(CP_ACP, * for the presence of any possible DBCS characters.
0, */
lpService->lpDisplayName, *lpcchBuffer = dwLength * sizeof(WCHAR);
(int)wcslen(lpService->lpDisplayName), return ERROR_INSUFFICIENT_BUFFER;
lpDisplayName,
dwLength + 1,
NULL,
NULL);
return ERROR_SUCCESS;
}
} }
dwError = (*lpcchBuffer > dwLength) ? ERROR_SUCCESS : ERROR_INSUFFICIENT_BUFFER;
*lpcchBuffer = dwLength * 2;
return dwError;
} }
@ -4583,10 +4575,10 @@ RGetServiceKeyNameA(
LPSTR lpServiceName, LPSTR lpServiceName,
LPBOUNDED_DWORD_4K lpcchBuffer) LPBOUNDED_DWORD_4K lpcchBuffer)
{ {
// PMANAGER_HANDLE hManager;
PSERVICE lpService; PSERVICE lpService;
DWORD dwLength;
DWORD dwError;
LPWSTR lpDisplayNameW; LPWSTR lpDisplayNameW;
DWORD dwLength;
DPRINT("RGetServiceKeyNameA() called\n"); DPRINT("RGetServiceKeyNameA() called\n");
DPRINT("hSCManager = %p\n", hSCManager); DPRINT("hSCManager = %p\n", hSCManager);
@ -4594,6 +4586,17 @@ RGetServiceKeyNameA(
DPRINT("lpServiceName: %p\n", lpServiceName); DPRINT("lpServiceName: %p\n", lpServiceName);
DPRINT("*lpcchBuffer: %lu\n", *lpcchBuffer); DPRINT("*lpcchBuffer: %lu\n", *lpcchBuffer);
#if 0
hManager = (PMANAGER_HANDLE)hSCManager;
if (hManager->Handle.Tag != MANAGER_TAG)
{
DPRINT("Invalid manager handle!\n");
return ERROR_INVALID_HANDLE;
}
#endif
/* Get service database entry */
dwLength = (DWORD)(strlen(lpDisplayName) + 1); dwLength = (DWORD)(strlen(lpDisplayName) + 1);
lpDisplayNameW = HeapAlloc(GetProcessHeap(), lpDisplayNameW = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY, HEAP_ZERO_MEMORY,
@ -4615,41 +4618,65 @@ RGetServiceKeyNameA(
if (lpService == NULL) if (lpService == NULL)
{ {
DPRINT("Could not find the service!\n"); DPRINT("Could not find the service!\n");
/* If the service could not be found and lpcchBuffer is 0,
put null in lpDisplayName and puts 1 in lpcchBuffer, verified WINXP. */
if (*lpcchBuffer == 0)
{
*lpcchBuffer = sizeof(CHAR);
if (lpServiceName != NULL)
{
*lpServiceName = 0;
}
}
return ERROR_SERVICE_DOES_NOT_EXIST; return ERROR_SERVICE_DOES_NOT_EXIST;
} }
/*
* NOTE: On Windows the comparison on *lpcchBuffer is made against
* the number of (wide) characters of the UNICODE service name, and
* not against the number of bytes needed to store the ANSI string.
*/
dwLength = (DWORD)wcslen(lpService->lpServiceName); dwLength = (DWORD)wcslen(lpService->lpServiceName);
if (lpServiceName != NULL &&
*lpcchBuffer > dwLength) if (*lpcchBuffer > dwLength)
{ {
WideCharToMultiByte(CP_ACP, if (lpServiceName != NULL &&
0, WideCharToMultiByte(CP_ACP,
lpService->lpServiceName, 0,
(int)wcslen(lpService->lpServiceName), lpService->lpServiceName,
lpServiceName, -1,
dwLength + 1, lpServiceName,
NULL, (int)*lpcchBuffer,
NULL); NULL,
NULL) == 0)
{
/*
* But then, if *lpcchBuffer was greater than the number of
* (wide) characters of the UNICODE service name, yet smaller
* than the number of bytes needed due to the possible presence
* of DBCS characters, the *exact* number of bytes is returned
* (without the NULL terminator).
*/
dwLength = (DWORD)WideCharToMultiByte(CP_ACP,
0,
lpService->lpServiceName,
(int)dwLength,
NULL,
0,
NULL,
NULL);
*lpServiceName = 0;
*lpcchBuffer = dwLength;
return ERROR_INSUFFICIENT_BUFFER;
}
/*
* NOTE: On Windows, RGetServiceKeyNameA() does not update
* *lpcchBuffer on success, contrary to RGetServiceKeyNameW().
*/
return ERROR_SUCCESS; return ERROR_SUCCESS;
} }
else
dwError = (*lpcchBuffer > dwLength) ? ERROR_SUCCESS : ERROR_INSUFFICIENT_BUFFER; {
/*
*lpcchBuffer = dwLength * 2; * NOTE: On Windows, if *lpcchBuffer is smaller than the number of
* (wide) characters of the UNICODE service name, only an upper
return dwError; * estimation is returned by doubling the string length, to account
* for the presence of any possible DBCS characters.
*/
*lpcchBuffer = dwLength * sizeof(WCHAR);
return ERROR_INSUFFICIENT_BUFFER;
}
} }

View file

@ -1693,6 +1693,11 @@ GetServiceDisplayNameW(SC_HANDLE hSCManager,
return FALSE; return FALSE;
} }
/*
* NOTE: A size of 1 character would be enough, but tests show that
* Windows returns 2 characters instead, certainly due to a WCHAR/bytes
* mismatch in their code.
*/
if (!lpDisplayName || *lpcchBuffer < sizeof(WCHAR)) if (!lpDisplayName || *lpcchBuffer < sizeof(WCHAR))
{ {
lpNameBuffer = szEmptyName; lpNameBuffer = szEmptyName;
@ -1810,6 +1815,11 @@ GetServiceKeyNameW(SC_HANDLE hSCManager,
return FALSE; return FALSE;
} }
/*
* NOTE: A size of 1 character would be enough, but tests show that
* Windows returns 2 characters instead, certainly due to a WCHAR/bytes
* mismatch in their code.
*/
if (!lpServiceName || *lpcchBuffer < sizeof(WCHAR)) if (!lpServiceName || *lpcchBuffer < sizeof(WCHAR))
{ {
lpNameBuffer = szEmptyName; lpNameBuffer = szEmptyName;