1. 程式人生 > >用Visual C++ 6.0寫Windows 服務

用Visual C++ 6.0寫Windows 服務

【摘要】
Windows 服務的Visual C++ 6.0寫法。
【全文】

// ServiceTest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <winsvc.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
CWinApp theApp;
using namespace std;
//***********************************************************************************//
//變數宣告
//***********************************************************************************//
//服務內部名
#define APPNAME "DBDataBackup"
//服務顯示名
#define SZSERVICENAME _T("資料庫定時清理服務")
//服務狀態
SERVICE_STATUS ssStatus;
//服務控制代碼
SERVICE_STATUS_HANDLE sshStatusHandle;
//SC控制代碼
SC_HANDLE schSCManager;
SC_HANDLE schService;
LPCTSTR * lpServiceArgVectors;
//服務除錯標誌
BOOL bDebug=FALSE;
//互斥訊號量
CRITICAL_SECTION errCritical;
//服務退出事件
HANDLE hSysExitEvent = NULL;
//***********************************************************************************//
//函式宣告
//服務入口函式
void  WINAPI  Service_Main(DWORD dwArgc, LPTSTR *lpszArgv);
//服務控制處理函式
void WINAPI Service_Ctrl(DWORD dwCtrlCode);
//標準錯誤輸出函式
void StdErrorHandler(CString errorMessage);
//服務安裝處理函式
void installService();
//服務解除安裝處理函式
void removeService();
//服務除錯處理函式
void debugService();
//寫服務日誌
void AddToAppLog(CString errorMessage);
//獲得系統時間
CString GetSystemTime();
//服務啟動處理函式
void ServiceStart();
BOOL ReportStatusToSCMgr(DWORD dwCurrentState,DWORD dwWin32ExitCode,DWORD dwWaitHint);
//服務停止處理函式
void ServiceStop();
//服務暫停處理函式
void ServicePause();
//服務繼續處理函式
void ServiceContinue();

//***********************************************************************************//
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
 //初始化訊號量
 InitializeCriticalSection(&errCritical);
 //初始化MFC
 if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
 {
  cerr << _T("致命錯誤: MFC初始化失敗") << endl;
  return -1;
 }
 else
 {
  //註冊服務入口,服務名、服務入口函式
  //Windows執行服務入口在Service_Main
  SERVICE_TABLE_ENTRY dispatchTable[2]={
   {SZSERVICENAME,Service_Main},    
   { NULL,NULL}};
  //獲得當前可執行檔案目錄和檔名
  char exeFullPath[MAX_PATH];
  GetModuleFileName(NULL,exeFullPath,MAX_PATH);
  for(int i=strlen(exeFullPath); i>=0; i--)
  {
   if(exeFullPath[i]=='//')
   {
    exeFullPath[i]=0;
    break;
   }
  }
  //設定當前程序的當前路徑為服務的路徑
  SetCurrentDirectory(exeFullPath); 
  //處理命令列引數
  if(argc>1&&*argv[1]=='-')
  {
   //引數為-install,安裝服務
   if(_stricmp("install",argv[1]+1)==0)
   {
    //執行安裝服務
    installService();
   }
   //引數為-remove,解除安裝服務
   else if(_stricmp("remove",argv[1]+1)==0)
   {
    //執行解除安裝服務
    removeService();
   }
   //引數為-debug,除錯服務
   else if(_stricmp("debug",argv[1]+1)==0)
   {
    //置服務除錯標誌
    bDebug=TRUE;
    //執行除錯服務
    debugService();
   }
   else
   {
    //引數格式不合法
    printf("輸入 %s - install 來安裝服務 /n",APPNAME);
    printf("輸入 %s - remove 來解除安裝服務 /n",APPNAME);
    printf("輸入 %s - debug 來除錯服務 /n",APPNAME);
   }
   exit(0);
  }
  //如果未能和上面的如何引數匹配,則可能是服務控制管理程式來啟動該程式。
  CString errmessage;
  printf("/n已呼叫StartServiceCtrlDispatcher./n");
  printf("這可能需要幾秒鐘,請等待./n");
  //立即呼叫StartServiceCtrlDispatcher函式。
  if(!StartServiceCtrlDispatcher(dispatchTable))
  {
   StdErrorHandler("呼叫StartServiceCtrlDispatcher失敗.");
   AddToAppLog(errmessage);
  }
  else
  {
   StdErrorHandler("呼叫StartServiceCtrlDispatcher成功.");
   AddToAppLog(errmessage);
  }
 }
 return 0;
}

//******************************************************//
//服務入口函式
//******************************************************//
void  WINAPI  Service_Main(DWORD dwArgc, LPTSTR *lpszArgv)
{
 //註冊服務控制處理函式,服務APPNAME,控制函式Service_Ctrl
 sshStatusHandle=RegisterServiceCtrlHandler(APPNAME,Service_Ctrl);
 //如果註冊失敗
 if(!sshStatusHandle)
 {
  CString err;
  err="服務註冊失敗";
  StdErrorHandler(err);
  goto cleanup;
  return;
 }
 //初始化 SERVICE_STATUS 結構中的成員
 ssStatus.dwServiceType=SERVICE_WIN32_OWN_PROCESS;
 ssStatus.dwServiceSpecificExitCode=0;
 //更新服務狀態為正在啟動SERVICE_START_PENDING
 if(!ReportStatusToSCMgr(
  SERVICE_START_PENDING, //服務狀態,The service is starting.
  NO_ERROR,    //退出碼        
  3000))     //等待時間
 {
  goto cleanup;   //更新服務狀態失敗則轉向 cleanup
 }
 //服務啟動主函式
 ServiceStart();
 return;

cleanup:
 //把服務狀態更新為 SERVICE_STOPPED,並退出。
 if(sshStatusHandle)
 {
  ReportStatusToSCMgr(SERVICE_STOPPED,GetLastError(),0);
 }
}

//******************************************************//
//服務控制處理函式
//******************************************************//
void WINAPI Service_Ctrl(DWORD dwCtrlCode)
{
 //處理控制請求碼
 switch(dwCtrlCode)
 {
 //先更新服務狀態為 SERVICDE_STOP_PENDING,再停止服務。
 case SERVICE_CONTROL_STOP:
  {
   ReportStatusToSCMgr(SERVICE_STOP_PENDING,NO_ERROR,500);
   ServiceStop();     //由具體的服務程式實現
   ssStatus.dwCurrentState=SERVICE_STOPPED;
   break;
  }
 //暫停服務
 case SERVICE_CONTROL_PAUSE:
  {
   ReportStatusToSCMgr(SERVICE_STOP_PENDING,NO_ERROR,500);
   ServicePause();    //由具體的服務程式實現
   ssStatus.dwCurrentState=SERVICE_PAUSED;
   break;
  }
 //繼續服務
 case SERVICE_CONTROL_CONTINUE:
  {
   ReportStatusToSCMgr(SERVICE_STOP_PENDING,NO_ERROR,500);
   ServiceContinue(); //由具體的服務程式實現
   ssStatus.dwCurrentState=SERVICE_RUNNING;
   break;
  }
 //更新服務狀態
 case SERVICE_CONTROL_INTERROGATE:
  {
   break;
  }
 //無效控制碼
 default:
  {
   break;
  }
    }
 //當前服務狀態
 ReportStatusToSCMgr(ssStatus.dwCurrentState,NO_ERROR,0);
}

//******************************************************//
//服務安裝處理函式
//******************************************************//
void installService()
{
 SC_HANDLE   schService;
    SC_HANDLE   schSCManager;
 TCHAR szPath[512];
 CString err;
 //得到程式磁碟檔案的路徑
 if(GetModuleFileName(NULL,szPath,512)==0)
 {
  err.Format("不能安裝 %s - %s /n",TEXT(APPNAME),GetLastError());
  StdErrorHandler(err);
  return;
 }
 //開啟服務管理資料庫
 schSCManager=OpenSCManager(
  NULL,       //本地計算機
  NULL,       //預設的資料庫
  SC_MANAGER_ALL_ACCESS   //要求所有的訪問權
  );
 if(schSCManager)
 {
  //登記服務程式
  schService=CreateService(
   schSCManager,    //服務管理資料庫控制代碼
   TEXT(APPNAME),    //服務名
   TEXT(SZSERVICENAME),  //用於顯示服務的標識
   SERVICE_ALL_ACCESS,   //響應所有的訪問請求
   SERVICE_WIN32_OWN_PROCESS, //服務型別
   SERVICE_AUTO_START,   //啟動型別
   SERVICE_ERROR_NORMAL,  //錯誤控制型別
   szPath,      //服務程式磁碟檔案的路徑
   NULL,      //服務不屬於任何組
   NULL,      //沒有tag識別符號
   NULL,      //啟動服務所依賴的服務或服務組,這裡僅僅是一個空字串
   NULL,      //LocalSystem 帳號
   NULL);
  if(schService)
  {
   err.Format("%s 已安裝.",TEXT(APPNAME));
   StdErrorHandler(err);
   CloseServiceHandle(schService);
  }
  else
  {
   err.Format("建立服務失敗,服務可能已經安裝");
   StdErrorHandler(err);
  }
  CloseServiceHandle(schSCManager);
 }
 else
 {
  err.Format("開啟SCManager失敗");
  StdErrorHandler(err);
 }
 CloseServiceHandle(schSCManager);
 return;
}

//******************************************************//
//服務解除安裝處理函式
//******************************************************//
void removeService()
{
 SC_HANDLE schService;
 SC_HANDLE schSCManager;
 CString err;
 //開啟服務管理資料庫
 schSCManager=OpenSCManager(
  NULL,    //本地計算機
  NULL,    //預設的資料庫
  SC_MANAGER_ALL_ACCESS  //要求所有的訪問權
  );
 if(schSCManager)
 {
  //獲取服務程式控制代碼
  schService=OpenService(
   schSCManager,   //服務管理資料庫控制代碼
   TEXT(APPNAME),   //服務名
   SERVICE_ALL_ACCESS  //響應所有的訪問請求
   );
  if(schService)
  {
   //試圖停止服務
   if(ControlService(
    schService,            //服務程式控制代碼
    SERVICE_CONTROL_STOP,  //停止服務請求碼
    &ssStatus              //接收最後的服務狀態資訊
    ))
   {
    err.Format("停止 %s 中",TEXT(APPNAME));
    StdErrorHandler(err);
    Sleep(1000);
    //等待服務停止
    while(QueryServiceStatus(schService,&ssStatus))
    {
     if(SERVICE_STOP_PENDING==ssStatus.dwCurrentState)
     {
      cout<<".";
      Sleep(1000);
     }
     else
     {
      break;
     }
    }
    if(SERVICE_STOPPED==ssStatus.dwCurrentState)
    {
     err.Format("/n %s 已被停止. /n",TEXT(APPNAME));
     StdErrorHandler(err);
    }
    else
    {
     err.Format("/n %s 試圖停止失敗. /n",TEXT(APPNAME));
     StdErrorHandler(err);
    }
   }
   //刪除已安裝的服務程式安裝
   if(DeleteService(schService))
   {
    err.Format("%s 已經被解除安裝. /n",TEXT(APPNAME));
    StdErrorHandler(err);
   }
   else
   {
    err.Format("刪除服務失敗");
    StdErrorHandler(err);
   }
   CloseServiceHandle(schService);
  }
  else
  {
   err.Format("連線服務失敗,不存在此服務");
   StdErrorHandler(err);
  }
  CloseServiceHandle(schSCManager);
 }
 else
 {
  err.Format("開啟SCManager失敗");
  StdErrorHandler(err);
 }
}

//******************************************************//
//服務啟動處理函式
//******************************************************//
void ServiceStart()
{
 //預設監測週期是3分鐘
 int nWaitTime=3000;
 // 向SCM報告目前的狀態
 ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 20000);

 if(!(hSysExitEvent = CreateEvent(NULL, TRUE, FALSE, NULL))){
  StdErrorHandler("服務退出事件建立失敗");
  return;
 }
 // 向SCM報告當前進度,執行狀態
 ReportStatusToSCMgr(SERVICE_RUNNING, NO_ERROR, 0);
 StdErrorHandler("服務已啟動");
 long int times=20;
 while(times)
 { 
  times--;
  cout<<times<<",";
  // 等待停止標誌
  if(WaitForSingleObject(hSysExitEvent, nWaitTime) == WAIT_OBJECT_0)
  {   
   // 通知SCM服務正在結束
   ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 10000);
   StdErrorHandler("資料庫定時清理服務正在終止...");
   return;
  }
  Beep(1000,200);
  Sleep(nWaitTime);
 }
}
//******************************************************//
//服務停止處理函式
//******************************************************//
void ServiceStop()
{
 SetEvent(hSysExitEvent);
}

//******************************************************//
//服務暫停處理函式
//******************************************************//
void ServicePause()
{}

//******************************************************//
//服務繼續處理函式
//******************************************************//
void ServiceContinue()
{}


//******************************************************//
//服務日誌記錄處理函式
//******************************************************//
void AddToAppLog(CString errorMessage)
{
 
}
//******************************************************//
//服務除錯處理函式
//******************************************************//
void debugService()
{
 ServiceStart();
}

//******************************************************//
//返回當前系統時間
//******************************************************//
CString GetSystemTime()
{
 SYSTEMTIME systemTime;
 CString timeString;
 GetLocalTime(&systemTime);
 timeString.Format("%04d年%02d月%02d日 %02d:%02d:%02d",
  systemTime.wYear, systemTime.wMonth, systemTime.wDay,
  systemTime.wHour, systemTime.wMinute, systemTime.wSecond,
  systemTime.wMilliseconds);
 return timeString;
}

/*******************************************************
名稱:ReportStatusToSCMgr
引數:
 dwCurrentState:當前狀態
 dwWin32ExitCode:win32退出碼
 dwWaitHint: 該狀態可能持續的時間
返回值:BOOL
功能:向Service Control Manager報告狀態
*******************************************************/
BOOL ReportStatusToSCMgr(DWORD dwCurrentState,
       DWORD dwWin32ExitCode,
       DWORD dwWaitHint)
{
 static DWORD dwCheckPoint = 1;
 BOOL fResult = TRUE;
 
 if(!bDebug) {
  // 如果在初始化狀態,不接受任何命令
  if(dwCurrentState == SERVICE_START_PENDING)
   ssStatus.dwControlsAccepted = 0;
  else
   ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;

  ssStatus.dwCurrentState = dwCurrentState;
  ssStatus.dwWin32ExitCode = dwWin32ExitCode;
  ssStatus.dwWaitHint = dwWin32ExitCode;

  // 如果狀態為正在執行或已經停止,則checkpoit為0
  if((dwCurrentState == SERVICE_RUNNING) ||
   (dwCurrentState == SERVICE_STOPPED)){
   ssStatus.dwCheckPoint = 0;
   dwCheckPoint = 1;
  }
  else
   ssStatus.dwCheckPoint = dwCheckPoint++;

  // 向服務管理器報告當前狀態
  if(!(fResult = SetServiceStatus(sshStatusHandle, &ssStatus))) {
//   StdErrorHandler(COMMANDHANDLER, "向SCM報告狀態失敗", SERVICEFLAG);
  }
 }
 return fResult;
}

//******************************************************//
//報告系統錯誤
//******************************************************//
void StdErrorHandler(CString errorMessage)
{
 EnterCriticalSection(&errCritical);
 AddToAppLog(errorMessage);
 printf("%s:%s/n",GetSystemTime(),errorMessage);
 LeaveCriticalSection(&errCritical);
}