3

So I have tried looking at a couple of different answers for this. One that I thought may have promise was this one:

How to check network interface type is Ethernet or Wireless on Windows using Qt?

However, I don't really know too much about Networking or even about Windows. Personally, I cannot understand most of the Microsoft documentation on their websites. I have tried things like INetworkConnection, NativeWiFi, etc. But either they do not do what I want, or I just cannot figure out how to do it from the available documentation.

With that being said, I would like to use C++ to check if the device this program is being run on is connected to the internet via Ethernet cable. Basically, I want to do the following:

  • If the computer is connected to Wireless only, run the program
  • If the computer is connected to Wired only, don't run the program
  • If the computer is connected to both Wired AND Wireless, don't run the program

However, the problem is that I don't know how to check if the device has Ethernet connected. Is there a way to do this? I am NOT using QT. Thank you!


EDIT: I should also include what I have tried so far.

I tried using GetAdaptersInfo and getting the Type trait from the PIP_ADAPTER_INFO variable type, but that always gives me Unknown type 71 whether I am on Ethernet or not.

The documentation for that GetAdaptersInfo is here: https://msdn.microsoft.com/en-us/library/aa365917%28VS.85%29.aspx

Thanks


EDIT 2: Here is the code I was using for GetAdaptersInfo

bool is_on_ethernet{
    PIP_ADAPTER_INFO pAdapterInfo;
    PIP_ADAPTER_INFO pAdapter = NULL;
    DWORD dwRetVal = 0;
    UINT i;

    struct tm newtime;
    char buffer[32];
    errno_t error;

    ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
    pAdapterInfo = (IP_ADAPTER_INFO *)malloc(sizeof(IP_ADAPTER_INFO));

    if(pAdapterInfo == NULL)
        printf("Error allocating memory need to call GetAdaptersInfo");

    if(GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW){
        free(pAdapterInfo);
        pAdapterInfo = (IP_ADAPTER_INFO *)malloc(ulOutBufLen);
    }

    if((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR){
        pAdapter = pAdapterInfo;

        switch(pAdapter->Type){
            case MIB_IF_TYPE_OTHER:
                printf("Other\n");
                return false;
                break;
            case MIB_IF_TYPE_ETHERNET:
                printf("Ethernet\h");
                return true;
                break;
            case MIB_IF_TYPE_TOKENRING:
                printf("Token Ring\n");
                return false;
                break;
            case MIB_IF_TYPE_FDDI
                printf("FDDI\n");
                return false;
                break;
            case MIB_IF_TYPE_PPP
                printf("PPP\n");
                return false;
                break;
            case MIB_IF_TYPE_LOOPBACK
                printf("Lookback\n");
                return false;
                break;
            case MIB_IF_TYPE_SLIP
                printf("Slip\n");
                return false;
                break;
            default
                printf("Unknown type %ld\n\n", pAdapter->Type);
                return false;
                break;
        }
    }

    if(pAdapterInfo)
        free(pAdapterInfo);

    return false;
}
Community
  • 1
  • 1
shovel_coder
  • 49
  • 1
  • 8
  • Do you have example code of your attempt to use GetAdaptersInfo? – David Zech Aug 14 '15 at 18:40
  • I do; I will add that as an edit now. Sorry for not including that! – shovel_coder Aug 14 '15 at 18:43
  • @Nighthawk441 Ok -- I have included the code as an edit. Again, sorry for the lack of code before! Thank you for commenting :) – shovel_coder Aug 14 '15 at 18:54
  • I don't have enough info to warrant an answer. But as far as I know, GetAdaptersInfo will return a linked list of all installed adapters. The code you have looks like the example code found on msdn, which only checks the first adapter. So you'd need to loop through each adapter using the Next pointer in IP_ADAPTER_INFO. You can get each adapter type, but it won't say if it's connected or not. – David Zech Aug 14 '15 at 18:56
  • @Nighthawk441 Ah ok, I did not realize that the code only checked the first adapter. I will work on looping through them and then try to figure out if each one is connected. Thank you! – shovel_coder Aug 14 '15 at 19:07
  • @Nighthawk441 I am not sure if this question is outside the scope of a comment or not; I have added a `while(pAdapterInfo->Next){}` loop around the switch statement (after the `if(...NO_ERROR)`), but that seems to cause an endless loop. Is this what you meant when referring to using the Next pointer, or did I implement your comment incorrectly? – shovel_coder Aug 14 '15 at 19:19
  • You probably are looking for while(pAdapterInfo = pAdapterInfo->Next()), but you should probably look at this: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365819(v=vs.85).aspx it has example code of nearly all the IP Helper functions – David Zech Aug 14 '15 at 19:20
  • i can answer your question - but first i need to understand it. What do you mean with `run the program` and `dont run the program`? What program? How do you intend to start it and why? What has the network adapter to do with programs? Are you sure you cant just include all of the algorithms of the program into yours? – specializt Aug 14 '15 at 20:15
  • @specializt Thanks for the interest specializt. All I mean is I have C++ code that I wrote that runs right now; it runs a series of functions. However I want to add some functionality to that code. All I want to do is that if the user has just wireless connected, then the code will proceed with running the functions. If there is Ethernet connected, however, the functions will never execute. Basically I just want to add an `if(ethernet is not connected){run functions}else{do not run functions}` into my currently functional C++ code. – shovel_coder Aug 14 '15 at 21:09
  • @specializt addendum: that is why I am using a boolean function -- so that I can just call the function in the if statement that I desire. For example `if(!is_on_ethernet){run rest of code}`. Hopefully that helps! Let me know if I can clarify any more or anything else. – shovel_coder Aug 14 '15 at 21:09
  • thats not what "to run a program" means .... the words you were searching for are "return true" and "return false". – specializt Aug 15 '15 at 08:11

3 Answers3

1

Your problem is somewhat difficult as it can be really complicated to get the "current" network adapter --- windows routes packets depending on network adapter configuration and destination reachability so your "current" adapter may change at any time ... but since you already know how to retrieve IPs and MACs ("hardware address") of available adapters you could simply use your hack to retrieve a MAC for your current IP and filter/search inside of my second function for it! The field "PhysicalAddress" is what you're looking for, thats the MAC adress

I have made the experience that the only, somewhat reliable way of doing that is via GetIfTable and GetIfTable2, the former returns somewhat superficial adpater info and the latter provides great detail. Heres a sample implementation, as it uses the detailed function you can also query for WLAN adapters :

vector<MIB_IF_ROW2>* getDevices(NDIS_PHYSICAL_MEDIUM type)
    {       
        vector<MIB_IF_ROW2> *v = new vector<MIB_IF_ROW2>();
        PMIB_IF_TABLE2 table = NULL;
        if(GetIfTable2Ex(MibIfTableRaw, &table) == NOERROR && table)
        {
            UINT32 i = 0;
            for(; i < table->NumEntries; i++)
            {
                MIB_IF_ROW2 row;

                ZeroMemory(&row, sizeof(MIB_IF_ROW2));
                row.InterfaceIndex = i;
                if(GetIfEntry2(&row) == NOERROR)
                {                   
                    if(row.PhysicalMediumType == type)
                    {
                        v->push_back(row);
                    }                   
                }           
            }
            FreeMibTable(table);
        }
        return v;
    }

Now all you need to do is iterate over the list and filter out disabled adapters and whatnot :

vector<MIB_IF_ROW2>* wlan = getDevices(NdisPhysicalMediumNative802_11); //WLAN adapters
//see https://msdn.microsoft.com/en-us/library/windows/desktop/aa814491(v=vs.85).aspx, "PhysicalMediumType" for a full list
for(auto &row : *v)
    {
        //do some additional filtering, this needs to be changed for non-WLAN           
        if( row.TunnelType == TUNNEL_TYPE_NONE &&
            row.AccessType != NET_IF_ACCESS_LOOPBACK &&         
            row.Type == IF_TYPE_IEEE80211 &&
            row.InterfaceAndOperStatusFlags.HardwareInterface == TRUE)              
            {
                //HERE BE DRAGONS!                    
            }
    }

Now its quite easy to generate lists of WLAN adapters and non-WLAN adapters (see comment in second function), search for your current MAC and conclude that it is wired or wireless - but be aware that these lists may overlap since 802.11 basically is an extended version of 802.3 but 802.3 does not include 802.11 (since its an extension) - so you will need a tiny bit of if/else logic going on in order to seperate WLAN from non-WLAN adapters.

You could also use WlanEnumInterfaces to get all of the WLAN adapters but thats basically the same as using the above function with NdisPhysicalMediumNative802_11 as parameter ...

specializt
  • 1,913
  • 15
  • 26
  • This seems like a good answer, but unfortunately I am a little bit confused. Once I am iterating through the vector like in your second block of code, a lot of the attributes for the rows are the same. How do I differentiate between wireless and wired? How do I know if the user is connected to Ethernet? I have tried using the Physical Address attributes, but those do not appear to give me a MAC address, so I am unsure what to compare to. Sorry for the confusion. Thank you! – shovel_coder Aug 17 '15 at 20:11
  • Ok, after some experimenting around I got this answer to work. I changed the if statement to `if(row.Type == IF_TYPE_ETHERNET_CSMACD && row.MediaConnectState == MdiaConnectStateConnected)` and then returned true inside that if statement. Thank you very much for your time and help. I will accept this answer now. – shovel_coder Aug 17 '15 at 22:30
  • ^addendum: I also changed the parameter I was sending to the `getDevices` function to be `NdisPhysicalMedium802_3` – shovel_coder Aug 17 '15 at 22:34
0

So first, thank you very much to user @Nighthawk441 for pointing me in the right direction for this. Without this user, I would most certainly have not come up with a solution.

That being said, the solution I have right now is, at best, a hack. It seems to work, but I don't think it is even close to the best option. Thus, I will leave this as an answer but I will not accept it for a little while in the event that a better answer is found. I am also very open to any comments anyone may have as it pertains to this answer.

In short, what I did was I looped through all of the Adapters from GetAdaptersInfo. In order to see if the adapter was connected, I compared the IP address of the adapter to the string "0.0.0.0", as if it were something other than this I felt it was safe to say that the adapter was connected. So, without further ado, here is the code that I implemented.

bool is_on_ethernet(){
    PIP_ADAPTER_INFO pAdapterInfo;
    PIP_ADAPTER_INFO pAdapter = NULL;
    DWORD dwRetVal = 0;
    UINT i;

    struct tm newtime;
    char buffer[32];
    errno_t error;

    ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
    pAdapterInfo = (IP_ADAPTER_INFO *)malloc(sizeof(IP_ADAPTER_INFO));

    if(pAdapterInfo == NULL)
        printf("Error allocating memory needed to call GetAdaptersInfo");

    if(GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW){
        free(pAdapterInfo);
        pAdapterInfo = (IP_ADAPTER_INFO *)malloc(ulOutBufLen);
    }

    if((dwRetValue = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR){
        do{
            pAdapter = pAdapterInfo;

            string ip_string = pAdapter->IpAddressList.IpAddress.String;

            switch(pAdapter->Type){
                case MIB_IF_TYPE_OTHER:
                    printf("Other\n");
                    break;
                ...
                case MIB_IF_TYPE_ETHERNET:
                    printf("Ethernet\n");

                    //new code
                    if(ip_string.compare("0.0.0.0") != 0){
                        free(pAdapterInfo);
                        return true;
                    }

                    break;
                default:
                    printf("Unknown type %ld\n", pAdapter->Type);
                    break;
            }
        }while(pAdapterInfo = pAdapterInfo->Next);
    }

    if(pAdapterInfo)
        free(pAdapterInfo);

    return false;
}

Looking at this reference really helped me:

https://msdn.microsoft.com/en-us/library/windows/desktop/aa365819%28v=vs.85%29.aspx

So thank you to Nighthawk for supplying that information to me. Hopefully this will help someone else! If anyone has any comments or any other answers, feel free to post them! Thanks!

shovel_coder
  • 49
  • 1
  • 8
  • 1
    I thought this was supposed to be C++? This code is full of disgusting manual memory management terribadness. – Puppy Aug 15 '15 at 10:18
  • 2
    its a beginners' attempt ... dont judge him yet - instead point out the flaws in great detail and provide examples of how its done. – specializt Aug 15 '15 at 10:32
  • @Puppy This code is ostensibly copied directly from the MSDN reference I mentioned in my answer. I have added a total of one[1] more `free(...)` statement. If the memory management is a problem, then I would have to deflect and say that it is an issue with the MSDN documentation. – shovel_coder Aug 17 '15 at 20:12
  • ^when I said answer, I of course meant my question. the documentation is [here](https://msdn.microsoft.com/en-us/library/aa365917%28VS.85%29.aspx) – shovel_coder Aug 17 '15 at 20:22
  • 1
    the MSDN is famous for having horrible examples ... most likely, inexperienced web developers write them all the time. Its best to not copy/paste them, instead look at the CORE functionality and use it -- not only are many examples bug-ridden but are also massive security-risks sometimes – specializt Aug 18 '15 at 11:24
  • @specializt That definitely makes sense. I will try to take that approach in the future. I just wish that the core functionality was easier to ascertain from the sparse documentation provided on that website. – shovel_coder Aug 18 '15 at 14:59
0

Based on @specializt's answer, and with some minor modification, I got it work for me as following:

BOOL getDevices(NDIS_PHYSICAL_MEDIUM type, vector<MIB_IF_ROW2>& vetIFRow)
{
    PMIB_IF_TABLE2 table = NULL;
    if (GetIfTable2Ex(MibIfTableRaw, &table) != NOERROR || !table)
    {
        return FALSE;
    }

    for (ULONG i = 0; i < table->NumEntries; i++)
    {
        MIB_IF_ROW2 row;
        ZeroMemory(&row, sizeof(MIB_IF_ROW2));
        row.InterfaceIndex = i;
        if (GetIfEntry2(&row) == NOERROR && row.PhysicalMediumType == type)
        {
            vetIFRow.push_back(row);
        }
    }

    FreeMibTable(table);
    return TRUE;
}

BOOL isNetIFConnected(const MIB_IF_ROW2& row, IFTYPE Type)
{
    return (row.TunnelType == TUNNEL_TYPE_NONE &&
        row.AccessType != NET_IF_ACCESS_LOOPBACK &&
        row.Type == Type &&
        row.InterfaceAndOperStatusFlags.HardwareInterface == TRUE &&
        row.MediaConnectState == MediaConnectStateConnected);
}

tstring getNetWorkType()
{
    vector<MIB_IF_ROW2> vectRow;
    BOOL bRet = getDevices(NdisPhysicalMedium802_3, vectRow); // ETHERNET adapters
    if (bRet)
    {
        for (auto it = vectRow.begin(); it != vectRow.end(); it++)
        {
            if (isNetIFConnected(*it, IF_TYPE_ETHERNET_CSMACD))
            {
                return L"ETHERNET";
            }
        }
    }

    vectRow.clear();
    bRet = getDevices(NdisPhysicalMediumNative802_11, vectRow); //WLAN adapters
    if (bRet)
    {
        for (auto it = vectRow.begin(); it != vectRow.end(); it++)
        {
            if (isNetIFConnected(*it, IF_TYPE_IEEE80211))
            {
                return L"WIFI";
            }
        }
    }

    return L"Unknown";
}

Thanks to @specializt