サービス アプリケーション
アプリケーションの作成 への移動
サービスアプリケーションは,クライアントからリクエストを受け,処理し,結果を返します。一般に,サービスアプリケーションはバックグラウンドで実行され,ユーザーの入力を直接受けることはほとんどありません。Web/FTP サーバーや電子メールサーバーなどがサービスアプリケーションの良い例です。
Win32 サービスを実装するアプリケーションを作成するには,次のようにします。
- [ファイル > 新規作成 > その他]を選択し,[新規作成]ダイアログボックスの[サービスアプリケーション]をダブルクリックします。プロジェクトに TServiceApplication 型の Application というグローバル変数が追加されます。
- サービス(TService)に対応するサービスウィンドウが表示されます。オブジェクトインスペクタにプロパティとイベントハンドラを設定して,サービスを実装します。
- [ファイル > 新規作成 > その他]を選択すると,サービスアプリケーションにサービスを追加できます。[新規作成]ダイアログボックスで[サービス]をダブルクリックします。サービスアプリケーションでないアプリケーションにサービスを追加しないでください。サービスアプリケーションでないアプリケーションにも TService オブジェクトを追加することはできますが,アプリケーションはサービスが必要とするイベントを生成しません。また,サービスを呼び出すこともありません。
- サービスアプリケーションを作成したら,サービスコントロールマネージャ(SCM: Service Control Manager)を使ってサービスをインストールします。これで,ほかのアプリケーションから SCM にリクエストを送ってサービスを起動できます。
サービスアプリケーションを作成したら,/INSTALL オプションを付けて実行し,サービスをインストールします。アプリケーションはサービスのインストールを開始し,インストールが完了したかどうかの確認メッセージを表示した後,終了します。この確認メッセージを抑止するには,/SILENT オプションを使ってサービスアプリケーションを実行します。
サービスの登録を解除するには,コントロールパネルから[停止]ボタンでサービスを停止し,サービスアプリケーションを /UNINSTALL オプションを付けて実行します(アンインストール時も,/SILENT オプションを使用すると確認メッセージを抑止できます)。
メモ: このサービスではポート番号 80 を使用する TServerSocket を作成します。このポートは,Web ブラウザと Web サーバーが対話する際の一般的なポート番号です。この例では,WebLogxxx.log(xxx は ThreadID)というファイルが C:\Temp ディレクトリに作られます。任意のポート番号に対して監視するサーバーは 1 つだけなので,すでに Web サーバーを実行している場合には既存の Web サーバーを停止してください。
結果を表示するには,ローカルマシン上でブラウザを開き,アドレスに「localhost」と入力します。しばらくするとブラウザはタイムアウトしますが,C:\Temp ディレクトリに weblogxxx.log が作成されています。
例を作成します。
- [ファイル > 新規作成 > その他]を選択し,[新規作成]ダイアログボックスから[サービスアプリケーション]を選択します。「Service1」という名前のウィンドウが表示されます。
- ツールパレットの[Internet]カテゴリから,ServerSocket コンポーネントをサービスウィンドウ(Service1)に追加します。
- TMemoryStream 型のプライベートデータメンバーを TService1 クラスに追加します。ユニットの interface 部は次のようになります。
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, SvcMgr, Dialogs,
ScktComp;
type
TService1 = class(TService)
ServerSocket1: TServerSocket;
procedure ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
procedure Service1Execute(Sender: TService);
private
{ Private 宣言 }
Stream: TMemoryStream; // ここに,この行を追加する
public
function GetServiceController: PServiceController; override;
{ Public 宣言 }
end;
var
Service1: TService1;
//---------------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <SysUtils.hpp>
#include <Classes.hpp>
#include <SvcMgr.hpp>
#include <ScktComp.hpp>
//---------------------------------------------------------------------------
class TService1 : public TService
{
__published:
TServerSocket *ServerSocket1;
private:
TMemoryStream *Stream; // ここに,この行を追加する
public:
__fastcall TService1(TComponent* Owner);
PServiceController __fastcall GetServiceController(void);
friend void __stdcall ServiceController(unsigned CtrlCode);
};
//---------------------------------------------------------------------------
extern PACKAGE TService1 *Service1;
//---------------------------------------------------------------------------
#endif
- 手順 1 で追加した ServerSocket1 コンポーネントを選択します。オブジェクトインスペクタで OnClientRead イベントをダブルクリックし,次のイベントハンドラを追加します。
procedure TService1.ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
var
Buffer: PChar;
begin
Buffer := nil;
while Socket.ReceiveLength > 0 do begin
Buffer := AllocMem(Socket.ReceiveLength);
try
Socket.ReceiveBuf(Buffer^, Socket.ReceiveLength);
Stream.Write(Buffer^, StrLen(Buffer));
finally
FreeMem(Buffer);
end;
Stream.Seek(0, soFromBeginning);
Stream.SaveToFile('c:\Temp\Weblog' + IntToStr(ServiceThread.ThreadID) + '.log');
end;
end;
void __fastcall TService1::ServerSocket1ClientRead(TObject *Sender,
TCustomWinSocket *Socket)
{
char *Buffer = NULL;
int len = Socket->ReceiveLength();
while (len > 0)
{
try
{
Buffer = (char *)malloc(len);
Socket->ReceiveBuf((void *)Buffer, len);
Stream->Write(Buffer, len);
}
__finally
{
free(Buffer);
}
Stream->Seek(0, soFromBeginning);
AnsiString LogFile = "C:\\Temp\\WebLog";
LogFile = LogFile + IntToStr(ServiceThread->ThreadID) + ".log";
Stream->SaveToFile(LogFile);
}
}
- 最後に,ウィンドウのクライアント領域([ServiceSocket]以外の領域)をクリックして Service1 を選択します。オブジェクトインスペクタで OnExecute イベントをダブルクリックし,次のイベントハンドラを追加します。
procedure TService1.Service1Execute(Sender: TService);
begin
Stream := TMemoryStream.Create;
try
ServerSocket1.Port := 80; // WWW ポート
ServerSocket1.Active := True;
while not Terminated do begin
ServiceThread.ProcessRequests(True);
end;
ServerSocket1.Active := False;
finally
Stream.Free;
end;
end;
void __fastcall TService1::Service1Execute(TService *Sender)
{
Stream = new TMemoryStream();
try
{
ServerSocket1->Port = 80; // WWW ポート
ServerSocket1->Active = true;
while (!Terminated)
ServiceThread->ProcessRequests(true);
ServerSocket1->Active = false;
}
__finally
{
delete Stream;
}
}
メモ: サービスアプリケーションは,クロスプラットフォームアプリケーションでは使用できません。