Wednesday, January 30, 2008

WNet Enum Class

WNetEnumClass. This class implements the discovery of connected computers, drives, and printers, using the WNet functions.

TODO: Add an array of all TNetResources, and functions to allow their access from the calling app. Or more properly, functions that will return useful info from the TNetResources, simplifying the determination of device type, and so on.

USAGE:

var
Obj :TWNetEnumClass;
begin
Obj := TWNetEnumClass.Create(Self);
try
Obj.Refresh;
Memo1.Lines := Obj.GetAllNames;
Memo2.Lines := Obj.GetCompNames;
Memo3.Lines := Obj.GetDiskNames;
Memo4.Lines := Obj.GetDomainNames;
Memo5.Lines := Obj.GetErrors;
Memo6.Lines := Obj.GetPrintNames;
finally
Obj.Free;
end;
end;



unit uWNetEnumClass;

interface

uses
Classes, Sysutils, Windows;

type
TWNetEnumClass = class(TObject)
private
FslAllNames: TStringList;
FslCompNames: TStringList;
FslDiskNames: TStringList;
FslDomainNames: TStringList;
FslErrors: TStringList;
FslPrintNames: TStringList;
procedure ErrorHandler(errorNum: Cardinal; s: string);
function EnumerateResources(startingPoint: TNetResource): Boolean;
protected
procedure EnumResources;
public
constructor Create(Owner: TComponent); virtual;
destructor Destroys; virtual;

function GetAllNames: TStringList;
function GetCompNames: TStringList;
function GetDiskNames: TStringList;
function GetDomainNames: TStringList;
function GetErrors: TStringList;
function GetPrintNames: TStringList;

procedure Refresh; // used by calling apps to populate the lists
end;

implementation

{ TWNetEnumClass }

const
BASE_RES = 128;
MAX_RES = 8192;

var
// establish a buffer to use to prime the drill-down process
base_buffer: array of TNetResource;

constructor TWNetEnumClass.Create(Owner: TComponent);
begin
inherited Create;

SetLength(base_buffer, BASE_RES); // initialize the base buffer

// now create the stringlists we will use
FslAllNames := TStringList.Create;
FslCompNames := TStringList.Create;
FslDiskNames := TStringList.Create;
FslDomainNames := TStringList.Create;
FslErrors := TStringList.Create;
FslPrintNames := TStringList.Create;
end;

destructor TWNetEnumClass.Destroys;
begin
// free the stringlists
FslPrintNames.Free;
FslErrors.Free;
FslDomainNames.Free;
FslDiskNames.Free;
FslCompNames.Free;
FslAllNames.Free;

base_buffer := nil; // free the base buffer

inherited Destroy;
end;

//

function TWNetEnumClass.EnumerateResources(startingPoint: TNetResource): Boolean;
var
res: Cardinal;
resEnum: Cardinal;
enumHandle: THandle;
buffer: array of TNetResource;
bufferSize: Cardinal;
numEntries: Cardinal;
i: Cardinal;
begin // EnumerateResources
// Open a container
res := WNetOpenEnum(
RESOURCE_GLOBALNET, RESOURCETYPE_ANY, 0, @startingPoint, enumHandle);
if (res <> NO_ERROR) then
ErrorHandler(res, 'WNetOpenEnum');

// loop through all the elements in the container
repeat
numEntries := Cardinal(-1);
SetLength(buffer, MAX_RES);
bufferSize := SizeOf(TNetResource) * MAX_RES;

// get resources
resEnum := WNetEnumResource(enumHandle, numEntries, buffer, bufferSize);
if (resEnum = NO_ERROR) then
begin
// loop through all entries
for i := 0 to numEntries - 1 do
begin
if (buffer[i].dwDisplayType = RESOURCEDISPLAYTYPE_SERVER) then
FslCompNames.Add(buffer[i].lpRemoteName)
else if (buffer[i].dwType = RESOURCETYPE_PRINT) then
FslPrintNames.Add(buffer[i].lpRemoteName)
else if (buffer[i].dwType = RESOURCETYPE_DISK) then
FslDiskNames.Add(buffer[i].lpRemoteName)
else if (buffer[i].dwDisplayType = RESOURCEDISPLAYTYPE_DOMAIN) then
FslDomainNames.Add(buffer[i].lpRemoteName);
// if the entry is a container, recursively open it
if (buffer[i].dwUsage and RESOURCEUSAGE_CONTAINER > 0) then
if (not EnumerateResources(buffer[i])) then
FslErrors.Add('Enumeration failed');
end;
end
else if (resEnum <> ERROR_NO_MORE_ITEMS) then
ErrorHandler(resEnum, 'WNetEnumResource');
{ added the test for ERROR_INVALID_HANDLE to deal with the case where a "remembered"
connection is no longer in existence. I need to look for a cleaner fix. }
until (resEnum = ERROR_NO_MORE_ITEMS) or (resEnum = ERROR_INVALID_HANDLE);

// clean up
buffer := nil;
res := WNetCloseEnum(enumHandle);
if (res <> NO_ERROR) then
begin
ErrorHandler(res, 'WNetCloseEnum');
result := False;
end
else
result := True;
end;

procedure TWNetEnumClass.EnumResources;
begin
EnumerateResources(base_buffer[0]);
end;

procedure TWNetEnumClass.ErrorHandler(errorNum: Cardinal; s: string);
var
res: Cardinal;
error: Cardinal;
errorStr: string;
nameStr: string;
begin
if (errorNum <> ERROR_EXTENDED_ERROR) then
begin
FslErrors.Add('Error number ' + IntToStr(errorNum) + ' returned by ' + s);
end
else
begin
res := WNetGetLastError(
error, PChar(errorStr), 1000, PChar(nameStr), 1000);
if (res <> NO_ERROR) then
FslErrors.Add('Failure in WNetGetLastError: ' + IntToStr(error))
else
begin
FslErrors.Add('Extended Error: ' + errorStr + '. Provider: ' + nameStr);
end;
end;
end;

function TWNetEnumClass.GetAllNames: TStringList;
begin
FslAllNames.Sort;
Result := FslAllNames;
end;

function TWNetEnumClass.GetCompNames: TStringList;
begin
FslCompNames.Sort;
Result := FslCompNames;
end;

function TWNetEnumClass.GetDiskNames: TStringList;
begin
FslDiskNames.Sort;
Result := FslDiskNames;
end;

function TWNetEnumClass.GetDomainNames: TStringList;
begin
FslDomainNames.Sort;
Result := FslDomainNames;
end;

function TWNetEnumClass.GetErrors: TStringList;
begin
Result := FslErrors;
end;

function TWNetEnumClass.GetPrintNames: TStringList;
begin
FslPrintNames.Sort;
Result := FslPrintNames;
end;

procedure TWNetEnumClass.Refresh;
begin
FslAllNames.Clear;
FslCompNames.Clear;
FslDiskNames.Clear;
FslDomainNames.Clear;
FslErrors.Clear;
FslPrintNames.Clear;
EnumResources;
end;

end.

No comments: