用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);
}