Thanks to DSL, everyone can host a website and mailserver or two (or 15) out of their home. All it takes is buying a domain, using a DNS service to redirect requests to your static or dynamic IP, switching on IIS in your Win2k/XP Pro or Win2kServer and running IISLockdown to close all those extra ports you don't want exposed to the script kiddies
Pretty much it's a cakewalk. Until you start relying on it.
I was tired of coming home and finding that my PPPOE (Point to Point Protocol Over Ethernet) connection was dropped; squirrels swinging on my lines, the evil local phone company not liking my 3rd party DSL provider, whatever the reason it was annoying. Sometimes it would chug along for weeks and sometimes it'd die a couple of times a day. And then there were the spooky times that even though I was not connected with another computer, my web box was connected with a dynamic IP instead of my static.
Some geeks rabidly hate PPPOE, but it isn't truly a big pain in itself, except when it comes to getting automatic connections, and because it is fast becoming the dominant system for DSL customers it's better to learn to work with it than fight the losing battle moving from isp to isp to avoid it. This article shows you how to make PPPOE your slave.
We know that we could make a scheduled app to check the connection every few minutes, but Windows Task Scheduler is "end-user" <g>. We also know that if we could easily create a Windows Service then we could just tell it to check the connections and reconnect as needed. However, MS chose to never give VBClassic developers a simple way to make a Service; You either had to go non-RAD with VC, use the unsupported and apparently no longer downloadable ntsvc.ocx (which, for some reason, is easier to trust at work than at home) or buy Desaware's NTService Toolkit (great product but a bit too much for my modest needs).
Now along comes VB.net which makes creating a Windows Service a breeze!
The Spec
- Make a Windows Service that wakes up every two minutes and checks the computer's IP Addresses
- If the count of addresses is equal to the count of LAN connection plus 1 (I personally have only 1 nic in my machine) and the static IP is in the list, we are connected so the service goes back to sleep
- If the count of addresses is equal to the count of LAN and, for safety, if the address is not the static then we are not connected to the internet, so force a new connection
- If the static IP is not in the list but the count of addresses is equal to the count of LAN connection plus 1 then we are connected to the internet but not with the static. In this case force a disconnect from the internet and force a reconnect in an attempt to get hold of the static IP.
- Write the important stuff to the EventLog so we can see where the system can be improved (gotta have bugs if you want to really learn).
- Hold general settings (static IP address & expected number of addreses) outside of the app so nothing has to be hard-coded. While .net has far better built-in Registry support the preference is to get out of the regedit habit and just use an XML configuration file.
The great thing about a project like this is that when you have a real need but there is no full sample code to steal you learn a lot. That said, I hope that you'll follow along with the logic that 's presented in this article rather than just copy&pasting and trusting it blindly. I'm not guaranteeing that it is the end-all, and there are probably alternative techniques that will make those ultra-picky snobdevs happy (no, they're never happy) ... but it is a fine working starting point and I hope you'll use it that way too.
The Architecture
The first thing in making a service is to decide whether you should do it as a single-file monolithic deployment or as a set of component assemblies. In the days of VBClassic and ntsvc.ocx you could make a generic service app to host the form to host the ocx and have the service call out to a server, but usually since we were already hosting that darned form we shoved too much into the one exe. These days the .net service samples all copy the same ms quickstart demo and show using physically discrete assemblies which is really the smarter way to go if there is a chance that the meat of the functionality could ever be reused elsewhere. Besides, it makes testing the functionality outside of the service a matter of making a quickie console harness. So that's the way we're going here.
The pieces of the solution will be:
- The functionality library dll (called IPConnection)
This will be a simple class with a single public method "ConnectIfNot". The method signature will have arguments to allow passing in an IP address (string) and the max number of expected addresses on the computer (int). For my purposes there is no need of a return value.
- The Service exe (called IPConnector)
This will include all the code it takes to register itself as a windows service and a timer whose event will cause a IPConnection object to do it's thing
The code pt1: IPConnection.dll
Fire up the VS IDE and create a new VB Windows Service project. Overwrite the default project name value with "IPConnector" as shown below.

Go to the solution explorer and right-click >>rename the class name from the default "Service1.vb" to "IPConnector.vb"

Right-click on the solution node (the topmost node in the solution tree, not the IPConnector project node) and click to Add >> New Project.

In the option box select Class Library and set the name value to "IPConnection"
Right-click on the new project's "Class1.vb" and rename the file to "IPConnection.vb" The tab for the class will be renamed automatically but you'll have to manually change the code window's default from "Public Class Class1" to "Public Class IPConnection"

Now we get to real typing! Ok, a little cut & pasting is acceptable. Let's make the entry point method we described earlier. Click your mouse inside the IPConnection class region and enter the private variables:
Private m_sStaticAddress as string = ""
Private m_shtMaxExpectedAddresses as short = 0
On the next line, type:
Public Sub ConnectIfNot(byval StaticAddress as string, byval MaxExpectedAddresses as short)
This method is the entry point that our service app to set those private variables and call into some private worker functions. Type the following inside the method stub:
m_sStaticAddress = Trim(StaticAddress)
m_shtMaxExpectedAddresses = MaxExpectedAddresses
If Not AmConnected() Then
System.Diagnostics.EventLog.WriteEntry _
("IPConnection", "Computer is not connected")
MakeConnection()
End If
These lines will be used to call a method that will check the status of the static ip address (AmConnected) and if it is not alive to force it to open (MakeConnection). We'll get to those next, but first notice that we added a fully declared framework namespace call to stick some information in the Windows EventLog. You don't have to always fully type out the namespaces unless you think there will be an ambiguity issue (calling an object of one parent namespace that is named the same as another object in another parent namespace), but it doesn't hurt especially now that we're all new to this stuff to occassionally start with "System." and follow the bouncing intellisense popups; it's enlightening to see all the options that a namespace provides.
SideNote: if you're making this class in notepad you get the Trim function by Importing "Microsoft.VisualBasic.Strings" or fully qualifing the function with "Microsoft.VisualBasic.Strings.Trim([your string here])" or you could create a new System.String object and call it's Trim method. Totally up to you. I'm typing in the IDE so I get the Microsoft.VisualBasic namespace automatically.
Now let's get to the meat. AmConnected. Click below the ConnectIfNot "End Sub" (but still inside the class region) and type this function:
Private Function AmConnected() As Boolean
Dim oIP As System.Net.IPAddress
Dim bConnected As Boolean = False
Dim shtCountIps As Short
Dim sAddr As String
Dim Hoster As System.Net.IPHostEntry
Hoster = System.Net.Dns.Resolve(System.Net.Dns.GetHostName())
shtCountIps = Hoster.AddressList.Length
With Hoster
If shtCountIps = m_shtMaxExpectedAddresses - 1 Then
If Hoster.AddressList(0).ToString() = m_sStaticAddress Then
bConnected = True
End If
ElseIf shtCountIps = m_shtMaxExpectedAddresses Then
For Each oIP In Hoster.AddressList
If oIP.ToString() = m_sStaticAddress Then
bConnected = True
Exit For
End If
Next
End If
End With
'if there are m_shtMaxExpectedAddresses but neither is the static
'disconnect the active
If shtCountIps = m_shtMaxExpectedAddresses And Not bConnected Then
DisconnectIncorrectConnection()
End If
Return bConnected
End Function
That took a squiggle out of our entry point method, but it added another. We'll get to that.
This function shows one way (there are others) to use the framework to easily get the local computer name as the return of System.NetDns.GetHostName().
Once you get the computer's hostname, you pass it to the System.Net.Dns.Resolve() function to get a IPHostEntry object, which contains a "collection" of IPAddress objects that you can iterate through to look for your static IP.
A funky thing about the AddressList object is that while it allows iteration with For...Each it does not have a .Count property. Instead the equivalent property is ".Length." This is because the object is a derivation of type System.Array rather than a derivation of System.Collections.ArrayList as it's name implies. How do we know that when it's details don't come up in Wincv? For me, one-off questions like this are most quicky answered with a fast aspx page (I don't like having the IDE make all of it's support files and folders just for a quickie). Copy the following into a text file, save it as test.aspx in your wwwroot folder, then load the page with "http://localhost/test.aspx":
<%@ Page Language="vb" %>
<script runat="server">
sub page_load(s as object, e as eventargs)
 lblMessage.text = Test()
end sub
Private Function Test() As string
 Dim Hoster As System.Net.IPHostEntry
 Hoster = System.Net.Dns.Resolve(System.Net.Dns.GetHostName())
 return Hoster.AddressList.GetType.BaseType.ToString()
End Function
</script>
<html>
<body>
<asp:Label id="lblMessage" runat="server"/>
</body>
</html>
Next we move to the MakeConnection method and question: "Where in the framework is the simple "ConnectToTheInternet" method?" If you can find it, let me know. In the end I reverted to the non-COM WinInet.dll.
If you've never used a win32 dll in your .net apps, relax. It's easier than Interop. Just paste the VB6 style declaration into the top of the class. In my first test I didn't even change the old Longs to Integers and it worked. But it's probably best to changes those types ... it's like porting from VB3 to VB5, only backwards!
Now you do it. Click your mouse at the end of the class header ("Public Class IPConnection") and press enter to give yourself some room above your variables. Then type in these lines (I changed the types for you here):
Private Const INTERNET_AUTODIAL_FORCE_UNATTENDED = 2
Private Declare Function InternetAutodial Lib "wininet.dll" _
(ByVal dwFlags As Integer, ByVal dwReserved As Integer) As Integer
Private Declare Function InternetAutodialHangup Lib "wininet.dll" _
(ByVal dwReserved As Integer) As Integer
And click on some other line (not in a method) to create the wrapper:
Private Sub MakeConnection()
System.Diagnostics.EventLog.WriteEntry("IpTest", "Attempting to make connection")
Dim l As Integer
l = InternetAutodial(INTERNET_AUTODIAL_FORCE_UNATTENDED, 0)
If l = 1 Then
System.Diagnostics.EventLog.WriteEntry _
("IPConnection", "Connection Succeeded")
Else
System.Diagnostics.EventLog.WriteEntry _
("IPConnection", "Connection Failed ")
End If
End Sub
All this routine does is call the WinInet function InternetAutodial, passing in a 2 as the first argument which does the connection without showing the user prompt (a service cannot show gui elements) and writes the yea/nay details to the EventLog. Note: you will need to have your username and password set in IE Connection properties, they are still required even though the system is doing the dialing. This will affect the service project too, as we'll see later.
The good thing about this function is that it is synchronous, so your code will wait for it to return, either after a connection is established or the dialer times out. For that reason it's a good idea to set your IE Connection properties to limit the number of retries to just one or two and the retry wait to just a few seconds.
Just one last method to create, the one that takes care of those poltergeist connections where your server is on the internet but is not using your static IP. Again it is a simple call in to the WinInet.dll:
Private Sub DisconnectIncorrectConnection()
System.Diagnostics.EventLog.WriteEntry _
("IPConnection", "Killing non-static ip connection")
InternetAutodialHangup(0)
End Sub
Scroll through your code window and make sure that all the squigglies are gone, if any remain hold your mouse over them and use the tooltip information to figure out what lines need adjusting. Then click the Build menu and choose "Build IPConnection"
That's it! Your class is done. But before we hook it up to the service, let's give it a whirl in a simple console app.
The code pt1.5: The Test Harness
For a simple quick test harness, there's no need to have the IDE make all of it's temporary project folders. We'll just do it in a texteditor.
Open up your UltraEdit or Notepad and type:
Public Module MyMod
Public Sub Main()
Dim oTest as New IPConnection.IPConnection
oTest.ConnectIfNot("[your static ip address]", 2)
oTest = Nothing
End Sub
End Module
Make sure you replace the [your static IP address] with your own address in the format ##.##.###.##. Now save this file as TestHarness.vb in the bin of your IPConnection project (by default the IDE would have saved the IPConnection.dll to your "C:\Documents and Settings\[user name]\My Documents\Visual Studio Projects\IPConnection\bin")
To compile this into an exe, open up the Visual Studio .Net console window, navigate to that bin folder (click here for a real timesaving tip on this!) and type the following at the console prompt:
vbc.exe /t:exe /out:TestHarness.exe TestHarness.vb /r:System.dll /r:IPConnection.dll
The running of the object is going to add entries to the EventLog so open your Event Viewer (Start >> Settings >> Control Panel >> Administrative Tools >> Event Viewer) and click on the Application Log option. If there are a lot of entries already you may want to clear them or save them off to make it easier to see your app's results.
Doubleclick on your TestHarness.exe. You may see a new console pop up and go away. Bring your Event Viewer back to focus and press F5 to refresh the list. See the new entries for "IPConnection"?
The code pt2a: IPConnector.exe Service
Creating a windows service is not a big deal ... so long as you or a friend have the IDE. There is a LOT of code under the hood, but happily you don't have to type much of it. When you choose Windows Service as the first project type in your solution the IDE churned out the hundreds of lines of plumbing for you.
If you don't yet have the IDE then you could just ask a friend to create a new Service solution and pass you the text .vb files. Most of it is boilerplate so you can get it all rolling via the Framework console window after a few error-filled compiles to track down the correct namespaces and references.
We're using the IDE so we have the pleasure of drag&drop to a non-gui designer (If you have Delphi experience, the .net IDE is very familiar, isn't it? <g>). Click on the IPConnector service project node to tell the IDE that you're working with that project now.
When your service starts, it will be calling on the IPConnection.dll you created earlier. You can either manually type an Imports at the top of the service class or use the IDE to add a reference to that dll. Adding a reference with clicks starts off like VBClassic; Go to Project|Add Reference. Unlike VBClassic now you get to choose from systemwide .net options (GAC-ed assemblies), registered COM components or from a list of .net projects in the current solution. Use the third tab and pick the IPConnection.dll as shown below, make sure you doubleclick on it or highlight it and press "Select" and see it added to the bottom listbox or the reference won't be made.
Time to make that reference useful.
In the solution explorer, doubleclick on the IPConnector.vb node to bring the Service designer to view. Go to your toolbox, click on the Components "drawer" (Not the Windows Forms drawer), select the Timer component and drag & drop to the Service designer.

Click on the timer icon to select it and hit F4 to make it current in the property sheet. Change the Interval property to "120000" (timer resolutions are in milliseconds, so each second is 1000, each minute is 60000 and our desired 2 minutes is 60000 x 2= 120000) then change the Name property to tmrDSLConnect.
Now go back to the icon and doubleclick on it to bring up it's Elapsed event handler code.
Type the following in the stub:
Dim sStaticIP As String
Dim sNumAddresses As String
Dim oIP As New IPConnection.IPConnection()
Try
sStaticIP = System.Configuration.ConfigurationSettings.AppSettings.Get _
("MyStaticIP")
sNumAddresses = System.Configuration.ConfigurationSettings.AppSettings.Get _
("NumberOfAddresses")
Try
oIP.ConnectIfNot(sStaticIP, CInt(sNumAddresses))
Catch ex2 As Exception
System.Diagnostics.EventLog.WriteEntry _
("IPConnector", ex2.Message)
End Try
Catch ex As Exception
System.Diagnostics.EventLog.WriteEntry("IPConnector", ex.Message)
End Try
oIP = Nothing
You should be able to generally follow along with this one; We're creating an instance of our IPConnection object, getting the values for it's entry point method arguments, calling it and killing it.
Once again, you see some very long, fully qualified Namespaces. Once again, I did this because for most of us the .net framework namespace hierarchies are not yet all committed to rote memory, so for the more esoteric objects it is good to see the whole qualified string as a help to learning.
The Namespace we haven't seen before is "System.Configuration.ConfigurationSettings." We use this to read our dynamic values from an XML configuration file so we don't have to hard-code or lose them in the registry. If we go to a new ISP and get a new static, or add another adapter to our box we just change the value in the XML file and the service is updated. (We'll cover how to make that file in a later section)
If you don't how like those calls look, you can just add "Imports System.Configuration.ConfigurationSettings" to the top of the class and replace that same text in the method with the more concise "Appsettings.Get(..."
On the flipside, if you like typing a lot of characters, feel free to replace our age-old VB CInt() function with the fully qualified Microsoft.VisualBasic.Conversions.Int() method. Same result.
The code pt2b: IPConnector.exe Installer Support
You're almost done now. There're just a couple of things to add before you can compile and move your solution to your server ... the code that will add it to that machine's services. Again, the IDE does a lot of typing for you ... but perhaps not all that it could.
Right-click your service app's designer and choose "Add Installer." In a second, you will get a new tab with a new designer and a new file added to the service project called "ProjectInstaller.vb" This time it's ok to just leave that file named the default, but there are other settings you'll have to tweak.

Select the icon "ServiceInstaller1" and press F4 to load it's property page. Change the ServiceName property to "IPConnector"
Now, select the icon "ServiceProcessInstaller1" and press F4 to load it's property page. Make sure that the Account property is set to "User."
If you've read other articles on making windows services with VB.net, you have probably read that that you should almost always change the Account to "LocalSystem" ... this is one of those times when the almost comes into play. Because this application is going to be used to make connections to your ISP it has to run under an account that knows your logon username and password; The System account doesn't have that knowledge. This has a couple of ramifications but each one of those has a good workaround. We'll get into those details when we go over installation. The property sheet has entries for UserName and Password, but personally I'd leave those blank, there're still some concerns about decompilers so it's probably best to play it safe. You'll be prompted for that information when you install the service anyway.
The StartMode property defaults to "Manual", keep it as-is so you can take your time setting up the initialization file on the server.
At key places in our projects we've typed in lines that will write to the EventLog. These will help us when we later want to see how our service is doing. Under the hood, the ProjectInstaller has one of those lines too ... it's called when you install and the service and adds a Log entry of "Service1 Started". Depending on your version of the IDE this text may or may not be correctly set to the name of your service project. Best to check it before you compile. Doubleclick on the "ServiceInstaller1" icon and scroll up to the top of the code window. There's a collapsed region labelled "Component Designer generated code" click on it's box to open it up. Now look down to the collapsed subregion labelled "Private Sub InitializeComponent()" click on it's box (or the ellipses at the end of the line) to open it. Look for the following line:
Me.ServiceInstaller1.ServiceName = "Service1"
Change that default text to whatever you want.
Now you can go ahead and build your solution. And get out a floppy, we're ready to move to the web server!
Installation
In my case I have the full framework on my web server and my .net dev box (with the full IDE) is not on my LAN (I still have a trust issue) so I use floppynet to transfer files. If your setup is different adjust the following to fit, ok?
Navigate to the bin of your IPConnector project (typically it will be "C:\Documents and Settings\[user name]\My Documents\Visual Studio Projects\IPConnector\bin" unless you set your project options manually) send the files "IPConnector.exe" and "IPConnection.dll" to your floppy.
Pop the floppy into your web server box.
Create a new folder in the location of your choice, you can name it whatever you want but it's good to use a name that is easy to type and that you can remember. Personally, I put all my service apps in the same folder and I'll use "C:\MyServices\" in this example.
Copy the exe and dll from the floppy to the folder.
Now call up the .net console window, and navigate to your C:\MyServices (click here for a helpful tip). At the prompt, type the following:
InstallUtil.exe IPConnector.exe
You may have read that the InstallUtil is only available if the full framework is on the machine and not if only the runtimes are installed. However, I just ran this on a box that only had the runtimes and it worked. If you get an issue, try copying the InstallUtil.exe from your dev box (might work) or you might have to make a real installation (see WROX Press' "Professional VB.Net" for the details on that route).
Assuming that InstallUtil started up for you, a ramification of using a "User" account for the service is that you will be prompted to provide the account name and password (obviously, the account you use must have local admin installation privileges). The trick here is that when you type in your username, preface it with the machine name or you'll get a security error and the install will rollback. As you see in the graphic below, I'm installing on the machine "KilnPerson" under the account "GreenieAnt"

Now the service is installed ... but it's not running yet. Before we start it we have to take care of it's XMLconfiguration file so that it knows what ip address to look for. We'll cover that next.
The XML Configuration file
This will be a short section. If any .net exe needs configuration or initialization settings, forget the registry and just put the settings in an XML text file in the same folder as the executable. For example:
<configuration>
<appSettings>
<add key="MyStaticIP" value="12.34.567.89" />
<add key="NumberOfAddresses" value="2" />
</appSettings>
<configuration>
The appSettings section holds name-value pairs, if you want to add a new setting, you just add a new line to the section.
Save the file named exactly as your executable plus the extension .config and you're done. So in the case of our DSPConnector.exe service, we save the above into a file named "IPConnection.exe.config"
If you're wondering why we didn't call the file "IPConnection.dll.config" and save outselves the trouble to getting these values in the service and passing them to the object, the answer is a curt "Because MS says no": Outside of ASP.net only exe's can have self-named config files, dlls can't.
There are other defined childnodes of <configuration> that you can use in your config files, see the docs later to track them down, right now we're going to start up our service.
Turning on the Service
The last part of this little tutorial is not really a .net thing, it's SOP for all services you have on your box. But since this might be the first time you've set up and started a user account service it might be helpful even for old-timers.
Open your Services applet (On WServer it's in Start >> Administrative Tools, for WPro it's at Start >> Settings >> Control Panel >> Administrative Tools >> Services) and look for the entry "IPConnector". You'll see that, just as you specified, it's startmode is "Manual" and that it is is going to run under your user account.
I want to just make a quick point here for your future reference: If you have to later remove the service (maybe you'll want to add some more EventLog writes to track down a bug) you should first use the Services applet to Stop the service, then close the Services applet (not just minimize), then run the InstallUtil commandline with the switch "/u" (no quotes). Attempting to uninstall the service while the Services applet is running might "confuse" you or your machine, as the Services subsystem will still be listing the service (and perhaps even trying to run it) even though it is gone.
Now it's time to do a round of tests against the spec.
Testing the service
We'll need a couple of other tools running to make sure that our service is working as expected.
Press Start >> Run, type "cmd" (no quotes) in the box and hit Enter. This brings up a generic windows command prompt which we'll need to list our current IP addresses.
Open up the Event Viewer, if it isn't already, and switch to view the Application Log.
The first two items in the spec combined to look for a current connection to our static IP and, if found, go back to sleep. Because of how we added our EventLog writes these alone will not do anything obvious so we'll begin with the third item:
- If the count of addresses is equal to the count of LAN and, for safety, if the address is not the static then we are not connected to the internet, so force a new connection
If you are currently connected to the internet, drop that connection.
Bring your cmd window to the front and at the prompt type "ipconfig.exe" (no quotes). The number of adapters that are listed depends on how your machine is set up but if your internet connnection was dropped successfully you should not see your static IP in the list of adapter connections. That is our starting point.
Go back to the Services applet and doubleclick on the "IPConnector" service to bring up it's properties. In the General tab's "Service status" section, click the "Start" button. In a second or two the status will change to "Started."
Bring your EventViewer to the front and press F5 to refresh the Application log. If all is well you should see that the "IPConnector" service was started.
Now sit back and wait for your systray clock to tick off a few minutes (you set the service to work every two, but remember that it is doing the ticks in milliseconds not on changes of the actual minute timepart). When you've waiting long enough, bring up the cmd window and press F3 (reloads the last typed command) and Enter. If all worked correctly you should now see your static IP in the list. Bring up your EventViewer and press F5 to refresh the Application Log, three New entries will be in the list. Doubleclick on the first one to bring up the details and you'll see, in order of occurance, "Computer is not connected", "Attempting to make connection" and "Connection Succeeded."
If anything went wrong with the service then the Events will give you a hint to the location of the problem. If this is your situation you may want to go back to your code and add more "Try...Catches" and EventLog writes to your code to help track down the exact issues. (Please see the next section "User Accounts and Uninstalls" for helpful hints on this.)
Assuming that your service woke up and made it's connection, you can check off that item in the spec (and the first item "Make a Windows Service that wakes up every two minutes and checks the computer's IP Addresses" because obviously it worked too!). Now we'll test item four:
- If the static IP is not in the list but the count of addresses is equal to the count of LAN connection plus 1 then we are connected to the internet but not with the static. In this case force a disconnect from the internet and force a reconnect in an attempt to get hold of the static IP.
For this is is neccessary to create a poltergeist connection. With my ISP I can do this by dropping my connection on the server and starting a connection on another box. Whatever box gets the first connection, gets the static; all subsequent connections get a dynamic IP address. You'll have to create the scenario in a way that works on your system, but I'll use mine as a general example.
With another computer connected to the static IP address, do a manual forced connection using the PPPOE software. Use ipconfig to verify that you are connected but not with the static. Now disconnect the static on the other box and wait for the service event.
When the milliseconds have gone by, check the ipconfig again to make sure that the old connection was indeed dropped and the static is now connected. Refresh the EventLog to see that there are new entries for "Killing non-static ip connection", "Computer is not connected", "Attempting to make connection" and "Connection Succeeded."
That covers the spec, since the other items were support oriented we were able to check them off as the result of the real functionality.
Congratulations, you've just made a service that taught you a lot and has a real-world use. And becuase you didn't just download the compiled assemblies you can go in and work on the functionality as required or desired.
In the next and final section, we'll tidy up a few loose ends and discuss how to deal with things if you didn't get your service completely running this first time
User Accounts and Uninstalls
We mentioned that because this particular service is being used to connect to your ISP, when it runs it has to be able to send your logon information. And it was for that reason that we had to make this service run under a User account. The thing about this setup is that it could fail in the future for a number of reasons totally unrelated to the code. For instance, if you change your windows logon password, the service will fail with a security issue because it is still set up to use your old password. Here are some tips for this type of service application:
- Instead of installing under your own user account, create a new user account with Local Admin (install) rights and a non-expiring password. Log on to your computer using that account, set up PPPOE and tell IE to allow autodials, make sure that you can get a connection with that account and then install the service (and use it's username and password at the service installation prompts)
- When you are done testing and happy that the service is running as desired, go to the Services applet, doubleclick on the service entry to bring up it's properties and change the "Startup type" to "Automatic" so that you won't have to manually restart the service every time you reboot the computer.
We mentioned earlier that when you uninstall a service you should close the Services applet so you and the computer don't get "confused." Here's the full step by step of how to get rid of a .net service:
1) Open the Services applet and doubleclick on your service to bring up it's properties.
2) Press the "Stop" button
3) Close the properties window and the Service applet
4) Open a .net console session and navigate to the location of your service exe (such as "C:\MyServices\")
5) At the prompt type InstallUtil, the neame of your service exe and the uninstall switch "/u". For example, our service was named IPConnector.exe so we would use:
InstallUtil.exe IPConnector.exe /u
6) ReOpen the Services applet and make sure that the service is no longer in the list.
Now you can feel free to delete the exe and dll or replace them with updates. To reinstall, follow the steps we went over previously in this article.
The Wrap
I don't know about you, but I learned a bunch of new things in making this project. In the article we moved along step by step but the truth is that in my day of banging out the code there were a number of false starts and blind alleys. For example, I had been under the impression that making this service would require creating a worker thread, or at least a new process. That wasn't the case, but I didn't know that at the time; it got me researching what exactly those things are and how, when and why you can or should do them in .net, and that gave me ideas and experiences that I otherwise wouldn't have had if I just sat around waiting for someone else to put the compiled assemblies up as shareware.
But you know the most important thing I learned from this little project? I learned that MS did a great job on .net. Our VBClassic experience is going to give us an edge over the newbies (especially when it coes to COM integration) with the flip being that we old farts have to be on the lookout for times when we're thinking too hard out of habit about issues that can now be solved using the framework alone.
If we want to get picky, the system we've made does not yet deal with poltergeist connections that reconnect to another dynamic address and, as written, it will take another timer event to get around to checking the new connection address. To fix that up we could do a bit of recursion on the AmConnected and keep looping till we get the static and that little feature will of course need support for restricting the number of tries within a run so that we don't end up looping forever (maybe there is a good reason to spawn a new process after all). Plus, instead of hard-coding the 120000 milliseconds we could use another line in our config file to specify the timer's Interval property. You can feel free to add those features and more. As we said in the beginning, this works and accomplishes the spec but it is only a starting point.
I've loved VB since 3 came out (I own 1 and 2), and I'm going to miss VBClassic's more useful IDE (with SDI and Edit&Continue), but for all the new power that we have with the framework, I'm happy to be able to get legitimately flowery and say "VB is dead, long live VB".
Robert Smith
Kirkland, WA
added to smithvoice Feb 18th, 2002