windows 结构的格式化
Formatting for windows structures
在使用 fmt lib 创建格式化程序以在我的代码中打印一些 Windows 结构时遇到一些问题。
我在下面准备了一个基本示例。目的是能够以预先结构化的方式打印出一个大结构,并且只打印我想要的,但我不太明白我得到的错误。
#include <windows.h>
#include <ntsecapi.h>
#include <iostream>
#include <fmt/format.h>
//#pragma comment(lib, "netapi32.lib")
#pragma comment(lib, "secur32.lib")
#pragma comment(lib, "ntdll.lib")
template <>
struct fmt::formatter<_SECURITY_LOGON_SESSION_DATA>
{
constexpr auto parse(format_parse_context& ctx)
{
// no specifiers
return ctx.begin();
}
template <typename F>
auto format(const _SECURITY_LOGON_SESSION_DATA& c, F &ctx)
{
std::wstring name(c.UserName.Buffer,
c.UserName.Length);
std::wstring domainName(c.LogonDomain.Buffer, c.LogonDomain.Length);
auto s = fmt::format(L"UserName: {}\n"
"LogonDomain: {}\n",
name, domainName);
return format_to(ctx.out(), s);
}
};
VOID DumpLogonData(PLUID LogonLuid)
{
NTSTATUS status;
PSECURITY_LOGON_SESSION_DATA pData = nullptr;
if (!LogonLuid)
return;
status = LsaGetLogonSessionData(LogonLuid,
&pData);
if (!NT_SUCCESS(status))
{
fmt::print("Failed to get logon session data: {}\n",
RtlNtStatusToDosError(status));
LsaFreeReturnBuffer(pData);
}
fmt::print(L"LogonSession Data\n{}\n", *pData);
LsaFreeReturnBuffer(pData);
}
BOOL EnumerateLogonSessions()
{
PLUID luidList = nullptr;
ULONG ulSessionCount;
NTSTATUS status;
status = LsaEnumerateLogonSessions(
&ulSessionCount,
&luidList);
if (!NT_SUCCESS(status))
{
fmt::print("Failed to enumerate logon sessions\n");
return FALSE;
}
for (ULONG i = 0; i < ulSessionCount; ++i)
{
DumpLogonData(&luidList[i]);
}
return TRUE;
}
int main(int argc, char **argv)
{
EnumerateLogonSessions();
std::getchar();
}
当调用 fmt::print(L"LogonSession Data\n{}\n", *pData);
时,此格式将抛出 error C2338: Cannot format an argument. To make type T formattable provide a formatter<T> specialization: https://fmt.dev/dev/api.html#udt
。
对使此格式化程序正常工作有何帮助?
一个问题是您试图传递指向 _SECURITY_LOGON_SESSION_DATA
的指针而不是对它的 (const) 引用。应该是:
fmt::print(L"LogonSession Data\n{}\n", *pData);
在 formatter::format
方法中相同。
另一个问题是你的formatter
不支持wchar_t
,应该是:
template <typename Char>
struct fmt::formatter<_SECURITY_LOGON_SESSION_DATA, Char> {
...
};
如果你想支持不同的字符类型。或者使用普通 multi-byte 字符串而不是宽字符串。
随着格式化程序的修复,我还需要专门化 parse
函数,下面是可用的格式化程序。
template <typename Char>
struct fmt::formatter<_SECURITY_LOGON_SESSION_DATA, Char>
{
template <typename ParseContext>
constexpr auto parse(ParseContext& ctx)
{
// no specifiers
return ctx.begin();
}
template <typename F>
auto format(const _SECURITY_LOGON_SESSION_DATA& c, F &ctx)
{
std::wstring name(c.UserName.Buffer,
c.UserName.Length);
std::wstring domainName(c.LogonDomain.Buffer, c.LogonDomain.Length);
auto s = fmt::format(L"UserName: {}\n"
"LogonDomain: {}\n",
name, domainName);
return format_to(ctx.out(), s);
}
};
在使用 fmt lib 创建格式化程序以在我的代码中打印一些 Windows 结构时遇到一些问题。 我在下面准备了一个基本示例。目的是能够以预先结构化的方式打印出一个大结构,并且只打印我想要的,但我不太明白我得到的错误。
#include <windows.h>
#include <ntsecapi.h>
#include <iostream>
#include <fmt/format.h>
//#pragma comment(lib, "netapi32.lib")
#pragma comment(lib, "secur32.lib")
#pragma comment(lib, "ntdll.lib")
template <>
struct fmt::formatter<_SECURITY_LOGON_SESSION_DATA>
{
constexpr auto parse(format_parse_context& ctx)
{
// no specifiers
return ctx.begin();
}
template <typename F>
auto format(const _SECURITY_LOGON_SESSION_DATA& c, F &ctx)
{
std::wstring name(c.UserName.Buffer,
c.UserName.Length);
std::wstring domainName(c.LogonDomain.Buffer, c.LogonDomain.Length);
auto s = fmt::format(L"UserName: {}\n"
"LogonDomain: {}\n",
name, domainName);
return format_to(ctx.out(), s);
}
};
VOID DumpLogonData(PLUID LogonLuid)
{
NTSTATUS status;
PSECURITY_LOGON_SESSION_DATA pData = nullptr;
if (!LogonLuid)
return;
status = LsaGetLogonSessionData(LogonLuid,
&pData);
if (!NT_SUCCESS(status))
{
fmt::print("Failed to get logon session data: {}\n",
RtlNtStatusToDosError(status));
LsaFreeReturnBuffer(pData);
}
fmt::print(L"LogonSession Data\n{}\n", *pData);
LsaFreeReturnBuffer(pData);
}
BOOL EnumerateLogonSessions()
{
PLUID luidList = nullptr;
ULONG ulSessionCount;
NTSTATUS status;
status = LsaEnumerateLogonSessions(
&ulSessionCount,
&luidList);
if (!NT_SUCCESS(status))
{
fmt::print("Failed to enumerate logon sessions\n");
return FALSE;
}
for (ULONG i = 0; i < ulSessionCount; ++i)
{
DumpLogonData(&luidList[i]);
}
return TRUE;
}
int main(int argc, char **argv)
{
EnumerateLogonSessions();
std::getchar();
}
当调用 fmt::print(L"LogonSession Data\n{}\n", *pData);
时,此格式将抛出 error C2338: Cannot format an argument. To make type T formattable provide a formatter<T> specialization: https://fmt.dev/dev/api.html#udt
。
对使此格式化程序正常工作有何帮助?
一个问题是您试图传递指向 _SECURITY_LOGON_SESSION_DATA
的指针而不是对它的 (const) 引用。应该是:
fmt::print(L"LogonSession Data\n{}\n", *pData);
在 formatter::format
方法中相同。
另一个问题是你的formatter
不支持wchar_t
,应该是:
template <typename Char>
struct fmt::formatter<_SECURITY_LOGON_SESSION_DATA, Char> {
...
};
如果你想支持不同的字符类型。或者使用普通 multi-byte 字符串而不是宽字符串。
随着格式化程序的修复,我还需要专门化 parse
函数,下面是可用的格式化程序。
template <typename Char>
struct fmt::formatter<_SECURITY_LOGON_SESSION_DATA, Char>
{
template <typename ParseContext>
constexpr auto parse(ParseContext& ctx)
{
// no specifiers
return ctx.begin();
}
template <typename F>
auto format(const _SECURITY_LOGON_SESSION_DATA& c, F &ctx)
{
std::wstring name(c.UserName.Buffer,
c.UserName.Length);
std::wstring domainName(c.LogonDomain.Buffer, c.LogonDomain.Length);
auto s = fmt::format(L"UserName: {}\n"
"LogonDomain: {}\n",
name, domainName);
return format_to(ctx.out(), s);
}
};