这里是一个示例代码(只是示例代码以便于理解,没有错误处理,没有关闭句柄等等):
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 的文档表示,它返回以 characters 为单位的缓冲区大小,不包括空终止符(没有详细记录,但我希望缓冲区大小在第二次调用中包含空终止符)。
为什么它在不同的构建配置中返回不同的缓冲区大小?
首先
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 实现中确实存在错误 - 如果字符缓冲区大小不够大 - 它返回需要多少 bytes 需要 unicode 服务名称(不包括空终止符号)。如果缓冲区足够大,则根本不会更新lpcchBuffer。这还有一个论点从不使用 A
版本的 api,但总是 W
我认为正确的答案是在 6 个月后(我在 3 年后才看到的)来自 Raymond Chen;
为什么它报告的所需缓冲区大小比实际大小更大 真的需要吗?
因为字符集转换比较困难。
当您调用GetServiceDisplayNameA函数(ANSI版本)时, 它将调用转发给 GetServiceDisplayNameW 函数(Unicode 版本)。如果 Unicode 版本说:“抱歉,该缓冲区太 小的;它需要足够大以容纳 N 个 Unicode 字符,” ANSI 版本不知道翻译成多少个 ANSI 字符。 单个 Unicode 字符可以扩展到多达两个 ANSI ANSI 代码页为 DBCS 时的字符。这 GetServiceDisplayNameA 函数谨慎行事并采用 最坏的情况是服务显示名称完全由 需要两个 ANSI 字符来表示的 Unicode 字符。
这就是它过度报告缓冲区大小的原因。