April 30, 2013

Getting Network Share List

Network neighborhood can be obtained programmatically in any application. It’s just a matter of a single Windows API call to the NetShareEnum function.

MSDN publish the complete description of NetShareEnum at http://msdn.microsoft.com/en-us/library/windows/desktop/bb525387(v=vs.85).aspx

Since Delphi doesn’t provide the definition, we have to define it our self. With the help of MSDN we find that NetShareEnum is defined for C/C++ language into Lmshare.h and the function is implemented in Netapi32.dll.

Lmshare.h can be found in Windows SDK which is a free download from Microsoft.

But don’t worry; I’ve done the work for you!

function NetShareEnum(ServerName       : PWideChar;
                      Level            : DWORD;
                      var BufPtr       : Pointer;
                      PrefMaxLen       : DWORD;
                      var EntriesRead  : DWORD;
                      var TotalEntries : DWORD;
                      var ResumeHandle : DWORD) : NET_API_STATUS; stdcall;
             external 'NetAPI32.dll' name 'NetShareEnum';

ServerName points to a Unicode string that specifies the DNS or NetBIOS name of the remote computer. Use nil to specify the local computer. It works with the name beginning with two backslashes or just the server name.

Level is an integer which defines which information you want to retrieve. In this sample, we will use the basic level of information which is level 1.

BufPtr is a variable which will be filled by the API with the address of a buffer where the requested information has been copied. This buffer must be freed later using NetApiBufferFree. BufPtr is defined as an untyped pointer since the actual data type change according to the level argument.

PrefMaxLen specifies the preferred maximum length of returned data. A special value (-1) specifies that the function allocate the amount of memory required for the data. There is practical reason to not use that value since all computers have plenty of RAM today.

EntriesRead returns the number of entries returned in the buffer.

TotalEntries receive the total number of entries. Useful if PrefMaxLength is too small. But Microsoft says it is only a hint.

ResumeHandle is a handle which can be used to resume an interrupted enumeration. Should be set to zero for the first call to the function.

The return value is an error code. Actually the NET_API_STATUS is simply a DWORD.

We saw that NetShareEnum is allocating memory for use and that this memory must be freed using NetApiBufferFree. This new API function is defined in the same file. Here is his Delphi declaration:

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

To be able to effectively use NetShareEnum, we still need the data type declaration. You can see on MSDN the various levels (see link above). We will use basic information which is level 1:

    SHARE_INFO_1 = record
        shi1_netname     :  PWideChar;
        shi1_type        :  DWORD;
        shi1_remark      :  PWideChar;

shi1_netname is the name of the network share, the one you see using Windows own Explorer.

shi1_type is a bit mask which defines what the share is, for example a disk or a printer.
    STYPE_PRINTQ    = 1;
    STYPE_DEVICE    = 2;
    STYPE_IPC       = 3;
    STYPE_TEMPORARY = $40000000;
    STYPE_SPECIAL   = $80000000;

shi1_remark is the description given when the share has been created on the server.

It’s time now to use NetShareEnum in a real program. Create a new VCL form application; drop a button and a TMemo on it. Use the code below.

procedure TForm1.EnumerateShares1(
    const Server : PChar;
    const Pfx    : String = '');
    NERR_SUCCESS         = 0;
    EntriesRead  : DWORD;
    TotalEntries : DWORD;
    ResHandle    : DWORD;
    ShareInfo1   : PSHARE_INFO_1;
    P            : PSHARE_INFO_1;
    Status       : NET_API_STATUS;
    I            : Integer;
    ResHandle := 0;
    Status := NetShareEnum(Server, 1, Pointer(ShareInfo1),
                           EntriesRead, TotalEntries, ResHandle);
        if Status <> NERR_SUCCESS then
        P := ShareInfo1;
        for I := 0 to TotalEntries - 1 do begin
            Memo1.Lines.Add(Pfx + P.shi1_netname +
                           ' ' + ShareTypeToStr(P.shi1_type));

This code is really simple. Isn’t it? There is a call to NetShareEnum and then a loop to iterate thru all the record returned in the buffer, displaying the share name and share type. Finally, NetApiBufferFree is called to release the memory allocated by NetShareEnum.

I created a small function to decode the share type:

function ShareTypeToStr(SType : DWORD) : String;
    case SType and $FFFF of
    STYPE_DISKTREE:  Result := '[Disk]';
    STYPE_PRINTQ:    Result := '[Printer]';
    STYPE_DEVICE:    Result := '[Device]';
    STYPE_IPC:       Result := '[IPC]';
                     Result := '[Type0x' + IntToHex(SType, 8) + ']';
    if (SType and STYPE_SPECIAL) <> 0 then
        Result := Result + '[Special]';
    if (STYpe and STYPE_TEMPORARY) <> 0 then
        Result := Result + '[Temporary]';

I should be clear to you that you have to call the nice function from the button onclick event handler like this:

procedure TForm1.Button1Click(Sender: TObject);

In that demo, ‘ML150’ is the name of a server on my network. Use an appropriate name for your network. Or an empty string to list the shares on the local computer.

A last note: this code has been built using Delphi XE4. It should work unchanged with all Unicode enabled version of Delphi (2009 and up). For older Delphi, you have to pay attention to Unicode strings that the API is using and convert it to Ansi strings.

This article is available from:

Follow me on Twitter
Follow me on LinkedIn
Follow me on Google+
Visit my website: http://www.overbyte.be

No comments: