Enumerating Network Resources

Microsoft Windows provides several functions (called WNet functions) that help us in implementing networking capabilities in our application. This article explains how to use the WNet functions to connect and disconnect the remote drive programmatically. It also shows how to enumerate the resources connected to the network.

  1. Create a dialog-based application using AppWizard and give the project name as enumnet.
  2. Add controls to the dialog template as shown in the following figure.

  3. The controls and their IDs are given in the following table.

    Control

    ID

    Connect Button

    IDC_CONNECT

    Disconnect Button

    IDC_DISCONNECT

    List

    IDC_LISTRES

    Browse Button

    IDC_BROWSE

    Exit Button

    IDCANCEL

  4. #include "winnetwk.h" file and link 'mpr.lib' library file. To link the file, select ‘Project | Settings | Link’ and in the edit box ‘Object/library modules’ type ‘mpr.lib’.
  5. Add a control variable m_listres of type CListCtrl for the list control. After m_listres is added to the class declare it as static since we wish to access it in a different thread. To use this variable add the following statement to the ‘enumDlg.cpp’ file.
  6. CListCtrl CEnumnetDlg::m_listres ;

  7. Create handlers for the ‘Connect’ and ‘Disconnect’ buttons and add the following code to it.
  8. void CEnumnetDlg::OnConnect( )

    {

    if ( WNetConnectionDialog ( m_hWnd, RESOURCETYPE_DISK ) == NO_ERROR )

    MessageBox ( "Drive Mapped" ) ;

    }

    void CEnumnetDlg::OnDisconnect( )

    {

    if ( WNetDisconnectDialog ( m_hWnd, RESOURCETYPE_DISK ) == NO_ERROR )

    MessageBox ( "Drive Disconnected" ) ;

    }

    WNetConnectionDialog( ) function displays a standard ‘Map Network Drive’ dialog box which allows the user to connect (Map) a remote drive to network resource. In WNetConnectionDialog( ) we have passed handle to the window as the first parameter and type of the resource to be connected. RESOURCETYPE_DISK specifies that we are going to connect a drive of the remote computer. If the function fails it automatically shows an error message dialog box.

    In the OnDisconnect( ) handler we have called the function WNetDisconnectDialog( ) which displays a standard dialog box to unmap the selected drive. WNetDisconnectDialog( ) takes the same parameters as the WNetConnectionDialog( ) function.

  9. Now, let us see how to display network resource in the list control. Add the following lines to CDialog::OnInitDialog( ) function.

m_listres.InsertColumn ( 0, "Remote Name", LVCFMT_LEFT, 160, 0 ) ;

m_listres.InsertColumn ( 1, "Comment", LVCFMT_LEFT, 150, 0 ) ;

m_listres.InsertColumn ( 2, "Display Type", LVCFMT_LEFT, 100, 0 ) ;

We have to display in the list control all the resources connected to the network, their comments and type of resource. Since browsing and displaying all this information can take some time we should do this job in a separate thread so that user can interact with the application even if browsing is going on. We should create this thread in OnBrowse( ) handler by calling the AfxBeginThread( ) function as shown below.

AfxBeginThread ( enumnet, NULL ) ;

AfxBeginThread( ) takes address of a thread function (enumnet in our case) as the first parameter. The second parameter is the parameter that would be passed to the thread function when it is called.

Declare enumnet in ‘enumnetDlg.h’ as follows:

UINT enumnet ( LPVOID param ) ;

The function enumnet is given below:

UINT enumnet ( LPVOID param )

{

LPNETRESOURCE lpnr = ( LPNETRESOURCE ) param ;

HANDLE h ;

DWORD cn = 0xFFFFFFFF, s = 16384 ;

LPNETRESOURCE nr ;

static int item = 0 ;

int res = WNetOpenEnum ( RESOURCE_GLOBALNET, RESOURCETYPE_ANY, 0, lpnr, &h ) ;

nr = ( LPNETRESOURCE ) new char[s] ;

do

{

res = WNetEnumResource ( h, &cn, nr, &s ) ;

if ( res == NO_ERROR )

{

for ( int i = 0 ; i <= cn ; i++ )

{

if (nr[i].dwType != RESOURCEDISPLAYTYPE_GENERIC )

{

CEnumnetDlg::m_listres.InsertItem ( item, nr[i].lpRemoteName ) ;

CString comment = nr[i].lpComment ;

if ( comment == "" )

comment = "None" ;

CEnumnetDlg::m_listres.SetItemText ( item, 1, comment ) ;

CString distype = CEnumnetDlg::getdisplaytype ( nr[i].dwDisplayType ) ;

CEnumnetDlg::m_listres.SetItemText ( item, 2, distype ) ;

item++ ;

}

if ( ( nr[i].dwUsage & RESOURCEUSAGE_CONTAINER ) == RESOURCEUSAGE_CONTAINER )

enumnet ( &nr[i] )

}

}

} while ( res == ERROR_NO_MORE_ITEMS ) ;

WNetCloseEnum ( h ) ;

return 0 ;

}

To enumerate a network resource, we have to use WNetOpenEnum( ) and WNetEnumResource( ) functions. In the first call to WNetOpenEnum( ) function we have passed NULL to it since passing NULL retrieves handle to the root of the network. The first parameter of WNetOpenEnum( ) is the scope of the enumeration. RESOURCE_GLOBALNET is given to enumerate all the resources of the net. The second parameter specifies the type of the resource (either disk or print or both) to be enumerated. Passing 0 as the third parameter indicates that we wish to enumerate the container as well as the connectable resources. (If a computer with name Alpha is connected to the network and has C and D drives, then Alpha becomes container and C and D drives become connectables). The fourth parameter is a pointer to the NETRESOURCE structure. Last parameter is the address of a HANDLE variable. The WNetOpenEnum( ) function sets up a handle to the network resource in the HANDLE variable. This handle is passed to WNetEnumResource( ) subsequently. A call to this function returns information about the resource in the form of an array of NETRESOURCE structures. The second parameter of WNetEnumResource( ) is the pointer to the variable specifying number of entries to list. 0xFFFFFFFF means that the function will return as many entries as possible. The third parameter is the buffer that receives enumeration results. Last parameter is the size of the buffer specified in the third parameter. Generally, the size is given as 16 KB. The information filled in the NETRESOURCE structure is then displayed in the list control.

To get the type of the resource we have called a function getdisplaytype( ). To add this function right click the CEnumnetDlg class in the ClassView tab. From the popup menu select ‘Add Member Function’ menu item. Specify the return type as CString and declaration as getdisplaytype (DWORD type). Check the Static check box. The getdisplaytype( ) function is declared as static since it is called from the thread function. The function is given below:

CString CEnumnetDlg::getdisplaytype ( DWORD type )

{

CString str ;

switch ( type )

{

case RESOURCEDISPLAYTYPE_DOMAIN :

str = "Domain" ;

break ;

case RESOURCEDISPLAYTYPE_SERVER :

str = "Container" ;

break ;

case RESOURCEDISPLAYTYPE_SHARE :

str = "Share" ;

}

return str ;

}

The function is self-explanatory. We have passed dwDisplayType element of NETRESOURCE structure to the function, checked the type and have returned the resource type accordingly.

If the dwUsage member of the NETRESOURCE structure is RESOURCEUSAGE_CONTAINER then we should enumerate every resource present in the container. For this we have called the function enumnet( ) recursively.

No comments: