找回密码
 用户注册

QQ登录

只需一步,快速开始

查看: 12099|回复: 10

请教一个制作NT_Service的问题

[复制链接]
发表于 2007-12-15 23:17:12 | 显示全部楼层 |阅读模式
参照ACE/example中的NT_Service的例子,我写了自己的NT_Service类
并且在NT_Service 的函数svc()中启动Service_Config.open()开启静态服务

----------------------------------------
  1. int
  2. Win_NT_Service::svc (void)
  3. {
  4.   ACE_DEBUG ((LM_DEBUG,
  5.               "Service::svc\n"));
  6.   // As an NT service, we come in here in a different thread than the
  7.   // one which created the reactor.  So in order to do anything, we
  8.   // need to own the reactor. If we are not a service, report_status
  9.   // will return -1.
  10.   if (report_status (SERVICE_RUNNING) == 0)
  11.     ACE_Reactor::instance ()->owner (ACE_Thread::self ());
  12. ACE_Service_Config::open
  13.     (argc, argv, ACE_DEFAULT_LOGGER_KEY, 0);
  14.   ACE_STATIC_SVC_REGISTER (Service_Descriptor);
  15.   stop_ = 0;
  16. while(!stop_) ACE_Reactor::instance ()->run_reactor_event_loop ();
  17.   // Cleanly terminate connections, terminate threads.
  18.   ACE_DEBUG ((LM_DEBUG,
  19.               "Shutting down\n"));
  20.   return 0;
  21. }
复制代码
--------------------------------------------

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

--------------------------------------------
  1. ACE_NT_SERVICE_DEFINE (Server,
  2.                        Win_NT_Service,
  3.                        ACE_TEXT ("Message Service"));
  4. int ACE_TMAIN (int argc, ACE_TCHAR *argv[]) {
  5.    WIN_NT_SERVICE::instance ()->name (ACE_TEXT ("Server"),
  6.                               ACE_TEXT ("Message Service"));
  7.   WIN_NT_SERVICE::instance ()->svc ();
  8.   return 0;
  9. }
复制代码
-------------------------------------------

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

----------------------------------------------------
  1. ACE_NT_SERVICE_DEFINE (Server,
  2.                        Win_NT_Service,
  3.                        ACE_TEXT ("Message Service"));
  4. int ACE_TMAIN (int argc, ACE_TCHAR *argv[]) {
  5. WIN_NT_SERVICE::instance ()->set_arg (argc, argv);
  6.    WIN_NT_SERVICE::instance ()->name (ACE_TEXT ("Server"),
  7.                               ACE_TEXT ("Message Service"));
  8.       ofstream *output_file = new ofstream("ntsvc.log", ios::out);
  9.       if (output_file && output_file->rdstate() == ios::goodbit)
  10.         ACE_LOG_MSG->msg_ostream(output_file, 1);
  11.       ACE_LOG_MSG->open(argv[0],
  12.                         ACE_Log_Msg::STDERR | ACE_Log_Msg::OSTREAM,
  13.                         0);
  14.       ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%T (%t): Starting service.\n")));
  15.       ACE_NT_SERVICE_RUN (RMServer,
  16.                           WIN_NT_SERVICE::instance (),
  17.                           ret);
  18.       if (ret == 0)
  19.         ACE_ERROR ((LM_ERROR,
  20.                     ACE_TEXT ("%p\n"),
  21.                     ACE_TEXT ("Couldn't start service")));
  22.       else
  23.         ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%T (%t): Service stopped.\n")));
  24.         
  25.   return 0;
  26. }
复制代码

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

另外在windows管理器中选择停止服务不能结束也是很奇怪的问题,我不知道该怎么调试,因为一旦这个服务被注册到windows服务管理器中,就不能象在VC中哪样单步跟踪和命令行输出DEBUG信息了,多谢指点~~!!
 楼主| 发表于 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,里面已经展示的很清晰了。
 楼主| 发表于 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工作在不同的线程控件中有没有什么关系呢?却不知道怎么检测判断。
  1. Win_NT_Service::svc (void)
  2. {
  3.   ACE_DEBUG ((LM_DEBUG,
  4.               "Service::svc\n"));
  5.   // As an NT service, we come in here in a different thread than the
  6.   // one which created the reactor.  So in order to do anything, we
  7.   // need to own the reactor. If we are not a service, report_status
  8.   // will return -1.
  9.   if (report_status (SERVICE_RUNNING) == 0)
  10.     reactor ()->owner (ACE_Thread::self ());
  11.   ACE_STATIC_SVC_REGISTER (Jabber_Room_Descriptor);
  12. ACE_Service_Config::open
  13.     (argc, argv, ACE_DEFAULT_LOGGER_KEY, 0);
  14. reactor ()->run_reactor_event_loop ();
  15.   // Cleanly terminate connections, terminate threads.
  16.   ACE_DEBUG ((LM_DEBUG,
  17.               "Shutting down\n"));
  18.   return 0;
  19. }
复制代码
 楼主| 发表于 2007-12-15 23:18:17 | 显示全部楼层
-d 是调试状态,直接调用svc方法,肯定是你实现的有问题。我用了N次都没错过的。
 楼主| 发表于 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[2] =                      \
  {                                                                        \
    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()启动静态或动态服务的? 可否帮我看看有什么本质差别?
 楼主| 发表于 2007-12-15 23:18:34 | 显示全部楼层
你问的问题很多,其实归结到一点,你得先研究一下什么是windows service 程序,有要求。
看看MSPress-ProgrammingServer-SideApplications for MS Windows2000
 楼主| 发表于 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 int  init (int argc, ACE_TCHAR *argv[])
Initializes object when dynamic linking occurs.  


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

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

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

NT_Service

启动服务后,关闭机器,如何kill掉此服务?
发表于 2011-1-22 16:20:33 | 显示全部楼层
我在WIN7下直接运行NT_Service的例子,出现“Couldn't start service:服务进程无法连接到服务控制器上”的错误。这是什么原因呢?
发表于 2011-1-22 16:34:41 | 显示全部楼层
楼上朋友,使用GetLastError()查看具体的错误代码。我估计跟权限有关系。
您需要登录后才可以回帖 登录 | 用户注册

本版积分规则

Archiver|手机版|小黑屋|ACE Developer ( 京ICP备06055248号 )

GMT+8, 2024-5-15 12:41 , Processed in 0.013720 second(s), 6 queries , Redis On.

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表