potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

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