How do I determine that another machine is present on the network?

If you know the machine's IP address, you can always ping it of course. But let's assume you only know its name and/or the machine doesn't have TCP/IP installed. At first glance the obvious answer to this is the api NetServerEnum. But NetServerEnum suffers from a problem. If a machine has recently been switched off, NetServerEnum will see it as still present. Why? Because you're effectively querying the network browser, you have to wait until that realises a machine has gone from the network. That will take three announcement intervals, at not less than 12 minutes each by default. That's a long time.

To see this behaviour, open up Network Neighborhood and switch a machine off. See how long it takes for the machine to disappear from the display. Warning, you might want to bring sandwiches and a hot drink for this experiment.....

OK, so how do we determine if a machine has really gone away? We obviously need to interact with the machine in some way. But what if the machine doesn't have a share, or we don't have administrator rights to access the admin shares? Fortunately, there's a way of doing this that doesn't rely on privileges, and it lies in the same part of the api as NetServerEnum. It's called NetQueryDisplayInformation. We treat the returned data from NetServerEnum as advisory only, then confirm it with NetQueryDisplayInformation. Take a look at the (rather long) code sample below. Note that this sample assumes it is being executed in an ANSI executable, and therefore being passed an ANSI target server name.

DWORD CMyDlg::FindServer (const CString& rcsTarget, BOOL& bFound)
{
   NET_API_STATUS  nas ;
   NET_DISPLAY_MACHINE * pndm;
   SERVER_INFO_101 * pSI_101 ;

   DWORD   dwEntriesRead;
   DWORD   dwTotalEntries;
   DWORD   dwRetVal = ERROR_SUCCESS;
   DWORD   dwNdmCount;

   int iCount;
   long lScratch;
   char szServer [64];
   char szDebug [256];
   WCHAR   wcServer [64];

   CString csServer;
   CString csTarget = rcsTarget;

   bFound= FALSE;
   nas = NetServerEnum (NULL,
                        101,
                        (LPBYTE*)&pSI_101,
                        MAX_PREFERRED_LENGTH,
                        &dwEntriesRead,
                        &dwTotalEntries,
                        SV_TYPE_SERVER, // type you're looking for.
                        NULL,
                        0);

   if (nas == NERR_Success)
   {
      wsprintf (szDebug, "NetServerEnum found %u server(s)\n", dwEntriesRead);
      OutputDebugString (m_szDebug);

      if (dwEntriesRead)
      {
         for (iCount=0; iCount<(int)dwEntriesRead; iCount++)
         {
            if (pSI_101[iCount].sv101_name == NULL ||
                IsBadReadPtr (pSI_101[iCount].sv101_name, sizeof(WCHAR)))
            {
               wsprintf (szDebug, "server name ptr 0x%08X is unusable\n",
                         pSI_101[iCount].sv101_name);
               OutputDebugString (m_szDebug);
            }
            else
            {
               if (WideCharToMultiByte (CP_ACP,
                                        WC_COMPOSITECHECK,
                                        (LPCWSTR)(pSI_101[iCount].sv101_name),
                                        -1,
                                        szServer,
                                        sizeof(szServer),
                                        NULL,
                                        NULL) == 0)
               {
                  dwRetVal = GetLastError ();
                  wsprintf (szDebug, "WCTMB gave error %u\n", dwRetVal);
                  OutputDebugString (m_szDebug);
               }
               else
               {
                  csServer = szServer;
                  wsprintf (szDebug, "server '%s' found\n", szServer);
                  OutputDebugString (m_szDebug);

                  csServer.MakeUpper();
                  csTarget.MakeUpper();
                  if (csServer == csTarget)
                  {
                     OutputDebugString ("Server matches target, try to access it\n");

                     // The browse info will continue to report machines
                     // for ages after they've been switched off, so try to
                     // talk direct to the machine.
                     wcscpy (wcServer, L"\\\\");
                     wcscat (wcServer, (LPCWSTR)(pSI_101[iCount].sv101_name));

                     nas = NetQueryDisplayInformation (wcServer,
                                                       2, 0, 1,
                                                       sizeof(NET_DISPLAY_MACHINE),
                                                       &dwNdmCount,
                                                       (PVOID*)&pndm);
                     switch (nas)
                     {
                        case NERR_Success:
                        case ERROR_MORE_DATA:
                           OutputDebugString ("NQDI successful\n");
                           bFound = TRUE;
                           break;
                        default:
                           wsprintf (szDebug, "NQDI gave error %u\n", nas);
                           OutputDebugString (m_szDebug);
                           dwRetVal = nas;
                           break;
                     }
                     NetApiBufferFree ((PVOID)pndm);
                     if (bFound) break;
                  }
               }
            }
         }
      }
      NetApiBufferFree (pSI_101);
   }
   else
   {
      wsprintf (szDebug, "NetServerEnum gave error %u\n", nas);
      OutputDebugString (m_szDebug);
      dwRetVal = nas;
   }
   return (dwRetVal);
}

Once we have a hit from NetServerEnum, I use NetQueryDisplayInformation to access that server itself. I don't care about any information returned, I just look for a good result or ERROR_MORE_DATA. You'll usually get error 1722 (an RPC server not available error) if the target machine is actually absent.

Downside? You can get some timeouts on the call to an absent machine. Typically it's in the range of a second to a few seconds, but it can get larger depending upon network and protocol configuration. Only experimentation will tell if this solution is suitable for your situation.

Download