0

Having some trouble using fmt lib to create a formatter to print some Windows structures in my code. I've prepared a basic example below. The purpose is to be able to print a large structure out in a pre-structured manner, and only print what I want out of it, but I dont quite understand the errors im getting.

#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();
}

This format will throw error C2338: Cannot format an argument. To make type T formattable provide a formatter<T> specialization: https://fmt.dev/dev/api.html#udt when fmt::print(L"LogonSession Data\n{}\n", *pData); is called.

Any help making this formatter work?

gandolf
  • 3,384
  • 4
  • 21
  • 20

2 Answers2

0

One problem is that you are trying to pass a pointer to _SECURITY_LOGON_SESSION_DATA instead of a (const) reference to it. It should be:

fmt::print(L"LogonSession Data\n{}\n", *pData);

Same in the formatter::format method.

Another problem is that your formatter doesn't support wchar_t, it should be:

template <typename Char>
struct fmt::formatter<_SECURITY_LOGON_SESSION_DATA, Char> {
  ...
};

if you want to support different character types. Or use normal multi-byte strings instead of wide strings.

vitaut
  • 49,672
  • 25
  • 199
  • 336
  • Hi, this also seems to assert. I tried to overload format to accept both a pointer and const ref, but even using `*pData` causes an assertion. here `core.h(1265): error C2338: Cannot format an argument. To make type T formattable provide a formatter specialization: https://fmt.dev/dev/api.html#udt` – gandolf Aug 27 '20 at 19:49
  • 1
    Great, fixed! your additional comment and also had to specialize `parse`. Full answer below. Thank you! – gandolf Aug 27 '20 at 20:36
  • Happy to help . – vitaut Aug 27 '20 at 20:47
0

With the fixes to the formatter, I also needed to specialize the parse function and below is working formatter.

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);
    }
};
gandolf
  • 3,384
  • 4
  • 21
  • 20