コンピューターSIDを取得する方法

# コンピューターSIDは見つけにくい
SIDを取得する際に `whoami /user` を実行するとユーザーSIDを取得することが出来る。

いろんなサイトだと、ここから末尾のRIDを除いた値がコンピューターSIDだと記載してるけど、ドメインユーザーでログインしてるとその方法は使えない。
その場合でもローカルユーザーのSIDを表示させれば同じ事は出来るんだけど、出来ればちゃんとした方法で取得したい。

# というわけで
最近C++を使ってみる機会があったし、関数の使い方をちゃんと把握するのに都合がよかったので C++ → C# (→ PowerShell)で作ってみました。
特にC++は初心者なので、書き方とか解放とか微妙かもしれません。エラー処理もしてません。
(wstringに変換しているのはコピーが楽そうだからです。)

取得方法含め、ご利用は自己責任でお願いします、というよくある但し書きをつけておきます。

# 参考
参考にしたページは以下です。(参考というより、必要な部分だけ削り出したような感じですが。)
PInvoke.NETの方はほとんど書き終わった後で必要なコードほぼ全部書いてるじゃん、と気づいてしまいました。

- [SIDによるアカウントの列挙：CodeZine](https://codezine.jp/article/detail/434)
- [PINVOKE.NET LookupAccountName](http://www.pinvoke.net/default.aspx/advapi32.lookupaccountname)
- [PowerShellスクリプト内でC#コードを書いて使う - YOMON8.NET](http://yomon.hatenablog.com/entry/2013/06/05/PowerShellスクリプト内でC%23コードを書いて使う)

# コンピューターSIDの取得(C++版)

```cpp:main.cpp
#include <iostream>
#include <windows.h>
#include <sddl.h>

std::wstring GetComputerSid() {

	// コンピューター名を取得する
	wchar_t szComputerName[MAX_COMPUTERNAME_LENGTH + 2];
	DWORD dwComputerNameSize = sizeof(szComputerName);
	GetComputerNameW(szComputerName, &dwComputerNameSize);

	// SID取得
	PSID pSid;
	DWORD dwSidLen = 0;
	wchar_t szDomain[256];
	DWORD dwDomainLen = sizeof(szDomain);
	SID_NAME_USE snu;

	// SIDに必要なメモリ領域を確認して確保する
	LookupAccountNameW(NULL, szComputerName, NULL, &dwSidLen, szDomain, &dwDomainLen, &snu);
	pSid = (PSID)HeapAlloc(GetProcessHeap(), 0, dwSidLen);

	// SIDを取得する
	LookupAccountNameW(NULL, szComputerName, pSid, &dwSidLen, szDomain, &dwDomainLen, &snu);

	// SIDを文字列に変換する
	wchar_t *szSid;
	ConvertSidToStringSidW(pSid, &szSid);

	// std::wstringに変換
	std::wstring sidString;
	sidString = std::wstring(szSid);

	// メモリの解放
	HeapFree(GetProcessHeap(), 0, pSid);
	LocalFree(szSid);

	return sidString;
}

int main()
{
	std::wstring computerSid = GetComputerSid();
	std::wcout << computerSid.c_str();

	return 0;
}
```

# コンピューターSIDの取得(C#版)
中身はC++をそのまま移植しただけです。

```csharp:Program.cs
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;

namespace GetComputerSID {
    class Program {
        static void Main(string[] args) {
            Console.WriteLine(GetComputerSid());
            Console.ReadLine();
        }

        public static string GetComputerSid() {
            // コンピューター名を取得する
            string computerName = Environment.MachineName;

            // SID取得
            byte[] sid;
            uint sidLen = 0;
            StringBuilder domainName = new StringBuilder(256);
            uint domainNameLen = (uint)domainName.Capacity;
            SID_NAME_USE snu;

            // SIDに必要なメモリ領域を確認して確保する
            if (!LookupAccountName(null, computerName, null, ref sidLen, domainName, ref domainNameLen, out snu)) {
                int lastError = Marshal.GetLastWin32Error();
                if (lastError != ERROR_NOT_ENOUGH_MEMORY) {
                    // ここもコンストラクターは空でいい気もするけど気分的にこのまま
                    throw new Win32Exception(lastError);
                }
            }

            // SIDを取得する
            sid = new byte[sidLen];
            domainName.EnsureCapacity((int)domainNameLen);
            if (!LookupAccountName(null, computerName, sid, ref sidLen, domainName, ref domainNameLen, out snu)) {
                throw new Win32Exception();
            }

            // SIDを文字列に変換する
            IntPtr pSidString;
            if (!ConvertSidToStringSid(sid, out pSidString)) {
                throw new Win32Exception();
            }

            // stringに変換
            string sidString = Marshal.PtrToStringUni(pSidString);

            // メモリの解放
            if (LocalFree(pSidString) != IntPtr.Zero) {
                throw new Win32Exception();
            }

            return sidString;
        }

        private const int ERROR_NOT_ENOUGH_MEMORY = 122;

        public enum SID_NAME_USE {
            SidTypeUser = 1,
            SidTypeGroup,
            SidTypeDomain,
            SidTypeAlias,
            SidTypeWellKnownGroup,
            SidTypeDeletedAccount,
            SidTypeInvalid,
            SidTypeUnknown,
            SidTypeComputer,
            SidTypeLabel
        }

        [DllImport("advapi32.dll", CharSet = CharSet.Unicode, EntryPoint = "LookupAccountNameW", SetLastError = true)]
        private static extern bool LookupAccountName(
            string lpSystemName,
            string lpAccountName,
            [MarshalAs(UnmanagedType.LPArray)] byte[] Sid,
            ref uint cbSid,
            StringBuilder ReferencedDomainName,
            ref uint cbReferencedDomainName, 
            out SID_NAME_USE peUse);

        [DllImport("advapi32.dll", CharSet = CharSet.Unicode, EntryPoint = "ConvertSidToStringSidW", SetLastError = true)]
        private static extern bool ConvertSidToStringSid([MarshalAs(UnmanagedType.LPArray)]byte[] sid, out IntPtr stringSid);

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern IntPtr LocalFree(IntPtr hMem);
    }

}

```

# コンピューターSIDの取得(PowerShell版)
PowerShellと見せかけてC#そのままです。

```powershell

$GetComputerSidDef =@'
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;

public class GetComputerSidClass {
    public static string GetComputerSid() {
        // コンピューター名を取得する
        string computerName = Environment.MachineName;

        // SID取得
        byte[] sid;
        uint sidLen = 0;
        System.Text.StringBuilder domainName = new System.Text.StringBuilder(256);
        uint domainNameLen = (uint)domainName.Capacity;
        SID_NAME_USE snu;

        // SIDに必要なメモリ領域を確認して確保する
        if (!LookupAccountName(null, computerName, null, ref sidLen, domainName, ref domainNameLen, out snu)) {
            int lastError = Marshal.GetLastWin32Error();
            if (lastError != ERROR_NOT_ENOUGH_MEMORY) {
                // ここもコンストラクターは空でいい気もするけど気分的にこのまま
                throw new System.ComponentModel.Win32Exception(lastError);
            }
        }

        // SIDを取得する
        sid = new byte[sidLen];
        domainName.EnsureCapacity((int)domainNameLen);
        if (!LookupAccountName(null, computerName, sid, ref sidLen, domainName, ref domainNameLen, out snu)) {
            throw new System.ComponentModel.Win32Exception();
        }

        // SIDを文字列に変換する
        IntPtr pSidString;
        if (!ConvertSidToStringSid(sid, out pSidString)) {
            throw new System.ComponentModel.Win32Exception();
        }

        // stringに変換
        string sidString = Marshal.PtrToStringUni(pSidString);

        // メモリの解放
        if (LocalFree(pSidString) != IntPtr.Zero) {
            throw new System.ComponentModel.Win32Exception();
        }

        return sidString;
    }

    private const int ERROR_NOT_ENOUGH_MEMORY = 122;

    public enum SID_NAME_USE {
        SidTypeUser = 1,
        SidTypeGroup,
        SidTypeDomain,
        SidTypeAlias,
        SidTypeWellKnownGroup,
        SidTypeDeletedAccount,
        SidTypeInvalid,
        SidTypeUnknown,
        SidTypeComputer,
        SidTypeLabel
    }

    [DllImport("advapi32.dll", CharSet = CharSet.Unicode, EntryPoint = "LookupAccountNameW", SetLastError = true)]
    private static extern bool LookupAccountName(
        string lpSystemName,
        string lpAccountName,
        [MarshalAs(UnmanagedType.LPArray)] byte[] Sid,
        ref uint cbSid,
        System.Text.StringBuilder ReferencedDomainName,
        ref uint cbReferencedDomainName, 
        out SID_NAME_USE peUse);

    [DllImport("advapi32.dll", CharSet = CharSet.Unicode, EntryPoint = "ConvertSidToStringSidW", SetLastError = true)]
    private static extern bool ConvertSidToStringSid([MarshalAs(UnmanagedType.LPArray)]byte[] sid, out IntPtr stringSid);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern IntPtr LocalFree(IntPtr hMem);
}
'@

Add-Type -TypeDefinition $GetComputerSidDef

# 実際は変数に入れなくてもいいけど、Qiitaで表示されなかったので一旦変数に格納
$sid = [GetComputerSidClass]::GetComputerSid()
$sid

```