HOWTO: Determine if a Windows NT Computer Is a Domain Member

ID: Q179891


The information in this article applies to:
  • Microsoft Windows NT Workstation versions 3.5, 3.51, 4.0
  • Microsoft Windows 2000


SUMMARY

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


MORE INFORMATION

To determine if a computer running Windows NT Workstation 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 using LsaOpenPolicy.


  2. Use LsaQueryInformationPolicy to retrieve domain information.


  3. Check the value of the 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; 
To access the UNICODE string returned by the API, follow these steps:

   PPOLICY_PRIMARY_DOMAIN_INFO pPolicy;
   // 
   // Open the policy object and call LsaQueryInformationPolicy.
   // 
   .
   .
   .
   // 
   // Print the Name field on the screen.
   // 
   printf("pPolicy->Name.Buffer : %S\n", pPolicy->Name.Buffer ); 
The following console application illustrates how to determine if a computer running Window NT Workstation is a member of a domain or workgroup or a stand-alone computer:

   #define UNICODE
   #define INCL_NET
   #include <windows.h>
   // 
   // ntsecapi.h is not automatically installed with the SDK.
   // You need to retrieve the file from the samples directory
   // tree for the win32 SDK or the platform SDK.
   // 
   // [sdk root]\samples\win32\winnt\security\include
   // 
   // You can statically link this code to the following library files:
   // 
   //  adv32api.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     : HOWTO: 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
       );
   //----------------------------------------------------------------------
   // 
   // 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
            {
               // 
               // 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.
                  // 
                  printf( "Member of Domain %S\n",
                                       ppdiDomainInfo->Name.Buffer);
               }
               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( ppdiDomainInfo->Name.Buffer,
                      pwkiWorkstationInfo->wki100_computername,
                      wcslen( 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",
                                           ppdiDomainInfo->Name.Buffer);
                  }
                  else
                                printf("%S is a standalone workstation\n" ,
                                          ppdiDomainInfo->Name.Buffer);
               }
            }
         }
         // 
         // 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
                   );
   }
   // 
   // 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);
       }
   } 

Additional query words:

Keywords : kbnetwork kbAPI kbWinOS2000 kbSDKPlatform kbNetAPI kbGrpNet
Version : WINDOWS:; winnt:3.5,3.51,4.0
Platform : WINDOWS winnt
Issue type : kbhowto


Last Reviewed: January 30, 2000
© 2000 Microsoft Corporation. All rights reserved. Terms of Use.