为什么GetServiceDisplayNameW()和GetServiceDisplayNameA()返回不同的必需缓冲区大小?

时间:2017-12-04 09:18:41

标签: c++ winapi windows-services

下面是一个示例代码(只有示例代码才能轻松理解,没有错误处理,没有关闭句柄等等):

SC_HANDLE hSCManager = ::OpenSCManager(nullptr, nullptr, 0);

DWORD buffSize = 0;
::GetServiceDisplayName(hSCManager, m_serviceName, nullptr, &buffSize);

LPTSTR buff = new TCHAR[++buffSize];
VERIFY(::GetServiceDisplayName(hSCManager, m_serviceName, buff, &buffSize));

我的示例服务的显示名称为"notepad starter"(15个字符)。

在构建配置之间切换,GetServiceDisplayName()在ANSI(GetServiceDisplayNameA)下返回缓冲区大小30,在UNICODE(GetServiceDisplayNameW)下返回15。

此API的文档说它返回字符中的缓冲区大小,不包括空终止符(没有详细记录,但我希望缓冲区大小在第二次调用中包含空终止符)。

为什么它在不同的构建配置中返回不同的缓冲区大小?

1 个答案:

答案 0 :(得分:3)

首先GetServiceDisplayName将服务控制管理器数据库( hSCManager )作为第一个参数处理,但不处理服务( hService ) - 所以你不需要为此任务提供开放服务。你不需要SC_MANAGER_ALL_ACCESS,但0就够了。

然而你的主要错误在下一个。你在字符中分配缓冲区new TCHAR[buffSize + 1] - 所以buffSize + 1 - 这是正确的,因为GetServiceDisplayName返回服务显示名称的大小,不包括空终止字符 - 所以我们需要额外的一个字符空间来终止0;

但是在下一行错误 - &buffSize - 最后一个参数 lpcchBuffer 必须包含缓冲区的大小(以字符为单位)。所以你分配的确切缓冲区大小。但是您分配buffSize + 1空格,而不是buffSize。所以代码必须是下一个:

if (SC_HANDLE hSCManager = OpenSCManagerW(nullptr, nullptr, 0))
{
    DWORD cch = 0;
    if (!GetServiceDisplayNameW(hSCManager, m_serviceName, nullptr, &cch))
    {
        if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
        {
            PWSTR buff =(PWSTR)alloca(++cch*sizeof(WCHAR));
            if (GetServiceDisplayNameW(hSCManager, m_serviceName, buff, &cch))
            {
                DbgPrint("%S\n", buff);
            }
        }
    }
    CloseServiceHandle(hSCManager);
}

因此,您的代码必须将buffSize + 1替换为++buffSize

关于ansi版本 - GetServiceDisplayNameA - 这里api实现真的很错误 - 如果字符中的缓冲区大小不够大 - 它会返回多少字节需要 unicode 服务名称,不包括空终止符号。如果缓冲区足够大,它根本不会更新 lpcchBuffer 。这还有一个参数从不使用api的A版本,但总是W