1. Writing a New Thread Method
Modify your existing ThreadFunc to match the code shown in Listing 1.
Listing 1. Updates to the current thread method.
Private Sub ThreadFunc() While Not m_ThreadAction.StopThread If Not m_ThreadAction.Pause Then Try For Each TextFile As String In My.Computer.FileSystem.GetFiles( _ My.Resources.IncomingPath, _ FileIO.SearchOption.SearchTopLevelOnly, "*.txt") If m_ThreadAction.Pause Or m_ThreadAction.StopThread Then Exit For End If Try WriteLogEvent(My.Resources.ThreadMessage + TextFile, _ THREAD_INFO, EventLogEntryType.Information, My.Resources.Source) Catch ex As Exception WriteLogEvent(My.Resources.ThreadErrorMessage + Now.ToString, _ THREAD_ERROR, EventLogEntryType.Error, My.Resources.Source) End Try Next Catch fio As IOException WriteLogEvent(My.Resources.ThreadIOError + Now.ToString, _ THREAD_ABORT_ERROR, EventLogEntryType.Error, My.Resources.Source) Catch tab As ThreadAbortException 'this must be listed first as Exception is the master catch 'Clean up thread here WriteLogEvent(My.Resources.ThreadAbortMessage + Now.ToString, _ THREAD_ABORT_ERROR, EventLogEntryType.Error, My.Resources.Source) Catch ex As Exception WriteLogEvent(My.Resources.ThreadErrorMessage + Now.ToString, _ THREAD_ERROR, EventLogEntryType.Error, My.Resources.Source) End Try End If If Not m_ThreadAction.StopThread Then Thread.Sleep(THREAD_WAIT) End If End While End Sub
|
The code in Listing 1
searches for any text files in the IncomingPath without looking in any
subdirectories. After it finds a file it attempts to log an event using
the text name and the standard message.
Note
You
must remember to set the constant IncomingPath to the directory you
created, otherwise the path will not exist and your query will always
fail with an IOException. |
Install and Verify
Now you need to recompile the new code, remove the old service, and install the new tutorials.exe.
Start
the service. If that directory has no .txt files, you won’t see any
entries in the Application log. However, if you were to create or copy
.txt files into the directory while the service was still running, not
only would you see entries in the Application log, but you would also
see them duplicated every five seconds, which is the sleep time. As you
remove, delete, add, and create .txt files in your temp file directory,
you will see new entries about every five seconds in the Application
log.
Note
Remember to stop the current service before continuing so that you do not fill the Application log with unwanted events. |
2. Monitoring with Multiple Threads
We
have successfully created a multithreaded polling service, which can be
very useful, if you had to monitor more than one directory; for
example, you could either have one thread monitor all the directories,
or you could create multiple threads, using one per directory. You
could also create a pool of threads, monitoring not only file system
directories but also FTP folders and MSMQ queues so you could make sure
that the proper thread handles the correct source and processing occurs
correctly.
Expanding Processing
For
this example, we will expand the service. We will monitor the incoming
directory for files and then move these files to a newly created output
directory. Although we don’t process these files directly, the local
service might be able to move files and then have another application
process them after they are moved to the output directory. We are
merely emulating the creation and pre-processing move to another
application’s processing folder.
Creating the Code
Create the code by following these steps:
1. | Start
by creating another folder, which we will call “outgoing.” I create
mine in the c:\temp directory, which is where the incoming folder is.
|
2. | Add another resource entry called OutgoingPath and set its value to c:\temp\outgoing.
|
3. | Add another set of InstanceId values in the modTutorial file.
|
4. | In the modService.vb module file, add the following constant directly below the THREAD_INFO constant:
Public Const FILE_WRITE_INFO As Integer = 3000
|
5. | We also want to add a new error const under the existing definitions, as shown in the following code:
Public Const FILE_WRITE_ERROR As Integer = 3001
|
Modifying the <ThreadFunc> Method
We need to modify the current <ThreadFunc>
method so that instead of only logging an eventlog entry, it will move
the file to the new output folder. We will modify the code as shown in Listing 2.
Listing 2. Modifications to the Thread method.
Private Sub ThreadFunc() While Not m_ThreadAction.StopThread If Not m_ThreadAction.Pause Then Try For Each TextFile As String In _ My.Computer.FileSystem.GetFiles( _ My.Resources.IncomingPath, _ FileIO.SearchOption.SearchTopLevelOnly, "*.txt") If m_ThreadAction.Pause Or m_ThreadAction.StopThread Then Exit For End If Try Dim FileOutput As String = My.Resources.OutgoingPath + "\" + My.Computer.FileSystem.GetName(TextFile) My.Computer.FileSystem.MoveFile(TextFile, FileOutput) WriteLogEvent(My.Resources.ThreadMessage + TextFile, THREAD_INFO, EventLogEntryType.Information, My.Resources.Source) Catch ex As Exception WriteLogEvent(My.Resources.ThreadErrorMessage + Now.ToString, THREAD_ERROR, EventLogEntryType.Error, My.Resources.Source) End Try Next Catch fio As IOException WriteLogEvent(My.Resources.ThreadIOError + Now.ToString, THREAD_ABORT_ERROR, EventLogEntryType.Error, My.Resources.Source) Catch tab As ThreadAbortException 'this must be listed first as Exception s the master catch 'Clean up thread here WriteLogEvent(My.Resources.ThreadAbortMessage + Now.ToString, THREAD_ABORT_ERROR, EventLogEntryType.Error, My.Resources.Source) Catch ex As Exception WriteLogEvent(My.Resources.ThreadErrorMessage + Now.ToString, THREAD_ERROR, EventLogEntryType.Error, My.Resources.Source) End Try End If
If Not m_ThreadAction.StopThread Then Thread.Sleep(THREAD_WAIT) End If
End While End Sub End Try
|
We are going to continue to use My Object to move the file from its current location to the OutgoingPath resource setting.
After
the file is written to the output folder we log an event to the
eventlog as we did in the previous version. If an exception occurs, we
write an event that covers the error.
Installation and Verification
Before
we compile and install the new service, we need to make one more
change. The Local Service account (which we’ve been using for the
service) doesn’t by default have the proper authority to write to the
Outgoing directory, and we must fix this.
Changing the StartUp Account
In
Solution Explorer, double-click ProjectInstaller.vb. Select the
ServiceProcessInstaller1 control, right-click, and select Properties.
Change the Account property from LocalService to LocalSystem. Save and
then build the project.
Note
You
could grant the Local Service account Modify permissions to both the
incoming and outgoing folders instead of using the Local System account. |
Install
the newly created service and start it. Open Windows Explorer and click
the c:\temp\incoming directory. In the right-hand pane, right-click,
and choose New Text Document—or you could copy one or more existing
text files into this folder from another location. Click in the white
space to save the file. Within five seconds the file should disappear.
The file will appear in your outgoing directory, and you should see a
2003 event in the Application log for each file that you created.