How To Determine If a Windows NT/Windows 2000 Computer Is a Domain Member (179891)



The information in this article applies to:

  • Microsoft Win32 Application Programming Interface (API), when used with:
    • the operating system: Microsoft Windows 2000
    • the operating system: Microsoft Windows NT 4.0

This article was previously published under Q179891

SUMMARY

This article describes how to determine if a computer that is running Windows NT 4.0 or Windows 2000 is a member of a domain, is a member of a workgroup, or is a stand-alone computer using the Local Security Authority APIs.

MORE INFORMATION

To determine if the computer running Windows NT or Windows 2000 is a member of a domain, a member of a workgroup, or a stand-alone computer using Local Security Authority APIs, follow these steps:
  1. Open the Policy object of the local computer by using LsaOpenPolicy.
  2. Use LsaQueryInformationPolicy to retrieve domain information.
  3. Check the value of the security identifier (SID) field. If the value is NULL, the computer is either a stand-alone computer or part of a workgroup. If the Name field points to a string that matches the local workstation name, the computer is a stand-alone computer. Otherwise, the Name field points to the workgroup name.
  4. If the SID pointer has a value, the computer is part of a domain. The domain name is stored in the Name field.
The LsaQueryInformationPolicy API returns a pointer to POLICY_PRIMARY_DOMAIN_INFO structure. The declaration for this structure follows:
typdef struct _POLICY_PRIMARY_DOMAIN_INFO {
    LSA_UNICODE_STRING Name;
    PSID Sid;
} POLICY_PRIMARY_DOMAIN_INFO, *PPOLICY_PRIMARY_DOMAIN_INFO;
				

Notice the Name field declaration. Name is a pointer to an LSA_UNICODE_STRING. The LSA_UNICODE_STRING declaration follows:
typedef struct _LSA_UNICODE_STRING {
   USHORT Length;
   USHORT MaximumLength;
   PWSTR  Buffer;
} LSA_UNICODE_STRING, *PLSA_UNICODE_STRING;
				

Note that the buffer member of LSA_UNICODE_STRING may not be null-terminated so you cannot access the string by using routines that assume null termination of strings. To access the UNICODE string returned by the API, follow the steps as provided in this code:
#define UNICODE
#define _UNICODE
#define INCL_NET
#include <windows.h>
// 
//  You should link this code to the following library files:
// 
//  advapi32.lib - Lsa APIs
//  netapi32.lib - LM APIs
// 
#include <ntsecapi.h>
#include <lm.h>
#include <stdio.h>
#include <string.h>
// 
// Function declarations
// ----------------------
// The following helper functions were copied from the following article
// in the Microsoft Knowledge Base:
// 
// ARTICLE-ID: Q170620
// TITLE     : How To  Manage Computer Domain Membership Programmatically
// 
// LsaOpenPolicy wrapper function.
// 
NTSTATUS OpenPolicy(LPWSTR computername,
               DWORD DesiredAccess,
               PLSA_HANDLE Phandle);
// 
// Quick and Dirty Error display routine.
// 
void
DisplayNtStatus(
   LPSTR szAPI,                // Pointer to function name (ANSI).
   NTSTATUS Status             // NTSTATUS error value.
 );
// 
// Lsa string initialization routine.
// 
void
InitLsaString(
   PLSA_UNICODE_STRING LsaString, // Destination.
   LPWSTR String                  // Source (Unicode).
  );
// 
// Displays the windows error string.
// 
void
DisplayWinError(
   LPSTR szAPI,
   DWORD WinError
  );

BOOL
AllocWStrFromLSAStr(
   PWSTR *ppwstrTarget,
   LSA_UNICODE_STRING LsaStr
  );

//----------------------------------------------------------------------
// 
// Main function declaration.
// 
int __cdecl wmain(int argc, wchar_t *argv[])
{
  LSA_HANDLE PolicyHandle;
  NTSTATUS status;
  PPOLICY_PRIMARY_DOMAIN_INFO ppdiDomainInfo;
  PWKSTA_INFO_100             pwkiWorkstationInfo;
  DWORD netret;
  // 
  // You need the local workstation name. Use NetWkstaGetInfo at level
  // 100 to retrieve a WKSTA_INFO_100 structure.
  // 
  // The wki100_computername field contains a pointer to a UNICODE
  // string containing the local computer name.
  // 
  netret = NetWkstaGetInfo(NULL, 100, (LPBYTE *)&pwkiWorkstationInfo);
  if (netret == NERR_Success)
  {
     // 
     // We have the workstation name in:
     // pwkiWorkstationInfo->wki100_computername
     // 
     // Next, open the policy object for the local system using
     // the OpenPolicy helper function described later in
     // this source file.
     // 
     status = OpenPolicy(NULL,
        GENERIC_READ | POLICY_VIEW_LOCAL_INFORMATION,
        &PolicyHandle);
     // 
     // Error checking.
     // 
     if (status)
     {
        DisplayNtStatus("OpenPolicy Error: ", status);
     }
     else
     {
        // 
        // You have a handle to the policy object. Now, get the
        // domain information using LsaQueryInformationPolicy.
        // 
        status = LsaQueryInformationPolicy(PolicyHandle,
           PolicyPrimaryDomainInformation,
           &ppdiDomainInfo);
        if (status)
        {
           DisplayNtStatus(
                           "LsaQueryInformationPolicy Error: ",
                            status);
        }
        else
        {
		   PWSTR ResName;

		   // Get name is useable format
		   AllocWStrFromLSAStr(&ResName,ppdiDomainInfo->Name);
           // 
           // Check the Sid pointer, if it is null, the
           // workstation is either a stand-alone computer
           // or a member of a workgroup.
           // 
           if (ppdiDomainInfo->Sid)
           {

              // 
              // Member of a domain. Display it.
              // 

			  // Ready to print
              printf("Member of Domain %S\n",ResName);
			  LocalFree(ResName);
           }
           else
           {
              // 
              // Since the pointer to the SID was null, it
              // could be either a stand-alone computer
              // (ppdiDomainInfo->Name.Buffer matches the
              // local machine name) or it could be part
              // of a workgroup.
              // 
              // Compare this string with the one returned
              // by NetWkstaGetInfo.
              // 
              if (wcsncmp(ResName,
				  (LPWSTR)pwkiWorkstationInfo->wki100_computername,
                 wcslen((LPWSTR)pwkiWorkstationInfo->wki100_computername)))
              {
                 // 
                 // The wcsncmp returns non zero if
                 // the two strings do not match. This means
                 // that the workstation is part of a
                 // workgroup.
                 // 
                 printf("Member of workgroup %S\n",ResName);
              }
              else
                 printf("%S is a standalone workstation\n",ResName);
           }
		   // Free unicode name string.
		   LocalFree(ResName);
        }
     }
     // 
     // Clean up all the memory buffers created by the LSA and
     // Net* APIs.
     // 
     NetApiBufferFree(pwkiWorkstationInfo);
     LsaFreeMemory((LPVOID)ppdiDomainInfo);
  }
  else DisplayNtStatus("NetWkstaGetInfo Error: ", netret);
  // 
  // Display a completion message.
  // 
  printf("Execution Completed\n");
  return 0;
}
// 
// LsaOpenPolicy wrapper function.
// 
NTSTATUS
OpenPolicy(
   LPWSTR ServerName,
   DWORD DesiredAccess,
   PLSA_HANDLE PolicyHandle
  )
{
   LSA_OBJECT_ATTRIBUTES ObjectAttributes;
   LSA_UNICODE_STRING ServerString;
   PLSA_UNICODE_STRING Server = NULL;

   // 
   // Always initialize the object attributes to all zeroes.
   // 
   ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));

   if (ServerName != NULL) {
       // 
       // Make a LSA_UNICODE_STRING out of the LPWSTR passed in.
       // 
       InitLsaString(&ServerString, ServerName);
       Server = &ServerString;
   }

   // 
   // Attempt to open the policy.
   // 
   return LsaOpenPolicy(
               Server,
               &ObjectAttributes,
               DesiredAccess,
               PolicyHandle
              );
}

// 
// Since LSA_UNICODE_STRING is not guaranteed to be terminated, this 
// function will allocate a WSTR for the buffer and add null termination.
// This buffer must later be freed via LocalFree();
// 
BOOL
AllocWStrFromLSAStr(
   PWSTR *ppwstrTarget,
   LSA_UNICODE_STRING LsaStr
  )
{
   // allocate buffer for str + null termination
   *ppwstrTarget = (PWSTR)LocalAlloc(0,LsaStr.Length + sizeof(WCHAR));
   if (*ppwstrTarget == NULL)
	  return FALSE;

   // copy unicode buffer
   memcpy(*ppwstrTarget,LsaStr.Buffer,LsaStr.Length);

   // add null termination
   (*ppwstrTarget)[LsaStr.Length] = '\0';

   return TRUE;
}


// 
// Simple status error display routine.
// 
void
DisplayNtStatus(
   LPSTR szAPI,
   NTSTATUS Status
  )
{
   // 
   // Convert the NTSTATUS to Winerror. Then call DisplayWinError().
   // 
   DisplayWinError(szAPI, LsaNtStatusToWinError(Status));
}
// 
// Helper function to build LSA UNICODE strings.
// 
void
InitLsaString(
   PLSA_UNICODE_STRING LsaString,
   LPWSTR String
  )
{
   DWORD StringLength;

   if (String == NULL) {
       LsaString->Buffer = NULL;
       LsaString->Length = 0;
       LsaString->MaximumLength = 0;
       return;
   }

   StringLength = wcslen(String);
   LsaString->Buffer = String;
   LsaString->Length = (USHORT) StringLength * sizeof(WCHAR);
   LsaString->MaximumLength=(USHORT)(StringLength+1) * sizeof(WCHAR);
}
// 
// Displays the error messages for a given code.
// 
void
DisplayWinError(
   LPSTR szAPI,
   DWORD WinError
  )
{
   LPSTR MessageBuffer;
   DWORD dwBufferLength;

   // 
   // TODO: Get this fprintf out of here.
   // 
   fprintf(stderr,"%s error!\n", szAPI);

   if (dwBufferLength=FormatMessageA(
                       FORMAT_MESSAGE_ALLOCATE_BUFFER |
                       FORMAT_MESSAGE_FROM_SYSTEM,
                       NULL,
                       WinError,
                       GetUserDefaultLangID(),
                       (LPSTR) &MessageBuffer,
                       0,
                       NULL
                      ))
   {
       DWORD dwBytesWritten; // unused

       // 
       // Output message string on stderr.
       // 
       WriteFile(
           GetStdHandle(STD_ERROR_HANDLE),
           MessageBuffer,
           dwBufferLength,
           &dwBytesWritten,
           NULL
          );

       // 
       // Free the buffer allocated by the system.
       // 
       LocalFree(MessageBuffer);
   }
}
				

Modification Type:MinorLast Reviewed:7/1/2004
Keywords:kbAPI kbhowto kbnetwork KB179891