Duncan Mackenzie Microsoft Developer Network March 26, 2003 Summary: Duncan Mackenzie describes how to build a tool that uses the System. NET Framework to check a POP3 e-mail account for unread messages.
(15 printed pages) Applies to: Microsoft® Visual Basic® . Coding without any specific goal is like going to the grocery store without a shopping list.
So let's break this down, as I like to do, into a set of distinct tasks that I needed to accomplish: The full code is available, but I will look at each of these items in order and explain how the code works in each case.
0 Then Dim i As Integer For i = 1 To msg Count 'get the headers for the message 'using the TOP command response = p3. I decided to implement this application as a System Tray icon, because it allowed me an easy and relatively unobtrusive way to provide access to my application's options and notification of new mail. I created a System Tray icon because you can never have enough of these.
I coded almost all of this application as a single Microsoft® Visual Basic® module (which, for you C# types, is equivalent to a class where all members are static), only using a Form for my options dialog.
So I was quite happy to find that I did not need one. Visible = True 'add an event handler for when the user clicks the balloon 'popped up by the Notify Icon. Exists(server Settings Path) Then 'load servers Dim settings File As IO. Stream Reader(server Settings Path) Dim my XMLSerializer As _ New Xml. Xml Serializer( _ Get Type(POP3Server Collection)) servers = Direct Cast( _ my XMLSerializer. Get Bytes(source) Dim encrypted Bytes As Byte() encrypted Bytes = dp. I have to admit that when I am writing code for myself, I am usually fine with manually changing values in the code, in a .config file, or in the database. Value Without a reference to Outlook, I had to use late binding, which means no Microsoft® Intelli Sense® and everything is treated as an object.
In this main module, I created an instance of Hans Blomme's wonderful Notify Icon XP, a context menu and some menu items, and a timer. Notify Icon 'general application preferences Dim app Settings As Settings 'I keep the form available as a module level 'variable so that the View Options menu can avoid 'multiple instances open at once Dim my Frm As frm Options Sub Main() 'load the server list and settings 'information from serialized xml files Reload() 'loop through all the loaded 'servers and attach a handler for 'the New E-mail event. Enabled = True 'add a handler for the Tick event of the timer Add Handler check Timer. The standard Notify Icon does 'not provide this event so you will need to remove this 'line if you are not using the Hans Blomme icon. Balloon Click, Address Of Notification Balloon Clicked 'set up the context menu, including event handlers Dim ctx Menu As New Context Menu() ctx Menu. Add("View Options", Address Of View Options Form) ctx Menu. Add("Exit", Address Of End Application) 'and assign it to the Notify Icon so that it will pop up when 'the icon is right-clicked on. Context Menu = ctx Menu 'run the application, using Application. Run() 'when the app is exiting 'hide the notify icon and ni. Deserialize(settings File), _ POP3Server Collection) settings File. Encrypt(source Bytes) 'I could store binary values, but since I am using 'XML as a storage mechanism, I prefer to just work 'with a string. User Store) Dim source Bytes As Byte() = _ Convert. That isn't a good habit to get into, though, because at some point you will want to give this code to someone else, and then you are going to have to explain how to change the settings or just bite the bullet and create an options dialog. Stack Trace) End Try End Sub To allow the servers collection to be edited, I data bound it to a set of controls and provided a New and a Delete button. On the positive side, though, the resulting code should work on Microsoft® Office 2000, Office XP, and even Office 11.
Then I wired up some event handlers for the menu items, the timer, and even one for the Balloon Click event of the notification icon (even though I don't use it, I thought you might want to). Dim srv As POP3Server For Each srv In servers Add Handler srv. Tick, Address Of Check E-mail_Tick 'Create new Icon, remove Hans Blomme section to work with 'the standard Notify Icon. Visible = False 'save the server info and settings to xml files Persist() End Sub At the start of the Main() routine, I called Reload(), and then I called Persist() at the end. Close() Else servers = New POP3Server Collection() End If If IO. Exists(settings Path) Then 'load settings Dim settings File As IO. Stream Reader(settings Path) Dim my XMLSerializer As _ New Xml. Xml Serializer( _ Get Type(Settings)) app Settings = Direct Cast( _ my XMLSerializer. To Base64String handles that for me Return Convert. From Base64String(source) Dim decrypted Bytes As Byte() decrypted Bytes = dp. In this case, I decided to be a bit more proactive, so I created a little dialog and provided a "View Options" menu option off my notification icon. The little navigational control is something I wrote for my own use that you might find helpful. Value)) End If End Sub Private Sub encrypted Binding_Parse( _ By Val sender As Object, _ By Val e As System. Public Shared Function Get Unread Messages() As Integer Dim Outlook App As Object Try Outlook App = _ Get Object(, "Outlook.
'servers list Dim servers As New POP3Server Collection() 'timer for polling e-mail servers Dim check Timer As New Timer() 'A notify Icon provides the main UI for the app 'this makes is available for popping up balloons, etc. New E-mail, Address Of New E-mail Next 'app Settings stores the interval in seconds 'Timers work in milliseconds, so a little conversion 'is necessary to make them work together. Other code changes would also 'be required ni = New Hans Blomme. These two procedures are how I implemented saving my settings and POP3 server information to disk (in Persist) and loading them up again at startup (in Reload). Stream Writer 'save the servers list my XMLSerializer _ = New Xml. Xml Serializer( _ Get Type(POP3Server Collection)) settings File _ = New IO. Close() 'save the settings class my XMLSerializer _ = New Xml. Xml Serializer( _ Get Type(Settings)) settings File = New IO. Serialize( _ settings File, app Settings) settings File. Local User App Data Path, "servers.xml") Dim settings Path As String _ = IO. Deserialize(settings File), _ Settings) settings File. Close() Else app Settings = New Settings() End If End Sub \ as an XML file. To Base64String(encrypted Bytes) End Function Function Decrypt Text(By Val source As String) As String 'I use the DPAPI component from Got Dot Net 'see the associated article for the link. Sub View Options Form( _ By Val sender As Object, _ By Val e As Event Args) 'Pop up the Options dialog Try 'Check if it is already up, 'if so, just set the focus to it If Not my Frm Is Nothing And Also my Frm. Focus() Else 'not already up, create a new copy my Frm = New frm Options() 'populate the form with data my Frm.servers = servers my Settings = app Settings If my Frm. OK Then 'I "clone" the app Settings on the form 'so you need to pull a copy back when the form 'closes app Settings = my Settings 'hey, you just changed all those settings 'I'd better save them! I could have just provided a couple of buttons that manipulated the Currency Manager's Position property if I did not want to use this control. The Option dialog allows you to set up your POP3 server information. Application") Catch Outlook App = Nothing End Try 'Uncomment this to automatically 'open Outlook if needed 'If Outlook App Is Nothing Then ' Outlook App = _ ' Create Object("Outlook. Un Read Item Count() Else Return -1 End If End Function Note that this will not work if Outlook is not open.
If I found a new message, I raised an event and passed along the subject and sender information.
The code snippet below shows the message header retrieval and raising the new e-mail event, but it makes a lot more sense if you look at in the context of the full source code. Top Cmd, i), True) msg = Parse Response(response) Dim msg Index As Integer = 0 Dim found As Boolean = False 'check if this is a new message Do While msg Index That event was caught by my main application, so this is a good time to move on to the central application code.
Sure, you have a good time, you end up with items—items that might potentially be useful to someone at some point in the future—but you are unlikely to end up with tonight's dinner.
Well, that's exactly what I started doing (the coding, not the shopping...) to write this column.
Coming from Visual Basic 6.0, this is exciting; I wrote an application that uses a system tray icon, a timer, and a context menu without requiring an invisible Form.