1. 导出dll制作步骤
可以参考<<ACE程序员指南>>p25,dll制作,在此基础上,增加6和7
1)制定dll名称,例如AgentService(.dll)
2)用generate_export_file.pl AgentService 生成自定义的AgentServiceExport.h头文件
3)在dll的工程main函数所在的源文件包含所程程的AgentServiceExprot.h头文件
4) 在进行类的声明时,在class关键字和类名之间插入关键字AgentService_Export
注:若本身就是dll工程,则不需要该关键字
5)在编译DLL的源文件时,定义宏AgentService_BUILD_DLL
6) 在DLL的源文件中加入如下宏
ACE_FACTORY_DEFINE (AgentService, AgentServer)
AgentService为dll名称,AgentServer为dll中具体实现类的名称
7)编写svc.conf文件,供调用exe使用,格式为
dynamic AgentService Service_Object * AgentService:_make_AgentServer() "AgentServer.xml"
2. 调用者exe制作步骤
1)如果是console,则main函数中举例如下
#include <WINSOCK2.H>
#include <atlbase.h>
#include <ace/OS.h>
#include <ace/Object_Manager.h>
#include <ace/Service_Config.h>
#include <ace/Service_Repository.h>
#include <ace/Service_Object.h>
#include <windows.h>
int main(int argc, char* argv[])
{
CoInitialize(NULL);
ACE::init();
ACE_TCHAR path[MAX_PATH];
ACE_TCHAR cwd[MAX_PATH];
::GetModuleFileName(GetModuleHandle(NULL), path, MAX_PATH);
//LogEvent(path);
ACE_TCHAR* ptr = path + ACE_OS::strlen(path);
while(*ptr != TEXT('/') && *ptr != TEXT('//') && *ptr != TEXT(':')){
--ptr;
if(ptr == path) {
break;
}
}
*(++ptr) = '/0';
//::SetCurrentDirectory(path);
ACE_OS::getcwd(cwd, MAX_PATH);
ACE_OS::chdir(path);
int argcSvc = 4;
ACE_TCHAR *argvSvc[4] = {
ACE_TEXT("OnmiService"),
ACE_TEXT("-d"),
ACE_TEXT("-f"),
ACE_TEXT("svc.conf")
};
// argv[3] = path;
if(ACE_Service_Config::open(argcSvc, argvSvc) == -1) {
ACE_DEBUG((LM_ERROR, ACE_TEXT("start ace_service_config failed/n")));
}
while(1){
cin >> ch;
if(ch == 'q' || ch == 'Q')
{
ACE_DEBUG ((LM_INFO, ACE_TEXT (" | %t | %N %l | Stop OnmiConsole!/n")));
ACE_Service_Config::fini_svcs();
break;
}
}
CoUninitialize();
return 0;
}
2)如果是基于MFC的dialog应用,则需要创建一线程启动起dll
m_pConfigService = DYNAMIC_DOWNCAST(CConfigService, AfxBeginThread(RUNTIME_CLASS(CConfigService)));
if(m_pConfigService == NULL)
{
AfxMessageBox("Start Agent Service failed");
return -1;
}
class CConfigService : public CWinThread
{
}
CConfigService的实现,参考AGSD工程中ConfigService.h和ConfigService.cpp文件
///
//ConfigService.h
#pragma once
// CConfigService
class CConfigService : public CWinThread
{
DECLARE_DYNCREATE(CConfigService)
protected:
CConfigService(); // 动态创建所使用的受保护的构造函数
virtual ~CConfigService();
public:
virtual BOOL InitInstance();
virtual int ExitInstance();
virtual int Run( );
BOOL Stop(void);
FILE * GetOutStream();
BOOL m_isRun;
protected:
DECLARE_MESSAGE_MAP()
private:
FILE * m_out;
};
// ConfigService.cpp : 实现文件
//
#include "stdafx.h"
#include "AGSD.h"
#include "ConfigService.h"
#include <ace/OS.h>
#include <ace/Object_Manager.h>
#include <ace/Service_Config.h>
#include <ace/Service_Repository.h>
#include <ace/Service_Object.h>
#include <ace/Reactor.h>
// CConfigService
IMPLEMENT_DYNCREATE(CConfigService, CWinThread)
CConfigService::CConfigService()
: m_out(NULL)
{
}
CConfigService::~CConfigService()
{
}
BOOL CConfigService::InitInstance()
{
ACE::init();
errno_t err = freopen_s( &m_out, "freopen.out", "w+", stdout );
if(err != 0)
{
AfxMessageBox(TEXT("Redirection error!"));
}
return TRUE;
}
int CConfigService::Run( )
{
ACE_TCHAR path[MAX_PATH];
ACE_TCHAR cwd[MAX_PATH];
::GetModuleFileName(GetModuleHandle(NULL), path, MAX_PATH);
//LogEvent(path);
ACE_TCHAR* ptr = path + ACE_OS::strlen(path);
while(*ptr != TEXT('/') && *ptr != TEXT('//') && *ptr != TEXT(':'))
{
--ptr;
if(ptr == path) {
break;
}
}
*(++ptr) = '/0';
//::SetCurrentDirectory(path);
ACE_OS::getcwd(cwd, MAX_PATH);
ACE_OS::chdir(path);
int argcSvc = 4;
ACE_TCHAR *argvSvc[4] = {
ACE_TEXT("OnmiService"),
ACE_TEXT("-d"),
ACE_TEXT("-f"),
ACE_TEXT("svc.conf")
};
if(ACE_Service_Config::open(argcSvc, argvSvc) == -1) {
ACE_DEBUG((LM_ERROR, ACE_TEXT("start ace_service_config failed/n")));
}
m_isRun = TRUE;
return ACE_Reactor::instance()->run_reactor_event_loop ();
}
int CConfigService::ExitInstance()
{
freopen( "CON", "w", stdout );
return CWinThread::ExitInstance();
}
BEGIN_MESSAGE_MAP(CConfigService, CWinThread)
END_MESSAGE_MAP()
// CConfigService 消息处理程序
BOOL CConfigService::Stop(void)
{
ACE_Reactor::instance()->end_event_loop ();
return 0;
}
FILE * CConfigService::GetOutStream()
{
return stdout;
}
3. 解释说明
3.1. 建立工程说明
1)若本身就是dll工程的话,且有DllMain入口函数,则不需要对AgentServer类进行导出声明 AGENTSERVICE_Export;
2)只有从console改造过来的工程,而且没有main函数或者是DllMain函数,则需要对AgentServer类进行导出声明,声明符号为 AGENTSERVICE_Export,具体的符号需要使用$ACE_ROOT/bin/generate_export_file.pl AgentService 命令进行导出(AgentServcie为dll名称)
3.2. 宏ACE_FACTORY_DEFINE(CLS,SERVICE_CLASS)
对于ACE_FACTORY_DEFINE (AgentService, AgentServer)宏的解释
AgentService是: CLS-是程序/库用来导入/导出声明的标识符. 取决于使用上述命令生成Export头文件时传入的参数,并与参数保持一致。
AgentServer是: SERVICE_CLASS是从ACE_Service_Object派生的类的名称,它会在服务初始化时被实例化,并于内部需要导出的实现类名保持一致。
3.3. 配置文件svc.conf
svc.conf格式
dynamic ident Service_Object * lib-pathname : factory-func() [active|inactive] [parameters]
实例
dynamic AgentService Service_Object * AgentService:_make_AgentServer() "AgentServer.xml"
其中AgentService为标识符,可以起另外的名字
AgentService代表dll名称
_make_AgentServer()中的AgentServer为dll中实现类的名称
"AgentServer.xml"为参数名称
4. 举例
Visual C++下ACE动态服务配置入门
摘要:
服务动态配置在编写服务端应用在有很明显的优点,本文简要介绍用visual C++ (7.1)
编写ACE动态服务的步骤。
本文适用于ACE初学者。
1. 主进程
1.1 创建主程序
用Viusal Studio创建一空Win32 Console项目,这里命名为GLIVR86ServiceD.注,这里
D表示Daemon,不是Debug。表示我们以后会把这个项目改造为了NT_Service(以后介绍步骤.
1.2 修改项目属性 (Configuation Properties)
1.2.1 为项目新增主文件 GLIVR86ServiceD.cpp,目的是为项目属性中,增加C/C++选项
1.2.1 General 修改程序输出路径$(OutDir)
1.2.2.Debugging Command Arguments: -d, 以调试模式启动
1.2.3 C/C++设置
1.2.3.1 Additional Include Directories /I[path]: $(ACE_ROOT);
1.2.3.2 Code Generation: /MTd ;/MT 调试版选MTd,发行版选 MT
1.2.3.3 Preprocessor:Preprocesor Definitions/D: WIN32;_DEBUG;_CONSOLE;
这是调试版,发行版将_DEBUG改为NDEBUG
1.2.4 链接设置
1.2.4.1 Input: Additinal Dependencise: ACE(d).lib,调试版选aced.lib,
发行版选ace.lib
1.2.4.2 System: SubSystem /subsystem: Console ; (/SUBSYSTEM:CONSOLE)
主程序代码
// @file: GLIVR86ServiceD.cpp
// @description: IVR 86业务服务主程序入口
// @author: jiangtao
// @version:2.0.0
#include " stdafx.h "
#include < memory > // 使用 auto_ptr
#include " ACE/OS_NS_unistd.h "
#include " ACE/TP_Reactor.h "
#include " ACE/Reactor.h "
#include " ACE/Service_Config.h "
#include " ACE/Thread_Manager.h "
// 线程池
static ACE_THR_FUNC_RETURN event_loop ( void * arg)
{
ACE_DEBUG((LM_INFO, " (%P|%t),event_loop()/n " ));
ACE_Reactor * reactor = static_cast < ACE_Reactor *> (arg);
reactor -> owner (ACE_OS::thr_self ());
reactor -> run_reactor_event_loop ();
return 0 ;
}
int
ACE_TMAIN ( int argc, ACE_TCHAR * argv[])
{
const size_t N_THREADS = 4 ;
ACE_TP_Reactor tp_reactor;
ACE_Reactor reactor ( & tp_reactor);
auto_ptr < ACE_Reactor > delete_instance(ACE_Reactor::instance ( & reactor));
if (ACE_Service_Config::open (argc, argv) == - 1 )
ACE_ERROR_RETURN ((LM_ERROR,
ACE_TEXT ( " %p/n " ),
ACE_TEXT ( " open " )),
1 );
ACE_Thread_Manager::instance () -> spawn_n
(N_THREADS, event_loop, ACE_Reactor::instance ());
ACE_Thread_Manager::instance () -> wait ();
return 0 ;
}
2. 创建被加载的服务的动态链接库
2.1 用Visual Studio创建一个新的项目GLIVR86Service,我们依然从空白的Win32 Console开
始
1.2.1 为项目新增主文件 GLIVR86Service.cpp,目的是为项目属性中,增加C/C++选项
1.2.1 General : Configration Type:改为 动态链接库 Dynamic Library(DLL)
1.2.3.1 Additional Include Directories /I[path]: $(ACE_ROOT);
1.2.3.2 Code Generation: /MTd ;/MT 调试版选MTd,发行版选 MT
1.2.3.3 Preprocessor:Preprocesor Definitions/D:
WIN32;_DEBUG;_WINDOWS;ACE_BUILD_SVC_DLL
这是调试版,发行版将_DEBUG改为NDEBUG
这里,特别注意,要增加 ACE_BUILD_SVC_DLL宏。如果用generate_export_file.pl
生成自定义的export头文件,这个宏也可以自定义
2.2.2 链接器设置
2.2.2.1 Input: Additinal Dependencise: ACE(d).lib,调试版选aced.lib,
发行版选ace.lib
2.2.2.2 System: SubSystem /subsystem: Console ; (/SUBSYSTEM:CONSOLE)
2.2.2.3 General ,Output file:
../GLIVR86ServiceD/GLIVR86ServiceD/GLIVR86ServiceD.dll
这里填写上GLIVR86ServiceD的路径或环境变量Path中指
示的路径,这样可以方便调试
2.2.2.4 Adanced, Import Libaray: $(OutDir)/GLIVR86ServiceD.lib
上面是调试版,发行版可以去掉后缀D,即
$(OutDir)/GLIVR86Service.lib
3.服务的动态链接库实现
3.1 为项目增加两个文件,分别声明和实现服务类工厂
//@file: ServiceFactory.h
//@file: ServiceFactory.cpp
代码分别如下
// @file: ServiceFactory.h
// @description: IVR 86业务服务
// @author: jiangtao
// @data: 2006-7-3
// @version:1.0.0
#ifndef SERVICEFACTORY_H
#define SERVICEFACTORY_H
#include " ACE/svc_export.h "
#include " ACE/Service_Config.h "
#include " ACE/Service_Object.h "
// 声明服务工厂
ACE_SVC_FACTORY_DECLARE (ServiceFactory_T)
class ACE_Svc_Export ServiceFactory_T : public ACE_Service_Object
{
public :
/// Initializes object when dynamic linking occurs.
virtual int init ( int argc, ACE_TCHAR * argv[]);
/// Terminates object when dynamic unlinking occurs.
virtual int fini ( void );
/// Returns information on a service object.
virtual int info (ACE_TCHAR ** info_string, size_t length = 0 ) const ;
};
#endif /* SERVICEFACTORY_H */
/*******************************************************************/
// @file: ServiceFactory.cpp
#include " ServiceFactory.h "
#include " ACE/Log_Msg.h "
// 实现服务工厂
ACE_SVC_FACTORY_DEFINE (ServiceFactory_T)
int ServiceFactory_T::init( int argc, ACE_TCHAR * argv[])
{
ACE_DEBUG((LM_INFO, " (%P|%t) 服务初始化完成/n " ));
return 0 ;
}
int ServiceFactory_T::info(ACE_TCHAR ** strp, size_t length) const
{
ACE_DEBUG((LM_INFO, " ServiceFactory_T::info() /n " ));
return 0 ;
}
int ServiceFactory_T::fini( void )
{
return 0 ;
}
5.服务配置文件svc.conf
dynamic IVR86Service Service_Object * GLIVR86Service: _make_ServiceFactory_T() active
6. 运行结果
5. 另一举例配置动态服务
ACE Service Configurator框架更强大的功能表现在配置动态服务上。如果在运行时收到指示,动态服务器可以从共享库(DLL)中动态加载。这种能力允许你在运行时替换服务,从而提供极大的灵活性。
ACE Service Configurator框架简化了全部的冗余工作。我们只需要按照设计规格实现我们需要的服务,把它载入到一个动态链接库中,编辑配置文件即可。换句话说:我们只需要创建一个合乎规格的动态链接库,然后在上例的svc.conf里面添加一两行指令,再次运行该程序即可加载此动态服务,我们甚至不需要对该程序进行重新编译。
动态链接库Mydll
5.1. 确定我们将要设计的动态链接库名字为Mydll
5.2. 运行$ACE_ROOT/bin/generate_export_file.pl Mydll,将输出写入Mydll_Export.h中去。
设计我们的服务的类,在类的源文件中包含该文件,并将关键字
#i nclude “Mydll_Export.h”
class Mydll_Export MyDynamicObj : public ACE_Service_Object
{
public:
MyDynamicObj();
virtual ~MyDynamicObj();
virtual int init (int argc, ACE_TCHAR *argv[])
{
printf("MyDynamicObj::init------/n");
return 0;
}
virtual int fini()
{
printf("MyDynamicObj::fini-----/n");
return 0;
}
};
ACE_FACTORY_DEFINE(Mydll,MyDynamicObj)
5.3. 编译,并在svc.conf里面加入如下指令,再运行,发现动态服务已被加载、移除。
dynamic MyDynamicObj Service_Object* Mydll:_make_MyDynamicObj() ""
remove MyDynamicObj