16

I need to get a fully qualified domain name for a Windows machine on a domain in Delphi.

I've tried to use LookupAccountSid but it gives me only the netbios domain name, in my case it is "intranet" but I need the full "intranet.companyname.com"

Any Ideas?

Andreas Rejbrand
  • 105,602
  • 8
  • 282
  • 384
Gilmor
  • 439
  • 1
  • 3
  • 11
  • is the domain server running on companyname.com otherwise it'll be blank even if you use the right function. – Joseph Le Brech Dec 09 '11 at 14:33
  • 1
    Is it an Active Directory domain? – Marcus Adams Dec 09 '11 at 18:48
  • 2
    Your question isnt very clear. All of these infos are related to the **machine**, not a **user**. – OnTheFly Dec 10 '11 at 01:04
  • I removed reference to "currently logged in user" because it is spurious, and shows only user confusion. – Warren P Dec 10 '11 at 19:47
  • 1
    There *is* an important distinction between the domain of the **machine** and the domain of the **user**. i can login to a machine with a local account, a `DomainA` account, or `DomainB` account. When you use `GetUserNameEx` you're going to get the name of the domain of the **user** (if there even is one), which can be different from the machine's joined domain. You *could* call `NetGetJoinInformation` to get the name of the domain/workgroup/none that the machine is joined to, but it returns the NetBIOS domain name. – Ian Boyd Mar 20 '12 at 17:46

4 Answers4

12

Try the GetUserNameEx Windows API function.

const
  NameUnknown            = 0;
  NameFullyQualifiedDN   = 1;
  NameSamCompatible      = 2;
  NameDisplay            = 3;
  NameUniqueId           = 6;
  NameCanonical          = 7;
  NameUserPrincipal      = 8;
  NameCanonicalEx        = 9;
  NameServicePrincipal   = 10;
  NameDnsDomain          = 12;

function GetUserNameExString(ANameFormat: DWORD): string;
var
  Buf: array[0..256] of Char;
  BufSize: DWORD;
  GetUserNameEx: function (NameFormat: DWORD; lpNameBuffer: LPSTR;
    var nSize: ULONG): BOOL; stdcall;
begin
  Result := '';
  BufSize := SizeOf(Buf) div SizeOf(Buf[0]);
  GetUserNameEx := GetProcAddress(GetModuleHandle('secur32.dll'), 'GetUserNameExA');
  if Assigned(GetUserNameEx) then
    if GetUserNameEx(ANameFormat, Buf, BufSize) then
      Result := Buf;
end;

using the NameDnsDomain format for example, will result www.mydomain.com\user_name if you are logged into "www.mydomain.com" domain.


Since I now implemented this for my own needs in our application, @iPath's comment was quit right. better use GetComputerNameEx, and specify one of the COMPUTER_NAME_FORMAT for your own needs.

A Delphi implementation would look like this (Unicode version):

interface
...
type
  COMPUTER_NAME_FORMAT = (
    ComputerNameNetBIOS,
    ComputerNameDnsHostname,
    ComputerNameDnsDomain,
    ComputerNameDnsFullyQualified,
    ComputerNamePhysicalNetBIOS,
    ComputerNamePhysicalDnsHostname,
    ComputerNamePhysicalDnsDomain,
    ComputerNamePhysicalDnsFullyQualified,
    ComputerNameMax);

function GetComputerNameExString(ANameFormat: COMPUTER_NAME_FORMAT): WideString;

implementation
...
function GetComputerNameExW(NameType: COMPUTER_NAME_FORMAT; lpBuffer: LPWSTR;
  var nSize: DWORD): BOOL; stdcall; external kernel32 name 'GetComputerNameExW';

function GetComputerNameExString(ANameFormat: COMPUTER_NAME_FORMAT): WideString;
var
  nSize: DWORD;
begin
  nSize := 1024;
  SetLength(Result, nSize);
  if GetComputerNameExW(ANameFormat, PWideChar(Result), nSize) then
    SetLength(Result, nSize)
  else
    Result := '';
end;
kobik
  • 21,001
  • 4
  • 61
  • 121
  • Could you suggest what parameters need to be passed to this API? – David Heffernan Dec 10 '11 at 11:45
  • I believe the user actually wants a machine name (including domain.com), not a \\ACTIVEDIRECTORYDOMAIN\username string. – Warren P Dec 10 '11 at 19:48
  • 2
    This is completely wrong approach: what if the computer is joined to FOO.com, but your program was started under user3@BAR.com? This can happen (and it happens) if BAR.com and FOO.com are trusted Active Directory domains. Better use GetComputerNameEx and specify COMPUTER_NAME_FORMAT: http://msdn.microsoft.com/en-us/library/windows/desktop/ms724301(v=vs.85).aspx – iPath ツ Mar 16 '13 at 12:02
  • COMPUTER_NAME_FORMAT and GetComputerNameExW() are already defined in Winapi.WIndows. – dominikkv Apr 18 '15 at 11:26
  • 2
    @dominikkv, That depends on the Delphi version. older versions do not have this API defined. – kobik Apr 21 '15 at 09:04
  • Be wary of using GetComputerNameEx. According to the documentation, if the computer does not have a Primary DNS Suffix, you will not get the domain name even if they do actually belong to a domain. – SiBrit May 07 '20 at 23:03
1

I tried all of the above, but without success. In the end, I settled for simply grabbing the environment variable.

uses jclSysInfo;

function GetDomain:string;
begin
     result:=GetEnvironmentVariable('USERDNSDOMAIN');
end;

Tested on Server 2008 R2 - works fine. Returns "server.home.lan". Results in an empty string on a Windows 7 non-domain connected PC.

  • Will need to also check InternetGetConnectedState as USERDNSDOMAIN does not get cleared if you lose connection. You can verify on a wired network by unplugging the network cable. – Paul McCarthy Sep 27 '17 at 15:21
1

NetGetJoinInformation should work fine.

MSDN:

http://msdn.microsoft.com/en-us/library/windows/desktop/aa370423(v=vs.85).aspx

Example:

type
  PWKSTA_INFO_100 = ^WKSTA_INFO_100;

  WKSTA_INFO_100 = packed record
    wki100_platform_id: DWord;
    wki100_computername: PWChar;
    wki100_langroup: PWChar;
    wki100_ver_major: DWord;
    wki100_ver_minor: DWord;
  end;

  TNetSetupJoinStatus =
  (
    NetSetupUnknownStatus,
    NetSetupUnjoined,
    NetSetupWorkgroupName,
    NetSetupDomainName
  );

  TNetApiBufferFreeFunction = function(ABuffer: Pointer): DWORD; stdcall;
  TNetWkstaGetInfoFunction  = function(const AServername: PWChar; const ALevel: DWord; const ABufptr: Pointer): DWORD; stdcall;
  TNetGetJoinInformationFunction = function(const AServerName: PWChar; out ANameBuffer: PWChar; out ABufferType: TNetSetupJoinStatus): DWORD; stdcall;

const
  NERR_SUCCESS = 0;

function GetLocalComputerDomainName: string;
var
  NetApiBuffer: Pointer;
  NetApi: THandle;
  NetApiBufferFree: TNetApiBufferFreeFunction;
  NetWkstaGetInfo: TNetWkstaGetInfoFunction;
  NetGetJoinInformation: TNetGetJoinInformationFunction;
  NetSetupJoinStatus: TNetSetupJoinStatus;
  NameBuffer: PWideChar;
begin
  Result := '';
  NetApi := LoadLibrary('netapi32.dll');
  if NetApi <> 0 then
  begin
    NetApiBufferFree      := TNetApiBufferFreeFunction(     GetProcAddress(NetApi, 'NetApiBufferFree'));
    NetGetJoinInformation := TNetGetJoinInformationFunction(GetProcAddress(NetApi, 'NetGetJoinInformation'));
    NetWkstaGetInfo       := TNetWkstaGetInfoFunction(      GetProcAddress(NetApi, 'NetWkstaGetInfo'));
    if @NetApiBufferFree <> nil then
    begin
      if @NetSetupJoinStatus <> nil then
      begin
        if NetGetJoinInformation(nil, NameBuffer, NetSetupJoinStatus) = NERR_SUCCESS then
        begin
          if NetSetupJoinStatus = NetSetupDomainName then
          begin
            Result := NameBuffer;
          end;
          NetApiBufferFree(NameBuffer);
        end;
      end;
    end;
    FreeLibrary(NetApi);
  end;
end;
Jens Mühlenhoff
  • 14,565
  • 6
  • 56
  • 113
  • What it returns probably depends on how the domain network is set up. Sorry, I don't know the details, but I only get a simple domain name using `NetGetJoinInformation` at work. – Ondrej Kelle Dec 09 '11 at 15:11
  • That's true if the domain name doesn't end in `.companyname.com`. In that case a reverse DNS lookup on the IP address might get the FQDN which may also be what OP wants. – Jens Mühlenhoff Dec 09 '11 at 17:01
  • 2
    Unfortunatelly yours code returns only an "intranet" part of my domain name. At least in my domain. – Gilmor Dec 11 '11 at 23:37
  • MSDN says: lpNameBuffer [out] - Pointer to the buffer that receives the NetBIOS name of the domain or workgroup to which the computer is joined. So NetGetJoinInformation DOES NOT return FQDN, but NETBIOS Name of the domain – iPath ツ Mar 16 '13 at 11:54
0

The only correct api to use is DsGetDcName. Because NetGetJoinInformation is still from the 'lanmanager age' so, the domain is LM compliant.

The code here is C, but you are smart enough to do the same in Delphi :)

PDOMAIN_CONTROLLER_INFOW pdomInfo ;
auto result1 = ::DsGetDcNameW(nullptr, nullptr, nullptr, nullptr, DS_DIRECTORY_SERVICE_PREFERRED | DS_RETURN_DNS_NAME, &pdomInfo);
if (result1 == ERROR_SUCCESS) {
auto retVal = SysAllocString(pdomInfo->DomainName);
                ::NetApiBufferFree(pdomInfo);
}
Egbert Nierop
  • 2,066
  • 1
  • 14
  • 16