Windows SharePoint Services (WSS) and Microsoft Office SharePoint Services (MOSS) 2007 are Microsoft’s innovative tools that allow for collaboration and communication within organizations, enabling the smooth flow of documents and information. SharePoint 2007 has an extended Application Programming Interface (API) and an Object Model that reaches most parts of the system, allowing for greater program functionality not present in SharePoint’s default system.
Windows Services are applications that launch automatically at the moment of booting and operate in the background as long as Windows runs. They are useful to create long-running, independent applications without user interaction. Because they require a major part of the physical resources of the servers, sometimes it is necessary to execute SharePoint processes in the background; Windows Services ensure these tasks function in periods of low-user load. Windows Services can also manage SharePoint activities because they run independently of WSS (and MOSS), depending exclusively on the Windows infrastructure.
Windows Services and WSS Timed Jobs
Windows Services utilize their own Windows sessions and security context, isolating them from other processes and users working on the same server. Although Windows Services has no user interface, there is an administrator function to start, pause, restart, and realize the necessary configurations. SharePoint also has an infrastructure to operate this type of process in the form of “SharePoint Timed Jobs.” Jobs perform actions at predefined intervals (in minutes) or during an allocated period of time (hour, day, week, month, or year). Among other functions, SharePoint utilizes Timed Jobs for installing Solutions, indexing, and with its search engine.
One disadvantage of Timed Jobs is that the time structure is organized in defined slots (hour, days, and so forth). If a task requires a specified time configuration, for example, to run each second Saturday of the month, the Jobs framework falls short. Another drawback is that Timed Jobs are SharePoint-dependent; imagine, for instance, a system that needs to track the availability of the Portal. In this scenario, if SharePoint is down, the Jobs infrastructurealso is unavailable for use. A Windows Service that runs autonomously is able to check the accessibility every few minutes, and if necessary, create a log.
Programming Windows Services for SharePoint
Services are a special type of application and function differently from other Windows software. One difference is that the compiled executable does not operate with the default Windows double-click, but needs to be installed using special tools. Debugging presents a special challenge for the developer and the interaction with users needs to be carefully prepared because dialogue boxes and other types of messaging may interrupt the Service.
Prior to the introduction of DotNet Windows Framework, it was possible only to program Windows Services using unmanaged code (C++, among other languages), but with the arrival of Visual Studio 2005, CSharp, Visual Basic, and managed code can be used. DotNet 2.0 (and up) provides the Classes, Methods, and Properties needed to create and interface with Windows Services. The Syste.ServiceProcess.ServiceBase NameSpace supplies the events, methods, and properties essential to run a Service. The System.Configuration.Install.Installer and System.ServiceProcess.ServiceProcessInstaller NameSpaces comprise the classes to install and initialize the Service. Finally, the System.ServiceProcess.ServiceController NameSpace takes care of the communication with external applications.
The following Windows Service example was created to scan the Portal structure every 24 hours searching for Sites, Webs, Lists, Libraries, and the documents in the Libraries. The process is a time-consuming and resources-unfriendly job for SharePoint servers if the installation is extensive, and the server response could be considerably degraded. For this reason, it’s desirable to run the process during low-load times.
Visual Studio 2005 has a template, “Windows Service”, for CSharp and Visual Basic that can be used to simplify the creation of a new Service. After the creation of a new project using the template and applying an appropriate name, the necessary references and declarations are ready for use. The new project commences in the design view of the “Service1” class. Change the class name (“ScanWssService”, in the example) and go to the code view window:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.ServiceProcess; using System.Text; namespace ScanWssWindowsService { public partial class ScanWssService : ServiceBase { public ScanWssService() { InitializeComponent(); } protected override void OnStart(string[] args) { // TODO: Add code here to start your service. } protected override void OnStop() { // TODO: Add code here to perform any tear-down // necessary to stop your service. } } }
It also is possible to create an empty Visual Studio project and manually set the necessary references to System.dll and System.ServiceProcess.dll. The main executable class needs to inherit from System.ServiceProcess.ServiceBase. The class constructor contains the name of the service and can initialize other properties such as “CanStop”, “CanShutdown”, and “CanPauseAndContinue”. In the code generated by the Visual Studio template, the constructor calls the partial function, “InitializeComponent”; at this point, the “Name” property is configured by default and the other properties can be added as desired.
The template creates two override routines, for “OnStart” and “OnStop”, but other overrides may be created manually (“OnPause”, “OnContinue”, “OnShutdown”, “OnSessionChange”, and so on). Also, if it is necessary for the program’s logic, a “Main” method can be added to the class.
The Windows Service sample must run every 24 hours, so it is necessary to program a timer. The System.Threading NameSpace contains the classes to create a timer and after a reference and directive for the NameSpace is configured, the following lines of code in the “OnStart” method create a timer for 86,400,000 milliseconds (24 hours):
int TimeInterval = 86400000; TimerCallback TimerDelegate = new TimerCallback(CreateOutputSharePoint); Timer ServiceTimer = new Timer(TimerDelegate, null, 500, TimeInterval);
Be aware that the integer used for the time interval has a maximum value of 655,465,540 milliseconds, or approximately 7 days. The “TimerDelegate” variable will run the “CreateOutputSharePoint” routine each time the “ServiceTimer” elapses; the “ServiceTimer” timer object starts after 500 milliseconds (time to stabilize the service after initialization), and fires the “TimerDelegate” after the “TimeInterval” has elapsed. The third parameter of the Timer can be an integer, as in this case, but if it is necessary to have more time between the events, a “Long” or “TimeSpan” can be applied.
The private method “CreateOutputSharePoint” loops across the Sites in the Portal and registers the Webs of each Site and collects the documents and elements of the Library and List in each Site. The information is written to a text file:
string myData = string.Empty; TextWriter myWriter = new StreamWriter(@"c:SharePointSummary.txt"); myWriter.WriteLine(DateTime.Now); SPSite mySite = new SPSite("http://ServerName"); foreach (SPWeb myWeb in mySite.AllWebs) { myData += " - Web: " + myWeb.Title + "rn"; foreach (SPList myList in myWeb.Lists) { if (myList.Hidden == false && myList.OnQuickLaunch == true) { myData += " - List: " + myList.Title + "rn"; foreach (SPListItem myItem in myList.Items) { myData += " - Item: " + myItem.Name + "rn"; } } } } myWriter.Write(myData); myWriter.Close();
In the code, the “TextWriter” and “StreamWriter” objects are constructed to save the found information. A “SPSite” reference is made for the Portal site collection and the first loop goes through the Webs (of the “SPWeb” type) in the site. The inside loop scans the Lists collection, searching for Lists and Libraries that are not hidden (SharePoint uses many hidden Lists to save internal working data) and enumerates the elements and documents found.
The timer interval and the URL to create the instance of the SPSite object are hard coded in the example, but if they need to be configured, it is possible to save them in the Windows Registry regardless of the method of configuration. Windows Service code can utilize all the DotNet techniques available for the developer, including write/read to/from the Registry or physical files, such as configuration archives.
A note of caution is necessary regarding the exception handling. Be conscientious with handling each possible exception because the Service will not sound an alert if there is a problem; instead, the thread will be cleared by the Windows Garbage Collector and the service will continue operating and generating exceptions. The try/catch statement in the example will trap each error and write it to the Event Log:
try { . . . } catch (Exception ex) { EventLog.WriteEntry("Error in Windows Service", ex.ToString(), EventLogEntryType.Error); }
In the same way, initialization and finalization are logged to the EventLog from the “OnStart” and “OnStop” methods.
The SharePoint functional source code is ready, but Windows Services needs an installer to alert the Service Control Manager that the service exists and to register the components inside Windows. Visual Studio has components to accomplish this: Return to the Design view of the Visual Studio project, right-click anywhere inside the background of the designer screen, and select “Add Installer”; this action will generate a new class (“ProjectInstaller”, named by default) and add two components (“serviceProcessInstaller1” and “serviceInstaller1”) to the project.
Select the “serviceProcessInstaller1” properties and change the Property “Account” to “LocalSystem”. Select the “serviceInstaller1” and make the following changes:
Property "StartType" = "Automatic" Property "Description" = "Example of Windows Service" Property "ServiceName" = "ScanWss Windows Service" Property "DisplayName" = "ScanWss Windows Service"
If necessary, the names of the components can be changed. Finally, build the solution and rectify any errors found by the compiler.
Install and Use Windows Services
After compilation, the service can be installed using the “InstallUtil.exe” tool because, unlike most Visual Studio projects, the application does not run directly from the IDE or double-clicking at the executable. The utility can be found in the directory “C:WINDOWSMicrosoft.NETFrameworkv2.0.50727” or can be used directly from the Visual Studio 2005 Command Prompt with the syntax:
installUtil [path to the service compiled exe file]
Figure 1: InstallUtil.exe tool in action
The tool will indicate the process for installing the service on screen and, if at the end, the following message appears: “The transacted install has completed”, the installation procedure is completed. A log file is generated in the same directory of the EXE file together with a résumé of the process and the errors found, if any.
In the same way, the service can be uninstalled by using the syntax:
installUtil /u [path to the service compiled exe file]
The Windows Service can be found in the Services Manager application (“Administrative Tools” – “Services”).
The Services Manager implements the functions start, stop, pause, and resume the working of the service; it defines the security context wherein the service will run (tab “Log On”); and the actions the service will take if a failure occurs (tab “Recovery”). A Windows Service runs in its own security context, which may be different from the context of the current user. There are four context types:
- LocalSystem Account is the Windows system administration account with widespread computer rights, giving the credentials of the account to any service that requests it
- LocalService Account provides fewer rights than the LocalSystem and gives anonymous credentials if requested
- NerworkService Account has the lowest level of rights, but give the credentials of the account if they are requested
- User Account specifies a single user in the system, whose rights are established using the Windows users accounts system
The service uses the credentials of its security context to read the information from SharePoint, so the account must have the appropriate rights inside the Portal. And, because the information to be read is width spread in SharePoint, the account needs to have Administrator rights in the Portal or the code require to use an Impersonator to be able to use the Object Model.
Debugging a Windows service is less straightforward than other Windows applications. To begin debugging, install and start the service before attaching the process to the Visual Studio debugger. The attachment process actually interrupts the service and a BreakPoint in Visual Studio may halt the service although technically the service is still running. After that the service begins, go to “Debug” in Visual Studio, “Processes”, click “Show system processes”, select the process of the service, and click “Attach”. The normal debug function of Visual Studio then can be used.
The debugger is attached after the service starts; therefore, it is not possible to debug the “OnStart” or “Main” methods. This is done using a dummy service that loads the service process to be debugged. If you attach the dummy service to the debugger, it is possible to trace the initialization of the real service. The “OnStart” method has a limit of 30 seconds for each start attempt, so it is difficult to completely debug the code in the method, especially if the code is extensive or complex.
The general SharePoint user is unaware of the working of the service; however, server administrators are able to see when the service has been started/stopped in the EventLog entries and can monitor failures. Every 24 hours, a file with all the gathered information from the SharePoint Portal is generated by the service.
Figure 2: Output file from the service
Conclusions
Windows Services, although not a part of the SharePoint infrastructure, can play an important role in the Portal as well. Automated tasks that need to be executed independent of SharePoint can be programmed as Windows Services. The Service can use all the programming code from the DotNet FrameWork and the SharePoint Object Model.
About the Author
Gustavo Velez is a MCSD Senior Application Developer for Winvision (http://www.winvision.nl), a Microsoft Gold Partner in the Netherlands. He has many years of experience developing Windows and Office applications, and more than five years of daily programming experience with SharePoint. The author’s articles can be found in many of the leading trade magazines in English, Dutch, and Spanish. He is also pleased to be Webmaster of http://www.gavd.net/servers, the only Spanish-language site dedicated to SharePoint. Spanish-language readers may want to consult Velez.s new book, Programacisn con SharePoint 2007 (http://www.dotnetmania.com/Libros/index.html).