Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Pro Visual C++-CLI And The .NET 2.0 Platform (2006) [eng]-1

.pdf
Скачиваний:
70
Добавлен:
16.08.2013
Размер:
24.18 Mб
Скачать

628 C H A P T E R 1 4 W I N D O W S S E R V I C E S

Basically, now your application is connected to the Windows service. You have read access (and a few with write access) to a number of the Windows services properties and the ability to trigger the Windows service’s handles by making ServiceController method calls. Table 14-4 shows some of the more common properties and methods available to you.

Table 14-4. Commonly Used ServiceController Properties and Methods

Property/Method

Description

CanPauseAndContinue

A property indicating whether the Windows service can be paused

 

and continued

CanShutDown

A property indicating whether the Windows service receives

 

shutdown events

CanStop

A property indicating whether the Windows service can stop

 

after starting

Close()

A method that closes down this instance of ServicesController and

 

releases all resources associated with the instance

Continue()

A method that triggers the OnContinue() handler

DependentServices

A property containing a list of all dependent Windows services

DisplayName

A property that allows you to get or set the friendly name of the

 

Windows service

ExecuteCommand()

A method that triggers the OnCustomCommand() handler

GetServices()

A static method that retrieves an array of all Windows services on

 

the system

MachineName

A property that allows you to get or set the name of the computer of

 

where the Windows service resides

Pause()

A method that triggers the OnPause() handler

Refresh()

A method that refreshes all the Windows Services properties

ServiceName

A property that allows you to get or set the name of the service this

 

instance of ServicesController is referencing

Start()

A method that triggers the OnStart() handler

Status

A property indicating the current status (state would be more accu-

 

rate) of the Windows service

Stop()

A method that triggers the OnStop() handler

WaitForStatus()

A method that waits until the Windows services becomes a specified

 

status (state)

 

 

The actual code to implement a custom service control application is nearly trivial. And as far as I can see, there is really only one gotcha. The properties in the ServiceController are a snapshot, and to get the most recent version of them, you need to call the Refresh() method.

To show you what I mean, add four buttons to the Windows Form that you created previously. The form should look something like Figure 14-12.

C H A P T E R 1 4 W I N D O W S S E R V I C E S

629

Figure 14-12. SimpleWinServive controller

Now let’s add the ability for the service control application to start the Windows service. Once you have your Windows Form laid out, double-click the Start button so that you can edit the code for the start Windows service event handler. Here is the code:

System::Void bnStart_Click(System::Object^ sender, System::EventArgs^ e)

{

serviceController1->Refresh();

if (serviceController1->Status == ServiceControllerStatus::Stopped)

{

serviceController1->Start(); MessageBox::Show("SimpleWinService Started");

}

else

{

MessageBox::Show("SimpleWinService Running");

}

}

Yes, that’s all it takes! Now let’s take a closer look. The first thing you need to do is Refresh() the properties. If you don’t, then the Status property will probably be out of sync with the actual Windows service. Then, before you call the Start() method, you need to make sure that the Windows services status is Stopped. Another option would be to enclose the Start() method in a try/catch block, as the Start() method throws an exception if the current start is not Stopped. I added the MessageBoxes so that you can verify all is well, but they are obviously not needed.

Now let’s stop the service:

System::Void bnStart_Click(System::Object^ sender, System::EventArgs^ e)

{

serviceController1->Refresh();

if (serviceController1->Status == ServiceControllerStatus::Stopped)

{

serviceController1->Start(); MessageBox::Show("SimpleWinService Started");

}

else

{

MessageBox::Show("SimpleWinService Running");

}

}

The code is nearly identical. In fact most of the handler trigger methods are handled this exact same way. There is one major exception: ExecuteCommand().

The ExecuteCommand() method allows you to trigger an event on the Windows service based on a numeric value between 128 and 255. Windows reserves the values 0 through 127. The implementation of the custom command is made up of two parts.

630 C H A P T E R 1 4 W I N D O W S S E R V I C E S

First you need to add a call to your Windows Form to ExecuteCommand(), passing it a number representing the command that you want the Windows service to execute. Here is the code for the button Interval 15. (The code for button Interval 20 is virtually the same except the numeric value passed in the ExecuteCommand() method.)

System::Void bnIntv15_Click(System::Object^ sender, System::EventArgs^ e)

{

serviceController1->Refresh();

if (serviceController1->Status == ServiceControllerStatus::Running)

{

serviceController1->ExecuteCommand(150); MessageBox::Show("SimpleWinService Interval in 15 seconds");

}

else

{

MessageBox::Show("SimpleWinService Not Running");

}

}

I’m pretty sure you are starting to see a pattern forming on these event handlers.

The second half the of the custom command is to add an OnCustomCommand() handler to your Windows service, which will process the numeric command sent by the ExecuteCommand() method. Here is an example that changes the interval time of the timer of the Windows service:

virtual void OnCustomCommand(int cmd) override

{

if (cmd == 150) this->timer->Interval = 15000;

else

this->timer->Interval = 20000;

}

I used an if statement due to the fact that only two numeric values are being sent to the OnCustomCommand() handler. Normally, you would probably use a case statement on the cmd parameter.

Normally, I include a full example of the source code, but I see no real added value in doing so for this example, as all the code is so trivial. But if you need the code example, it is available on the Apress and ProCppCLI.net Web sites.

Debugging Windows Services

The process of debugging a Windows service is a little different from the generic Windows Forms application or console application, since you do not start or execute the service via the main() method. Fortunately, all is not lost, as you have two techniques for debugging your Windows service. Which debugging process you use depends on what functionality you are trying to test.

C H A P T E R 1 4 W I N D O W S S E R V I C E S

631

Attaching the Debugger to the Windows Service

The first process is outlined in the many C# books out there that cover Windows services: attach the debugger to the service after it is running. This allows you to use all the standard debugging features available on Visual Studio 2005. The process, while straightforward, is, as far as I’m concerned, far from intuitive. But once you know the steps, you can replicate it for any Windows service.

To attach the debugger to a Windows service requires the following steps:

1.Start your Windows service using the Services application or your own custom service control application.

2.Select from the main Visual Studio 2005 menu Debug and then the menu item Attach to Process. This will display a dialog box similar to the one in Figure 14-13.

Figure 14-13. Attach to Process dialog box

3.Click Show processes from all users. You may not need this if you started the process using your own user security context.

4.Select your Windows service from the Available Processes list.

5.Click the Attach button.

632 C H A P T E R 1 4 W I N D O W S S E R V I C E S

When you complete these steps, the dialog box will disappear, the debugger will be attached to your Windows service, and you will be in debug mode of Visual Studio 2005. At this point, you can set break point, watches, etc., just like you would for any other Windows or console application.

The problem with this method is that you cannot test the OnStart() handler, as it has already run. And executing the OnStop() handler ends the debug session, so you can’t restart the Windows service to test the OnStart() either.

This is where the other testing process comes in.

A Special Main() Function

A Windows service is really just a specialized application. Due to this fact, you can write a slightly modified main() function to test your Windows service’s startup process. I think it’s easier just to show you the code first and walk you through it than try to explain things beforehand. Listing 14-7 shows the new main() method.

Listing 14-7. Debug-enhanced main() Method

#include "stdafx.h"

#include "SimpleWinService.h"

using namespace Simple;

using namespace System::Collections; using namespace System::ServiceProcess;

void main()

{

#ifndef COMMANDLINE_DEBUG array<ServiceBase^>^ ServicesToRun;

ServicesToRun = gcnew array<ServiceBase^> { gcnew SimpleWinService() }; ServiceBase::Run(ServicesToRun);

#else

SimpleWinService ^svc = gcnew SimpleWinService(); svc->OnStart(nullptr);

Console::WriteLine("Any key stop stop"); Console::ReadLine();

svc->OnStop(); #endif

}

The code uses the #ifndef directive (covered in Chapter 4) to split the main() method into two parts. If you recall, the #ifndef directive causes the compiler only to compile code in the enclosed region (between #else, #elseif, or #endif) when the symbol specified does not exist. Thus, the first block compiles the code just like normal, if the symbol COMMANDLINE_DEBUG does not exist, whereas the second block compiles the special code allowing you to debug the OnStart() handler, if the symbol does exist.

You can place the symbol COMMANDLINE_DEBUG either as a #define directive in stdafx.h or in SimpleWinService.h anywhere before the line

#include "SimpleWinService.h"

C H A P T E R 1 4 W I N D O W S S E R V I C E S

633

or in the application’s Processor Definitions property as shown in Figure 14-14. You need to place it before the preceding #include statement because SimpleWinService.h also uses the symbol, as I’ll point out next.

Figure 14-14. Processor Definitions property

One more issue remains. When you compile the preceding code, you get two errors telling you that the OnStart() and OnStop() methods are not accessible. The reason is the auto-generated template code for Windows services defines these two methods as protected and thus not accessible.

To fix this, add

#ifdef COMMANDLINE_DEBUG public:

#endif

right before the call to OnStart(). This will cause the methods to now be public when the symbol is defined.

Now you can compile and debug the Windows service exactly like any other Windows or console application. This, unfortunately, also means you cannot access the Windows service using the Services application or your custom service control application, as it has not actually been started as a service. So long as you don’t try to interface it with either of these, it will behave just like the Windows service does when compiled as a service, with the added bonus that you can now debug the OnStart() method.

By the way, you can debug the other handlers as well by calling them in the main() function.

634 C H A P T E R 1 4 W I N D O W S S E R V I C E S

Summary

Admittedly, this chapter has simplified the coding of Windows services, but you should be well on your way to understanding Windows services after reading it. The chapter started by discussing what a Windows service is and its three parts: service application, service configuration application, and service control application. You moved on by creating a simple Service application. You then saw how to implement a service configuration application using the ServiceProcessInstaller and ServiceInstaller classes. Next, you saw how to use the Windows-provided service control application called the Services application and how to write your own. Finally, you saw two methods for debugging your Windows services.

In the next chapter, you’ll explore a different kind of service, the Web service.

C H A P T E R 1 5

■ ■ ■

Web Services

Web services are the central hub of everything that is .NET. The basic goal of Web services is to make distributed applications much easier to design and develop than ever before. They change the way Internet, intranet, extranet, or whichevernet-based applications are written. Basically, Web services put the “net” in .NET.

Web services aren’t unique to Microsoft or .NET. In fact, all of the major industry players have a Web services offering. This chapter, however, focuses only on the Microsoft .NET Framework implementation of Web services.

The chapter starts by providing you with a general understanding of Web services. You’ll discover how to implement and consume a simple Web service using C++/CLI. With the basics under your belt, you’ll then be ready for a more elaborate implementation of a Web service, this time working with the ADO.NET skills you acquired back in Chapter 12.

What Are Web Services?

In simple terms, Web services are software components that can be accessed and executed remotely via a network by a client application using standard protocols such as Hypertext Transfer Protocol (HTTP) and Simple Object Access Protocol (SOAP), as shown in the following illustration.

Note Web services are not restricted to SOAP or HTTP, but this chapter will focus on these two as they are all I use and are the most common.

What does this mean in English? You can create a class, make it available on the Internet, and have someone on the other side of the world execute the methods on that class as if the methods were on their local machine. Likewise, Web services enable you to execute other developers’ classes from anywhere around the world as long as they’re hosted on the Internet. You can also place the class on a server within your LAN or WAN and execute it exactly the same way using your intranet, extranet, or whatevernet, but that simply isn’t as exciting, so I’ll stick to Web services’ Internet capabilities in this chapter.

635

636 C H A P T E R 1 5 W E B S E R V I C E S

Another cool feature of Web services is that they aren’t just a .NET thing. You can access Web services written in any computer language on any platform as long as they conform to an agreedupon set of standards to communicate, nearly always HTTP and SOAP. This feature allows for simple integration of diverse legacy systems and new .NET applications.

For those of you who have been coding for a few years, Web services are a much improved alternative to DCOM, COBRA, and the like.

Components of a Web Service

Web services are based on well-established networking protocols and a few newer technologies. In truth, you really don’t have to know much about any of these technologies because .NET handles them, for the most part, in the background for you. In fact, the first few Web services I wrote were in complete blissful ignorance of these technologies. But, of course, true to my developer nature, I wanted to see what happens behind the curtain.

Basically, for a Web service to function, you need

A communication protocol so that the service and its consuming client can communicate

A description service so that the consuming client will be able to understand how to use the Web service

A discovery service so that the consuming client can find the Web service

In the following sections you’ll take a look at each requirement in a little more detail.

Communication Protocols

Communication between .NET Web services and client consumers is handled via generic HTTP using, normally, port 80. (For those of you who are HTTP knowledgeable, you are aware that HTTP is not restricted to port 80.) If you know something about Internet technology, you will recognize this as the same communication method used by standard Web browsers. Thus, if your system supports a Web browser, it can also support Web services. This is a key aspect of Web services, as other distributed application methods use their own specific communication protocols.

Communication between a Web service and a consumer client is always initiated by the client. Clients communicate with the Web service over HTTP in two different ways:

HTTP POST commands

SOAP

If you have done any Web programming, you should be quite comfortable with using HTTP POST commands. Normally, you will not use this method when implementing Web services because it is limited to simple data types for passing between the client and the service.

Caution Make sure you are using HTTP POST and not HTTP GET. HTTP GET is supported by Web services, but you need to change your default machine.config file. (You must uncomment the line <add name="HttpGet"/>.) My guess is that Microsoft plans to phase this out, so I recommend that you don’t use HTTP GET, and except for basic Web service testing, I don’t really see any reason to use HTTP GET anyway.

C H A P T E R 1 5 W E B S E R V I C E S

637

SOAP is a powerful XML-based protocol that packages up a method call to be executed, along with any parameters it requires for implementing. This package is then sent using a standard HTTP request to the Web service. Upon the completion of the execution of a method, SOAP packages up any return values and sends them back to the client using a standard HTTP response.

The best part of SOAP, at least when it comes to .NET, is you get it for free in almost all cases, and you don't have to know anything about it so long as you code within the Common Language Specification (CLS) specified by .NET. As you will see later in this chapter when I show how to send a DataSet from a Web service to a client, it is possible to transmit fairly complex data objects using SOAP.

Description Service

It’s all well and good that you send stuff back and forth between the client and the Web service. But before this communication can take place, the client needs some way to find out what it can request the Web service to do and what format the request needs to be in. (The format is also known as the method signature.) You might think that you could use SOAP to handle the descriptive service, but SOAP was not designed to describe method signatures, only to package them for transport.

The Web service provides this description of its interfaces using the standard called the Web Services Description Language (WSDL). Like SOAP, WSDL is XML based. But instead of packaging like SOAP, WSDL actually describes the method signatures. In fact, WSDL describes method signatures in such detail that Visual Studio 2005 imports the WSDL’s XML definitions and uses them to provide IntelliSense help.

Like all the previous technologies for Web services, WSDL is completely handled by Visual Studio 2005.

Discovery Service

Even if you can communicate between a client and a Web service and describe how this communication needs to take place, it’s all still moot if the client doesn’t know where to find the required Web service it needs to execute. This is the job of the discovery service. .NET provides two discovery services:

Web Services Discovery tool (DISCO)

Universal Description, Discovery, and Integration (UDDI)

DISCO is used to describe each Web service in any given virtual directory and any related subdirectories. Originally, .NET was going to use DISCO as its primary method of discovery, but with the advent of the superior UDDI, DISCO has become optional. It is still created automatically by Visual Studio 2005 for those who want to stick with DISCO, but I think it will most probably disappear in the future.

UDDI’s scope is more far-reaching than DISCO’s. With UDDI, you register your Web service with a central agency. Once your Web service is registered, third parties can search the agency to locate your registered Web service.

Personally, I find discovery services only useful if I don’t know the exact URL of the Web service (which for me is rarely as I am usually the author of the Web service). As you will see later in the chapter, if you know the URL of the Web service you can access it directly without worrying about directory services.