Obtaining network usage information from the Windows Runtime

Raymond Chen

Raymond

Network usage information on Windows can be obtained from classes in the Windows.Networking.Connectivity namespace. The Network­Information class is your starting point.

We’ll start with C# and translate to C++/WinRT when we’re done.

Prepare a C# console application to use Windows Runtime asynchronous operations as described last time, and start typing.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Windows.Networking.Connectivity;

class Program
{
    static async Task DoIt()
    {
        var now = DateTime.Now;
        var states = new NetworkUsageStates
        { Roaming = TriStates.DoNotCare, Shared = TriStates.DoNotCare };

        var profiles = NetworkInformation.GetConnectionProfiles();
        foreach (var profile in profiles)
        {
            var usages = await profile.GetNetworkUsageAsync(
                now.AddDays(-1), now, DataUsageGranularity.PerDay,
                states);
            var usage = usages[0];
            if (usage.ConnectionDuration > TimeSpan.Zero)
            {
                Console.WriteLine(profile.ProfileName);
                Console.WriteLine($"BytesReceived = {usage.BytesReceived}");
                Console.WriteLine($"BytesSent = {usage.BytesSent}");
                Console.WriteLine($"ConnectionDuration = {usage.ConnectionDuration}");
                Console.WriteLine($"------------------");
            }
        }

    }
    static void Main()
    {
        DoIt().GetAwaiter().GetResult();
    }
}

All of the work happens in the creatively-named DoIt method. The main function just calls DoIt() and waits for the task to complete. If you can upgrade to C# 7.1 or better, you can take advantage of async main support.

Okay, so back to the DoIt() method.

We first capture the current time into a local variable now. This ensures that our time queries are consistent through the loop.

We also create a local Network­Usage­States object which says that we don’t care about distinguishing whether you were Roaming or Shared.

We then ask the Network­Information object for all the connection profiles. For each profile, we ask for usage in the last 24 hours, aggregated by day. Given those parameters, there will be exactly one usage report.¹

For any connection that was used for a nonzero amount of time, we report the various properties of that daily usage.

Note that generating these reports is time-consuming, so if you already know which connections you want, you can filter on their name. If you are interested only in the connection that is currently being used for Internet access, you can use Get­Internet­Connection­Profile instead of enumerating through all of the connections.

And here’s the C++/WinRT version.

#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Networking.Connectivity.h>
#include <stdio.h>

using namespace std::literals::chrono_literals;
using namespace winrt;
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Networking::Connectivity;

IAsyncAction DoIt()
{
    auto now = clock::now();
    NetworkUsageStates states{ TriStates::DoNotCare, TriStates::DoNotCare };

    auto profiles = NetworkInformation::GetConnectionProfiles();
    for (auto profile : profiles)
    {
        auto usages = co_await profile.GetNetworkUsageAsync(
            now - 24h, now, DataUsageGranularity::PerDay, states);
        auto usage = usages.GetAt(0);
        auto seconds = static_cast<int>(std::chrono::duration_cast<
            std::chrono::seconds>(usage.ConnectionDuration()).count());
        if (seconds > 0)
        {
            printf("%ls\n", profile.ProfileName().c_str());
            printf("BytesReceived = %I64u\n", usage.BytesReceived());
            printf("BytesSent = %I64u\n", usage.BytesSent());
            printf("ConnectionDuration = %d seconds\n", seconds);
            printf("------------------\n");
        }

    }
}

int main()
{
    init_apartment();
    DoIt().get();
}

Next time, we’ll look at usage attribution.

¹ If we had asked for multiple days, then there would be one report per day, in chronological order. Note that the definition of “day” is not “calendar day” but “24-hour period starting at the provided start time.” If the time range is not an exact multiple of the granularity, then the last report will cover only part of the granularity interval.

4 comments

Comments are closed. Login to edit/delete your existing comments

  • Clockwork-Muse
    Clockwork-Muse

    In C# `GetNetworkUsageAsync` takes `DateTimeOffset`. If you subtract one day from a `DateTime`, and there’s a DST boundary, the difference will not be 24 hours (and the resulting time may not exist, although the implicit conversion won’t throw an error).

  • Avatar
    紅樓鍮

    Isn’t it possible to just write if (usage.ConnectionDuration() > 0s) in C++?

    • Raymond Chen
      Raymond ChenMicrosoft employee

      It’s possible, but it’s not the same thing. Though now I realized that I could have written auto seconds = usage.ConnectionDuration() / 1s;

      • Avatar
        紅樓鍮

        Or you could have written if (usage.ConnectionDuration() >= 1s) if the absolute identical behavior must be preserved.