peakzhang 发表于 2007-12-15 23:17:12

请教一个制作NT_Service的问题

参照ACE/example中的NT_Service的例子,我写了自己的NT_Service类
并且在NT_Service 的函数svc()中启动Service_Config.open()开启静态服务

----------------------------------------

int
Win_NT_Service::svc (void)
{
ACE_DEBUG ((LM_DEBUG,
            "Service::svc\n"));

// As an NT service, we come in here in a different thread than the
// one which created the reactor.So in order to do anything, we
// need to own the reactor. If we are not a service, report_status
// will return -1.
if (report_status (SERVICE_RUNNING) == 0)
    ACE_Reactor::instance ()->owner (ACE_Thread::self ());


ACE_Service_Config::open
    (argc, argv, ACE_DEFAULT_LOGGER_KEY, 0);

ACE_STATIC_SVC_REGISTER (Service_Descriptor);

stop_ = 0;
while(!stop_) ACE_Reactor::instance ()->run_reactor_event_loop ();

// Cleanly terminate connections, terminate threads.
ACE_DEBUG ((LM_DEBUG,
            "Shutting down\n"));
return 0;
}

--------------------------------------------

出现的问题是:
如果我在main函数中简单的调用NT_Service的svc()函数,则服务正常的运行

--------------------------------------------

ACE_NT_SERVICE_DEFINE (Server,
                     Win_NT_Service,
                     ACE_TEXT ("Message Service"));

int ACE_TMAIN (int argc, ACE_TCHAR *argv[]) {

   WIN_NT_SERVICE::instance ()->name (ACE_TEXT ("Server"),
                              ACE_TEXT ("Message Service"));


WIN_NT_SERVICE::instance ()->svc ();
return 0;
}

-------------------------------------------

然而把NT_Service对象注册到WIndows中后,在服务管理器中启动它,服务却没有启动(客户端无法连接)
并且,在windows服务管理器中选择停止也无法停止该服务,只能在进程管理器中杀死它。

----------------------------------------------------

ACE_NT_SERVICE_DEFINE (Server,
                     Win_NT_Service,
                     ACE_TEXT ("Message Service"));

int ACE_TMAIN (int argc, ACE_TCHAR *argv[]) {

WIN_NT_SERVICE::instance ()->set_arg (argc, argv);

   WIN_NT_SERVICE::instance ()->name (ACE_TEXT ("Server"),
                              ACE_TEXT ("Message Service"));

      ofstream *output_file = new ofstream("ntsvc.log", ios::out);
      if (output_file && output_file->rdstate() == ios::goodbit)
      ACE_LOG_MSG->msg_ostream(output_file, 1);
      ACE_LOG_MSG->open(argv,
                        ACE_Log_Msg::STDERR | ACE_Log_Msg::OSTREAM,
                        0);
      ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%T (%t): Starting service.\n")));

      ACE_NT_SERVICE_RUN (RMServer,
                        WIN_NT_SERVICE::instance (),
                        ret);
      if (ret == 0)
      ACE_ERROR ((LM_ERROR,
                  ACE_TEXT ("%p\n"),
                  ACE_TEXT ("Couldn't start service")));
      else
      ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%T (%t): Service stopped.\n")));
      
return 0;
}


不清楚当NT_Service在windows服务管理器中被启动的时候,是不是直接调用我注册的exe,并且是指向svc()这个函数的句柄,它的调用喝平常的启动exe程序有没有什么本质的区别,以至于两种方式会产生完全不同的结果。

另外在windows管理器中选择停止服务不能结束也是很奇怪的问题,我不知道该怎么调试,因为一旦这个服务被注册到windows服务管理器中,就不能象在VC中哪样单步跟踪和命令行输出DEBUG信息了,多谢指点~~!!

peakzhang 发表于 2007-12-15 23:17:22

你没有正确理解NT Service的概念。
NT Service程序,无非是一个console的程序,但是这个console程序,实现了NT系统规定的几个接口,比如启动、暂停、退出等等。ACE的包装很简单,那个例子也很实用,我的程序都是抄那个实例的。

你的ACE_TMAIN中,是不能直接调用处理器的svc方法的,这样只能用于调试,否则肯定无法相应NT的服务管理器的处理信号。

正确的办法,是你照抄那个实例中的CProcess并且稍加修改。
ACE\ACE_wrappers\examples\NT_Service\main.cpp,里面已经展示的很清晰了。

peakzhang 发表于 2007-12-15 23:17:31

嗯,我照抄这个例子,只做了极少的改动,已经可以在windows服务管理中启动和停止服务。
但是最大的问题还是:使用-d(debug模式)我的服务能正确的运行,能看到debug信息,而且客户端也能正确的连接,但是一旦我用-s(以启动服务的方式启动)或者在windows服务管理中选择启动服务,客户端都不能正确的连接,查看监听的端口也不在监听中。
至少这个能说明-d下和-s启动的结果不同,请问这两种启动方式在原理上会有什么差异,而使得启动的结果不同呢?
以下是我的svc()函数,感觉是我在这里使用 ACE_Service_Config::open()启动静态注册的服务,在-s的方式下并没有被初始化,而且在停止服务的时候,ACE::fini()清理的时候出现错误,原因是我的Acceptor类并没有在Service:init()中被实例化,因此析构的时候析构到空值。
这是为什么呢?只有在-d的时候,注册的静态服务类才正确的被实例化、启动了。
我也猜想这个和reactor工作在不同的线程控件中有没有什么关系呢?却不知道怎么检测判断。

Win_NT_Service::svc (void)
{
ACE_DEBUG ((LM_DEBUG,
            "Service::svc\n"));
// As an NT service, we come in here in a different thread than the
// one which created the reactor.So in order to do anything, we
// need to own the reactor. If we are not a service, report_status
// will return -1.
if (report_status (SERVICE_RUNNING) == 0)
    reactor ()->owner (ACE_Thread::self ());
ACE_STATIC_SVC_REGISTER (Jabber_Room_Descriptor);
ACE_Service_Config::open
    (argc, argv, ACE_DEFAULT_LOGGER_KEY, 0);
reactor ()->run_reactor_event_loop ();

// Cleanly terminate connections, terminate threads.
ACE_DEBUG ((LM_DEBUG,
            "Shutting down\n"));
return 0;
}

peakzhang 发表于 2007-12-15 23:18:17

-d 是调试状态,直接调用svc方法,肯定是你实现的有问题。我用了N次都没错过的。

peakzhang 发表于 2007-12-15 23:18:24

我看到-d的结果就是

if (opt_debug)
    {
      SetConsoleCtrlHandler (&ConsoleHandler, 1);
      SERVICE::instance ()->svc ();
    }
else

所以说-d就是直接调用的SERVICE::instance ()->svc ();

而服务启动的代码是下面这个宏来完成的:

#define ACE_NT_SERVICE_RUN(SVCNAME, SVCINSTANCE, RET)                      \
ACE_TEXT_SERVICE_TABLE_ENTRY _ace_nt_svc_table =                      \
{                                                                        \
    ACE_NT_SERVICE_ENTRY(ACE_TEXT (#SVCNAME), SVCNAME),                  \
    { 0, 0 }                                                               \
};                                                                     \
_ace_nt_svc_obj_##SVCNAME = SVCINSTANCE;                                 \
_ace_nt_svc_obj_##SVCNAME->capture_log_msg_attributes ();                \
ACE_OS::last_error (0);                                                \
int RET = ACE_TEXT_StartServiceCtrlDispatcher(_ace_nt_svc_table);

这个StartServiceCtrlDispatcher()做得什么工作呢??他是告诉windows本服务的启动句柄是SERVICE::instance ()->svc ()么?

按道理说我也觉得不应该会有什么不同,只不过一个是由自身启动svc(),另一个是由windows来调用.

但确实就不正确,又不知道该怎么调试,所以很郁闷

我在静态服务启动的初始代码处放置了输出信息到文件的代码,在-d的方式下,没有任何问题,相关信息被写到文件中,但是用其他方式(没有console输出)启动服务则这段代码没有被执行,也就是ACE_Service_Config::open
    (argc, argv, ACE_DEFAULT_LOGGER_KEY, 0);

这行在svc()中的代码没有被正确执行,不知为何? 难道启动静态服务在普通application中和在NT_Service有什么不同和需要注意的么?

请问你有没有和我类似的代码,也就是用ACE_Service_Config::open()启动静态或动态服务的? 可否帮我看看有什么本质差别?

peakzhang 发表于 2007-12-15 23:18:34

你问的问题很多,其实归结到一点,你得先研究一下什么是windows service 程序,有要求。
看看MSPress-ProgrammingServer-SideApplications for MS Windows2000

peakzhang 发表于 2007-12-15 23:18:42

郁闷了很久,居然使用MessageBeep (MB_OK);这样的代码来定位看服务是否运行了某处代码,终于解决了问题。

结果如下:我在svc.conf定义了一个静态服务对象static AAA.这个类继承自ACE_Service_Object。

它在debug的情况下被正确的实例化并且调用了挂钩函数:init (int argc,ACE_TCHAR *argv[]),在这个函数中我实例化了自己的ACCEPTOR类,并启动监听。

然而在windows服务管理器来启动服务的时候,AAA被实例化,但是这个init()函数并没有被调用,我查了一下,这个init()函数是继承自ACE_Shared_Object类,这个函数的说明是:
virtual intinit (int argc, ACE_TCHAR *argv[])
Initializes object when dynamic linking occurs.


动态连接的时候被调用?不懂具体是什么意思,启动服务为什么不会激活这个函数?

于是我把原来放在init()中的代码放到了,AAA的构造函数中,代码于是被执行了。

所以说制作一个NT_Service还是有一些需要注意的细节问题,希望大家能更多的发布经验交流。
另外发现在服务中要读取文件的话也不能使用exe的相对路径,我是暂时用的绝对路径,看能不能有获取应用程序当前路径,和系统path变量之类的方式来解决文件方便定位的问题。

mouse0518 发表于 2009-9-24 11:21:41

NT_Service

启动服务后,关闭机器,如何kill掉此服务?

jamesyang680 发表于 2011-1-22 16:20:33

我在WIN7下直接运行NT_Service的例子,出现“Couldn't start service:服务进程无法连接到服务控制器上”的错误。这是什么原因呢?

winston 发表于 2011-1-22 16:34:41

楼上朋友,使用GetLastError()查看具体的错误代码。我估计跟权限有关系。
页: [1] 2
查看完整版本: 请教一个制作NT_Service的问题