Windows上のユーザはそれぞれ固有のSID(Security Identifier/セキュリティ識別子)で管理されています。
オブジェクトを識別するSIDとは?:Tech TIPS - @IT
ユーザ名とSIDを相互に変換するにはWin32APIのLookupAccountName関数とLookupAccountSid関数を使用します。ところがこれらの関数でSIDは文字列ではなくSID構造体で扱う必要があります。ということで文字列表現のSIDとSID構造体を相互に変換する必要がありますが、これを行うのがConvertSidToStringSid関数とConvertStringSidToSid関数になります。
それではまずユーザ名をSIDに変換するほうから。
uses {$IF RTLVersion < 23.0} Windows, SysUtils; {$ELSE} Winapi.Windows, System.SysUtils; {$IFEND} {$IF RTLVersion < 28.0} { Win32API ConvertSidToStringSid } {$IFNDEF Unicode} function ConvertSidToStringSid(Sid: PSID; var StringSid: LPSTR): BOOL; stdcall; external advapi32 name 'ConvertSidToStringSidA'; {$ELSE} function ConvertSidToStringSid(Sid: PSID; var StringSid: LPWSTR): BOOL; stdcall; external advapi32 name 'ConvertSidToStringSidW'; {$ENDIF} {$EXTERNALSYM ConvertSidToStringSid} {$IFEND} {$IF RTLVersion < 19.0} { Win32API LookupAccountName } {$IFNDEF Unicode} function LookupAccountName(lpSystemName, lpAccountName: LPCSTR; Sid: PSID; var cbSid: DWORD; ReferencedDomainName: LPSTR; var cbReferencedDomainName: DWORD; var peUse: SID_NAME_USE): BOOL; stdcall; external advapi32 name 'LookupAccountNameA'; {$ELSE} function LookupAccountName(lpSystemName, lpAccountName: LPCWSTR; Sid: PSID; var cbSid: DWORD; ReferencedDomainName: LPWSTR; var cbReferencedDomainName: DWORD; var peUse: SID_NAME_USE): BOOL; stdcall; external advapi32 name 'LookupAccountNameW'; {$ENDIF} {$EXTERNALSYM LookupAccountName} {$IFEND} function UsernameToSid(const AUsername: String): String; var PSID: Pointer; SidSize: DWORD; Domain: String; DomainLen: DWORD; SidName: SID_NAME_USE; SidStr: PChar; begin PSID := nil; SidStr := nil; try SidSize := 0; DomainLen := 0; LookupAccountName(nil,PChar(AUsername),nil,SidSize,nil,DomainLen,SidName); if GetLastError <> ERROR_INSUFFICIENT_BUFFER then begin RaiseLastOSError; end; PSID := Pointer(LocalAlloc(LPTR,SidSize)); SetLength(Domain,DomainLen); if LookupAccountName(nil,PChar(AUsername),PSID,SidSize,PChar(Domain),DomainLen,SidName) = False then begin RaiseLastOSError; end; ConvertSidToStringSid(PSID,SidStr); SetString(Result,SidStr,StrLen(SidStr)); finally if PSID <> nil then begin {$IF RTLVersion >= 32.0} if LocalFree(PSID) <> nil then {$ELSE} {$IFNDEF WIN64} if LocalFree(DWORD(PSID)) <> 0 then {$ELSE} if LocalFree(UInt64(PSID)) <> 0 then {$ENDIF} {$IFEND} begin RaiseLastOSError; end; end; if SidStr <> nil then begin {$IF RTLVersion >= 32.0} if LocalFree(SidStr) <> nil then {$ELSE} {$IFNDEF WIN64} if LocalFree(DWORD(SidStr)) <> 0 then {$ELSE} if LocalFree(UInt64(SidStr)) <> 0 then {$ENDIF} {$IFEND} begin RaiseLastOSError; end; end; end; end;まずLookupAccountName関数でユーザ名に対応するSIDをSID構造体に取得し、これをConvertSidToStringSid関数で文字列に変換します。このときLookupAccountName関数を一旦SID=nil、ReferencedDomainName=nilで呼び出して必要なサイズを取得し、SIDはLocalAlloc関数で、ReferencedDomainNameは(文字列なので)SetLengthで領域を確保して、もう一度LookupAccountName関数を呼ぶようにしているのと、LocalAlloc関数、ConvertSidToStringSid関数で確保された領域はLocalFree関数で解放しなければならない、というところに気をつける必要があります(DelphiのバージョンによってLocalFree関数の宣言に差異があるので$IF RTLVersionと$IFNDEF WIN64で分岐しています)。
次にSIDをユーザ名に変換します。
{$IF RTLVersion < 28.0} { Win32API ConvertStringSidToSid } {$IFNDEF Unicode} function ConvertStringSidToSid(StringSid: LPCSTR; var Sid: PSID): BOOL; stdcall; external advapi32 name 'ConvertStringSidToSidA'; {$ELSE} function ConvertStringSidToSid(StringSid: LPCWSTR; var Sid: PSID): BOOL; stdcall; external advapi32 name 'ConvertStringSidToSidW'; {$ENDIF} {$EXTERNALSYM ConvertStringSidToSid} {$IFEND} {$IF RTLVersion < 19.0} { Win32API LookupAccountSid } {$IFNDEF Unicode} function LookupAccountSid(lpSystemName: LPCSTR; Sid: PSID; Name: LPSTR; var cbName: DWORD; ReferencedDomainName: LPSTR; var cbReferencedDomainName: DWORD; var peUse: SID_NAME_USE): BOOL; stdcall; external advapi32 name 'LookupAccountSidA'; {$ELSE} function LookupAccountSid(lpSystemName: LPCWSTR; Sid: PSID; Name: LPWSTR; var cbName: DWORD; ReferencedDomainName: LPWSTR; var cbReferencedDomainName: DWORD; var peUse: SID_NAME_USE): BOOL; stdcall; external advapi32 name 'LookupAccountSidW'; {$ENDIF} {$EXTERNALSYM LookupAccountSid} {$IFEND} function SidToUsername(const ASID: String): String; var PSID: Pointer; UserNameLen: DWORD; Domain: String; DomainLen: DWORD; SidName: SID_NAME_USE; begin PSID := nil; try if ConvertStringSidToSid(PChar(ASID),PSID) = False then begin RaiseLastOSError; end; UserNameLen := 0; DomainLen := 0; LookupAccountSid(nil,PSID,nil,UserNameLen,nil,DomainLen,SidName); if GetLastError <> ERROR_INSUFFICIENT_BUFFER then begin RaiseLastOSError; end; SetLength(Result,UserNameLen); SetLength(Domain,DomainLen); if LookupAccountSid(nil,PSID,PChar(Result),UserNameLen,PChar(Domain),DomainLen,SidName) = False then begin RaiseLastOSError; end; SetLength(Result,StrLen(PChar(Result))); finally if PSID <> nil then begin {$IF RTLVersion >= 32.0} if LocalFree(PSID) <> nil then {$ELSE} {$IFNDEF WIN64} if LocalFree(DWORD(PSID)) <> 0 then {$ELSE} if LocalFree(UInt64(PSID)) <> 0 then {$ENDIF} {$IFEND} begin RaiseLastOSError; end; end; end; end;こちらはまずConvertStringSidToSid関数でSIDの文字列をSID構造体に変換し、LookupAccountSid関数でユーザ名に変換します。こちらもLookupAccountSid関数をName=nil、ReferencedDomainName=nilで呼び出して必要なサイズを取得し、SetLengthで領域を確保してからもう一度LookupAccountSid関数を呼び出しています。またConvertStringSidToSid関数で確保したSID構造体はLocalFree関数で解放します。
使用しているWin32APIのうち、LookupAccountSid関数/LookupAccountName関数はDelphi 2009で、ConvertStringSidToSid関数/ConvertSidToStringSid関数はDelphi XE7で(Win32API.)Windowsユニットに関数宣言が追加されたため、それ以前のバージョンでは明示的に定義が必要です。
→Windows上のユーザ名とSIDを相互変換(Gist)
0 件のコメント:
コメントを投稿