将基于MFC的对话框应用程序修改为服务程序
文章转自王牌软件
站长推荐:NSetup一键部署软件
一键式完成美化安装包制作,自动增量升级,数据统计,数字签名。应对各种复杂场景,脚本模块化拆分,常规复杂的脚本代码,图形化设置。无需专业的研发经验,轻松完成项目部署。(www.nsetup.cn)
只回答业务咨询
站长推荐:NSetup一键部署软件
一键式完成美化安装包制作,自动增量升级,数据统计,数字签名。应对各种复杂场景,脚本模块化拆分,常规复杂的脚本代码,图形化设置。无需专业的研发经验,轻松完成项目部署。(www.nsetup.cn)
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 |
Windows(NT/2000)下有很多服务程序(它们在系统登录前运行),它们一般都没有界面,我们可以在服务管理器(运行services.msc)中启动和关闭它们。下面我试着修改一个有界面的MFC对话框程序,使它成为一个服务程序。网上提到了一种方法就是,从建立一个COM服务程序入手,然后将一个MFC项目改造成服务程序,最后让这一程序在启动时可以显示图形界面。这种方法的优点就是,程序向导已经帮我们写好了服务程序的主要代码,我们的任务就是把它们移植到MFC程序中(这个方法很不错!)。我的方法和这种方法思想基本一致,但也不完全一样。我是直接将有些写在CUI服务程序中的代码移植过来。主要思想就是把主服务函数等定义为全局函数,这样在主对话框类中就可以访问它们了。 此程序需要注意的地方: 一次只能安装一个服务,如果已安装过一个服务,先将其卸载再安装其他服务,设置其他应用程序时,在SCM启动后,因没有相应启动请求会被kill掉。本来以为任何程序都可以被设置为服务程序,后来实验发现,一般的应用程序被设置为服务程序后,由于它不能够与SCM进行通信,所以SCM无法将其启动。错误提示如下:(下面wcdj是我的服务名字) 本地计算机无法启动wcdj服务 错误1053:服务没有及时响应启动或控制请求 这个服务程序的主要流程如下: SERVICE_TABLE_ENTRY DispatchTable[]={{"Service1",ServiceMain},{NULL,NULL}}; if (!StartServiceCtrlDispatcher(DispatchTable)) { AfxMessageBox("当不是用SCM启动程序的时候,程序执行下面的代码"); ... //显示我们服务程序的对话框 CTestDlg dlg; //m_pMainWnd = &dlg; int nResponse = dlg.DoModal(); if (nResponse == IDOK) { // TODO: Place code here to handle when the dialog is // dismissed with OK } else if (nResponse == IDCANCEL) { // TODO: Place code here to handle when the dialog is // dismissed with Cancel } } 否则程序会执行回调函数: void WINAPI ServiceMain(DWORD argc, LPTSTR *argv) { AfxMessageBox("当用SCM启动程序的时候,程序执行下面的代码"); //初始化 m_ServiceStatus.dwServiceType = SERVICE_WIN32; m_ServiceStatus.dwCurrentState = SERVICE_START_PENDING; ... while(1) { //Sleep(3000); //Place Your Code for processing here.... //显示我们服务程序的对话框(当SCM启动服务程序的时候(系统重启时或手动在SCM中启动时),也让它显示主对话框界面) CTestDlg dlg; //m_pMainWnd = &dlg; int nResponse = dlg.DoModal(); if (nResponse == IDOK) { // TODO: Place code here to handle when the dialog is // dismissed with OK } else if (nResponse == IDCANCEL) { // TODO: Place code here to handle when the dialog is // dismissed with Cancel } ... } } 具体的细节看下面的步骤,主要步骤如下: (1)首先生成一个基于对话框的应用程序框架,假设我的工程名称为test。 (2)在test.cpp中添加几个全局变量和几个全局函数。 //设置两个全局变量 SERVICE_STATUS m_ServiceStatus; SERVICE_STATUS_HANDLE m_ServiceStatusHandle; //添加几个全局函数 ////////////////////////////////////////////////////////////////////////// //函数声明 void WINAPI ServiceMain(DWORD argc, LPTSTR *argv); void WINAPI ServiceCtrlHandler(DWORD Opcode); BOOL InstallService(CString &strPath); BOOL DeleteService(); ////////////////////////////////////////////////////////////////////////// //函数定义 void WINAPI ServiceMain(DWORD argc, LPTSTR *argv) { // DWORD status; // DWORD specificError; AfxMessageBox("当用SCM启动程序的时候,程序执行下面的代码"); m_ServiceStatus.dwServiceType = SERVICE_WIN32; m_ServiceStatus.dwCurrentState = SERVICE_START_PENDING; m_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; m_ServiceStatus.dwWin32ExitCode = 0; m_ServiceStatus.dwServiceSpecificExitCode = 0; m_ServiceStatus.dwCheckPoint = 0; m_ServiceStatus.dwWaitHint = 0; m_ServiceStatusHandle = RegisterServiceCtrlHandler("Service1",ServiceCtrlHandler); if (m_ServiceStatusHandle == (SERVICE_STATUS_HANDLE)0) { AfxMessageBox("Handler not installed"); return; } m_ServiceStatus.dwCurrentState = SERVICE_RUNNING; m_ServiceStatus.dwCheckPoint = 0; m_ServiceStatus.dwWaitHint = 0; if (!SetServiceStatus (m_ServiceStatusHandle, &m_ServiceStatus)) { } //bRunning=true; //while(bRunning) while(1) { //Sleep(3000); //Place Your Code for processing here.... CTestDlg dlg; //m_pMainWnd = &dlg; int nResponse = dlg.DoModal(); if (nResponse == IDOK) { // TODO: Place code here to handle when the dialog is // dismissed with OK } else if (nResponse == IDCANCEL) { // TODO: Place code here to handle when the dialog is // dismissed with Cancel //AfxMessageBox("cancel"); //break;//跳出while循环 m_ServiceStatus.dwCurrentState = SERVICE_STOPPED; m_ServiceStatus.dwCheckPoint = 0; m_ServiceStatus.dwWaitHint = 0; if (!SetServiceStatus (m_ServiceStatusHandle, &m_ServiceStatus)) { } exit(0);//后面退出不了,只能强行退出 } #ifdef _temp_delete//使用下面代码,只能关闭对话框不能关闭程序 //关闭对话框用,传递消息 MSG msg; while (GetMessage(&msg, 0, 0, 0)) { if (msg.message) { CString strMsg; strMsg.Format("%d",msg.message); AfxMessageBox(strMsg); } DispatchMessage(&msg); } #endif } return; } void WINAPI ServiceCtrlHandler(DWORD Opcode) { switch(Opcode) { case SERVICE_CONTROL_PAUSE: m_ServiceStatus.dwCurrentState = SERVICE_PAUSED; break; case SERVICE_CONTROL_CONTINUE: m_ServiceStatus.dwCurrentState = SERVICE_RUNNING; break; case SERVICE_CONTROL_STOP: m_ServiceStatus.dwWin32ExitCode = 0; m_ServiceStatus.dwCurrentState = SERVICE_STOPPED; m_ServiceStatus.dwCheckPoint = 0; m_ServiceStatus.dwWaitHint = 0; SetServiceStatus (m_ServiceStatusHandle,&m_ServiceStatus); //bRunning=false; break; case SERVICE_CONTROL_INTERROGATE: break; } return; } BOOL InstallService(CString &strPath)//无法创建其他应用程序为服务,因为它们不能响应启动请求 { //char strDir[1024]={0}; HANDLE schSCManager,schService; // GetCurrentDirectory(1024,strDir); // strcat(strDir,"\\test.exe"); schSCManager = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS); if (schSCManager == NULL) return false; //LPCTSTR lpszBinaryPathName=strDir; LPCTSTR lpszBinaryPathName; if (strPath=="") { AfxMessageBox("You must tell me Exepath!"); return FALSE; } else { lpszBinaryPathName=strPath; } schService = CreateService(schSCManager,"Service1","wcdj",// service name to display SERVICE_ALL_ACCESS, // desired access SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS, // service type //SERVICE_DEMAND_START, // start type SERVICE_AUTO_START, //系统启动时自动启动 SERVICE_ERROR_NORMAL, // error control type lpszBinaryPathName, // service's binary NULL, // no load ordering group NULL, // no tag identifier NULL, // no dependencies NULL, // LocalSystem account NULL); // no password if (schService == NULL) return false; CloseServiceHandle(schService); return true; } BOOL DeleteService() { HANDLE schSCManager; SC_HANDLE hService; schSCManager = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS); if (schSCManager == NULL) return false; hService=OpenService(schSCManager,"Service1",SERVICE_ALL_ACCESS); if (hService == NULL) return false; if(DeleteService(hService)==0) return false; if(CloseServiceHandle(hService)==0) return false; else return true; } (3)修改BOOL CTestApp::InitInstance()中代码,还是在test.cpp中。 //在别处显示对话框 // CTestDlg dlg; // m_pMainWnd = &dlg; // int nResponse = dlg.DoModal(); // if (nResponse == IDOK) // { // // TODO: Place code here to handle when the dialog is // // dismissed with OK // } // else if (nResponse == IDCANCEL) // { // // TODO: Place code here to handle when the dialog is // // dismissed with Cancel // } //启动服务(两种方式:双击运行和SCM启动,执行流程如刚开始提到的那样),对话框在下面显示 SERVICE_TABLE_ENTRY DispatchTable[]={{"Service1",ServiceMain},{NULL,NULL}}; //StartServiceCtrlDispatcher(DispatchTable); if (!StartServiceCtrlDispatcher(DispatchTable)) { AfxMessageBox("当不是用SCM启动程序的时候,程序执行下面的代码"); CTestDlg dlg; //m_pMainWnd = &dlg; int nResponse = dlg.DoModal(); if (nResponse == IDOK) { // TODO: Place code here to handle when the dialog is // dismissed with OK } else if (nResponse == IDCANCEL) { // TODO: Place code here to handle when the dialog is // dismissed with Cancel } } 再在上面声明全局函数: extern void WINAPI ServiceMain(DWORD argc, LPTSTR *argv); (4)在主对话框上,添加4个新按钮和1个编辑框,功能分别是:安装服务、删除服务、打开服务管理器、显示启动项。代码如下: void CTestDlg::OnButton1() //安装服务 { UpdateData(TRUE);//读取编辑框变量m_strSvrPath的值 if(InstallService(m_strSvrPath)) { //printf("\n\nService Installed Sucessfully\n"); MessageBox("Service Installed Sucessfully","note",MB_OK|MB_ICONINFORMATION); } else { //printf("\n\nError Installing Service\n"); MessageBox("Error Installing Service","note",MB_OK|MB_ICONWARNING); } } void CTestDlg::OnButton2() //删除服务 { if(DeleteService()) { //printf("\n\nService UnInstalled Sucessfully\n"); MessageBox("Service UnInstalled Sucessfully","note",MB_OK|MB_ICONINFORMATION); } else { //printf("\n\nError UnInstalling Service\n"); MessageBox("Error UnInstalling Service","note",MB_OK|MB_ICONWARNING); } } void CTestDlg::OnButton3() //打开服务管理器 { //打开服务管理器 ::ShellExecute(NULL,"open","cmd.exe","/c services.msc",NULL,SW_HIDE); } void CTestDlg::OnButton4() //显示启动项 { //打开启动项 ::ShellExecute(NULL,"open","cmd.exe","/c msconfig",NULL,SW_HIDE); } 最后别忘了在开头声明全局函数: extern void WINAPI ServiceCtrlHandler(DWORD Opcode); extern BOOL InstallService(CString &strPath); extern BOOL DeleteService(); 这个服务程序还不是很完善,应该再添加一些判断和LogEvent记录信息,但是主要的服务程序框架应该都包含了。 最后,希望读到此篇文章的朋友提出自己的意见。 :) |
学习日记,兼职软件设计,软件修改,毕业设计。
本文出自 学习日记,转载时请注明出处及相应链接。
本文永久链接: https://www.softwareace.cn/?p=460