2011-10-03 32 views
7

我正在尋找一種使用Delphi代碼提取計算機SID的方法。 SysInternals中有一個名爲PsGetSid的工具,但我不能在我的應用程序中使用它。我在Google搜索了一個代碼示例,但找不到一個。如何提取電腦/機器SID?

我該如何在Delphi中實現這個功能?

請幫忙。

回答

8

這是使用LookupAccountName樣品作爲@MikeKwan的WinAPi函數建議。

{$APPTYPE CONSOLE} 

uses 
    Windows, 
    SysUtils; 


function ConvertSidToStringSid(Sid: PSID; out StringSid: PChar): BOOL; stdcall; external 'ADVAPI32.DLL' name {$IFDEF UNICODE} 'ConvertSidToStringSidW'{$ELSE} 'ConvertSidToStringSidA'{$ENDIF}; 

function SIDToString(ASID: PSID): string; 
var 
    StringSid : PChar; 
begin 
    if not ConvertSidToStringSid(ASID, StringSid) then 
    RaiseLastWin32Error; 

    Result := string(StringSid); 
end; 

function GetLocalComputerName: string; 
var 
    nSize: DWORD; 
begin 
    nSize := MAX_COMPUTERNAME_LENGTH + 1; 
    SetLength(Result, nSize); 
    if not GetComputerName(PChar(Result), {var}nSize) then 
    begin 
    Result := ''; 
    Exit; 
    end; 

    SetLength(Result, nSize); 
end; 

function GetComputerSID:string; 
var 
    Sid: PSID; 
    cbSid: DWORD; 
    cbReferencedDomainName : DWORD; 
    ReferencedDomainName: string; 
    peUse: SID_NAME_USE; 
    Success: BOOL; 
    lpSystemName : string; 
    lpAccountName: string; 
begin 
    Sid:=nil; 
    try 
    lpSystemName:=''; 
    lpAccountName:=GetLocalComputerName; 

    cbSid := 0; 
    cbReferencedDomainName := 0; 
    // First call to LookupAccountName to get the buffer sizes. 
    Success := LookupAccountName(PChar(lpSystemName), PChar(lpAccountName), nil, cbSid, nil, cbReferencedDomainName, peUse); 
    if (not Success) and (GetLastError = ERROR_INSUFFICIENT_BUFFER) then 
    begin 
     SetLength(ReferencedDomainName, cbReferencedDomainName); 
     Sid := AllocMem(cbSid); 
     // Second call to LookupAccountName to get the SID. 
     Success := LookupAccountName(PChar(lpSystemName), PChar(lpAccountName), Sid, cbSid, PChar(ReferencedDomainName), cbReferencedDomainName, peUse); 
     if not Success then 
     begin 
     FreeMem(Sid); 
     Sid := nil; 
     RaiseLastOSError; 
     end 
     else 
     Result := SIDToString(Sid); 
    end 
    else 
     RaiseLastOSError; 
    finally 
    if Assigned(Sid) then 
    FreeMem(Sid); 
    end; 
end; 


begin 
try 
    Writeln(GetComputerSID); 
except 
    on E:Exception do 
     Writeln(E.Classname, ':', E.Message); 
end; 
Writeln('Press Enter to exit'); 
Readln; 
end. 
+0

我希望儘可能使用Win API而不是WMI,這正是我所需要的。謝謝! – Ran

+0

GetLocalComputerName返回不正確的數據:如果GetComputerName API成功nSize包含數字或TCHAR,則複製到輸出緩衝區(result var)。因此,我們必須在成功時做setLength(result,nSize) –

+2

我認爲應該在'SIDToString'結尾添加以下內容: LocalFree(HLocal(StringSid)); –

0

您可以通過LookupAccountName得到它。第一個參數傳入NULL,第二個傳入機器名稱。

+0

應該傳遞的機器名與您從GetComputerName()獲得的機器名相同還是應該採用其他格式? – Ran

3

您可以使用Win32_Account WMI類,從用戶帳戶SID中提取機器SID。

例如對於其中SID是用戶帳戶

S-1-5-21-1299824301-1797996836-594316699-1009 

機器SID將是

S-1-5-21-1299824301-1797996836-594316699 

檢查該樣品

program GetWMI_Info; 

{$APPTYPE CONSOLE} 

uses 
    SysUtils, 
    ActiveX, 
    ComObj, 
    Variants; 

function GetComputerSID:string; 
const 
    WbemUser   =''; 
    WbemPassword  =''; 
    WbemComputer  ='localhost'; 
    wbemFlagForwardOnly = $00000020; 
var 
    FSWbemLocator : OLEVariant; 
    FWMIService : OLEVariant; 
    FWbemObjectSet: OLEVariant; 
    FWbemObject : OLEVariant; 
    oEnum   : IEnumvariant; 
    iValue  : LongWord; 
begin; 
    FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator'); 
    FWMIService := FSWbemLocator.ConnectServer(WbemComputer, 'root\CIMV2', WbemUser, WbemPassword); 
    FWbemObjectSet:= FWMIService.ExecQuery('SELECT SID FROM Win32_Account Where SIDType=1','WQL',wbemFlagForwardOnly); 
    oEnum   := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant; 
    if oEnum.Next(1, FWbemObject, iValue) = 0 then 
    begin 
    Result:=FWbemObject.SID; 
    Result:=Copy(Result,1,LastDelimiter('-',Result)-1); 
    FWbemObject:=Unassigned; 
    end; 
end; 


begin 
try 
    CoInitialize(nil); 
    try 
     Writeln(GetComputerSID); 
    finally 
     CoUninitialize; 
    end; 
except 
    on E:EOleException do 
     Writeln(Format('EOleException %s %x', [E.Message,E.ErrorCode])); 
    on E:Exception do 
     Writeln(E.Classname, ':', E.Message); 
end; 
Writeln('Press Enter to exit'); 
Readln; 
end.