📄 cb199910nt0602_f.asp.htm
字号:
<p class=Code><span class=Code> RegisterServiceCtrlHandler(PChar(Name),
GetServiceController); </span></p>
<p class=BodyText> </p>
<p class=BodyText> Granted,
this is Object Pascal code, but it's easy enough to understand even if you
aren't a Pascal programmer. This code calls the Windows <i>RegisterServiceCtrlHandler</i> function, passing the service name in
the first parameter. The second parameter is a call to the service's <i
style='mso-bidi-font-style:normal'>GetServiceController</i> method. As you can
see from the earlier code snippet, this method returns a pointer to the
standalone <i>ServiceController</i>
function. Once registered, the <i>ServiceController</i>
function will be called each time the service receives a control request from
the SCM. It's not vital that you understand service control requests at this
time, but I wanted to explain the meaning of these two functions. </p>
<p class=BodyText> </p>
<p class=BodyText> You may
have noticed that the <i>ServiceController</i>
function calls the service's <i>Controller</i>
method. The <i>Controller</i> method is
declared as protected in the <i>TService</i>
class. How does the standalone <i>ServiceController</i>
function gain access to the <i>Controller</i>
method? If you look at the service's class declaration in Figure 1, you will
notice that the <i>ServiceController</i>
function is declared as a friend of the service class: </p>
<p class=BodyText> </p>
<p class=Code><span class=Code><b>friend</b>
<b>void</b> <b>__stdcall</b> ServiceController(<b>unsigned</b>
CtrlCode); </span></p>
<p class=BodyText> </p>
<p class=BodyText> This
makes it possible for the <i>ServiceController</i>
function to call the protected <i>Controller</i>
method. </p>
<p class=BodyText> </p>
<p class=BodyText> Note:
There is a minor bug in the code generation for service applications. When you
create a new service, C++Builder gives the service a default name of Service1.
It then generates a <i>ServiceController</i>
function that looks like this: </p>
<p class=BodyText> </p>
<p class=Code><span class=Code><b>void</b> <b
style='mso-bidi-font-weight:normal'>__stdcall</b> ServiceController(<b
style='mso-bidi-font-weight:normal'>unsigned</b> CtrlCode) </span></p>
<p class=Code><span class=Code>{</span></p>
<p class=Code><span class=Code> Service1->Controller(CtrlCode); </span></p>
<p class=Code><span class=Code>}</span></p>
<p class=BodyText> </p>
<p class=BodyText> Now
let's say you change the service's <i>Name</i>
property to <i>MyService</i>. C++Builder
will change all of the references to Service1 with <i>MyService</i>, except the reference in the <i>ServiceController</i> function. Before your code will compile, you will
have to manually change the code in the <i>ServiceController</i>
function so that it uses the correct class name. In this case, you would have
to modify the code as follows: </p>
<p class=BodyText> </p>
<p class=Code><span class=Code><b>void</b> <b
style='mso-bidi-font-weight:normal'>__stdcall</b> ServiceController(<b
style='mso-bidi-font-weight:normal'>unsigned</b> CtrlCode) </span></p>
<p class=Code><span class=Code>{</span></p>
<p class=Code><span class=Code> MyService->Controller(CtrlCode); </span></p>
<p class=Code><span class=Code>}</span></p>
<p class=BodyText> </p>
<p class=Subheads>Service
Threads and the <i>OnExecute</i> Event</p>
<p class=BodyText> Each
service has its own thread. The <i>ServiceThread</i>
property is a pointer to the service's internal thread. <i>ServiceThread</i> is an instance of the <i>TServiceThread</i> class. <i>TServiceThread</i>
is derived from <i>TThread</i>. <i
style='mso-bidi-font-style:normal'>TServiceThread</i> only provides one method
beyond the usual methods of <i>TThread</i>.
That method is <i>ProcessRequests</i> and
its purpose is to process service requests sent to the service by the SCM. </p>
<p class=BodyText> </p>
<p class=BodyText> For
simple services, you can use the service's built-in thread. That's what the
BeepService example does. All of the code for BeepService is in the service's <i
style='mso-bidi-font-style:normal'>OnExecute</i> event handler: </p>
<p class=BodyText> </p>
<p class=Code><span class=Code><b>void</b> <b
style='mso-bidi-font-weight:normal'>__fastcall</b>
TBeepService::Service1Execute(TService *Sender) </span></p>
<p class=Code><span class=Code>{</span></p>
<p class=Code><span class=Code> <b> while</b>
(!Terminated) </span></p>
<p class=Code><span class=Code> { </span></p>
<p class=Code><span class=Code> MessageBeep(0); </span></p>
<p class=Code><span class=Code> Sleep(1000); </span></p>
<p class=Code><span class=Code> ServiceThread->ProcessRequests(<b
style='mso-bidi-font-weight:normal'>false</b>);</span></p>
<p class=Code><span class=Code> } </span></p>
<p class=Code><span class=Code>}</span></p>
<p class=BodyText> </p>
<p class=BodyText> This
code is fairly straightforward. It simply executes a loop as long as the
service thread is still running. The code within the loop beeps the PC speaker,
sleeps for one second, and then calls the service thread's <i>ProcessRequests</i> method. The call to <i>ProcessRequests</i> is vital. If you don't call <i>ProcessRequests</i>, your service will execute the loop once and then
stop. </p>
<p class=BodyText> </p>
<p class=BodyText> This
service is not very exciting. In fact, it's downright annoying when it's
running. Still, it's a perfectly valid service and shows the simplest type of
service you can write. What may not be obvious, though, is all the work being
done behind the scenes by the VCL. The code for this service is available for
download; see end of article for details. Load the project in C++Builder and
build it. You can then proceed to the next step, installing the service. </p>
<p class=BodyText> </p>
<p class=Subheads>Installing
the Service</p>
<p class=BodyText> A
service must be installed after it is built. Installing a service registers the
service with the SCM. The SCM, in turn, adds the service's configuration
information to the registry. Figure 3 shows the registry key for BeepService. </p>
<p class=BodyText> </p>
<img width=250
height=130 src="images/cb199910NT0602_f_image002.gif" tppabs="http://www.cbuilderzine.com/features/1999/10/cb199910NT0602_f/cb199910NT0602_f_image002.gif" align=left> <br
style='mso-ignore:vglayout' clear=ALL>
<b>Figure 3:</b> The registry key created by the
SCM when it registered BeepService.
<p class=BodyText> </p>
<br clear=all>
<p class=BodyText> To
install a service, run the service application from the command line using the
INSTALL switch: </p>
<p class=BodyText> </p>
<p class=Code><span class=Code>beepsrvc
/install</span></p>
<p class=BodyText> </p>
<p class=BodyText> After a
second or so, you will see a message box that reads as follows: </p>
<p class=BodyText> </p>
<p class=Code><span class=Code>Service
installed successfully</span></p>
<p class=BodyText> </p>
<p class=BodyText> If the
service didn't install successfully, you'll need to check your code to see if
you've missed anything. If you have loaded BeepService from the accompanying
download, it will install without error. </p>
<p class=BodyText> </p>
<p class=BodyText> Note: NT
services are, naturally, for use with the NT operating system. If you attempt
to run a stock service application from Win9x, it will return without doing
anything at all. Technically speaking, it is possible to run services in Win9x
using various tricks and utilities. On the other hand, the Win9x operating
systems certainly do not have full support for services and I do not attempt to
cover the use of services on those platforms in this article series. </p>
<p class=BodyText> </p>
<p class=BodyText> To
uninstall a service, first stop the service if it is started. (I discuss
controlling services in the next section.) Run the service application again,
this time with the UNINSTALL switch: </p>
<p class=BodyText> </p>
<p class=Code><span class=Code>beepsrvc
/uninstall</span></p>
<p class=BodyText> </p>
<p class=BodyText> A
message box will tell you that the service was successfully uninstalled.
Understand that uninstalling a running service does not terminate the service;
it only removes the service from the registry. For that reason, you should stop
the service before uninstalling the service. </p>
<p class=BodyText> </p>
<p class=BodyText> Note:
While developing a service, you will continually be in a cycle of changing
code, compiling, and running the service to test the results of your code
changes. It is not necessary to uninstall the service each time you build the
service application from C++Builder. It is, however, necessary for you to stop
the service before building the service application. Failure to stop the
service prior to building the service application will result in a C++Builder
linker error message that reads as follows: </p>
<p class=BodyText> </p>
<p class=Code><span class=Code>Could not open
D:\Services\BeepSrvc.exe (program still running?). </span></p>
<p class=BodyText> </p>
<p class=BodyText> You will
know that you need to stop the service before continuing if you get this error
message when building the service application. </p>
<p class=BodyText> </p>
<p class=BodyText> I
usually keep a command prompt box open when developing a service. Once I have
typed the install or uninstall command, I can easily install or uninstall a
service by taking advantage of NT's capability to recall commands typed at the
command prompt (using the up and down arrows on the keyboard). </p>
<p class=BodyText> </p>
<p class=Subheads>Testing the
Service</p>
<p class=BodyText> If the
service installed successfully, open the SCP from the Control Panel and you
will see the service listed among the other installed services. You will see
the service name, BeepService, and the <i>Startup</i> type listed as <i>Automatic</i>.
The Status column will be blank, indicating that the service is stopped. Even
though the service is configured to start automatically, this doesn't happen
until the system is restarted. You will have to reboot NT to test your
service's auto-start capability. </p>
<p class=BodyText> </p>
<p class=BodyText> To start
BeepService, click the Start button on the SCP. NT will start the service and you will hear
the PC speaker beep every second. Figure 4 shows the SCP when starting
BeepService. </p>
<p class=BodyText> </p>
<p class=Captions> <img width=250
height=139 src="images/cb199910NT0602_f_image004.gif" tppabs="http://www.cbuilderzine.com/features/1999/10/cb199910NT0602_f/cb199910NT0602_f_image004.gif" align=left> <br
style='mso-ignore:vglayout' clear=ALL>
<b>Figure 4:</b> The SCP will start BeepService
when you click the <b>Start </b>button. </p>
<p class=BodyText> </p>
<br clear=all>
<p class=BodyText> After
the service has started, the Status column in the SCP will show Started. Test the service's capability to
pause by clicking the Pause button. Restart the service by clicking the Continue
button. Stop the
service and restart it. You will notice that the service responds to the
service control requests automatically. It's impossible to overstate the
benefits of writing services with C++Builder. If you've ever had to write a
service using the API, you can appreciate all the work that the VCL is doing in
the background. </p>
<p class=BodyText> </p>
<p class=BodyText> Most of
the time, it's convenient to simply use the SCP to start, stop, pause,
continue, or configure your service. One annoyance of the SCP is that it has no
associated taskbar button. This means that you can't easily find the SCP when
it gets lost behind other windows. Further, the SCP has no capability to
refresh its display. If the SCP is already running and you double-click its
icon in the Control Panel, NT simply brings the SCP to the top - it won't
update its display. Say, for example, that you have the SCP open and you
install your service from a command prompt box. If you switch back to the SCP,
your service won't show up in the list of installed services. You have to close
the SCP and then reopen it by double-clicking the services icon in the Control
Panel. If for some reason your service doesn't show up when installed, or
doesn't disappear when uninstalled, remember to close the SCP and reopen it. </p>
<p class=BodyText> </p>
<p class=Subheads>Using a
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -