Windows2000 サービスプログラミング


[1]今回のサービスプログラミング環境
(1)Microsoft VisualC++6.0
・Win32 コンソールアプリケーションベース(?)
・簡単に作成するためにサンプルコードを改造する。
・システムサービス等々の詳細は「Win32システムサービスプログラミング改訂版」。
[2]コード概要
※サービスはシステムに管理(SCM:ServiceControlManager)してもらうためコーディングは定められた手順を守る必要がある。
(1)メイン関数(mainまたはWinMain関数)
・通常のメイン関数(main())と同じここから開始される。(WinMain()の場合もある)
・サービステーブルエントリ(SERVICE_TABLE_ENTRY)へ実装するサービス(サービス自体のメイン関数)を登録。
・SCMへサービステーブルエントリを渡す(StartServiceCtrlDispatcher関数)。

(2)サービスメイン関数
※この関数内ではサービススレッドを実際に開始/停止するために手順に従いSCMへの通知を怠らず記述する必要がある。
・実装するサービス自体のメイン関数(関数名はサービステーブルエントリに登録したもの)。
・サービスメイン関数内での処理毎にSendStatusToSCM関数で進捗状況をSCMに通知する。
・サービスメイン関数が呼ばれたら即座に実装するサービスのコントロール関数をSCM登録(RegisterServiceCtrlHandler)しなければならない。
・サービスメイン関数では、SCMから停止要求が発行されるまで制御を返さないようイベントを作成する。
・サービスメイン関数では通常のmain関数のようにパラメータを渡すことができるので必要があればチェックする。
・実際にサービススレッドを開始するまでに初期化処理を実施しておく。
・サービススレッド開始関数をコールする。(別に関数を作る必要はない?)。
・待機関数(WaitForSingleObject)を呼び出し、terminateEventイベントオブジェクトがシグナル状態にセットされるまでCPU時間を消費せず効率良く待機する。
・停止関数により終了イベントオブジェクトがセットされると終了関数(terminate関数)を呼び出してサービスを終了する。

(3)終了関数
・サービス開始失敗、サービス停止の場合にサービスメイン関数からコールする。
・終了関数により全てのハンドルをクリーンアップし、サービス停止をSCMへ通知する。

(4)サービスコントロール関数(ServiceCtrlHandler関数)
・サービスの一時停止、再開、停止、状態問い合わせなどを行う場合、SCMからコールされる。
・各コントロールパラメータを受け取りそれぞれの動作を実施する。
(ex.)
・SERVICE_CONTROL_STOP:サービスの停止要求。
・SERVICE_CONTROL_PAUSE:サービスの一時停止要求。
・SERVICE_CONTROL_CONTINUE:サービスの再開要求。
・SERVICE_CONTROL_INTERROGATE:サービスの状態要求。
・SERVICE_CONTROL_SHUTDOWN:システム自体がシャットダウンすることをサービスへ通知。

(5)サービス状態通知関数
・サービスの現在の状態をSCMへ通知する。
SERVICE_STATUS構造体に値をセットしサービス状態登録関数(SetServiceStatus関数)に渡す。
※SERVICE_STATUS構造体のメンバと設定できる値は別途機会があれば記述する予定。

(6)サービススレッド関数
・サービスの実体を実装する。
・完全に独立して動作する。

(7)ハンドラ関数(一時停止/再開/停止要求を実装する)
・SCMからの要求により、サービスコントロール関数を呼び出すよう実装する。
・一時停止/再開要求の場合、それぞれSuspendThreadResumeThread関数を使用する。


とりあえず参考までにコード表示。
MailProxyService for Win2Kのコードです。
2つのサービスをマルチスレッドで実装しているのでちょっと混沌としてるけど。。。
// Mail Proxy Service
#include 
#include 
#include 
#include 
#include 
#include 

// 定数定義
#define FILE_NO_MAX		20
#define POP_DATA_FILE	"c:\\usr\\bin\\maildata_p."
#define SMTP_DATA_FILE	"c:\\usr\\bin\\maildata_s."
#define SV_IP_ADDR			"172.16.39.92"
#define POP_PX_PORT (u_short) 60110
#define POP_PORT	(u_short)	110
#define SMTP_PX_PORT (u_short) 60025
#define SMTP_PORT	(u_short)	 25
#define MAX_BUFF_SIZE	640
#define MAX_DATA_SIZE	65536

// スレッドパラメータ
typedef struct _ThreadPrm{
	SOCKADDR_IN	serverSockAddr;
	SOCKET		serverSocket;
	SOCKET	clientSocket;
}ThreadPrm;

// グローバル変数。
// ファイル番号
unsigned int	sfile_no = 0;
unsigned int	pfile_no = 0;

// サービスの名前。
char *MAILPROXY_S = "MailProxy_S";
char *MAILPROXY_P = "MailProxy_P";

// 中継先メールサーバアドレス
//// メモリリード衝突回避のため2つ
char sv_ip_addr_s[MAX_BUFF_SIZE];
char sv_ip_addr_p[MAX_BUFF_SIZE];

// ServiceMainが完了するのを防止するために使用するイベント。
HANDLE MailProxyS_terminateEvent = NULL;
HANDLE MailProxyP_terminateEvent = NULL;

// 状態情報をSCMとの間で通知し合うために使用するハンドル。
// RegisterServiceCtrlHandler関数によって作成される。
SERVICE_STATUS_HANDLE serviceStatusHandle;

// サービスの現在の状態を格納するフラグ。
BOOL pauseService = FALSE;
BOOL runningService = FALSE;


// 実際の処理を行うためのスレッド。
HANDLE MailProxyS_threadHandle = 0;
HANDLE MailProxyP_threadHandle = 0;

//関数プロトタイプ
int sock_addr_ini(struct sockaddr_in *,unsigned long,unsigned short);
int socket_close( SOCKET );
unsigned long send_loop(SOCKET,void *,unsigned int);
int recv_with_term(SOCKET ,unsigned char ,void *);
int send_with_term(SOCKET ,unsigned char ,void *);
VOID talkToClient_P(VOID *);
VOID talkToClient_S(VOID *);

VOID talkToClient_P(VOID *prm)
{
	char sv_buff[MAX_BUFF_SIZE],cl_buff[MAX_BUFF_SIZE];
	char data_buff[MAX_DATA_SIZE];
	char mail_data_file[MAX_BUFF_SIZE];
	int status;
	int wpoint;
	ThreadPrm threadprm;
	SOCKADDR_IN serverSockAddr;
	SOCKET serverSocket;
	SOCKET clientSocket;
	FILE *fp;

	memcpy((void *)&threadprm,prm,sizeof(ThreadPrm));
	serverSockAddr = threadprm.serverSockAddr;
	serverSocket = threadprm.serverSocket;
	clientSocket = threadprm.clientSocket;


	memset(sv_buff,0x00,MAX_BUFF_SIZE);
	status = recv_with_term
			(
				serverSocket,
				'\n',
				(void *)sv_buff
			);
	status = send_with_term
			(
				clientSocket,
				'\n',
				(void *)sv_buff
			);

	while(1){
		memset(cl_buff,0x00,MAX_BUFF_SIZE);
		status = recv_with_term
				(
					clientSocket,
					'\n',
					(void *)cl_buff
				);
		if(!strncmp(cl_buff,"LIST",4)){
			status = send_with_term
					(
						serverSocket,
						'\n',
						(void *)cl_buff
					);
				//printf("=>[%s]\n",data_buff);

			memset(data_buff,0x00,MAX_DATA_SIZE);
			wpoint = 0;
			while(1){
				status = recv
						(
							serverSocket,
							&data_buff[wpoint],
							1,
							0
						);
				//printf("%c",data_buff[wpoint]);
				if((wpoint > 3)
					&&(data_buff[wpoint-3] == 0x0a
					&& data_buff[wpoint-2] == '.'
					&& data_buff[wpoint-1] == 0x0d
					&& data_buff[wpoint] == 0x0a)){
						break;
				}
				wpoint++;
			}
				status = send_loop
				(
					clientSocket,
					(void *)data_buff,
					strlen(data_buff)
				);
		}else if(!strncmp(cl_buff,"RETR",4)){
			status = send_with_term
				(
					serverSocket,
					'\n',
					(void *)cl_buff
				);

			memset(sv_buff,0x00,MAX_BUFF_SIZE);
			status = recv_with_term
					(
						serverSocket,
						'\n',
						(void *)sv_buff
					);
			status = send_with_term
					(
						clientSocket,
						'\n',
						(void *)sv_buff
					);

			memset(data_buff,0x00,MAX_DATA_SIZE);
			wpoint = 0;
			while(1){
				status = recv
						(
							serverSocket,
							&data_buff[wpoint],
							1,
							0
						);
				//printf("%c",data_buff[wpoint]);
				if((wpoint > 3)
					&&(data_buff[wpoint-3] == 0x0a
					&& data_buff[wpoint-2] == '.'
					&& data_buff[wpoint-1] == 0x0d
					&& data_buff[wpoint] == 0x0a)){
						break;
				}
				wpoint++;
			}
			status = send_loop
				(
					clientSocket,
					(void *)data_buff,
					strlen(data_buff)
				);
			memset(mail_data_file,0x00,MAX_BUFF_SIZE);
			sprintf(mail_data_file,"%s%04d",POP_DATA_FILE,pfile_no);
			pfile_no++;
			if(pfile_no>FILE_NO_MAX){
				pfile_no=0;
			}
			if(NULL==(fp = fopen(mail_data_file,"w"))){
				printf("can't open pw file\n");
			}else{
				fprintf(fp,"%s\n",data_buff);
				fclose(fp);
			}
		}else{
			status = send_with_term
			(
				serverSocket,
				'\n',
				(void *)cl_buff
			);
			//printf("cl -> sv ===>[%s]\n",cl_buff);
			memset(sv_buff,0x00,MAX_BUFF_SIZE);
			status = recv_with_term
			(
				serverSocket,
				'\n',
				(void *)sv_buff
			);
			//printf("===>[%s]\n",sv_buff);
			status = send_with_term
			(
				clientSocket,
				'\n',
				(void *)sv_buff
			);
			//printf("sv -> cl ===>[%s]\n",sv_buff);
			if(!strncmp(cl_buff,"QUIT",4)){
				wpoint = 0;
				socket_close( serverSocket );
				socket_close( clientSocket );
				_endthread();
			}
		}
	}
}

VOID talkToClient_S(VOID *cs)
{
	char sv_buff[MAX_BUFF_SIZE],cl_buff[MAX_BUFF_SIZE];
	char data_buff[MAX_DATA_SIZE];
	char smtp_data_file[MAX_BUFF_SIZE];
	int status;
	int wpoint;
	SOCKADDR_IN serverSockAddr;
	SOCKET serverSocket;
	SOCKET clientSocket = (SOCKET) cs;
	FILE *fp;

	//MailServerとの接続
	memset(&serverSockAddr,0x00,sizeof(struct sockaddr_in));
	serverSocket = socket( AF_INET,SOCK_STREAM,0 );
	if (serverSocket == INVALID_SOCKET)
	{
		cerr << "エラー: socketの実行が失敗しました。" << endl;
		status = WSACleanup();
		if (status == SOCKET_ERROR){
			cerr << "エラー: WSACleanupの実行が失敗しました。"<< endl;
			_endthread();
		}
	}

	sock_addr_ini
	(
		(struct sockaddr_in *)&serverSockAddr,
		inet_addr(sv_ip_addr_s),
		htons(SMTP_PORT)
	);
	status = connect
	      	(
	        	serverSocket,
	        	(struct sockaddr *)&serverSockAddr,
	        	sizeof(serverSockAddr)
	        );
	if(status != 0){
		cout << "接続できませんでした。" << endl;
		socket_close(serverSocket);
		socket_close(clientSocket);
		_endthread();
	}

	memset(sv_buff,0x00,MAX_BUFF_SIZE);
	status = recv_with_term
			(
				serverSocket,
				'\n',
				(void *)sv_buff
			);
	status = send_with_term
			(
				clientSocket,
				'\n',
				(void *)sv_buff
			);

	while(1){
		memset(cl_buff,0x00,MAX_BUFF_SIZE);
		status = recv_with_term
				(
					clientSocket,
					'\n',
					(void *)cl_buff
				);
		status = send_with_term
				(
					serverSocket,
					'\n',
					(void *)cl_buff
				);
			//printf("==>[%s]\n",data_buff);

		memset(sv_buff,0x00,MAX_BUFF_SIZE);
		status = recv_with_term
				(
					serverSocket,
					'\n',
					(void *)sv_buff
				);
		status = send_with_term
				(
					clientSocket,
					'\n',
					(void *)sv_buff
				);
			//printf("<==[%s]\n",temp_buff);

		if(!strncmp(cl_buff,"QUIT",4)){
			socket_close(serverSocket);
			socket_close(clientSocket);
			_endthread();
		}
		memset(data_buff,0x00,MAX_DATA_SIZE);
		if(!strncmp(cl_buff,"DATA",4)){
			//データ送信
			wpoint=0;
			memset(data_buff,0x00,MAX_DATA_SIZE);
			while(1){
				status = recv
						(
							clientSocket,
							&data_buff[wpoint],
							1,
							0
						);
				//printf("%c",data_buff[wpoint]);
				if((wpoint > 3)
					&&(data_buff[wpoint-3] == 0x0a
					&& data_buff[wpoint-2] == '.'
					&& data_buff[wpoint-1] == 0x0d
					&& data_buff[wpoint] == 0x0a)){
						break;
				}
				wpoint++;
			}
			sprintf(smtp_data_file,"%s%04d",SMTP_DATA_FILE,sfile_no);
			sfile_no++;
			if(sfile_no>FILE_NO_MAX){
				sfile_no=0;
			}
			if(NULL==(fp = fopen(smtp_data_file,"w"))){
				printf("can't open sw file\n");
			}else{
				fprintf(fp,"%s\n",data_buff);
				fclose(fp);
			}
			status = send_loop
					(
						serverSocket,
						data_buff,
						strlen(data_buff)
					);
			memset(sv_buff,0x00,MAX_BUFF_SIZE);
			status = recv_with_term
					(
						serverSocket,
						'\n',
						(void *)sv_buff
					);
					// check status = 354
			status = send_with_term
					(
						clientSocket,
						'\n',
						(void *)sv_buff
					);
			memset(smtp_data_file,0x00,MAX_DATA_SIZE);
			wpoint = 0;
		}
	}
}


// ErrorHandler
void ErrorHandler(char *s, DWORD err)
{
   cout << s << endl;
   cout << "エラー番号: " << err << endl;
   ExitProcess(err);
}

DWORD MailProxyS_Thread(LPDWORD param)
{
//	WSADATA Data;
	SOCKADDR_IN proxySockAddr;
	SOCKET proxySocket;
	SOCKET clientSocket;
	int addrLen = sizeof(struct sockaddr);
	int status;
	DWORD threadID;

	// ソケットを作成する。
	proxySocket = socket(AF_INET, SOCK_STREAM, 0);
	if (proxySocket == INVALID_SOCKET)
	{
		cerr << "エラー: socketの実行が失敗しました。"<< endl;
		status = WSACleanup();
		if (status == SOCKET_ERROR){
			cerr << "エラー: WSACleanupの実行が失敗しました。"<< endl;
			return(1);
		}
	}

	sock_addr_ini
	(
		(struct sockaddr_in *)&proxySockAddr,
		0,
		htons(SMTP_PX_PORT)
	);


	// ソケットをアドレスと関連付ける。
	status = bind(
					proxySocket,
					(LPSOCKADDR) &proxySockAddr,
					sizeof(proxySockAddr)
				);

	if (status == SOCKET_ERROR)
		cerr << "エラー: bindの実行が失敗しました。" << endl;

	// ソケットで接続を受け付けることを可能にする。
	status = listen(proxySocket, 3);
	if (status == SOCKET_ERROR)
		cerr << "エラー: listenの実行が失敗しました。" << endl;

	while(1)
	{
		// 接続要求を受け取ったら,その要求を受け付ける。
		clientSocket = accept(
							proxySocket,
							//(LPSOCKADDR) &proxySocket,
							(struct sockaddr *)&proxySockAddr,
						    &addrLen
						);
		if (clientSocket == INVALID_SOCKET)
		{
			cerr << "エラー: 接続を受け付けることができません。"<< endl;
			return(1);
		}
		threadID = _beginthread
					(
						talkToClient_S,
						0,
						(VOID *) clientSocket
					);
		if (threadID == -1){
			cerr << "エラー: スレッドを作成できません。"<< endl;
			// ソケットを閉じる。
			socket_close(clientSocket);
		}
	} // whileループ
	return 0;
}

DWORD MailProxyP_Thread(LPDWORD param)
{
//	WSADATA Data;
	SOCKADDR_IN serverSockAddr;
	SOCKET serverSocket;
	SOCKADDR_IN proxySockAddr;
	SOCKET proxySocket;
	SOCKET clientSocket;
	int addrLen = sizeof(struct sockaddr);
	int status;
	DWORD threadID;
	ThreadPrm	threadPrm;

   // ソケットを作成する。
   proxySocket = socket(AF_INET, SOCK_STREAM, 0);
   if (proxySocket == INVALID_SOCKET)
   {
      cerr << "エラー: socketの実行が失敗しました。"<< endl;
      status = WSACleanup();
      if (status == SOCKET_ERROR)
         cerr << "エラー: WSACleanupの実行が失敗しました。"<< endl;
      return(1);
   }
	sock_addr_ini
	(
		(struct sockaddr_in *)&proxySockAddr,
		0,
		htons(POP_PX_PORT)
	);

   // ソケットをアドレスと関連付ける。
   status = bind(proxySocket,
      (LPSOCKADDR) &proxySockAddr,
      sizeof(proxySockAddr));

   if (status == SOCKET_ERROR)
      cerr << "エラー: bindの実行が失敗しました。" << endl;

   // ソケットで接続を受け付けることを可能にする。
   status = listen(proxySocket, 3);
   if (status == SOCKET_ERROR)
      cerr << "エラー: listenの実行が失敗しました。" << endl;

   while(1)
   {
      // 接続要求を受け取ったら,その要求を
      // 受け付ける。
      clientSocket = accept(proxySocket,
//         (LPSOCKADDR) &proxySocket,
         (struct sockaddr *)&proxySockAddr,
         &addrLen);
      if (clientSocket == INVALID_SOCKET)
      {
         cerr << "エラー: 接続を受け付けることができません。"<< endl;
         return(1);
      }


		//MailServerとの接続
		memset(&serverSockAddr,0x00,sizeof(struct sockaddr_in));
		serverSocket = socket( AF_INET,SOCK_STREAM,0 );
		if (serverSocket == INVALID_SOCKET)
		{
			cerr << "エラー: socketの実行が失敗しました。" << endl;
			status = WSACleanup();
			if (status == SOCKET_ERROR){
				cerr << "エラー: WSACleanupの実行が失敗しました。"<< endl;
				break;
			}
		}
		sock_addr_ini
		(
			(struct sockaddr_in *)&serverSockAddr,
			inet_addr(sv_ip_addr_p),
			htons(POP_PORT)
		);
		status = connect
		      	(
			       	serverSocket,
			    	(struct sockaddr *)&serverSockAddr,
					sizeof(serverSockAddr)
				);
		if(status != 0){
			cout << "接続できませんでした。" << endl;
			socket_close(serverSocket);
			socket_close(clientSocket);
			break;
		}

		threadPrm.serverSockAddr = serverSockAddr;
		threadPrm.serverSocket = serverSocket;
		threadPrm.clientSocket = clientSocket;

		threadID = _beginthread
					(
						talkToClient_P,
						0,
						(VOID *)&threadPrm
					);
		if (threadID == -1){
			cerr << "エラー: スレッドを作成できません。"<< endl;
			// ソケットを閉じる。
			socket_close(clientSocket);
		}

	} // whileループ
   return 0;
}



// サービスを初期化するためにそのスレッドを開始する。
BOOL InitService()
{
   DWORD id;

   // サービスのスレッドを開始する。
   MailProxyS_threadHandle = CreateThread(0, 0,
      (LPTHREAD_START_ROUTINE) MailProxyS_Thread,
      0, 0, &id);
   MailProxyP_threadHandle = CreateThread(0, 0,
      (LPTHREAD_START_ROUTINE) MailProxyP_Thread,
      0, 0, &id);

   if (MailProxyS_threadHandle==0 || MailProxyP_threadHandle == 0)
      return FALSE;
   else
   {
      runningService = TRUE;
      return TRUE;
   }
}


// 一時停止されたサービスを再開する。
VOID ResumeService()
{
   pauseService=FALSE;
   ResumeThread(MailProxyS_threadHandle);
   ResumeThread(MailProxyP_threadHandle);
}

// サービスを一時停止する。
VOID PauseService()
{
   pauseService = TRUE;
   SuspendThread(MailProxyS_threadHandle);
   SuspendThread(MailProxyP_threadHandle);
}

// ServiceMain関数を完了させることによって、サービスを
// 停止する。
VOID StopService()
{
   runningService=FALSE;
   // ServiceMain関数が制御を返すことができるようにするために
   // ServiceMain関数が完了するのを防止しているイベントをセットする。
   SetEvent(MailProxyS_terminateEvent);
   SetEvent(MailProxyP_terminateEvent);
}

// この関数は、SetServiceStatus関数によって
// サービスの状態を更新する処理をまとめて
// 実行する。
BOOL SendStatusToSCM (DWORD dwCurrentState,
   DWORD dwWin32ExitCode,
   DWORD dwServiceSpecificExitCode,
   DWORD dwCheckPoint,
   DWORD dwWaitHint)
{
   BOOL success;
   SERVICE_STATUS serviceStatus;

   // SERVICE_STATUS構造体のすべてのメンバに値を設定する。
   // サービススレッドが複数の場合はSERVICE_WIN32_SHARE_PROCESSを使用する!!!
   //serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
   serviceStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;//←複数のサービススレッドを実装する場合
   serviceStatus.dwCurrentState = dwCurrentState;

   // 何らかの処理を行っている場合は、コントロールイベントを受け
   // 取らない。それ以外の場合は、コントロールイベントを受け取る。
   if (dwCurrentState == SERVICE_START_PENDING)
      serviceStatus.dwControlsAccepted = 0;
   else
      serviceStatus.dwControlsAccepted =
         SERVICE_ACCEPT_STOP |
         SERVICE_ACCEPT_PAUSE_CONTINUE |
         SERVICE_ACCEPT_SHUTDOWN;

   // 特定の終了コードが定義されている場合は、
   // win32の終了コードを正しく設定する。
   if (dwServiceSpecificExitCode == 0)
      serviceStatus.dwWin32ExitCode =
         dwWin32ExitCode;
   else
      serviceStatus.dwWin32ExitCode =
         ERROR_SERVICE_SPECIFIC_ERROR;
   serviceStatus.dwServiceSpecificExitCode =
      dwServiceSpecificExitCode;

   serviceStatus.dwCheckPoint = dwCheckPoint;
   serviceStatus.dwWaitHint = dwWaitHint;

   // 状態レコードをSCMに渡す。
   success = SetServiceStatus (serviceStatusHandle,
      &serviceStatus);
   if (!success)
      StopService();

   return success;
}

// サービスコントロールマネージャから受け取ったイベントを
// ディスパッチする。
VOID MailProxyS_CtrlHandler (DWORD controlCode)
{
   DWORD currentState = 0;
   BOOL success;

   switch(controlCode)
   {
      // 開始時にはServiceMain関数が呼び出されるので
      // START(開始)オプションはない。

      // サービスを停止する。
      case SERVICE_CONTROL_STOP:
         currentState = SERVICE_STOP_PENDING;
         // 現在の状態をSCMに通知する。
         success = SendStatusToSCM(
            SERVICE_STOP_PENDING,
            NO_ERROR, 0, 1, 5000);
         // 成功しなかった場合、特に何も行わない。

         // サービスを停止する。
         StopService();
         return;

      // サービスを一時停止する。
      case SERVICE_CONTROL_PAUSE:
         if (runningService && !pauseService)
         {
            // 現在の状態をSCMに通知する。
            success = SendStatusToSCM(
               SERVICE_PAUSE_PENDING,
               NO_ERROR, 0, 1, 1000);
            PauseService();
            currentState = SERVICE_PAUSED;
         }
         break;

      // 一時停止から再開する。
      case SERVICE_CONTROL_CONTINUE:
         if (runningService && pauseService)
         {
            // 現在の状態をSCMに通知する。
            success = SendStatusToSCM(
               SERVICE_CONTINUE_PENDING,
               NO_ERROR, 0, 1, 1000);
            ResumeService();
            currentState = SERVICE_RUNNING;
         }
         break;

      // 現在の状態を更新する。
      case SERVICE_CONTROL_INTERROGATE:
         // このswitch文の後の行に進み、状態を送信する。
         break;

      // シャットダウン時には何もしない。ここでクリーンアップを
      // 行うことができるが、非常にすばやく行わなければならない。
      case SERVICE_CONTROL_SHUTDOWN:
         // シャットダウン時には何もしない。
         return;
      default:
         break;
   }
   SendStatusToSCM(currentState, NO_ERROR,
      0, 0, 0);
}

// サービスコントロールマネージャから受け取ったイベントを
// ディスパッチする。
VOID MailProxyP_CtrlHandler (DWORD controlCode)
{
   DWORD currentState = 0;
   BOOL success;

   switch(controlCode)
   {
      // 開始時にはServiceMain関数が呼び出されるので
      // START(開始)オプションはない。

      // サービスを停止する。
      case SERVICE_CONTROL_STOP:
         currentState = SERVICE_STOP_PENDING;
         // 現在の状態をSCMに通知する。
         success = SendStatusToSCM(
            SERVICE_STOP_PENDING,
            NO_ERROR, 0, 1, 5000);
         // 成功しなかった場合、特に何も行わない。

         // サービスを停止する。
         StopService();
         return;

      // サービスを一時停止する。
      case SERVICE_CONTROL_PAUSE:
         if (runningService && !pauseService)
         {
            // 現在の状態をSCMに通知する。
            success = SendStatusToSCM(
               SERVICE_PAUSE_PENDING,
               NO_ERROR, 0, 1, 1000);
            PauseService();
            currentState = SERVICE_PAUSED;
         }
         break;

      // 一時停止から再開する。
      case SERVICE_CONTROL_CONTINUE:
         if (runningService && pauseService)
         {
            // 現在の状態をSCMに通知する。
            success = SendStatusToSCM(
               SERVICE_CONTINUE_PENDING,
               NO_ERROR, 0, 1, 1000);
            ResumeService();
            currentState = SERVICE_RUNNING;
         }
         break;

      // 現在の状態を更新する。
      case SERVICE_CONTROL_INTERROGATE:
         // このswitch文の後の行に進み、状態を送信する。
         break;

      // シャットダウン時には何もしない。ここでクリーンアップを
      // 行うことができるが、非常にすばやく行わなければならない。
      case SERVICE_CONTROL_SHUTDOWN:
         // シャットダウン時には何もしない。
         return;
      default:
         break;
   }
   SendStatusToSCM(currentState, NO_ERROR,
      0, 0, 0);
}


// ServiceMain関数でエラーが発生した場合の処理として、クリーンアップを
// 行い、サービスが開始しなかったことをSCMに通知する。
VOID terminate(DWORD error)
{
   // terminateEventハンドルが作成されている場合は、それを閉じる。
   if (MailProxyS_terminateEvent)
      CloseHandle(MailProxyS_terminateEvent);
   if (MailProxyP_terminateEvent)
      CloseHandle(MailProxyP_terminateEvent);

   // サービスが停止したことを通知するために、
   // SCMにメッセージを送信する。
   if (serviceStatusHandle)
      SendStatusToSCM(SERVICE_STOPPED, error,
         0, 0, 0);

   // スレッドが開始されている場合は、それを終了する。
   if (MailProxyS_threadHandle)
      CloseHandle(MailProxyS_threadHandle);
   if (MailProxyP_threadHandle)
      CloseHandle(MailProxyP_threadHandle);

   // serviceStatusHandleハンドルは閉じる必要がない。
}

// ServiceMain関数は、SCMがサービスを開始するときに
// 呼び出される。ServiceMain関数が制御を返すと、サービスは
// 停止する。したがって、関数の実行が終了する直前まで、
// ServiceMain関数はイベントを待機している状態にしておき、
// 停止するときになったら、そのイベントをシグナル状態にセットする。
// また、エラーが発生した場合にはサービスを開始できないので、
// エラーが発生した場合にもServiceMain関数は制御を返す。
VOID mail_proxy_s_main(DWORD argc, LPTSTR *argv)
{
	BOOL success;

	// 即座に登録関数を呼び出す。
	serviceStatusHandle =
		RegisterServiceCtrlHandler(
			MAILPROXY_S,
			(LPHANDLER_FUNCTION)MailProxyS_CtrlHandler);
	if (!serviceStatusHandle)
	{
		terminate(GetLastError());
		return;
	}

	// 進捗状況をSCMに通知する。
	success = SendStatusToSCM(
				SERVICE_START_PENDING,
				NO_ERROR, 0, 1, 5000);
	if (!success)
	{
		terminate(GetLastError());
		return;
	}

	// 終了イベントを作成する。
	MailProxyS_terminateEvent = CreateEvent (0, TRUE, FALSE, 0);
	if (!MailProxyS_terminateEvent)
	{
		terminate(GetLastError());
		return;
	}

	// 進捗状況をSCMに通知する。
	success = SendStatusToSCM(
		SERVICE_START_PENDING,
		NO_ERROR, 0, 2, 1000);
	if (!success)
	{
		terminate(GetLastError());
		return;
	}

	// 開始パラメータの有無をチェックする。
	memset(sv_ip_addr_s,0x00,MAX_BUFF_SIZE);
	memset(sv_ip_addr_p,0x00,MAX_BUFF_SIZE);
	if (argc < 2)
	{
        strcpy(sv_ip_addr_s,SV_IP_ADDR);
        strcpy(sv_ip_addr_p,SV_IP_ADDR);
	}else{
        strcpy(sv_ip_addr_s,argv[1]);
        strcpy(sv_ip_addr_p,argv[1]);
	}

   // 進捗状況をSCMに通知する。
   success = SendStatusToSCM(
      SERVICE_START_PENDING,
      NO_ERROR, 0, 3, 5000);
   if (!success)
   {
      terminate(GetLastError());
      return;
   }

   // サービス自体を開始する。
   success = InitService();
   if (!success)
   {
      terminate(GetLastError());
      return;
   }

   // この時点でサービスは実行状態になっている。
   // 進捗状況をSCMに通知する。
   success = SendStatusToSCM(
      SERVICE_RUNNING,
      NO_ERROR, 0, 0, 0);
   if (!success)
   {
      terminate(GetLastError());
      return;
   }

   // 終了シグナルを待機し、それを検出したら終了する。
   WaitForSingleObject (MailProxyS_terminateEvent, INFINITE);
   terminate(0);
}

VOID mail_proxy_p_main(DWORD argc, LPTSTR *argv)
{
   BOOL success;
   // 即座に登録関数を呼び出す。
   serviceStatusHandle =
      RegisterServiceCtrlHandler(
         MAILPROXY_P,
         (LPHANDLER_FUNCTION)MailProxyP_CtrlHandler);
   if (!serviceStatusHandle)
   {
      terminate(GetLastError());
      return;
   }
   // 進捗状況をSCMに通知する。
   success = SendStatusToSCM(
      SERVICE_START_PENDING,
      NO_ERROR, 0, 1, 5000);
   if (!success)
   {
      terminate(GetLastError());
      return;
   }

   // 終了イベントを作成する。
   MailProxyP_terminateEvent = CreateEvent (0, TRUE, FALSE, 0);
   if (!MailProxyP_terminateEvent)
   {
      terminate(GetLastError());
      return;
   }

   // 進捗状況をSCMに通知する。
   success = SendStatusToSCM(
      SERVICE_START_PENDING,
      NO_ERROR, 0, 2, 1000);
   if (!success)
   {
      terminate(GetLastError());
      return;
   }

	FILE *fpDebug;

	fpDebug = fopen("c:\\usr\\bin\\pDebug.txt","w");

	fprintf(fpDebug,"*** start\n");


	// 開始パラメータの有無をチェックする。
/*	if (argc < 2)
	{
		memset(sv_ip_addr_p,0x00,MAX_BUFF_SIZE);
        strcpy(sv_ip_addr_p,SV_IP_ADDR);
	}else{
        memset(sv_ip_addr_p,0x00,MAX_BUFF_SIZE);
        strcpy(sv_ip_addr_p,argv[1]);
	}
*/



   // 進捗状況をSCMに通知する。
   success = SendStatusToSCM(
      SERVICE_START_PENDING,
      NO_ERROR, 0, 3, 5000);
   if (!success)
   {
      terminate(GetLastError());
      return;
   }

   // サービス自体を開始する。
   success = InitService();
   if (!success)
   {
      terminate(GetLastError());
      return;
   }

   // この時点でサービスは実行状態になっている。
   // 進捗状況をSCMに通知する。
   success = SendStatusToSCM(
      SERVICE_RUNNING,
      NO_ERROR, 0, 0, 0);
   if (!success)
   {
      terminate(GetLastError());
      return;
   }

   // 終了シグナルを待機し、それを検出したら終了する。
   WaitForSingleObject (MailProxyP_terminateEvent, INFINITE);

   terminate(0);
}

/*****************************************************************************/
/****** MAIN *****************************************************************/
/*****************************************************************************/
VOID main(VOID)
{
   SERVICE_TABLE_ENTRY serviceTable[] =
   {
      { MAILPROXY_S, (LPSERVICE_MAIN_FUNCTION) mail_proxy_s_main},
      { MAILPROXY_P, (LPSERVICE_MAIN_FUNCTION) mail_proxy_p_main},
      { NULL, NULL }
   };
	BOOL success;
	WSADATA Data;
	int status;

	// WindowsソケットDLLを初期化する。
	status = WSAStartup(MAKEWORD(2, 0), &Data);
	if (status != 0){
		ErrorHandler
		(
			"エラー: WSAStartupの実行が失敗しました。",
			GetLastError()
		);
	}

   // SCMに登録する。
   success =
      StartServiceCtrlDispatcher(serviceTable);
   if (!success)
      ErrorHandler("StartServiceCtrlDispatcherでエラーが発生",
         GetLastError());
}

/*****************************************************************************/
int sock_addr_ini
(
	struct sockaddr_in	*addr_tmp
	,unsigned long		ip_addr
	,unsigned short		port
)
{
	// sockaddr_in構造体への代入
	// アドレスのポート部分を指定する。
	addr_tmp->sin_port		= port;
	// アドレスファミリをインターネットとして指定する。
	addr_tmp->sin_family	= AF_INET;
	// アドレスを指定する。
	addr_tmp->sin_addr.s_addr	= ip_addr;
	return(0);
}

/*****************************************************************************/
int socket_close( SOCKET socket_num )
{
	int status;	//ステータス
	status = shutdown(socket_num,2);
	if (status == SOCKET_ERROR){
		cerr << "エラー: socketのshutdown実行が失敗しました。"<< endl;
		return(status);
	}
	status = closesocket(socket_num);
	if (status == SOCKET_ERROR){
		cerr << "エラー: closesocketの実行が失敗しました。"<< endl;
	}
	return(status);
}

/*****************************************************************************/
int recv_with_term(SOCKET soc,unsigned char term_char,void *recv_buff){
	int point = 0;
	int retval;

	while(1){
		retval = recv
			(
				soc,
				(char *)recv_buff+point,
				1,
				0
			);
			//printf("%c",*( (unsigned char *)recv_buff+point ));
			if( *( (unsigned char *)recv_buff+point ) == term_char ){
					break;
			}
			point++;
		}

	return(0);
}

/*****************************************************************************/
int send_with_term(SOCKET soc,unsigned char term_char,void *send_buff){
	int point = 0;
	int retval;

	while(1){
		retval = send
			(
				soc,
				(const char *)send_buff+point,
				1,
				0
			);
			//printf("%c",*( (unsigned char *)recv_buff+point ));
			if( *( (unsigned char *)send_buff+point ) == term_char ){
					break;
			}
			point++;
		}

	return(0);
}

/*********************************************************************  ***/
unsigned long send_loop
(
	SOCKET			soc,		// ソケット記述子
	void 			*buff,		// 送信バッファ
	unsigned int	len			// 送信したいバイト数
)
{
	unsigned long	sum = 0;
	int		retval = 0;
	unsigned long	tmp_len;
	unsigned long	MaxBuffSize = 32000;	// MAX:32767

	do
	{
		tmp_len = len - sum;
		if( tmp_len>MaxBuffSize )
		{
			tmp_len = MaxBuffSize;
		}

		retval = send
			(
				soc,
				((char *)buff)+sum,
				tmp_len,
				0
			);

		if( (retval != -1) && (retval > 0) )
		{
			sum += retval;
		}
	} while( (retval != -1) && (retval > 0) && (sum < len) );

	if( sum == len )
	{
		return sum;
	}
	else
	{
		return (-1);
	}
}

[3]サービスのシステムへの登録/削除
●インストーラ
OpenScManager関数
・SCMへの接続を開く。
・システム上にサービスを作成できるようにSCMデータベースを開く。
CreateService関数
・SCMデータベースをサービスを登録。
●アンインストーラ
OpenScManager関数
・SCMへの接続を開く。
・システム上にサービスを作成できるようにSCMデータベースを開く。
OpenService関数
・サービスへの接続を開く。
QueryServiceStatus関数
・SCMからのサービスの状態情報(SERVICE_STATUS構造体)を返す。
ControlService関数
・サービスのコントロールを実装します。
・アンインスト−ラでは停止要求。


またまた参考までにコード表示。
MailProxyService for Win2Kのコードです。
※注>1つのサービスプロセス内に複数のサービススレッドを実装したサービスのインストールの場合、
CreateService関数へのパラメータSERVICE_WIN32_OWN_PROCESS
SERVICE_WIN32_SHARE_PROCESSに変更する。

※インストール方法
≫複数サービスを実装している
≫インストーラプログラム名:install_mailproxy.exe
≫サービスプログラムPG内SCMへの登録サービス名:MailProxy_S、MailProxy_P
≫システム上表示サービス名:SMTP Proxy"、"POP3 Proxy"
≫サービス実行ファイル名:mailproxy_service.exe
(path)\install_mailproxy.exe MailProxy_S "SMTP Proxy" (path)\mailproxy_service.exe
(path)\install_mailproxy.exe MailProxy_P "POP3 Proxy" (path)\mailproxy_service.exe
≫サービス実行ファイル名は同じで順番にSCMへの登録名を使用しインストールしていく。

===インストーラ===
// install.cpp

#include 
#include 

void ErrorHandler(char *s, DWORD err)
{
   cout << s << endl;
   cout << "エラー番号: " << err << endl;
   ExitProcess(err);
}

void main(int argc, char *argv[])
{
   SC_HANDLE newService, scm;

   if (argc != 4)
   {
      cout << "使用法:\n";
      cout << "  install  \
 \n";
      cout << "        は、\
SCMによって内部的に使用されるサービスの名前です。\n";
      cout << "        は、\
[サービス]ウィンドウの一覧に表示されるサービスの名前です。\n";
      cout << "            (名前の中に半角のスペースを含める場合は、\
名前全体をダブルクォーテーションで囲みます)\n";
      cout << "        は、\
EXEファイルへのフルパスです。\n";
      cout << endl;
      return;
   }

   cout << "インストールを開始します...\n";
   // SCMへの接続を開く。
   scm = OpenSCManager(0, 0,
      SC_MANAGER_CREATE_SERVICE);
   if (!scm)
      ErrorHandler("OpenScManagerでエラーが発生",
         GetLastError());

   // 新しいサービスをインストールする。
   newService = CreateService(
      scm, argv[1], // たとえば、"beep_srv"。
      argv[2], // たとえば、"Beep Service"。
      SERVICE_ALL_ACCESS,
//      SERVICE_WIN32_OWN_PROCESS,
      SERVICE_WIN32_SHARE_PROCESS,	//←複数のサービススレッドを実装する場合
      SERVICE_DEMAND_START,
      SERVICE_ERROR_NORMAL,
      argv[3], // たとえば、"c:\winnt\xxx.exe"。
      0, 0, 0, 0, 0);
   if (!newService)
      ErrorHandler("CreateServiceでエラーが発生",
         GetLastError());
   else
      cout << "サービスはインストールされました。\n";

   // クリーンアップ。
   CloseServiceHandle(newService);
   CloseServiceHandle(scm);
   cout << "インストールを終了します...\n";
}


===アンインストーラ===
// remove.cpp

#include 
#include 

void ErrorHandler(char *s, DWORD err)
{
   cout << s << endl;
   cout << "エラー番号: " << err << endl;
   ExitProcess(err);
}

void main(int argc, char *argv[])
{
   SC_HANDLE service, scm;
   BOOL success;
   SERVICE_STATUS status;

   if (argc != 2)
   {
      cout << "使用法:\n";
      cout << "  remove \n";
      return;
   }

   cout << "削除を開始します...\n";
   // SCMへの接続を開く。
   scm = OpenSCManager(0, 0,
      SC_MANAGER_CREATE_SERVICE);
   if (!scm)
      ErrorHandler("OpenScManagerでエラーが発生",
         GetLastError());
   // サービスのハンドルを取得する。
   service = OpenService(
      scm, argv[1],
      SERVICE_ALL_ACCESS | DELETE);
   if (!service)
      ErrorHandler("OpenServiceでエラーが発生",
         GetLastError());
   // 必要ならサービスを停止する。
   success = QueryServiceStatus(service, &status);
   if (!success)
      ErrorHandler("QueryServiceStatusでエラーが発生",
         GetLastError());
   if (status.dwCurrentState != SERVICE_STOPPED)
   {
      cout << "サービスを停止します...\n";
      success = ControlService(service,
         SERVICE_CONTROL_STOP,
         &status);
      if (!success)
         ErrorHandler("ControlServiceでエラーが発生",
            GetLastError());
      Sleep(500);
   }

   // サービスを削除する。
   success = DeleteService(service);
   if (success)
      cout << "サービスを削除しました。\n";
   else
      ErrorHandler("DeleteServiceでエラーが発生",
         GetLastError());

   // クリーンアップ。
   CloseServiceHandle(service);
   CloseServiceHandle(scm);
   cout << "削除を終了します...\n";
}