This article is a real-life example of NotifyIconLibrary in action. We see the "business end" of the product, the installer, and database maintenance procedures.
This article provides an opportunity to learn about the range of topics I explored while building the back end of an ad blocker for the Windows 10 desktop. My primary goal was to create an ad blocker that could be enabled or disabled by clicking on a notify icon.
This article (Part 4: Ad Blocker Back End) is a real-life example of
NotifyIconLibrary in action. Here, we see the "business end" of the product, the installer, and database maintenance procedures.
I've known for some time about one of the simplest methods of blocking ads on the internet. Unlike the first ad blocker I ever used, which analyzed and rewrote the web content as seen by a browser plugin, this method does not overreach its abilities and really messes up the page content. Also, now that there are so many non-browser apps that use their own APIs and skip the browser completely, there is a need to block internet traffic that is bypassing the browser.
The technique which I'm referring to is the replacement of the drivers.etc.hosts file. This file is used by the operating system to spot routes that need to be changed, such as routes to hosts on a local network. This re-routing takes place during the translation between domain names and IP addresses. For the specific purpose of blocking, rather than re-routing the DNS translation, the host file entries for all of the domain names to be blocked have an IP address of 0.0.0.0 (or the ipV6 equivalent).
The Winhelp 2002 organization has been collecting a list of domains worth blocking for a long time: presumably since 2002. The current list can be found at winhelp2002.mvps.org.txt. It is updated periodically. I have taken the most recent version of this list and added a few entries of my own at the beginning of the file for use in this project. I could write another entire article on techniques for collecting these entries, and perhaps I will.
Sleight of Hand
Replacing c:\windows\system32\drivers\etc\hosts is not easy. To do it manually, you might start by logging in as a local administrator and opening an elevated command prompt. Since you will already have the current directory set to c:\windows\system32, you can cd to drivers\etc. From there, you will have no problem copying a new version of hosts over the existing one or using Notepad to edit hosts in place.
Automating this process is a different story. Windows is designed to make it impossible to elevate a process without manual intervention. The best way I could find to do this is to use the task scheduler to submit a request to run a pre-defined and pre-registered task. If you run a task that is set up to run under the
SYSTEM account, it is run in a non-interactive window station, so it can't implement a user interface. If you configure a task to run under one of your administrative accounts, you are forced to either enter your password every time you want to activate the task or leave your password in a vulnerable place. If you change your password, it needs to be changed in the task activation code. Since we're trying to run the task elevated, there might even be an elevation dialog that needs to be dismissed manually. It's been too long since I tried this, I can't remember all of the details. Since my plan was to have the task activated automatically whenever I logged on, dealing with pop-ups at that point did not seem like a good idea.
Another approach I tried was using a client/server architecture. This produced separate installs for the client and the server. I had to make the ports configurable in the installers so that the client and server could communicate over otherwise unused ports. For the most part, this worked okay, but the server would occasionally get confused and refuse to talk to the client. It was more work than I wanted to do to make my protocol bulletproof, so I dropped this approach.
My next approach was to accept the reality of requiring explicit elevation for the install, launch and uninstall procedures. That brings this matter up to the present time.
The Notify Icon
I was inspired to use a notify icon by a program entitled "
SystemTrayApp", written by Kevin Sacro. You can find it at https://github.com/kesac/SystemTrayApp. I adopted this code wholesale, then started removing and replacing various parts as needed.
One result of this effort led to
NotifyIconLibrary, which we saw in Part 1 of this series. My work also led to
AdBlockerTest, which we saw in Part 3 of this series. In this part of the series, we will take a look at
AdBlocker, the back end of the ad blocker and the last piece of the puzzle.
Using the Code
As an end user, using the code is simple: Find AdBlockerAny.msi and copy it to a more appropriate place from which to install it. Double-click on this file in File Explorer or launch by your favorite method. I'm not going to list twelve different ways to do it or run a contest to see who can find the most obscure method.
After elevation, accept all of the default options unless you know of some reason not to do so. The installation should be quick. Once installed and every time you re-boot or log in thereafter, you should consider using the Desktop icon or the Start Menu icon (a red and green traffic signal) to launch the application. You will need to elevate each time you do so. Alternatively, you can wait until you want to change the ad blocking state.
Once the application starts, you will find a notify icon in the form of a traffic signal on the Task Bar. If this is the first time you are launching the application since installing, the traffic signal will be green. If not, the traffic signal will be red or green, depending on how you last set it. After a few seconds, the notify icon will migrate to the Notification Area. That's the rectangular pop-up window that is displayed when you click on the "^" symbol on the Task Bar. You can left double click on the notify icon to reverse the setting of the traffic signal. Red (stop) means that ads are currently blocked. Green (go) means that ads are currently unblocked. You can also use the keyboard to navigate to the notify icon or the "^" symbol on the Task Bar by pressing Windows+B followed by some number of right arrows. If the notify icon is hidden, once you reach the "^" symbol, press the space bar to open the Notification Area. Then use the arrow keys to navigate to the notify icon. Now you can use the Context Menu key or the Enter key to open the application's Context Menu. You can navigate the Context Menu by using the up and down arrow keys or close the Context Menu by using the escape key. You can also open the Context Menu by right clicking the notify icon. You can select an item from the Context Menu by using the Enter key or by left clicking the desired item. There are four items:
- Block item blocks ads
- Unblock option unblocks ads
- About option opens the About dialog
- Exit option closes the Context Menu, removes the notify icon and exits the application.
You can close the About dialog by clicking the "X" by pressing the escape key.
It is not necessary to launch the application on login or reboot in order to continue blocking ads. This is only necessary if you want to verify or change the current setting. It is also not necessary to explicitly exit the application when logging out or rebooting. Even a power failure, short of damaging the file system, will not affect the continued operation of the application.
Sometimes, you might find that blocking a particular domain has some undesirable side effects. You can get around situations like this by disabling the ad blocker and trying again, perhaps by refreshing your browser screen. If this is not a satisfactory solution, you can comment out the offending line in the c:\windows\system32\drivers\etc\hosts.blocked file, unblock ads using Ad Blocker if it is currently in the blocked state, then block ads using AdBlocker. If this does not provide satisfactory results, you might want to block individual subdomains, leaving just the subdomains that are unsafe to block in the clear. This requires some snooping, a topic for another article.
If you are happy with your changes, copy the hosts.blocked file that you edited into your Ad Blocker build project and rebuild so that you will be ready the next time you want to install Ad Blocker.
Points of Interest
If you have never created an installer for a .NET program, you might get some benefit from reviewing the two examples provided with the source code.
AdBlockerSetupAny can be used with the files produced by a release build in the Any configuration. Select the build and configuration before selecting the build option from setup project's context menu. The resulting .msi file can be run on any Windows 10 system to install AdBlocker in a 32-bit or a 64-bit environment. Likewise,
AdBlockerSetupX64 can be used with the files produced by a release build in the X64 configuration. The resulting .msi file will only work in an X64 version of Windows 10.
If there is enough interest in the topic, I could do another article on using the installer with custom tasks (as, for example, in this case, to elevate a portion of the installation process).
This application provides the basic tools needed for internationalization. Just create duplicate resource files with the proper names in the same folders as the provided en-US versions. It is somewhat controversial, but I have placed all of my .resx files in the Properties folders directly under each project. Some might prefer to use a Resources folder directly under each project. If a .resx file has a name of the form: LocalizedResources.resx, the British version must have a name of the form: LocalizedResources.en-GB.resx. Use the resource editor to modify the first column displayed there to its proper translation. Building the project will create the required subfolders and satellite assemblies. It's somewhat of a naming nightmare, but that's what you get when you force new functionality into an established structure and demand backward compatibility. Don't forget to translate the resource files in every assembly you have used. In the case of
AdBlocker, the assemblies are
This is the last article in this series. I hope that you have enjoyed reading it as much as I have enjoyed writing it.
- 1st December, 2020: Initial version