Microsoft Visual C#.NET 2003 Kick Start

There are three programmer-to-programmer topics in this chapter: Windows services, Web services, and how to deploy applications using Windows installer (.MSI) files. All these issues are important ones, and we'll use them to round off the GUI-oriented application coverage. We'll start with Windows services.

Windows services are not typically front-line applications that the user runs and interacts with. Instead, they provide support services, often for device drivers, such as printer device drivers, audio devices, data providers, CD creation software, and so on. As such, Windows services don't need a real user interface as you see in standard Windows applications. They often do have a control panel-like interface that the users can open by clicking or right-clicking an icon in the taskbar, however. (You can create taskbar icons in Windows applications using the NotifyIcon control from the Windows Forms tab in the toolbox.) Users can customize, and even start or stop, a Windows service using that control panel. Other Windows applications can interact with the service at runtime; for example, SQL Server uses a Windows service to make it accessible to other applications.

We'll see how to create a working Windows service in this chapter. In the FCL, Windows services are based on the ServiceBase class, and that class gives us most of the support we'll need. When you write a Windows service, you should override the OnStart and OnStop methodseven though their names imply they are event handlers, they're actually methods. These methods are called when the service starts and stops. You might also want to override the OnPause and OnContinue event handlers to handle occasions where the service is paused and resumed.

SHOP TALK : ABUSING WINDOWS SERVICES

Currently, there is a great deal of abuse of Windows services, and it's getting so bad that sooner or later there's going to be a user revolt. Too many software manufacturers just decide that the user's computer has nothing better to do than to continuously run their software, and so you find Windows services that do nothing else besides checkonce a secondwhether the software manufacturer's software is running, or printer drivers that run as a Windows service, also polling once a second and displaying multiple taskbar icons and pop-ups. Some one-time-use applications, like a few tax programs, appear to install Windows services that run continuously as long as the user has the computer. Other manufacturers use Windows services to gather information about the user's work habits, installed software, and/or accessed files to send over the Internet without the user's knowledge.

Because Windows services can be invisible, they've been incredibly abused. If you look in the Service Control Manager tool that we'll discuss in this chapter, you may find a dozen or so Windows services running that you've never heard of before. It's very important to resist this temptation to monopolize the user's machine. If you need to poll your device driver or service, you can start a Timer object (see Chapter 7, "Creating C# Windows Applications") when the service's OnStart method is called, but don't use this technique to wrest control from the user more than just occasionally, unless you specifically let the user know what's going on. Many users, when told what some formerly unknown Windows services are doing, consider them no better than viruses.

You can configure Windows services to start automatically when the computer starts, or you can start them manually using an administration tool built into Windows, the Service Control Manager (SCM).

We'll see how this works in practice now. You can see an example, ch12_01, in the code for this book, and we'll take that application apart here. To follow along, create a new Windows service project. Choose File, New, Project in the IDE, and select the Windows Service icon in the Templates box of the New Project dialog box. Give this new service the name ch12_01 and click OK. This creates the new Windows service project in Figure 12.1; the default name for this new service is "Service1" .

Figure 12.1. A new Windows service.

In our Windows service, we are going to write to our Windows service's event log when the OnStart and OnStop methods are called. To handle an event log, you must specify or create an event source . An event source registers your application with the event log as a source of data so the event log can listen for that data. You can give the event source as any string, but the name must be unique among other registered sources.

In this example, we're going to register our event log in the Windows service's constructor, which you can find in the Component Designer generated code region of the Windows service's code, Service1.cs . That constructor looks like this now:

public Service1() { // This call is required by the Windows.Forms Component Designer. InitializeComponent(); // TODO: Add any initialization after the InitComponent call }

In this example, we create an event source named "CSSource1" . After the new source is created, we assign its name to the eventLog1 object's Source property like this:

public Service1() { // This call is required by the Windows.Forms Component Designer. InitializeComponent(); if (!System.Diagnostics.EventLog.SourceExists("CSSource1")) { System.Diagnostics.EventLog.CreateEventSource("CSSource1", "currentLog1"); } eventLog1.Source = "CSSource1"; }

Now we're ready to write to our event log when the service starts and stops. You can do that in the OnStart and OnStop methods, which look like this in Service1.cs currently:

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. }

To write text to a Windows service's event log, you can use the log's WriteEntry method. In this case, we'll insert a message into the log indicating that the service started or stopped , like this:

protected override void OnStart(string[] args) { eventLog1.WriteEntry("Starting ch12_01."); } protected override void OnStop() { eventLog1.WriteEntry("Stopping ch12_01."); }

When our Windows service starts, our code will write "Starting ch12_01." to event log currentLog1 , and when the service stops, our code will write "Stopping ch12_01." to the log.

We've created our Windows service. To install that service, we'll need an installer, so click the Service1.cs[Design] tab now to open the designer for Service1 . Make sure that eventLog1 in that designer does not have the focus (we want to create an installer for the service itself, not the event log object in the service), and then click the Add Installer link in the description section of the properties window (this link is visible at bottom right in Figure 12.2).

Figure 12.2. Adding an event log to a Windows service.

This creates ProjectInstaller.cs with two objects in it, serviceProcessInstaller1 and serviceInstaller1 , as you see in Figure 12.3.

Figure 12.3. Creating an installer for a Windows service.

ServiceInstaller objects inform Windows about a service by writing Windows Registry values for the service to a Registry subkey under the HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services Registry key. The service is identified by its ServiceName value in this subkey. ServiceProcessInstaller objects handle the individual processes started by our service.

When you install a Windows service, you have to indicate which account it should run under. In this example, we'll do that by clicking the serviceProcessInstaller1 object to give it the focus and setting its Account property to LocalSystem . Besides LocalSystem , you can also set this property to LocalService , NetworkService , or User . When you set Account to User , you must set the Username and Password properties of the serviceProcessInstaller1 object to configure this object for a specific user account.

Now click the serviceInstaller1 object and make sure its ServiceName property is set to the name of this service, Service1 (it should already be set that way). You use the ServiceInstaller1 object's StartType property to indicate how to start the service. Here are the possible ways of starting the service, using values from the ServiceStartMode enumeration:

  • ServiceStartMode.Automatic The service should be started automatically when the computer boots.

  • ServiceStartMode.Disabled The service is disabled (so it cannot be started).

  • ServiceStartMode.Manual The service can only be started manually (by either using the Service Control Manager, or by an application).

The safest of these while testing a new Windows service is Manual , so set the StartType property of the ServiceInstaller1 object to Manual now.

USING MANUAL STARTUP WHILE DEVELOPING WINDOWS SERVICES

A Windows service with errors in it that starts automatically on boot can make Windows unstable, so be careful when testing Windows services. Until you're sure a service is working correctly, it's best to keep its start mode Manual , making sure it doesn't start again automatically if you need to reboot. (If you get into a loop where a problematic Windows service is starting automatically when you boot and causing Windows to hang, press and hold the F8 key while booting so Windows comes up in safe mode.)

Installing a Windows Service

The next step is to build and install our new Windows service, Service1 . To build the service, select Build, Build ch12_01, which creates ch12_01.exe. To actually install the service in Windows, you can use the InstallUtil.exe tool that comes with the .NET Framework. In Windows 2000, for example, you can find InstallUtil.exe in the C:\WINNT\Microsoft.NET\Framework\ xxxxxxxx directory, where xxxxxxxx is the .NET Framework's version number.

Here's how you install ch12_01.exe using InstallUtil.exe at the DOS command prompt (note that the command line here is too wide for the page, so it's split into two lines):

C:\WINNT\Microsoft.NET\Framework\ xxxxxxxxxx >installutil c:\c#\ch12\ch12_01\bin\Debug\ch12_01.exe Microsoft (R) .NET Framework Installation utility Version xxxxxxxxxx Copyright (C) Microsoft Corporation 1998-2002. All rights reserved. Running a transacted installation. Beginning the Install phase of the installation. See the contents of the log file for the c:\c#\ch12\ch12_01\bin\debug\ch12_01.exe assembly's progress. The file is located at c:\c#\ch12\ch12_01\bin\debug\ch12_01.InstallLog. Installing assembly 'c:\c#\ch12\ch12_01\bin\debug\ch12_01.exe'. Affected parameters are: assemblypath = c:\c#\ch12\ch12_01\bin\debug\ch12_01.exe logfile = c:\c#\ch12\ch12_01\bin\debug\ch12_01.InstallLog Installing service Service1... Service Service1 has been successfully installed. Creating EventLog source Service1 in log Application... The Install phase completed successfully, and the Commit phase is beginning. See the contents of the log file for the c:\c#\ch12\ch12_01\bin\debug\ch12_01.exe assembly's progress. The file is located at c:\c#\ch12\ch12_01\bin\debug\ch12_01.InstallLog. Committing assembly 'c:\c#\ch12\ch12_01\bin\debug\ch12_01.exe'. Affected parameters are: assemblypath = c:\c#\ch12\ch12_01\bin\debug\ch12_01.exe logfile = c:\c#\ch12\ch12_01\bin\debug\ch12_01.InstallLog The Commit phase completed successfully. The transacted install has completed. C:\WINNT\Microsoft.NET\Framework\ xxxxxxxxxx >

Now our Windows service has been installed. If InstallUtil hadn't been able to install the new service without problems, it would have rolled back the installation and removed the non-working service.

Our new Windows service is installed, but not yet started, because we chose manual startup. In this case, we'll use the Service Control Manager to start our service. The SCM is part of Windows; for example, in Windows 2000, you can start the SCM this way:

  • In Windows 2000 Server, you select Start, select Programs, click Administrative Tools, and click Services.

  • In Windows 2000 Professional, right-click the My Computer icon on the desktop and select the Manage item in the menu that pops up. In the dialog box that appears, expand the Services and Applications node and click the Services item.

AUTO-INSTALLING A WINDOWS SERVICE

You can also deploy Windows services with setup programs in C#; we'll cover setup programs at the end of this chapter. You create setup programs with setup projects, and to install a Windows service, you add a custom action to a setup project. In the Solution Explorer, right-click the setup project, select View, and then select Custom Actions, making the Custom Actions dialog box appear. In the Custom Actions dialog box, right-click the Custom Actions item and select Add Custom Action, making the Select Item in Project dialog box appear. Double-click the Application Folder in the list box, opening that folder. Select Primary Output from ServiceName (Active), where ServiceName is the name of your service, and click OK. The primary output (that is, the Windows service itself) is added to all four custom action foldersInstall, Commit, Rollback, and Uninstall.

You can see the Service Control Manager in Figure 12.4, and you can see our newly installed service, Service1 , listed in the SCM in the figure.

Figure 12.4. The Service Control Manager.

To start our new service, right-click Service1 in the Service Control Manager now and select the Start item in the menu that appears. Doing so starts the service, as you see in Figure 12.5, where Service1 is listed as Started .

Figure 12.5. Starting a Windows service.

RUNNING A WINDOWS SERVICE FROM THE SERVER EXPLORER

You can also run a Windows service from the Server Explorer; just expand the Services node, and then right-click the service you want to start and click Start.

To stop the service, right-click Service1 in the Service Control Manager and select the Stop item.

We've been able to start and stop our new service, so it should have written to our event log, currentLog1 . You can check whether it has from inside the IDE; you just open the Server Explorer's Event Logs node as you see in Figure 12.6, and take a look at the entry for CSSource1 in currentLog1 . Here, you can see our service's two entries Starting ch12_01. and Stopping ch12_01. in the event log in the Server Explorer in Figure 12.6. (If you don't see the messages there, or messages don't appear when you start and stop the service a number of times, refresh the event log by right-clicking currentLog1 in the Server Explorer and selecting the Refresh item.) And that's itthe Windows service is a success.

Figure 12.6. The Server Explorer.

Our Windows service did exactly what it was supposed toit wrote to an event log when it was started and stopped. Now that you can run code in a Windows service, you can see this is only the beginning. You can get the service's code started when the OnStart method is called, and run it in the background, supplying its service for as long as needed.

Uninstalling a Windows Service

You can uninstall a Windows service, removing it from the SCM, with InstallUtil.exe; just use the /u option to uninstall. Here's what you see when you uninstall the Windows servicenotice that the command line is the same except for the /u :

C:\WINNT\Microsoft.NET\Framework\ xxxxxxxxxx >installutil c:\c#\ch12\ch12_01\bin\Debug\ch12_01.exe /u Microsoft (R) .NET Framework Installation utility Version xxxxxxxxxx Copyright (C) Microsoft Corporation 1998-2002. All rights reserved. The uninstall is beginning. See the contents of the log file for the c:\c#\ch12\ch12_01\bin\debug\ch12_01.exe assembly's progress. The file is located at c:\c#\ch12\ch12_01\bin\debug\ch12_01.InstallLog. Uninstalling assembly 'c:\c#\ch12\ch12_01\bin\debug\ch12_01.exe'. Affected parameters are: assemblypath = c:\c#\ch12\ch12_01\bin\debug\ch12_01.exe logfile = c:\c#\ch12\ch12_01\bin\debug\ch12_01.InstallLog Removing EventLog source Service1. Service Service1 is being removed from the system... Service Service1 was successfully removed from the system. The uninstall has completed. C:\WINNT\Microsoft.NET\Framework\ xxxxxxxxxx >

Interacting with Windows Services from Other Applications

We've seen how to run a Windows service in the background, but how do you connect to that Windows service from another application? All you need to do is drag a ServiceController object from the Components tab of the Toolbox to a form in a Windows application, creating a new object, serviceController1 . Then you set these properties in the properties window for this object:

  • MachineName The name of the computer that hosts the service, or "." for the local computer.

  • ServiceName The name of the service you want to work with.

Now using the power of the Windows service becomes easyyou can use the properties and methods exposed by the Windows service as though they were properties and methods of the serviceController1 object. For example, here's how you might use the CanStop and ServiceName properties of a Windows service as connected to by serviceController1 :

if (serviceController1.CanStop) { MessageBox.Show(serviceController1.ServiceName + " can be stopped."); }

You can also create a ServiceController object in code. To do that, you add a reference to the System.ServiceProcess DLL by right-clicking the current project in the Solution Explorer and selecting Add Reference. Then, click the .NET tab in the Add Reference dialog box, select System.ServiceProcess.dll and click Select. Finally, click OK to close the Add Reference dialog box. Also, you should include a using statement in your application's code for the System.ServiceProcess namespace. Now you can access the properties and methods that are built into a Windows service using the new ServiceController object like this:

using System.ServiceProcess; . . . ServiceController controller1 = new ServiceController("Service1"); if (controller1.CanStop) { MessageBox.Show(controller1.ServiceName + " can be stopped."); }

That's all it takes to access a Windows service from code. (Note that you can even implement events in a Windows service, and, using ServiceController objects, handle those events in other applications.)

Next, we'll take a look at the properties, methods, and events of a few of the important Windows services classes to round off this topic.

Working with the ServiceBase Class

The ServiceBase class is the base class for Windows services, and you can find the significant public properties of ServiceBase objects in Table 12.1, and their significant protected methods in Table 12.2 (note that although the items in Table 12.2 look like events, they're actually methods you can override).

Table 12.1. Significant Public Properties of ServiceBase Objects

PROPERTY

PURPOSE

AutoLog

Sets whether to record in the event log automatically.

CanPauseAndContinue

Returns or sets whether the service can be paused and continued .

CanShutdown

Returns or sets whether the service should be informed when the computer shuts down.

CanStop

Returns or sets whether the service can be stopped.

EventLog

Returns the event log.

ServiceName

Returns or sets the name of the service.

Table 12.2. Significant Protected Methods of ServiceBase Objects

METHOD

PURPOSE

OnContinue

Called when a service continues (after it was paused).

OnPause

Called when a service is paused.

OnShutdown

Called when the system shuts down.

OnStart

Called when the service starts.

OnStop

Called when a service stops running.

Working with the EventLog Class

The EventLog class supports access to Windows event logs used by Windows services; you can find the significant public static methods of EventLog in Table 12.3, the significant public properties of EventLog objects in Table 12.4, their significant methods in Table 12.5, and their significant events in Table 12.6.

Table 12.3. Significant Public Static Methods Properties of the EventLog Class

METHOD

PURPOSE

CreateEventSource

Creates an event source to let you write to a log.

Delete

Deletes a log.

DeleteEventSource

Deletes an event source.

Exists

Returns true if a log exists.

GetEventLogs

Returns an array of event logs.

SourceExists

Checks whether an event source exists.

WriteEntry

Writes an entry to the log.

Table 12.4. Significant Public Properties of EventLog Objects

PROPERTY

PURPOSE

Entries

Returns the contents of the log.

Log

Returns or sets the name of the log.

LogDisplayName

Returns the log's display name.

MachineName

Returns or sets the name of the log's computer.

Source

Returns or sets the source name to use when writing to the log.

Table 12.5. Significant Public Methods of EventLog Objects

METHOD

PURPOSE

BeginInit

Begins initialization of a log.

Clear

Clears all the entries in a log.

Close

Closes the log.

EndInit

Ends initialization of a log.

WriteEntry

Writes an entry in the log.

Table 12.6. Significant Public Events of EventLog Objects

EVENT

PURPOSE

EntryWritten

Happens when data is written to a log.

Working with the ServiceProcessInstaller Class

As we've already seen, ServiceProcessInstaller objects let you install specific processes in a Windows service. You can find the significant public properties of objects of the ServiceProcessInstaller class in Table 12.7, their significant methods in Table 12.8, and their significant events in Table 12.9.

Table 12.7. Significant Public Properties of ServiceProcessInstaller Objects

PROPERTY

PURPOSE

Account

Returns or sets the type of account for the service.

HelpText

Returns help text.

Installers

Returns the service's installers.

Parent

Returns or sets the parent installer.

Password

Returns or sets the password for a user account.

Username

Returns or sets a user account.

Table 12.8. Significant Public Methods of ServiceProcessInstaller Objects

METHOD

PURPOSE

Install

Installs a service, writing information to the Registry.

Rollback

Rolls back an installation, removing data written to the Registry.

Uninstall

Uninstalls an installation.

Table 12.9. Significant Public Events of ServiceProcessInstaller Objects

EVENT

PURPOSE

AfterInstall

Happens after an installation.

AfterRollback

Happens after an installation is rolled back.

AfterUninstall

Happens after an uninstallation.

BeforeInstall

Happens before installation.

BeforeRollback

Happens before installers are rolled back.

BeforeUninstall

Happens before an uninstallation.

Committed

Happens after all installers have committed installations.

Committing

Happens before installers commit installations.

Working with the ServiceInstaller Class

You use ServiceInstaller objects to install Windows services, and you can find the significant public properties of objects of this class in Table 12.10, their significant methods in Table 12.11, and their significant events in Table 12.12.

Table 12.10. Significant Public Properties of ServiceInstaller Objects

PROPERTY

PURPOSE

DisplayName

The display name for this service.

HelpText

Help text for the installers.

Installers

Returns the installers.

Parent

Returns or sets the parent installer.

ServiceName

The name of this service.

ServicesDependedOn

Specifies services that must be running in order to support this service.

StartType

Specifies when to start this service.

Table 12.11. Significant Public Methods of ServiceInstaller Objects

METHOD

PURPOSE

Commit

Commits an installation.

Install

Installs the service by writing data to the Registry.

Rollback

Rolls back a service's data written to the Registry.

Uninstall

Uninstalls the service.

Table 12.12. Significant Public Methods of ServiceInstaller Objects

EVENT

PURPOSE

AfterInstall

Happens after the installers have installed.

AfterRollback

Happens after the installations are rolled back.

AfterUninstall

Happens after all the installers finish their uninstallations.

BeforeInstall

Happens just before the each installer's Install method runs.

BeforeRollback

Happens before the installers are rolled back.

BeforeUninstall

Happens before the installers uninstall.

Committed

Happens after all the installers commit their installations.

Committing

Happens before the installers commit their installations.

That completes our look at Windows services; next up are Web services.

Категории