基于数据共享进行通信的服务编程
2009年4月14日星期二
通常为了提高程序的运行权限,如在系统启动是进行启动运行,就需要用到服务,这里简单的给出基于Win32应用程序框架的服务(和控制台类似),详细不再介绍,见代码注释。
void Init(); // 初始化函数
BOOL IsInstalled(); // 判断服务是否已被装载
BOOL Install(); // 装载服务
BOOL Uninstall(); // 卸载服务
void LogEvent(LPCTSTR pszFormat,...); // 打印日志
void WINAPI ServiceMain(); // 服务主函数
void WINAPI ServiceStrl(DWORD dwOpcode); // 处理服务事件函数
// Your function for your complement in the service.
BOOL Test(int i);
TCHAR szServiceName[] = _T("ServiceTest"); // 服务名
BOOL bInstall; // 服务是否被装载
SERVICE_STATUS_HANDLE hServiceStatus; // 服务状态句柄
SERVICE_STATUS status; // 服务状态
DWORD dwThreadID; // 线程ID
// Win32应用程序入口函数
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE,
LPSTR lpCmdLine,
int nShowCmd)
{
// 初始化
Init();
// 获取当前应用程序主线程ID
dwThreadID = ::GetCurrentThreadId();
SERVICE_TABLE_ENTRY st[] =
{
{ szServiceName, (LPSERVICE_MAIN_FUNCTION)ServiceMain },
{ NULL, NULL }
};
// 比较判断命令行输入命令参数
if (_stricmp(lpCmdLine,"/install") == 0)
{
Install();
}
else if (_stricmp(lpCmdLine, "/uninstall") == 0)
{
Uninstall();
}
else
{
// 将当前线程挂接到服务控制管理器
if (!StartServiceCtrlDispatcher(st))
{
DWORD d = GetLastError();
LogEvent(_T("Register Service Main Function Error!"));
}
}
return 0;
}
// 初始化函数
void Init()
{
// 初始化SERVICE_STATUS结构
hServiceStatus = NULL;
status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
status.dwCurrentState = SERVICE_STOPPED;
status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
status.dwWin32ExitCode = 0;
status.dwServiceSpecificExitCode = 0;
status.dwCheckPoint = 0;
status.dwWaitHint = 0;
}
// 服务主函数
void WINAPI ServiceMain()
{
// 创建共享数据区
CreateDataShare();
// 设置参数为:启动等待
status.dwCurrentState = SERVICE_START_PENDING;
status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
// 注册服务处理函数
hServiceStatus = RegisterServiceCtrlHandler(szServiceName,ServiceStrl);
if (hServiceStatus == NULL)
{
LogEvent(_T("Handler not installed"));
return;
}
// 将服务设置为参数指定的服务状态
SetServiceStatus(hServiceStatus,&status);
// 设置服务为运行状态
status.dwWin32ExitCode = S_OK;
status.dwCheckPoint = 0;
status.dwWaitHint = 0;
status.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(hServiceStatus,&status);
// 向前台窗口发服务启动消息
WriteData(Service_Start);
// Add your code here./
int i = 0;
while (1)
{
if (Test(i))
break;
else
Sleep(1000);
i++;
}
// 向前台窗口发服务停止消息
WriteData(Service_End);
// 将服务设置为停止状态
status.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(hServiceStatus,&status);
LogEvent(_T("YS:Service stopped"));
// 删除共享数据区
DeleteDataShare();
}
// 服务处理函数
void WINAPI ServiceStrl(DWORD dwOpcode)
{
// 根据服务控制码,进行状态设置
switch (dwOpcode)
{
case SERVICE_CONTROL_STOP:
status.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus(hServiceStatus,&status);
PostThreadMessage(dwThreadID,WM_CLOSE,0,0);
break;
case SERVICE_CONTROL_PAUSE:
break;
case SERVICE_CONTROL_CONTINUE:
break;
case SERVICE_CONTROL_INTERROGATE:
break;
case SERVICE_CONTROL_SHUTDOWN:
break;
default:
LogEvent(_T("Bad service request"));
}
}
// 判断服务是否已被装载
BOOL IsInstalled()
{
BOOL bResult = FALSE;
// 打开服务控制管理器
SC_HANDLE hSCM = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
if (hSCM != NULL)
{
// 打开服务
SC_HANDLE hService = OpenService(hSCM,szServiceName,SERVICE_QUERY_CONFIG);
if (hService != NULL)
{
bResult = TRUE;
// 关闭服务句柄
CloseServiceHandle(hService);
}
// 关闭服务控制管理器句柄
CloseServiceHandle(hSCM);
}
return bResult;
}
// 装载服务
BOOL Install()
{
// 如果已安装,则返回
if (IsInstalled()) return TRUE;
// 打开服务控制管理器
SC_HANDLE hSCM = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
if (hSCM == NULL)
{
MessageBox(NULL,_T("Couldn't open service manager"), szServiceName, MB_OK);
return FALSE;
}
// 获取当前执行的.exe的全路径
TCHAR szFilePath[MAX_PATH];
GetModuleFileName(NULL,szFilePath,MAX_PATH);
// 创建服务
SC_HANDLE hService = CreateService(hSCM,szServiceName,szServiceName,SERVICE_ALL_ACCESS,SERVICE_WIN32_OWN_PROCESS,
SERVICE_DEMAND_START,SERVICE_ERROR_NORMAL,szFilePath, NULL, NULL, _T(""), NULL, NULL);
if (hService == NULL)
{
CloseServiceHandle(hSCM);
MessageBox(NULL, _T("Couldn't create service"), szServiceName, MB_OK);
return FALSE;
}
CloseServiceHandle(hService);
CloseServiceHandle(hSCM);
return TRUE;
}
// 卸载服务
BOOL Uninstall()
{
if (!IsInstalled()) return TRUE;
// 打开服务控制管理器
SC_HANDLE hSCM = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
if (hSCM == NULL)
{
MessageBox(NULL, _T("Couldn't open service manager"), szServiceName, MB_OK);
return FALSE;
}
// 打开服务
SC_HANDLE hService = OpenService(hSCM,szServiceName,SERVICE_STOP | DELETE);
if (hService == NULL)
{
CloseServiceHandle(hService);
MessageBox(NULL, _T("Couldn't open service"), szServiceName, MB_OK);
return FALSE;
}
// 将服务控制码发送给系统
ControlService(hService,SERVICE_CONTROL_STOP,&status);
// 删除服务
BOOL bDelete = DeleteService(hService);
CloseServiceHandle(hService);
CloseServiceHandle(hSCM);
if (bDelete) return TRUE;
LogEvent(_T("Service could not be deleted"));
return FALSE;
}
// 打印日志函数
void LogEvent(LPCTSTR pFormat,...)
{
TCHAR chMsg[256];
HANDLE hEventSource;
LPTSTR lpszStrings[1];
va_list pArg; // #include <stdarg.h>
// 实现函数可变参数
va_start(pArg, pFormat);
_vstprintf_s(chMsg, 256, pFormat, pArg);
va_end(pArg);
lpszStrings[0] = chMsg;
// 返回注册事件日志句柄
hEventSource = RegisterEventSource(NULL,szServiceName);
if (hEventSource != NULL)
{
// 打印事件日志
ReportEvent(hEventSource, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (LPCTSTR*) &lpszStrings[0], NULL);
// 注销事件日志句柄
DeregisterEventSource(hEventSource);
}
}
服务通常是无操作界面的,因此需要和前台应用程序进行结合使用,那么就涉及到进程间通信的问题了。这里我使用共享内存的方法(即内存映射),但由于在服务里默认创建的内存映射权限较高,应用程序无法访问,因此需要手动指定服务里的内存映射的权限。下面给出详细代码:
// 设置安全访问控制
void init_sec_attr(SECURITY_ATTRIBUTES &sa)
{
DWORD dwRes;
PSID pEveryoneSID = NULL, pAdminSID = NULL;
PACL pACL = NULL;
PSECURITY_DESCRIPTOR pSD = NULL;
EXPLICIT_ACCESS ea[2];
SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;
// Create a well-known SID for the Everyone group.
if(!AllocateAndInitializeSid(&SIDAuthWorld, 1,
SECURITY_WORLD_RID,
0, 0, 0, 0, 0, 0, 0,
&pEveryoneSID))
{
printf("AllocateAndInitializeSid Error %u/n", GetLastError());
}
// Initialize an EXPLICIT_ACCESS structure for an ACE.
// The ACE will allow Everyone read access to the key.
ZeroMemory(&ea, 2 * sizeof(EXPLICIT_ACCESS));
ea[0].grfAccessPermissions = STANDARD_RIGHTS_ALL;
ea[0].grfAccessMode = SET_ACCESS;
ea[0].grfInheritance= NO_INHERITANCE;
ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
ea[0].Trustee.ptstrName = (LPTSTR) pEveryoneSID;
// Create a SID for the BUILTIN/Administrators group.
if(! AllocateAndInitializeSid(&SIDAuthNT, 2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&pAdminSID))
{
printf("AllocateAndInitializeSid Error %u/n", GetLastError());
}
// Initialize an EXPLICIT_ACCESS structure for an ACE.
// The ACE will allow the Administrators group full access to
// the key.
ea[1].grfAccessPermissions = KEY_ALL_ACCESS;
ea[1].grfAccessMode = SET_ACCESS;
ea[1].grfInheritance= NO_INHERITANCE;
ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
ea[1].Trustee.ptstrName = (LPTSTR) pAdminSID;
// Create a new ACL that contains the new ACEs.
dwRes = SetEntriesInAcl(2, ea, NULL, &pACL);
if (ERROR_SUCCESS != dwRes)
{
printf("SetEntriesInAcl Error %u/n", GetLastError());
}
// Initialize a security descriptor.
pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR,
SECURITY_DESCRIPTOR_MIN_LENGTH);
if (NULL == pSD)
{
printf("LocalAlloc Error %u/n", GetLastError());
}
if (!InitializeSecurityDescriptor(pSD,
SECURITY_DESCRIPTOR_REVISION))
{
printf("InitializeSecurityDescriptor Error %u/n",
GetLastError());
}
// Add the ACL to the security descriptor.
if (!SetSecurityDescriptorDacl(pSD,
TRUE, // bDaclPresent flag
NULL, //pACL,
FALSE)) // not a default DACL
{
printf("SetSecurityDescriptorDacl Error %u/n",
GetLastError());
}
sa.nLength = sizeof (SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = pSD;
sa.bInheritHandle = FALSE;
}
// 创建共享数据区
BOOL CreateDataShare()
{
// 打开文件映射
hMap = ::OpenFileMapping(FILE_MAP_ALL_ACCESS,FALSE,Data_Share);
if (!hMap)
{
SECURITY_ATTRIBUTES sa;
// 设置安全访问控制
init_sec_attr(sa);
// 创建文件映射
hMap = ::CreateFileMapping(INVALID_HANDLE_VALUE,&sa,PAGE_READWRITE,0,MAX_PATH,Data_Share);
if (!hMap) return FALSE;
}
// 映射对象的一个视图,得到指向共享内存的指针
pBuffer = ::MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0,0,0);
if (!pBuffer) return FALSE;
return TRUE;
}
// 删除共享数据区
void DeleteDataShare()
{
// 解除文件映射
::UnmapViewOfFile(pBuffer);
// 关闭内存映射文件对象句柄
::CloseHandle(hMap);
}
// 在共享数据区写入要发送的消息
void WriteData(LPCSTR lpszMessage)
{
memset(pBuffer,0,MAX_PATH);
memcpy(pBuffer,lpszMessage,strlen(lpszMessage));
}