一路探索者

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
统计
 

首先,服务程序的架构是服务器/客服端这样的结构呈现;服务程序就是承担服务器的功能,另外还必须有客户端程序与它配合使用,否则仅有服务程序就失去了意义。

我们可以利用VC创建Win32项目中的控制台程序,也可以创建MFC项目程序,这里我们采用第二种。

一、服务端介绍

1、 服务程序的工作原理

(1)、服务程序的安装

服务程序的运行环境是SCM(Service Control Manager),因此,要运行服务程序,首先要将它加入SCM。安装服务程序,简单地说,就是将服务程序加入“开始-控制面板-管理工具-服务”中的“服务”对话框。由于SCM并不直接支持服务程序的安装,只能通过自己的服务程序来安装。

每个服务程序在SCM中有一个显示名称以区别于其它服务程序,这个名称就是SERVICE.h文件中的SZSERVICEDISPLAYNAME宏变量,它为" My MFC Simple Service",它要通过CreateService()来实现。

与服务程序安装相关的主要API函数是CreateService(),这部分请参考本项目中的CmdInstallService()函数。

(2)、服务程序的启动

服务程序可能加入SCM就开始运行,也可能加入后并未运行,需要手动运行,这取决于CreateService()的参数。

手动启动服务程序的方法:单击“开始-控制面板-管理工具-服务”项,在“服务”对话框中,在所需的服务程序的快捷菜单中,选择“启动”即可。

与服务程序启动相关的主要API函数是StartServiceCtrlDispatcher(), 该函数的一大特点是只能在SCM启动,直接运行会报1063错误。不过,我们的项目编译成功后,会调用该函数,就总会在系统日志文件中写入这样的错误。别紧张,错误是暂时的,只要我们将服务程序安装进SCM,然后启动它就可以了。

 

2、 服务程序的相关文件

(1)、服务程序的躯壳部分

服务程序有自身的特点,就是要在SCM中操作,要实现这个功能,就会有相关的代码来保障,这就是服务程序的躯壳。

这部分代码的相关文件位于SERVICE.h/Service.cpp。

(2)、服务程序的实现部分

服务程序不是花瓶,供人观看的,它是要做事的,为客户端提供功能的。这部分代码的相关文件位于SIMPLE.cpp文件中的ServiceStart()函数,客户端就是与这部分打交道。

服务器与客服端通信的方式较多,这里采用管道来实现的。

(3)、MFC程序使用命令的实现部分

这部分在MyCommandLineInfo.h/MyCommandLineInfo.cpp中实现。

 

3、 服务程序的注意事项

要关闭360安全卫士和360杀毒软件,否则注册表不能修改,自己开发的服务程序也不能启动,还不能将文件放人启动菜单。

 

4、 服务程序的使用方法

服务程序的使用方法,不是鼠标单击或者双击打开,一般是以命令的方式执行, 即在操作系统桌面左下角的命令框中运行。

如,我的执行文件位于G:\Temp\ Service\Release\Service.exe,我要安装服务,可以在命令框中输入如下内容再回车:

G:\Temp\ Service\Release\Service –install

或者G:\Temp\ Service\Release\ Service/install

其它操作如调试、移除的用法,如法炮制。

 

二、客户端介绍

使用管道技术与服务器通信。

 

本例的关键代码如下:

//一、服务端

//1、SERVICE.h文件

//服务的内部名称

#define SZSERVICENAME        "MyMFCSimpleService"

 

//显示服务的名称

#define SZSERVICEDISPLAYNAME "My MFC Simple Service"

 

//服务依赖项列表 - "dep1\0dep2\0\0"

#define SZDEPENDENCIES       "" 

 

VOID ServiceStart(DWORD dwArgc, LPTSTR *lpszArgv);

VOID ServiceStop();

BOOL ReportStatusToSCMgr(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint);

void AddToMessageLog(LPTSTR lpszMsg);

 

class CServiceApp : public CWinApp

{

……

};

 

//2、Service.cpp文件

#include "stdafx.h"

#include "Service.h"

#include "ServiceDlg.h"

#include <winSvc.h>

#include "MyCommandLineInfo.h"

 

SERVICE_STATUS          ssStatus;      

SERVICE_STATUS_HANDLE   sshStatusHandle;

DWORD                   dwErr = 0;

BOOL                    bDebug = FALSE;

TCHAR                   szErr[256];

 

VOID WINAPI service_ctrl(DWORD dwCtrlCode);

VOID WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv);

VOID CmdInstallService();

VOID CmdRemoveService();

VOID CmdDebugService(int argc, char **argv);

BOOL WINAPI ControlHandler ( DWORD dwCtrlType );

LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );

 

//service_main函数为服务程序的入口,它与ServiceMain函数的格式一致。

void WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv)

{

    //注册我们的服务控制处理器

    sshStatusHandle = RegisterServiceCtrlHandler(TEXT(SZSERVICENAME), service_ctrl);

 

    if (!sshStatusHandle)

        goto cleanup;

 

       //示例中,没有更改SERVICE_STATUS的成员 

    ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;

    ssStatus.dwServiceSpecificExitCode = 0;

 

    //将状态信息报告给服务控制管理器。

    if (!ReportStatusToSCMgr(

        SERVICE_START_PENDING, //服务状态

        NO_ERROR,              //退出代码

        3000))                 //等待提示

        goto cleanup;

 

       //以下函数定义于Simple.CPP文件,用以实现服务的实际功能;

       //其它的都是服务的架构相关的代码

    ServiceStart(dwArgc, lpszArgv);

 

cleanup:

    //将状态报告给服务控制管理器

    if (sshStatusHandle)

        (VOID)ReportStatusToSCMgr(

                            SERVICE_STOPPED,

                            dwErr,

                            0);

}

 

//当这个服务调用ControlService()时,这个函数会被SCM调用。

//ControlService()于CmdRemoveService()中被调用。

//即调用CmdRemoveService()时,service_ctrl()被SCM调用。

//参数为请求的控件类型。

VOID WINAPI service_ctrl(DWORD dwCtrlCode)

{

    switch(dwCtrlCode)

    {

              //调用定义于Simple.CPP文件中的ServiceStop(),以停止服务。   

        //这可能会导致1053 -服务没有回应…错误。 

        case SERVICE_CONTROL_STOP:

            ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 0);

            ServiceStop();

            return;

        //更新服务状态。

        case SERVICE_CONTROL_INTERROGATE:

            break;

 

        //无效的控制代码。

        default:

            break;

    }

 

    ReportStatusToSCMgr(ssStatus.dwCurrentState, NO_ERROR, 0);

}

 

//本函数用于将状态报告给服务控制管理器;

//参数分别表示:服务状态、退出代码、等待提示。

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 = dwWaitHint;

 

        if ((dwCurrentState == SERVICE_RUNNING) ||

             (dwCurrentState == SERVICE_STOPPED))

            ssStatus.dwCheckPoint = 0;

        else

            ssStatus.dwCheckPoint = dwCheckPoint++;

 

 

        //真正实现将状态报告给服务控制管理器。

        if (!(fResult = SetServiceStatus(sshStatusHandle, &ssStatus)))

              {

            AddToMessageLog(TEXT("SetServiceStatus函数"));

        }

    }

    return fResult;

}

 

//本函数用于将错误消息发送到系统日志文件。

VOID AddToMessageLog(LPTSTR lpszMsg)

{

    TCHAR   szMsg[256];

    HANDLE  hEventSource;

    LPTSTR  lpszStrings[2];

 

 

    if (!bDebug)

    {

        dwErr = GetLastError();

 

        //使用事件日志记录错误。

        hEventSource = RegisterEventSource(NULL, TEXT(SZSERVICENAME));

 

        _stprintf(szMsg, TEXT("%s 错误: %d"), TEXT(SZSERVICENAME), dwErr);

        lpszStrings[0] = szMsg;

        lpszStrings[1] = lpszMsg;

 

        if (hEventSource != NULL)

              {

            ReportEvent(hEventSource, //事件源句柄

                EVENTLOG_ERROR_TYPE,  //事件类型

                0,                    //事件类别

                0,                    //事件标识符

                NULL,                 //当前用户的SID

                2,                    //在lpszStrings中的字符串

                0,                    //没有字节的原始数据

                (LPCSTR*)lpszStrings, //错误字符串数组                 

                NULL);                //没有原始数据

 

            (VOID) DeregisterEventSource(hEventSource);

        }

    }

}

 

//本函数用于安装服务程序

//执行该命令的方法:在DOS下,切换到Service.exe所在路径,输入Service -install或者Service/install后回车。

//此时printf()函数不起作用,而AfxMessageBox()起作用;

//因为属性页中链接器-系统-子系统为窗口(/SUBSYSTEM:WINDOWS),此时还是窗口程序。

void CmdInstallService()

{

    SC_HANDLE   schService;

    SC_HANDLE   schSCManager;

       CString strTip;

    TCHAR szPath[512];

    if (GetModuleFileName(NULL, szPath, 512) == 0)

    {

              strTip.Format(TEXT("不能安装%s - %s\n"),

                     TEXT(SZSERVICEDISPLAYNAME),

                     GetLastErrorText(szErr, 256));

              AfxMessageBox(strTip);

        //_tprintf(TEXT("不能安装%s - %s\n"),

              //     TEXT(SZSERVICEDISPLAYNAME),

              //     GetLastErrorText(szErr, 256));

        return;

    }

 

    schSCManager = OpenSCManager(

                        NULL, //参数为NULL,则连接到本机的服务控制管理器。

                        NULL, //参数为NULL,则使用默认数据库。

                        SC_MANAGER_ALL_ACCESS //允许使用所有的权限。

                        );

    if (schSCManager)

    {

              //创建服务对象并将其添加到指定的服务控制管理器数据库中

        schService = CreateService(

            schSCManager,               //由OpenSCManager函数返回的句柄。

            TEXT(SZSERVICENAME),        //要安装的服务的名称。

            TEXT(SZSERVICEDISPLAYNAME), //用户界面程序用来标识服务的显示名称。

            SERVICE_ALL_ACCESS,         //服务的访问权限,这里为允许使用所有的权限。

            SERVICE_WIN32_OWN_PROCESS,  //服务类型,这里为在自己的进程中运行服务。

            SERVICE_DEMAND_START,       //服务启动选项,这里为调用StartService函数时,由服务控制管理器启动的服务。

            SERVICE_ERROR_NORMAL,       //服务启动失败的错误值,这里为将错误记录在事件日志中,但继续启动操作。

            szPath,                     //服务文件的完全限定路径。

            NULL,                       //服务所属组的名称,这里为不属于某个组。 

            NULL,                       //接收上一个参数中指定的组中唯一的标记值,这里为不更改现有标记。 

            TEXT(SZDEPENDENCIES),       //服务依赖项。 

            NULL,                       //服务运行时使用的帐户的名称,这里为 LocalSystem帐户。

            NULL);                      //服务运行时使用的密码,这里为不需要密码。

 

        if (schService)

        {

                     strTip.Format(TEXT("%s 安装\n"),TEXT(SZSERVICEDISPLAYNAME));

                     AfxMessageBox(strTip);

            //_tprintf(TEXT("%s 安装\n"), TEXT(SZSERVICEDISPLAYNAME));

            CloseServiceHandle(schService);

        }

        else

        {

                     strTip.Format(TEXT("创建服务失败 - %s\n"), GetLastErrorText(szErr, 256));

                     AfxMessageBox(strTip);

            //_tprintf(TEXT("创建服务失败 - %s\n"), GetLastErrorText(szErr, 256));

        }

 

        CloseServiceHandle(schSCManager);

    }

    else

       {

              strTip.Format(TEXT("打开管理器失败 - %s\n"),GetLastErrorText(szErr,256));

              AfxMessageBox(strTip);

        //_tprintf(TEXT("打开管理器失败 - %s\n"), GetLastErrorText(szErr,256));

       }

       AfxMessageBox("安装成功!");

}

 

//本函数用于移除服务程序

//执行该命令的方法:在DOS下,切换到Service.exe所在路径,输入Service -remove或者Service/remove后回车。

void CmdRemoveService()

{

    SC_HANDLE   schService;

    SC_HANDLE   schSCManager;

       CString strTip;

 

    schSCManager = OpenSCManager(

                        NULL, //参数为NULL,则连接到本机的服务控制管理器。

                        NULL, //参数为NULL,则使用默认数据库。

                        SC_MANAGER_ALL_ACCESS //允许使用所有的权限。

                        );

    if (schSCManager)

    {

        schService = OpenService(schSCManager, TEXT(SZSERVICENAME), SERVICE_ALL_ACCESS);

        if (schService)

        {

            //向服务发送控制代码,这里为停止服务。

            if (ControlService(schService, SERVICE_CONTROL_STOP, &ssStatus))

            {//函数操作成功

                            strTip.Format(TEXT("停止过程中 %s。"),TEXT(SZSERVICEDISPLAYNAME));

                      AfxMessageBox(strTip);

                //_tprintf(TEXT("停止过程中 %s。"), TEXT(SZSERVICEDISPLAYNAME));

                Sleep(1000);

 

                            //根据指定的信息级别检索指定服务的当前状态。

                while(QueryServiceStatus(schService, &ssStatus))

                {

                    if (ssStatus.dwCurrentState == SERVICE_STOP_PENDING)

                    {

                        _tprintf(TEXT("."));

                        Sleep(1000);

                    }

                    else

                        break;

                }

 

                if (ssStatus.dwCurrentState == SERVICE_STOPPED)

                            {

                                   strTip.Format(TEXT("\n%s 已经停止。\n"),TEXT(SZSERVICEDISPLAYNAME));

                          AfxMessageBox(strTip);

                    //_tprintf(TEXT("\n%s 已经停止。\n"), TEXT(SZSERVICEDISPLAYNAME));

                            }

                else

                            {

                                   strTip.Format(TEXT("\n%s 停止失败。\n"),TEXT(SZSERVICEDISPLAYNAME));

                          AfxMessageBox(strTip);

                    //_tprintf(TEXT("\n%s 停止失败。\n"), TEXT(SZSERVICEDISPLAYNAME));

                            }

 

            }

 

            //移除服务

            if(DeleteService(schService))

                     {

                            strTip.Format(TEXT("%s 移除。\n"),TEXT(SZSERVICEDISPLAYNAME));

                      AfxMessageBox(strTip);

                //_tprintf(TEXT("%s 移除。\n"), TEXT(SZSERVICEDISPLAYNAME) );

                     }

            else

                     {

                            strTip.Format(TEXT("移除服务失败。 - %s\n"), GetLastErrorText(szErr,256));

                      AfxMessageBox(strTip);

                //_tprintf(TEXT("移除服务失败。 - %s\n"), GetLastErrorText(szErr,256));

                     }

 

 

            CloseServiceHandle(schService);

        }

        else

              {

                     strTip.Format(TEXT("打开服务失败 - %s\n"), GetLastErrorText(szErr,256));

                  AfxMessageBox(strTip);

            //_tprintf(TEXT("打开服务失败 - %s\n"), GetLastErrorText(szErr,256));

              }

 

        CloseServiceHandle(schSCManager);

    }

    else

       {

              strTip.Format(TEXT("打开管理器失败 - %s\n"), GetLastErrorText(szErr,256));

              AfxMessageBox(strTip);

        //_tprintf(TEXT("打开管理器失败 - %s\n"), GetLastErrorText(szErr,256));

       }

       AfxMessageBox("移除成功!");

}

 

//本函数用于运行服务程序

//执行该命令的方法:在DOS下,切换到Service.exe所在路径,输入Service -debug或者Service/debug后回车。

//本函数同安装函数相比,可以测试客户端与服务端的通信,但不会添加到SCM。

void CmdDebugService(int argc, char ** argv)

{

    DWORD dwArgc;

    LPTSTR *lpszArgv;

       CString strTip;

 

#ifdef UNICODE

    lpszArgv = CommandLineToArgvW(GetCommandLineW(), &(dwArgc));

#else

    dwArgc   = (DWORD) argc;

    lpszArgv = argv;

#endif

 

       strTip.Format(TEXT("调试过程中 %s.\n"), TEXT(SZSERVICEDISPLAYNAME));

       AfxMessageBox(strTip);

    //_tprintf(TEXT("调试过程中 %s.\n"), TEXT(SZSERVICEDISPLAYNAME));

    SetConsoleCtrlHandler(ControlHandler, TRUE);

    ServiceStart(dwArgc, lpszArgv);

       AfxMessageBox("调试成功!");

}

 

BOOL WINAPI ControlHandler(DWORD dwCtrlType)

{

    switch(dwCtrlType)

    {

              //使用Ctrl+C或Ctrl+Break来模拟。

        case CTRL_BREAK_EVENT: 

 

              //在调试模式下使用SERVICE_CONTROL_STOP,以停止服务。

        case CTRL_C_EVENT:

            _tprintf(TEXT("停止过程中 %s。\n"), TEXT(SZSERVICEDISPLAYNAME));

            ServiceStop();

            return TRUE;

            break;

 

    }

    return FALSE;

}

 

LPTSTR GetLastErrorText(LPTSTR lpszBuf, DWORD dwSize)

{

    DWORD dwRet;

    LPTSTR lpszTemp = NULL;

 

    dwRet = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,

                           NULL,

                           GetLastError(),

                           LANG_NEUTRAL,

                           (LPTSTR)&lpszTemp,

                           0,

                           NULL);

 

    if (!dwRet || ((long)dwSize < (long)dwRet+14 ))

        lpszBuf[0] = TEXT('\0');

    else

    {

        lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0'); 

        _stprintf(lpszBuf, TEXT("%s (0x%x)"), lpszTemp, GetLastError());

    }

 

    if (lpszTemp)

        LocalFree((HLOCAL) lpszTemp);

 

    return lpszBuf;

}

 

//1、要关闭360安全卫士和360杀毒软件,否则注册表不能修改,

//自己开发的服务程序也不能启动,还不能将文件放人启动菜单。

//2、将软件(服务软件除外)在注册表中设置为开机启动,则会出现在msconfig.exe是“系统配置”对话框;

//该软件的“启动项目”对应rs文件中的"ProductName",“制造商”对应rs文件中的"CompanyName"。

//3、因为服务软件不用设置为开机启动,也会出现在msconfig.exe是“系统配置”对话框。

BOOL CServiceApp::InitInstance()

{

       ……

 

       SERVICE_TABLE_ENTRY dispatchTable[] =

    {

        {TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION)service_main},

        {NULL, NULL}

    };

 

       CMyCommandLineInfo cmdInfo;

       ParseCommandLine(cmdInfo);

 

       if(cmdInfo.m_bInstall)

       {

              CmdInstallService();

              exit(0);

       }

       else if(cmdInfo.m_bRemove)

       {

              CmdRemoveService();

              exit(0);

       }

       else if(cmdInfo.m_bDebug)

       {

              bDebug = TRUE;

        CmdDebugService(__argc, __argv);

              exit(0);

       }

       else

    {

        goto dispatch;

    }

 

       dispatch:

    //如果不匹配上述任何参数

    //服务控制管理器可能会启动服务 

    //所以我们必须调用StartServiceCtrlDispatcher

   

       //服务进程的主线程连接到服务控制管理器;

       //即注册服务名称和服务入口ServiceMain;

       //这里的服务入口实际上是service_main,不过它应该与ServiceMain函数的格式一致。

 

       //编译后自动运行,或者单独执行软件,StartServiceCtrlDispatcher()都会报错。

       //正确的方法:编译后在DOS下运行安装程序,这样会将本服务添加进SCM,然后在SCM中手工启动。

    if (!StartServiceCtrlDispatcher(dispatchTable))

       {

              GetLastErrorText(szErr,256);

              CString strRes;

              strRes.Format("%s",szErr);

              ::AfxMessageBox(strRes); //服务进程无法连接到服务控制器上(0x427)

        AddToMessageLog(TEXT("StartServiceCtrlDispatcher失败。"));

       }

 

       /*CServiceDlg dlg;

       m_pMainWnd = &dlg;

       INT_PTR nResponse = dlg.DoModal();

       if (nResponse == IDOK)

       {

       }

       else if (nResponse == IDCANCEL)

       {

       }*/

 

       return FALSE;

}

 

//3、SIMPLE.cpp文件

#include "stdafx.h"

#include <windows.h>

#include <stdio.h>

#include <stdlib.h>

#include <process.h>

#include <tchar.h>

#include "service.h"

 

 

//服务应该结束时,该事件有信号 

HANDLE  hServerStopEvent = NULL;

 

//服务的实际代码 

//dwArgc - 命令行参数的数量

//lpszArgv - 命令行参数数组

 

//默认的行为是打开一个命名管道,\\.\pipe\simple,并从它读取。 

//它修改数据并将其写回管道。 

//当hServerStopEvent被通知时,服务停止。

VOID ServiceStart (DWORD dwArgc, LPTSTR *lpszArgv)

{

    HANDLE                  hPipe = INVALID_HANDLE_VALUE;

    HANDLE                  hEvents[2] = {NULL, NULL};

    OVERLAPPED              os;

    PSECURITY_DESCRIPTOR    pSD = NULL;

    SECURITY_ATTRIBUTES     sa;

    TCHAR                   szIn[80];

    TCHAR                   szOut[80];

    LPTSTR                  lpszPipeName = TEXT("\\\\.\\pipe\\simple");

    BOOL                    bRet;

    DWORD                   cbRead;

    DWORD                   cbWritten;

    DWORD                   dwWait;

    UINT                    ndx;

 

    //

    // 开始服务初始化

    //

 

    //将状态信息报告给服务控制管理器。

    if (!ReportStatusToSCMgr(

        SERVICE_START_PENDING, //服务状态

        NO_ERROR,              //退出代码

        3000))                 //等待提示

        goto cleanup;

 

    //创建事件对象。控制处理器函数发出信号 

    //当它接收到"stop"控制代码时发生此事件。 

    hServerStopEvent = CreateEvent(

        NULL,    //没有安全属性

        TRUE,    //手工重置的事件

        FALSE,   //无初始信号

        NULL);   //无名称

 

    if (hServerStopEvent == NULL)

        goto cleanup;

 

    hEvents[0] = hServerStopEvent;

 

    if (!ReportStatusToSCMgr(

        SERVICE_START_PENDING,

        NO_ERROR,              

        3000))                

        goto cleanup;

 

    hEvents[1] = CreateEvent(

        NULL,   

        TRUE,   

        FALSE,  

        NULL);  

 

    if (hEvents[1] == NULL)

        goto cleanup;

 

    if (!ReportStatusToSCMgr(

        SERVICE_START_PENDING,

        NO_ERROR,             

        3000))                

        goto cleanup;

 

    //创建一个安全描述符,允许任何人写入管道…

    pSD = (PSECURITY_DESCRIPTOR) malloc( SECURITY_DESCRIPTOR_MIN_LENGTH );

    if (pSD == NULL)

        goto cleanup;

    if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION))

        goto cleanup;

 

    //添加一个空disc. ACL到安全描述符。  

    if (!SetSecurityDescriptorDacl(pSD, TRUE, (PACL) NULL, FALSE))

        goto cleanup;

 

    sa.nLength = sizeof(sa);

    sa.lpSecurityDescriptor = pSD;

    sa.bInheritHandle = TRUE;

 

    //将状态信息报告给服务控制管理器。

    if (!ReportStatusToSCMgr(

        SERVICE_START_PENDING, //服务状态

        NO_ERROR,              //退出代码

        3000))                 //等待提示

        goto cleanup;

 

 

    //允许用户tp定义管道名称

    for (ndx = 1; ndx < dwArgc-1; ndx++)

    {

 

        if (((*(lpszArgv[ndx]) == TEXT('-')) ||

               (*(lpszArgv[ndx]) == TEXT('/'))) &&

             _tcsicmp(TEXT("pipe"), lpszArgv[ndx]+1) == 0)

        {

            lpszPipeName = lpszArgv[++ndx];

        }

    }

 

    //打开我们的命名管道...

    hPipe = CreateNamedPipe(

                    lpszPipeName,  //管道的名字

                    FILE_FLAG_OVERLAPPED |

                    PIPE_ACCESS_DUPLEX,     //管道打开模式

                    PIPE_TYPE_MESSAGE |

                    PIPE_READMODE_MESSAGE |

                    PIPE_WAIT,           //管道类型

                    1,                    //实例数量

                    0,                    //输出的大小,0则根据需要分配

                    0,                    //输入的大小

                    1000,                 //默认的超时值

                    &sa);                 //安全属性

 

    if (hPipe == INVALID_HANDLE_VALUE)

       {

        AddToMessageLog(TEXT("无法创建命名管道"));

        goto cleanup;

    }

   

    if (!ReportStatusToSCMgr(

        SERVICE_RUNNING,      

        NO_ERROR,             

        0))                   

        goto cleanup;

 

    //

    //结束服务初始化

    //

 

    //服务现在正在运行,执行工作直到关机。 

    while (1)

    {

        memset( &os, 0, sizeof(OVERLAPPED) );

        os.hEvent = hEvents[1];

        ResetEvent( hEvents[1] );

 

        //等待连接……

        ConnectNamedPipe(hPipe, &os);

        if (GetLastError() == ERROR_IO_PENDING)

        {

            dwWait = WaitForMultipleObjects( 2, hEvents, FALSE, INFINITE );

            if (dwWait != WAIT_OBJECT_0+1)     //没有重叠I/O事件-错误发生,

                break;                         //或服务器停止信号。

        }

   

        memset(&os, 0, sizeof(OVERLAPPED));

        os.hEvent = hEvents[1];

        ResetEvent( hEvents[1] );

        //抓取管子里的东西…     

        bRet = ReadFile(

                    hPipe,          //要读取的文件

                    szIn,           //输入缓冲器地址

                    sizeof(szIn),   //要读取的字节数

                    &cbRead,        //实际读取的字节数

                    &os);           //重叠I/O

 

        if (!bRet && (GetLastError() == ERROR_IO_PENDING))

        {

            dwWait = WaitForMultipleObjects( 2, hEvents, FALSE, INFINITE );

            if (dwWait != WAIT_OBJECT_0+1)   

                break;                        

        }

 

        _stprintf(szOut, TEXT("Hello! [%s]"), szIn);

 

        memset( &os, 0, sizeof(OVERLAPPED) );

        os.hEvent = hEvents[1];

        ResetEvent( hEvents[1] );

 

        //写东西进管子…  

        bRet = WriteFile(

                    hPipe,          //要写的文件

                    szOut,          //输出缓冲器地址

                    sizeof(szOut),  //要写的字节数

                    &cbWritten,     //实际写的字节数

                    &os);           //重叠I/O

 

        if (!bRet && ( GetLastError() == ERROR_IO_PENDING))

        {

            dwWait = WaitForMultipleObjects( 2, hEvents, FALSE, INFINITE );

            if (dwWait != WAIT_OBJECT_0+1)    

                break;                         

        }

 

        //抛弃连接

        DisconnectNamedPipe(hPipe);

    }

 

  cleanup:

    if (hPipe != INVALID_HANDLE_VALUE)

        CloseHandle(hPipe);

 

    if (hServerStopEvent)

        CloseHandle(hServerStopEvent);

 

    if (hEvents[1]) // 重叠i/o事件

        CloseHandle(hEvents[1]);

 

    if (pSD)

        free(pSD);

 

}

 

//停止该服务。

//如果ServiceStop过程将要执行时间超过3秒, 

//它应该生成一个线程来执行停止代码,并返回。 

//否则,ServiceControlManager认为服务已经停止响应。

VOID ServiceStop()

{

    if (hServerStopEvent)

        SetEvent(hServerStopEvent);

}

 

4、MyCommandLineInfo.h文件

class CMyCommandLineInfo : public CCommandLineInfo

{

public:

       CMyCommandLineInfo();

public:

       BOOL m_bInstall;

       BOOL m_bRemove;

       BOOL m_bDebug;

       CString m_sArg;

public:

       void ParseParam(const TCHAR* pszParam,BOOL bFlag,BOOL bLast);

       virtual ~CMyCommandLineInfo();

};

 

5、MyCommandLineInfo.cpp文件

#include "stdafx.h"

#include "Service.h"

#include "MyCommandLineInfo.h"

CMyCommandLineInfo::CMyCommandLineInfo()

{

       m_bInstall = FALSE;

       m_bRemove = FALSE;

       m_bDebug = FALSE;

       m_sArg=_T("");

}

CMyCommandLineInfo::~CMyCommandLineInfo()

{

}

void CMyCommandLineInfo::ParseParam(const TCHAR* pszParam,BOOL bFlag,BOOL bLast)

{

       CString sArg(pszParam);

       if (bFlag)

       {

              m_bInstall = !sArg.CompareNoCase("install");

              m_bRemove = !sArg.CompareNoCase("remove");

              m_bDebug = !sArg.CompareNoCase("debug");

       }

       else if (m_strFileName.IsEmpty())

       {

              m_sArg=sArg;

       }

       CCommandLineInfo::ParseParam(pszParam,bFlag,bLast);

}

 

//二、客户端

//发送并接收数据

//服务端接收到数据后,将数据加上“Hello!”前缀后,

//且使用[]括起来,送回客户端。

void CClientDlg::OnClickedButton1()

{

       char    inbuf[80];

    char    outbuf[80];

    DWORD   bytesRead;

    BOOL    ret;

    LPSTR   lpszPipeName = "\\\\.\\pipe\\simple";

       LPSTR lpszString;

       CString strSend;

       ((CEdit*)GetDlgItem(IDC_EDIT1))->GetWindowText(strSend);

       strcpy(outbuf, strSend);

 

       //连接到服务端的管道服务器

    ret = CallNamedPipeA(lpszPipeName,

                         outbuf, sizeof(outbuf),

                         inbuf, sizeof(inbuf),

                         &bytesRead, NMPWAIT_WAIT_FOREVER);

       CString strTemp;

    if (ret)

       {

              strTemp.Format("%s\n", inbuf);

              m_listBox.AddString(strTemp); //将接收数据插入列表框。

       }

}

posted on   一路探索者  阅读(643)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
 
点击右上角即可分享
微信分享提示