程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> Windows服務操作技巧

Windows服務操作技巧

編輯:關於.NET

伴隨著研究Windows服務,逐漸掌握了一些小技巧,現在與大家分享一下。

將Windows服務轉變為控制台程序

由於默認的Windows服務程序,編譯後為Win32的窗口程序。我們在程序啟動或運行過程中,如果想看到一些調試信息,那麼就只能通過DebugView或者輸出到日志的方式了。因為如果我們通過printf或者std::cout輸出調試信息的話,Win32窗口程序是無法顯示的。

此時,我們是多麼懷念我們的經典的控制台程序啊,它可以很方便的將我們的調試信息輸出出來,簡直是太方便了。既然如此,那我們就讓它一秒鐘變格格吧,額,應該是一秒鐘變控制台。

下面分享一下我的實現代碼

Collapse#ifdef _DEBUG
//Debug版本,直接輸出到控制台
#define OUT(s) printf_s(s); 
#define OUT_LN(s) printf_s(s##"\r\n"); 
#else
//非Debug版本,則輸出到調試器,一般使用DebugView
#define OUT(s) OutputDebugString(s);
#define OUT_LN(s) OutputDebugString(s);
#endif
    
class CServicesModule : public ATL::CAtlServiceModuleT< CServicesModule, IDS_SERVICENAME >
{
public :
    DECLARE_LIBID(LIBID_ServicesLib)
    DECLARE_REGISTRY_APPID_RESOURCEID(IDR_SERVICES, "{0794CF96-5CC5-432E-8C1D-52B980ACBE0F}")
        HRESULT InitializeSecurity() throw()
    {
        return S_OK;
    }
    //服務啟動
    HRESULT Load();
    //服務停止
    HRESULT UnLoad();
    
    HRESULT Run(_In_ int nShowCmd = SW_HIDE) throw()
    {
        HRESULT hr = S_OK;
        OUT_LN("准備啟動服務");
        hr = Load();
        if(hr)
        {
            OUT_LN("啟動服務失敗");
            return hr;
        }
        OUT_LN("Services服務已啟動");
    
        hr = ATL::CAtlServiceModuleT< CServicesModule, IDS_SERVICENAME >::Run(nShowCmd);
    
        hr = UnLoad();
        OUT_LN("Services服務正常退出");
        return hr;
    }
};
    
CServicesModule _AtlModule;
    
//
#ifndef _DEBUG
//非Debug版本,編譯為Win32程序
extern "C" int WINAPI _tWinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, 
                                LPTSTR /*lpCmdLine*/, int nShowCmd)
{
    return _AtlModule.WinMain(nShowCmd);
}
#else
//Debug版本,編譯為控制台程序
int _tmain(int argc, _TCHAR* argv[])
{
    return _AtlModule.WinMain(SW_SHOW);
}
#endif
    
HRESULT CServicesModule::Load()
{
    OUT_LN("服務正在啟動");
    return 0;
}
    
HRESULT CServicesModule::UnLoad()
{
    OUT_LN("服務正在停止");
    return 0;
}

通過_DEBUG宏來區分是否編譯成控制台程序。

當指定編譯Debug版本時,可以將程序編譯為控制台程序,通過RegServer注冊服務,然後直接運行服務exe程序,這樣通過printf輸出的信息,就可以在控制台上顯示了,如下圖。

當指定編譯Release版本時,將程序編譯為Win32程序,通過Service注冊服務,通過服務管理器管理服務的運行和停止。

當然,這還不是全部,還有一個地方需要設置,下面分別是Debug和Release下的設置

當然,還有一種更簡單的方法,可以將Debug和Release模式下的“子系統”項修改為“未設置”。這樣編譯器在編譯鏈接時,會根據代碼中的入口函數,自動將代碼鏈接為對應的程序。如圖

注冊服務為自動啟動服務

查看本欄目

添加自定義命令行參數

添加自定義命令行參數 Auto, 用來設置啟動方式為自動啟動, 並且給Service參數添加依賴項,實現代碼如下

Collapse// Services.cpp : WinMain 的實現
    
    
#include "stdafx.h"
#include "resource.h"
#include "Services_i.h"
    
    
using namespace ATL;
    
#include <stdio.h>
    
#ifdef _DEBUG
#define OUT(s) printf_s(s); 
#define OUT_LN(s) printf_s(s##"\r\n"); 
#else
#define OUT(s) OutputDebugString(s);
#define OUT_LN(s) OutputDebugString(s);
#endif
    
class CServicesModule : public ATL::CAtlServiceModuleT< CServicesModule, IDS_SERVICENAME >
{
public :
    DECLARE_LIBID(LIBID_ServicesLib)
    DECLARE_REGISTRY_APPID_RESOURCEID(IDR_SERVICES, "{0794CF96-5CC5-432E-8C1D-52B980ACBE0F}")
        HRESULT InitializeSecurity() throw()
    {
        // TODO : 調用 CoInitializeSecurity 並為服務提供適當的安全設置
        // 建議 - PKT 級別的身份驗證、
        // RPC_C_IMP_LEVEL_IDENTIFY 的模擬級別
        // 以及適當的非 NULL 安全描述符。
    
        return S_OK;
    }
    //服務啟動
    HRESULT Load();
    //服務停止
    HRESULT UnLoad();
    
    // Parses the command line and registers/unregisters the rgs file if necessary
    bool ParseCommandLine(
        _In_z_ LPCTSTR lpCmdLine,
        _Out_ HRESULT* pnRetCode) throw();
    //注冊服務
    BOOL Install() throw();
    
    //重寫此方法,主要是為了調用重寫的Install方法
    inline HRESULT RegisterAppId(_In_ bool bService = false) throw()
    {
        if (!Uninstall())
            return E_FAIL;
    
        CServicesModule::UpdateRegistryAppId(TRUE);
    
        CRegKey keyAppID;
        keyAppID.Open(HKEY_CLASSES_ROOT, _T("AppID"), KEY_WRITE);
    
        CRegKey key;
    
        key.Create(keyAppID, CServicesModule::GetAppIdT());
    
        key.DeleteValue(_T("LocalService"));
    
        key.SetStringValue(_T("LocalService"), m_szServiceName);
    
        // Create service
        if (!Install())
            return E_FAIL;
        return S_OK;
    }
        
    
    HRESULT Run(_In_ int nShowCmd = SW_HIDE) throw()
    {
        HRESULT hr = S_OK;
        OUT_LN("准備啟動服務");
        hr = Load();
        if(hr)
        {
            OUT_LN("啟動服務失敗");
            return hr;
        }
        OUT_LN("Services服務已啟動");
    
        hr = ATL::CAtlServiceModuleT< CServicesModule, IDS_SERVICENAME >::Run(nShowCmd);
    
        hr = UnLoad();
        OUT_LN("Services服務正常退出");
        return hr;
    }
    
private:
    _TCHAR dependServices[256];
    DWORD size;
    DWORD dwStartType;
};
    
CServicesModule _AtlModule;
    
//
#ifndef _DEBUG
extern "C" int WINAPI _tWinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, 
                                LPTSTR /*lpCmdLine*/, int nShowCmd)
{
    return _AtlModule.WinMain(nShowCmd);
}
#else
int _tmain(int argc, _TCHAR* argv[])
{
    return _AtlModule.WinMain(SW_SHOW);
}
#endif
    
HRESULT CServicesModule::Load()
{
    memset(dependServices, 0, sizeof(dependServices));
    dwStartType = SERVICE_DEMAND_START;
    
    OUT_LN("服務正在啟動");
    return 0;
}
    
HRESULT CServicesModule::UnLoad()
{
    OUT_LN("服務正在停止");
    return 0;
}
    
// Parses the command line and registers/unregisters the rgs file if necessary
bool CServicesModule::ParseCommandLine(
    _In_z_ LPCTSTR lpCmdLine,
    _Out_ HRESULT* pnRetCode) throw()
{
    if (!CAtlExeModuleT<CServicesModule>::ParseCommandLine(lpCmdLine, pnRetCode))
        return false;
    
    TCHAR szTokens[] = _T("-/");
    *pnRetCode = S_OK;
    LPCTSTR lpszToken = FindOneOf(lpCmdLine, szTokens);
    while (lpszToken != NULL)
    {
        if (WordCmpI(lpszToken, _T("Service"))==0)
        {
            lpszToken += _tcslen(_T("Service"));
            //循環讀取依賴項
            while (true)
            {
                LPCTSTR lpszTokenBegin = lpszToken;
                while(isprint(*lpszToken) && *lpszToken != _T(' ') && *lpszToken != _T('-') && *lpszToken != _T('/'))
                {
                    lpszToken++;
                }
                DWORD tokenSize = (lpszToken - lpszTokenBegin);
                memcpy_s(dependServices + size, sizeof(dependServices) - size, lpszTokenBegin, tokenSize * sizeof(_TCHAR));
                size += tokenSize;
                dependServices[size] = _T('\0');
                if(tokenSize)
                {
                    size++;
                }
    
                while(isprint(*lpszToken) && *lpszToken == ' ')
                {
                    lpszToken++;
                }
                if((*lpszToken == _T('\0') || *lpszToken == _T('-') || *lpszToken == _T('/')))
                {
                    dependServices[size + 1] = _T('\0');
                    break;
                }
            }
            *pnRetCode = this->RegisterAppId(true);
            if (SUCCEEDED(*pnRetCode))
                *pnRetCode = this->RegisterServer(TRUE);
            return false;
        }
    
        //設置啟動類型
        if (WordCmpI(lpszToken, _T("Auto"))==0)
        {
            dwStartType = SERVICE_AUTO_START;
        }
        lpszToken = FindOneOf(lpszToken, szTokens);
    }
    return true;
}
    
BOOL CServicesModule::Install() throw()
{
    if (IsInstalled())
        return TRUE;
    
    // Get the executable file path
    TCHAR szFilePath[MAX_PATH + _ATL_QUOTES_SPACE];
    DWORD dwFLen = ::GetModuleFileName(NULL, szFilePath + 1, MAX_PATH);
    if( dwFLen == 0 || dwFLen == MAX_PATH )
        return FALSE;
    
    // Quote the FilePath before calling CreateService
    szFilePath[0] = _T('\"');
    szFilePath[dwFLen + 1] = _T('\"');
    szFilePath[dwFLen + 2] = 0;
    
    SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    
    //創建服務、根據命令行設置啟動方式,設置依賴關系
    SC_HANDLE hService = ::CreateService(
        hSCM, m_szServiceName, m_szServiceName,
        SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
        dwStartType, SERVICE_ERROR_NORMAL,
        szFilePath, NULL, NULL, dependServices, NULL, NULL);
    
    if (hService == NULL)
    {
        ::CloseServiceHandle(hSCM);
        return FALSE;
    }
    
    ::CloseServiceHandle(hService);
    ::CloseServiceHandle(hSCM);
    return TRUE;
}

自定義命令行參數演示

注冊服務時使用如下命令行

Services.exe -Auto -service CryptSvc RPCSS DcomLaunch

注冊後,效果如下

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved