potisanのプログラミングメモ

プログラミング素人です。昔の自分を育ててくれたネット情報に少しでも貢献できるよう、情報を貯めていこうと思っています。Windows環境のC++やC#がメインです。

C++20&Win32 API スレッドまたはプロセストークンのユーザー情報SIDの文字列表記・ユーザー名・参照ドメイン名を取得する

スレッドまたはプロセスのトークンのユーザー情報SIDから文字列表記・ユーザー名・参照ドメイン名を取得するコードです。Win32 API関数のラッパー関数・クラスを定義していますが、サンプルコードなので名前空間で囲ったりはしていません。

#include <bit>
#include <vector>
#include <string>
#include <memory>

#define STRICT
#define NOMINMAX
#include <Windows.h>
#include <sddl.h>

struct HANDLEDeleter final {
    inline void operator() (HANDLE Handle) const noexcept {
        ::CloseHandle(Handle);
    }
};
using unique_handle = std::unique_ptr<std::remove_pointer_t<HANDLE>, HANDLEDeleter>;

BOOL OpenCurrentThreadOrProcessToken(DWORD DesiredAccess, BOOL ThreadOpenAsSelf, unique_handle& Result) noexcept;
BOOL GetTokenInformation(HANDLE TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, std::vector<BYTE>& Result) noexcept;
BOOL ConvertSidToStringSidW(PSID Sid, std::wstring& Result) noexcept;
BOOL LookupAccountSidW(LPCWSTR SystemName, PSID Sid, std::wstring& Name, std::wstring& ReferencedDomainName, SID_NAME_USE& Use);

// トークンのユーザー情報を管理します。
// GetTokenInformation関数を使用します。
class TokenUserInformation final
{
public:
    constexpr TokenUserInformation(HANDLE tokenHandle)
    {
        std::vector<BYTE> buffer;
        if (!::GetTokenInformation(tokenHandle, TOKEN_INFORMATION_CLASS::TokenUser, buffer))
            return {};
        m_buffer = std::move(buffer);
    }

    constexpr TokenUserInformation(TokenUserInformation&& source)
        : m_buffer(std::move(source.m_buffer))
    {
    }

    constexpr TokenUserInformation(const TokenUserInformation& source) = delete;

    constexpr bool IsEmpty() const noexcept
    {
        return m_buffer.empty();
    }

    constexpr void Swap(TokenUserInformation& x) noexcept
    {
        m_buffer.swap(x.m_buffer);
    }

    constexpr void Clear() noexcept
    {
        m_buffer.clear();
    }

    constexpr explicit operator const TOKEN_USER* () const noexcept
    {
        return std::bit_cast<const TOKEN_USER*>(m_buffer.data());
    }

    constexpr const TOKEN_USER* operator->() const noexcept
    {
        return std::bit_cast<const TOKEN_USER*>(m_buffer.data());
    }

private:
    std::vector<BYTE> m_buffer;
};

int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
{
    // スレッドまたはプロセスのトークンを取得します。
    unique_handle tokenHandle;
    if (!OpenCurrentThreadOrProcessToken(TOKEN_QUERY, TRUE, tokenHandle))
        return GetLastError();

    // トークンからユーザートークンのSIDを取得します。
    TokenUserInformation userInfo(tokenHandle.get());

    // SIDの情報を取得します。
    std::wstring strSid;               // SIDの文字列表記
    std::wstring userName;             // ユーザー名
    std::wstring referencedDomainName; // 参照ドメイン名
    SID_NAME_USE sidNameUse;           // ユーザー名の用法

    ConvertSidToStringSidW(userInfo->User.Sid, strSid);
    LookupAccountSidW(nullptr, userInfo->User.Sid, userName, referencedDomainName, sidNameUse);

    return 0;
}

// 現在のスレッドまたはプロセスのトークンを取得します。
BOOL OpenCurrentThreadOrProcessToken(DWORD DesiredAccess, BOOL ThreadOpenAsSelf, unique_handle& Result) noexcept
{
    HANDLE TokenHandle{ nullptr };
    if (!OpenThreadToken(GetCurrentThread(), DesiredAccess, ThreadOpenAsSelf, &TokenHandle))
    {
        if (GetLastError() != ERROR_NO_TOKEN)
            return FALSE;
        if (!OpenProcessToken(GetCurrentProcess(), DesiredAccess, &TokenHandle))
            return FALSE;
    }
    Result.reset(TokenHandle);
    return TRUE;
}

// トークン情報を取得します。
BOOL GetTokenInformation(HANDLE TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, std::vector<BYTE>& Result) noexcept
{
    DWORD size{ 0 };
    if (!GetTokenInformation(TokenHandle, TokenInformationClass, nullptr, 0, &size))
    {
        if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
            return FALSE;
    }

    Result.resize(size);
    return GetTokenInformation(TokenHandle, TokenInformationClass, Result.data(), size, &size);
}

// Win32 API ConvertSidToStringSidW関数のSTLラッパーです。
BOOL ConvertSidToStringSidW(PSID Sid, std::wstring& Result) noexcept
{
    LPWSTR ps;
    if (!ConvertSidToStringSidW(Sid, &ps))
        return FALSE;
    std::wstring s(ps, wcslen(ps));
    LocalFree(ps);
    Result = std::move(s);
    return TRUE;
}

// Win32 API LookupAccountSidW関数のSTLラッパーです。
BOOL LookupAccountSidW(LPCWSTR SystemName, PSID Sid, std::wstring& Name, std::wstring& ReferencedDomainName, SID_NAME_USE& Use)
{
    DWORD nameSize{ 0 };
    DWORD refDomainNameSize{ 0 };
    SID_NAME_USE sidNameUse;
    LookupAccountSidW(
        SystemName, Sid,
        nullptr, &nameSize,
        nullptr, &refDomainNameSize,
        &sidNameUse);
    Name.resize(nameSize);
    ReferencedDomainName.resize(refDomainNameSize);
    return LookupAccountSidW(
        nullptr, Sid,
        &*Name.begin(), &nameSize,
        &*ReferencedDomainName.begin(), &refDomainNameSize,
        &Use);
}