Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

The "New ipconfig" and the IP Helper API

0.00/5 (No votes)
5 May 2003 2  
This article details a class that allows the manipulation of network adapters and uses that class to build a graphical ipconfig utility for anyone using Windows2000 or Windows XP. In order to accomplish this, the IP helper API is used.

Sample Image - netcfg_main.jpg

Introduction

The main class presented here actually came about secondary to my desire to have a GUI replacement for the console ipconfig utility. Awhile back I was doing a lot of network related stuff and got tired of continually bringing up the command prompt which I would always close again without fail.  After a few different versions the one I am actually happy with was born.  Behold NetCfg-The Sequel.

What the Primary Control Class Provides

  • Programmatic Renew/Release Feature
  • Network Adapter Enumeration
  • Network Adapter Details such as lease information, DHCP Servers, WINS Servers, subnet info, etc..
  • Adapter Description Retrieval.

What the Application Provides ( A Manifestation of Above )

  • Allows reviewing of multiple adapters on a given system.
  • Shows how IP addresses are obtained.
  • Shows adapter information such as the WINS, DHCP and DNS Servers.
  • Show Lease Times ( obtained and expires )
  • Allows and adapter to be released and renewed.
  • Can be minimized to the system tray where a tooltip is display when hovering that contains IP data.

 

The Network Adapter Class

First a quick note: In my personal library this class exists as an MFC/ATL based class by which I mean it uses many classes in the new VS.NET are supported in non MFC projects but would not be supported in VS6 such as CTime and CString. I also used other constructs for the arrays such as CAtlArray rather than all of the STL based classes that are in this project.  I used the STL here to minimize portability issues for those wishing to use this class in an application developed in the VS6 environment. Anyone wanting the MFC version ( for some reason ) may feel free to contact me about it.

Now onto the goodies. 

class CNetworkAdapter {
    public:
        CNetworkAdapter();
        ~CNetworkAdapter();
        BOOL SetupAdapterInfo( IP_ADAPTER_INFO* pAdaptInfo );

            // information about the adapters name for the users

            // and its name to the system

        tstring GetAdapterName() const;
        tstring GetAdapterDescription() const;
        
            // dhcp lease access functions

        time_t    GetLeaseObtained() const;
        time_t    GetLeaseExpired() const;
        
            // access to lists of various server's ip address

        SIZE_T    GetNumIpAddrs() const;        
        SIZE_T    GetNumDnsAddrs() const;
        tstring    GetIpAddr( int nIp = 0 ) const;    
        tstring GetSubnetForIpAddr( int nIp = 0 ) const;
        tstring    GetDnsAddr( int nDns = 0 ) const;        
        tstring GetCurrentIpAddress() const;
        
            // dhcp function

        BOOL    IsDhcpUsed() const;        
        tstring    GetDchpAddr() const;

            // wins function

        BOOL    IsWinsUsed() const;
        tstring GetPrimaryWinsServer() const;
        tstring GetSecondaryWinsServer() const;

        tstring   GetGatewayAddr( int nGateway = DEFAULT_GATEWAY_ADDR) const;
        SIZE_T    GetNumGatewayAddrs() const;

        static    tstring GetAdapterTypeString( UINT nType );
        UINT    GetAdapterType() const;

        DWORD    GetAdapterIndex() const;
        BOOL    ReleaseAddress();
        BOOL    RenewAddress();
        
    protected:
        :
    
    private:
        :
};

Aside from the class interface itself there is one other thing that must be mentioned in order for this class to be used effectively and that is the function used to enumerate the adapters on the computer. This function looks like:

  DWORD EnumNetworkAdapters( CNetworkAdapter* lpBuffer, ULONG ulSzBuf, LPDWORD lpdwOutSzBuf );

This function works like many of the standard windows functions that enumerate items.  You must pass it a buffer and the total size of that buffer in bytes along with the parameter used to return to you the actual amount of bytes needed.  If your buffer is too small ERROR_INSUFFICIENT_BUFFER is returned.

Operations

Enumeration

To do anything with this class the first thing you need to do is actually set one up.  While you can feel free to do the enumeration yourself and call the setup function I have provided an enumeration function that will make your life a bit easier should you wish it. This is where much of the actual work happens.

DWORD EnumNetworkAdapters( CNetworkAdapter* pAdapters, 
                         ULONG ulSzBuf,LPDWORD lpdwOutSzBuf ) {    
    IP_ADAPTER_INFO* pAdptInfo    = NULL;
    IP_ADAPTER_INFO* pNextAd    = NULL;    
    ULONG ulLen                    = 0;
    int nCnt                    = 0;
    CWinErr erradapt;
    

    erradapt = ::GetAdaptersInfo( pAdptInfo, &ulLen );
    if( erradapt == ERROR_BUFFER_OVERFLOW ) {
        pAdptInfo = ( IP_ADAPTER_INFO* )ALLOCATE_FROM_PROCESS_HEAP( ulLen );
        erradapt = ::GetAdaptersInfo( pAdptInfo, &ulLen );        
    }

    pNextAd = pAdptInfo;
    while( pNextAd ) {
        nCnt++;
        pNextAd = pNextAd->Next;
    }

    *lpdwOutSzBuf = nCnt * sizeof( CNetworkAdapter );
    if( ulSzBuf < *lpdwOutSzBuf ) {        
        DEALLOCATE_FROM_PROCESS_HEAP( pAdptInfo );
        return ERROR_INSUFFICIENT_BUFFER;
    }

        // this needs to be reset for future use.

    nCnt = 0;

    if( erradapt == ERROR_SUCCESS ) {
            // initialize the pointer we use the move through

            // the list.

        pNextAd = pAdptInfo;

            // loop through for all available interfaces and setup an 

            // associated CNetworkAdapter class.

        while( pNextAd ) {
            pAdapters[ nCnt ].SetupAdapterInfo( pNextAd );    
                // move forward to the next adapter in the list so

                // that we can collect its information.

            pNextAd = pNextAd->Next;
            nCnt++;
        }
    }

        // free any memory we allocated from the heap before

        // exit.  we wouldn't wanna leave memory leaks now would we? ;p

    DEALLOCATE_FROM_PROCESS_HEAP( pAdptInfo );        

    return ERROR_SUCCESS;
}
        

In order to get the adapter information the first thing we do is call into the IP function GetAdaptersInfo. Once we have that list we are going to have to make sure if we got enough space from the caller to store the new list of adapters in.  If we don't have a large enough buffer we cleanup the memory we allocated and return the error back to the caller along with the total amount of space that is needed to complete the operation so that the user can resize his buffer and perform the call again.  If the buffer we received was large enough than we can now iterate through the entire list of adapters setting up each adapter in sequence. To do this the member function SetupAdapterInfo is called.

BOOL CNetworkAdapter::SetupAdapterInfo( IP_ADAPTER_INFO* pAdaptInfo ) {
    BOOL bSetupPassed                = FALSE;
    IP_ADDR_STRING* pNext            = NULL;
    IP_PER_ADAPTER_INFO* pPerAdapt    = NULL;
    ULONG ulLen                        = 0;
    CWinErr err;

    _IPINFO iphold;
    
    if( pAdaptInfo ) {        
#ifndef _UNICODE 
        m_sName            = pAdaptInfo->AdapterName;        
        m_sDesc            = pAdaptInfo->Description;
#else
        USES_CONVERSION;
        m_sName            = A2W( pAdaptInfo->AdapterName );
        m_sDesc            = A2W( pAdaptInfo->Description );
#endif

        m_sPriWins        = pAdaptInfo->PrimaryWinsServer.IpAddress.String;
        m_sSecWins        = pAdaptInfo->SecondaryWinsServer.IpAddress.String;
        m_dwIndex        = pAdaptInfo->Index;        
        m_nAdapterType    = pAdaptInfo->Type;    
        m_bDhcpUsed        = pAdaptInfo->DhcpEnabled;
        m_bWinsUsed        = pAdaptInfo->HaveWins;    
        m_tLeaseObtained= pAdaptInfo->LeaseObtained;
        m_tLeaseExpires    = pAdaptInfo->LeaseExpires;
        m_sDhcpAddr        = pAdaptInfo->DhcpServer.IpAddress.String;
        
        if( pAdaptInfo->CurrentIpAddress ) {
           m_sCurIpAddr.sIp = pAdaptInfo->CurrentIpAddress->IpAddress.String;
           m_sCurIpAddr.sSubnet = pAdaptInfo->CurrentIpAddress->IpMask.String;
        }else{
            m_sCurIpAddr.sIp        = _T("0.0.0.0");
            m_sCurIpAddr.sSubnet    = _T("0.0.0.0");
        }


            // since an adapter may have more than one ip address we need

            // to populate the array we have setup with all available

            // ip addresses.

        pNext = &( pAdaptInfo->IpAddressList );
        while( pNext ) {
            iphold.sIp        = pNext->IpAddress.String;
            iphold.sSubnet    = pNext->IpMask.String;
            m_IpAddresses.push_back( iphold );
            pNext = pNext->Next;
        }

         // an adapter usually has just one gateway however the provision 

         // exists for more than one so to "play" as nice as possible 

         // we allow for it here // as well. 

pNext = &( pAdaptInfo->GatewayList ); 
while( pNext ) 
{ m_GatewayList.push_back(pNext->IpAddress.String); 
  pNext = pNext->Next; 
} 
  // we need to generate a IP_PER_ADAPTER_INFO structure in order 

  // to get the list of dns addresses used by this adapter. 

  err = ::GetPerAdapterInfo( m_dwIndex, pPerAdapt, &ulLen ); 
  if( err == ERROR_BUFFER_OVERFLOW ) 
  { 
    pPerAdapt = (IP_PER_ADAPTER_INFO*) 
    ALLOCATE_FROM_PROCESS_HEAP( ulLen ); 
    err = ::GetPerAdapterInfo( m_dwIndex, pPerAdapt, &ulLen ); 
    // if we succeed than we need to drop into our loop 

    // and fill the dns array will all available IP 

    // addresses. 

    if( err == ERROR_SUCCESS ) 
    { 
      pNext = &(pPerAdapt->DnsServerList); 
      while(pNext) 
      { 
       m_DnsAddresses.push_back( pNext->IpAddress.String ); 
       pNext = pNext->Next; 
      } 
      bSetupPassed = TRUE; } 
      // this is done outside the err == ERROR_SUCCES just in case.

      // the macro uses NULL pointer checking so it is ok if 

      // pPerAdapt was never allocated. 

DEALLOCATE_FROM_PROCESS_HEAP(pPerAdapt); 
    } 
  } 
return bSetupPassed; 
}

While just a bit lengthy this function is very simple in nature. It simply transfers much of the data from the IP_ADAPTER_INFO structure to our member variables so they can be used and retrieved later.  This is also where we ensure that we account for the possibility of having more than one IP address for local IP address and Gateways. (multiple IPs and Gateways are supported in this class but are not used in the demo application.)  The next thing we do here is collect DNS information for the adapter which is given to us in the IP_PER_ADAPTER_INFO structure. To get this information we must call the ::GetPerAdapterInfo API function.  It doesn't make a lot of sense to me why the DNS information was not kept in the adapter structure like everything else but none the less it isn't.  Finally, we just need to cleanup our allocated memory and exit the function.

Renew & Release

While there are separate functions in the interface used to release or renew an adapter this is really only one function that we use. We can do this because the operations required to renew or release an address are the exact same except for the actual API call to either renew or release. To solve this we use a single function that is passed a pointer to the proper API function for the renew/release operations.

BOOL CNetworkAdapter::DoRenewRelease(DWORD ( __stdcall *func)
                                ( PIP_ADAPTER_INDEX_MAP AdapterInfo ) ) {    
    IP_INTERFACE_INFO*    pInfo    = NULL;
    BOOL bDidIt                    = FALSE;
    ULONG ulLen                    = 0;
    int nNumInterfaces            = 0;
    int nCnt                    = 0;
    CWinErr err;

    
    err = ::GetInterfaceInfo( pInfo, &ulLen );
    if( err == ERROR_INSUFFICIENT_BUFFER ) {
        pInfo = ( IP_INTERFACE_INFO* ) ALLOCATE_FROM_PROCESS_HEAP( ulLen );
        err = ::GetInterfaceInfo( pInfo, &ulLen );

        if( err != NO_ERROR ) {
            return FALSE;            
        }
    }

        // we can assume from here out that we have a valid array

        // of IP_INTERFACE_INFO structures due to the error 

        // checking one above.

    nNumInterfaces = ulLen / sizeof( IP_INTERFACE_INFO );
    for( nCnt = 0; nCnt < nNumInterfaces; nCnt++ ) {
        if( pInfo[ nCnt ].Adapter[ 0 ].Index == m_dwIndex ) {
            err = func( &pInfo[ nCnt ].Adapter[ 0 ] );
            
                // free all used memory since we don't need it any more.

            DEALLOCATE_FROM_PROCESS_HEAP( pInfo );    
            
            bDidIt = ( err == NO_ERROR );            
            if( ! bDidIt ) {                
                return FALSE;
            }

            break;
        }
    }            

    return bDidIt;
}

The first that that we do when we get here is to enumerate the system's adapters and iterate the list that we get back until we find the entry that identify the same adapter that we represent.  Once we have this we can that call whatever IP function was passed to us with that data and the operation will be performed returning to us the error code.  All that is left to do is deallocate our memory and return back.

Note about deallocation: Looking back I suppose it would have been safer to put the deallocate block outside of the loop so that it is guaranteed to be deleted however, the adapter is always going to be found because we our enumerating hardware. The only way the adapter itself could not be found during this loop is if the application was started, the adapter was removed and then someone tried to release the adapter. That would require hotswapable cards (since the hardware would be running) which means this is running on a high end server which was not my target audience.

Misc

There are a lot of other functions here that you can feel free to review on your own.  They are very simple and really don't warrant any real attention here as they are mainly accessor function for data retrieved from the adapter and per adapter info structures.

Finishing Up

Well, I hope some people learn something useful from this code and for those who don't I at least hope you now have access to a nice little utility that will replace the command line version.  The one sad note about this utility is that is doesn't support NT4 because of the iphlpapi.lib requirement.  This hasn't been a real problem for me as I don't use NT4 anymore but it still would have been nice.

Demo Notes

The only real note I want to make about the demo is about machine with more that one adapter and my alleged support for it.  For anyone who doesn't have more than one adapter you will not get to see how this works but trust me it is there.  For those of you who have multiple NICs, there is a small spinner next to the adapter name which can be used to move through your list of adapters on the computer.

Future Improvements

The one thing on my plate right now as an improvement to this software is to take hold of the MAC address.  I have made some provisions for this in the code present here but it is not done as of yet. This is mainly because this code itself is pretty old for me.  I just dusted it off and presented here recently.  One of those thing meant to do a long while ago but kept getting pushed off. Shame how that works.  Feel free to send me suggestions/comments/concerns/requests about this application or class in general.

History

Initial Release

Credit

Davide Calabro's CXPStyleButtonST and associated classes were used in the demo application.  Thanks Davide!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here