Use the Windows API to generate a list of available Network Resources.

By: Justin Swett

Abstract: Using WNetOpenEnum and WNetEnumerate to get a list of the computer names from Network Neighborhood?

QUESTION:

      How can I get a list of the computer names from Network Neighborhood?

ANSWER:

      Windows provides a set of API functions that allow enumeration of Network Resources. There are three basic steps to follow:
The first step is to call WNetOpenEnum():


DWORD WNetOpenEnum(
  DWORD dwScope,                // scope of enumeration
  DWORD dwType,                 // resource types to list
  DWORD dwUsage,                // resource usage to list
  LPNETRESOURCE lpNetResource,  // resource structure
  LPHANDLE lphEnum              // enumeration handle buffer
);

The parameters passed into WNetOpenEnum determine what type of enumeration you will get.  In this example dwScope is set to RESOURCE_CONTEXT.  This should limit WNetEnumResource() to finding only Network resources within your workgroup, which should be the list of computers retrieved from "Computers Near Me".  Setting dwType to RESOURCE_DISK keeps WNetResource() from returning any printers, etc. Check the on line help for more documentation on WNetOpenEnum().

Once WNetOpenEnum() has been called the next step is to to enumerate the handle passed into WNetOpenEnum(), lphEnum.  This is done through a call to WNetEnumResource():

DWORD WNetEnumResource(
  HANDLE hEnum,          // handle to enumeration
  LPDWORD lpcCount,      // entries to list
  LPVOID lpBuffer,       // buffer
  LPDWORD lpBufferSize   // buffer size
);

WNetEnumResource() also takes a pointer to an array of Network Resources, lpBuffer, which will be stuffed chuck full of yummy network goodness.
Finally, a call to WNetCloseEnum() should be made to close the resource handle:

DWORD WNetCloseEnum(
  HANDLE hEnum   // handle to enumeration
);


Here is an example that I ported from Microsoft's Web site Seen Here. I have changed the example to get the computers in the current workgroup, RESOURCE_CONTEXT, instead of grabbing all available global resources.

In my example I use an array of TNetResource as opposed to allocating space for a pointer. I also used FillChar() to initialize the array. These are the main differences from my example and Microsoft's.

function EnumerateFunc( hwnd: HWND; hdc: HDC ; lpnr: PNetResource ): Boolean;
const
  cbBuffer : DWORD  	 = 16384;      // 16K is a good size
var
  hEnum, dwResult, dwResultEnum : DWORD;
  lpnrLocal : array
        [0..16384 div SizeOf(TNetResource)] of TNetResource;    // pointer to enumerated structures
  i : integer;
  cEntries : Longint;             
begin
  centries := -1;			   // enumerate all possible entries

  // Call the WNetOpenEnum function to begin the enumeration.
  dwResult := WNetOpenEnum(
                          RESOURCE_CONTEXT,  // Enumerate currently connected resources.
                          RESOURCETYPE_DISK, // all resources
                          0,                 // enumerate all resources
                          lpnr,              // NULL first time the function is called
                          hEnum              // handle to the resource
                          );

  if (dwResult <> NO_ERROR) then
  begin
    // Process errors with an application-defined error handler
    Result := False;
    Exit;
  end;

  // Initialize the buffer.
  FillChar( lpnrLocal, cbBuffer, 0 );

  // Call the WNetEnumResource function to continue
  //  the enumeration.
  dwResultEnum := WNetEnumResource(hEnum,           // resource handle
                                  DWORD(cEntries),  // defined locally as -1
                                  @lpnrLocal,       // LPNETRESOURCE
                                  cbBuffer);        // buffer size

  // This is just printing
  for i := 0 to cEntries - 1 do
  begin
    // loop through each structure and 
    // get remote name of resource... lpnrLocal[i].lpRemoteName)
  end;

  // Call WNetCloseEnum to end the enumeration.
  dwResult := WNetCloseEnum(hEnum);

  if(dwResult <> NO_ERROR) then
  begin
    // Process errors... some user defined function here
    Result := False;
  end
  else
    Result :=  True;
end;


Server Response from: ETNASC03