Enumerate Local Shares via NetShareEnum - Delphi

DarkCoderSc personal avatar
DarkCoderSc

Jean-Pierre LESUEUR

uses
  System.SysUtils, Winapi.Windows, Generics.Collections;

const
  MAX_PREFERRED_LENGTH = DWORD(-1);
  NERR_Success = 0;

  STYPE_DISKTREE  = $00000000;
  STYPE_PRINTQ    = $00000001;
  STYPE_DEVICE    = $00000002;
  STYPE_IPC       = $00000003;
  STYPE_TEMPORARY = $40000000;
  STYPE_SPECIAL   = $80000000;

  STYPE_MASK = $0000000F;

// ...

type
  NET_API_STATUS = DWORD;

  _SHARE_INFO_2 = record
    shi2_netname      : PWideChar;
    shi2_type         : DWORD;
    shi2_remark       : PWideChar;
    shi2_permissions  : DWORD;     // Deprecated
    shi2_max_uses     : DWORD;
    shi2_current_uses : DWORD;
    shi2_path         : PWideChar;
    shi2_passwd       : PWideChar;
  end;
  TShareInfo2 = _SHARE_INFO_2;
  PShareInfo2 = ^TShareInfo2;

// ...

function NetShareEnum(
  servername        : PWideChar;
  level             : DWORD;
  var bufptr        : Pointer;
  prefmaxlen        : DWORD;
  var entriedread   : DWORD;
  var totalentries  : DWORD;
  var resume_handle : DWORD
) : NET_API_STATUS; stdcall; external 'Netapi32.dll';

function NetApiBufferFree(
  Buffer : Pointer
) : NET_API_STATUS; stdcall; external 'Netapi32.dll';

// ...

type
  TNetworkShare = class
  private
    FName        : String;
    FRemark      : String;
    FPath        : String;
    FPasswd      : String;
    FType        : DWORD;
    FPermissions : DWORD;
    FMaxUses     : DWORD;
    FCurrentUses : DWORD;

    {@M}
    function GetReadableType() : String;
  public
    {@C}
    constructor Create(const AShareInfo2: TShareInfo2);

    {@G}
    property Name         : String read FName;
    property Remark       : String read FRemark;
    property Path         : String read FPath;
    property Passwd       : String read FPasswd;
    property ShareType    : DWORD  read FType;
    property ReadableType : String read GetReadableType;
    property Permissions  : DWORD  read FPermissions;
    property MaxUses      : DWORD  read FMaxUses;
    property CurrentUses  : DWORD  read FCurrentUses;
  end;

// ...

(* TNetworkShare *)

constructor TNetworkShare.Create(const AShareInfo2: TShareInfo2);
begin
  inherited Create();
  ///

  FName        := string(AShareInfo2.shi2_netname);
  FRemark      := string(AShareInfo2.shi2_remark);
  FPath        := string(AShareInfo2.shi2_path);
  FPasswd      := string(AShareInfo2.shi2_passwd);
  FType        := AShareInfo2.shi2_type;
  FPermissions := AShareInfo2.shi2_permissions;
  FMaxUses     := AShareInfo2.shi2_max_uses;
  FCurrentUses := AShareInfo2.shi2_current_uses;
end;

function TNetworkShare.GetReadableType(): string;
begin
  case FType and STYPE_MASK of
    STYPE_DISKTREE  : result := 'Disk';
    STYPE_PRINTQ    : result := 'Print Queue';
    STYPE_DEVICE    : result := 'Communication Device';
    STYPE_IPC       : result := 'IPC';
    STYPE_SPECIAL   : result := 'Special';
    STYPE_TEMPORARY : result := 'Temporary';
    else
      result := 'Unknown';
  end;
end;

// ...

(* Local *)

function EnumerateLocalShares(var AList : TObjectList<TNetworkShare>) : Cardinal;
begin
  result := 0;
  ///

  if not Assigned(AList) then
    AList := TObjectList<TNetworkShare>.Create(True)
  else begin
    if not AList.OwnsObjects then
      AList.OwnsObjects := True;

    ///
    AList.Clear();
  end;
  ///

  var pShareInfo : PShareInfo2;

  var ANumberOfEntries : DWORD;
  var AEntriesRead : DWORD;
  var AResumeHandle : DWORD := 0;

  const LEVEL_2 = 2;
  var AStatus := NetShareEnum(nil, LEVEL_2, Pointer(pShareInfo), MAX_PREFERRED_LENGTH, AEntriesRead, ANumberOfEntries, AResumeHandle);
  try
    if AStatus <> NERR_Success then
      Exit();
    ///

    var pShareInfoRow := pShareInfo;

    for var I := 0 to ANumberOfEntries -1 do begin
      AList.Add(TNetworkShare.Create(pShareInfoRow^));

      ///
      Inc(pShareInfoRow);
    end;

    ///
    result := AList.Count;
  finally
    if pShareInfo <> nil then
      NetApiBufferFree(pShareInfo);
  end;
end;

// ...

var AList := TObjectList<TNetworkShare>.Create(True);
try
  EnumerateLocalShares(AList);
  ///

  const NETWORK_SHARE_ROW_MASK = '%-10s | %-20s | %-10s | %s';

  const ALine = StringOfChar('-', 80);

  WriteLn(ALine);

  WriteLn(Format(NETWORK_SHARE_ROW_MASK, [
    'Name',
    'Path',
    'Type',
    'Description'
  ]));

  WriteLn(ALine);

  for var ANetworkShare in AList do begin
    WriteLn(Format(NETWORK_SHARE_ROW_MASK, [
      ANetworkShare.Name,
      ANetworkShare.Path,
      ANetworkShare.ReadableType,
      ANetworkShare.Remark
    ]));
  end;

  WriteLn(ALine);
finally
  FreeAndNil(AList);
end;

// ...

Creating and researching code snippets takes time and effort. You’re welcome to share them through your own platforms, but please don’t forget to credit the original author, here: Jean-Pierre LESUEUR.


Created

May 9, 2025

Last Revised

May 14, 2025