怎么把程序作为服务来运行?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了怎么把程序作为服务来运行?相关的知识,希望对你有一定的参考价值。
写了个服务端程序,不过主机只能运行一次,想在系统启动时(还没有登录到帐户时)就自动启动,不过在多用户下也只会启动一次,也就是说在远程登录时不会启动(因为在开机时程序就已经启动了).就像WINDOWS系统中的服务一样.怎么实现?
---- Windows NT与Windows 9x有一个非常重要的区别,即WindowsNT提供了很多功能强大的Service(服务)。这些Service可以随着NT的启动而自启动,也可以让用户通过控制面板启动,还可以被Win32应用程序起停。甚至在没有用户登录系统的情况下,这些Service也能执行。许多FTP、WWW服务器和数据库就是以Service的形式存在于NT上,从而实现了无人值守。就连最新版的“黑客”程序BackOrifice 2000也是以Service形式在NT上藏身的。由于Service的编程较复杂,许多开发者想开发自己的Service但往往都望而却步。鉴于此,下面我们就从头到尾来构造一个全新的Service,读者只要在程序中注明的地方加上自己的代码,那么就可以轻松拥有一个自己的Service。在编写Service之前,先介绍一下几个重要的函数:
---- 1. SC_HANDLE OpenSCManager( LPCTSTR lpMachineName,
LPCTSTR lpDatabaseName, DWORD dwDesiredAccess)
---- OpenSCManager 函数打开指定计算机上的service control
manager database。其中参数lpMachineName指定计算机名,若为空则指定为本机。LpDatabaseName为指定要打开的service
control manager database名, 默认为空。dwDesiredAccess指定操作的权限, 可以为下面取值之一:
---- SC_MANAGER_ALL_ACCESS file://所有权限
---- SC_MANAGER_CONNECT file://允许连接到service control
manager database
---- SC_MANAGER_CREATE_SERVICE file://允许创建服务对象并把它加入database
---- SC_MANAGER_ENUMERATE_SERVICE file://允许枚举database
中的Service
---- SC_MANAGER_LOCK file://允许锁住database
---- SC_MANAGER_QUERY_LOCK_STATUS file://允许查询database的封锁信息
---- 函数执行成功则返回一个指向service control manager
database的句柄,失败则返回NULL。注意:WINNT通过一个名为service control manager database的数据库来管理所有的Service,因此对Service的任何操作都应打开此数据库。
---- 2. SC_HANDLE CreateService(SC_HANDLE
hSCManager,
LPCTSTR lpServiceName,
LPCTSTR lpDisplayName,
DWORD dwDesiredAccess,
DWORD dwServiceType,
DWORD dwStartType,
DWORD dwErrorControl,
LPCTSTR lpBinaryPathName,
LPCTSTR lpLoadOrderGroup,
LPDWORD lpdwTagId,
LPCTSTR lpDependencies,
LPCTSTR lpServiceStartName,
LPCTSTR lpPassword)
---- CreatService函数产生一个新的SERVICE。其中参数hSCManager为指向service
control manager database 的句柄,由OpenSCManager返回。LpServiceName为SERVICE的名字,lpDisplayName为Service显示用名,dwDesiredAccess是访问权限,本程序中用SERVICE_ALL_ACCESS。wServiceType,指明SERVICE类型,本程序中用SERVICE_WIN32_OWN_PROCESS|
SERVICE_INTERACTIVE_PROCESS。dwStartType为Service启动方式,本程序采用自启动,即dwStartType等于SERVICE_AUTO_START。
dwErrorControl说明当Service在启动中出错时采取什么动作,本程序采用SERVICE_ERROR_IGNORE即忽约错误,读者可以改为其他的。LpBinaryPathName指明Service本体程序的路径名。剩下的五个参数一般可设为NULL。如函数调用成功则返回这个新Service的句柄,失败则返回NULL。与此函数对应的是DeleteService(
hService),它删除指定的Service。
---- 3. SC_HANDLE OpenService(SC_HANDLE hSCManager,LPCTSTR
lpServiceName, DWORD dwDesiredAccess )
---- OpenService函数打开指定的Service。其中参数hSCManager为指向service
control manager database 的句柄,由OpenSCManager返回。LpServiceName为Service的名字,dwDesiredAccess是访问权限,其可选值比较多,读者可以参看SDK
Help. 函数调用成功则返回打开的Service句柄,失败则返回NULL。
---- 4. BOOL StartService( SC_HANDLE hService,
DWORD dwNumServiceArgs,LPCTSTR *lpServiceArgVectors )
---- StartService函数启动指定的Service。其中参数hService
为指向Service的句柄,由OpenService返回。dwNumServiceAr为启动服务所需的参数的个数。lpszServiceArgs
为 启 动 服务所需的参数。函数执行成功则返回True, 失败则返回False。
---- 5. BOOL ControlService(SC_HANDLE hService
DWORD dwControl,LPSERVICE_STATUS lpServiceStatus )
---- Service程序没有专门的停止函数,而是用ControlService函数来控制Service的暂停、继续、停止等操作。参数dwControl指定发出的控制命令,可以为以下几个值:
SERVICE_CONTROL_STOP file://停止Service
SERVICE_CONTROL_PAUSE file://暂停Service
SERVICE_CONTROL_CONTINUE file://继续Service
SERVICE_CONTROL_INTERROGATE file://查询Service的状态
SERVICE_CONTROL_SHUTDOWN file://让ControlService调用失效
---- 参数lpServiceStatus是一个指向SERVICE_STATUS的指针。SERVICE_STATUS是一个比较重要的结构,它包含了Service的各种信息,如当前状态、可接受何种控制命令等等。
---- 6. BOOL QueryServiceStatus( SC_HANDLE
hService,LPSERVICE_STATUS lpServiceStatus )
---- QueryServiceStatus函数比较简单,它查询并返回当前Service的状态。
---- 编制一个Service一般需要两个程序,一个是Service本体,一个是用于对Service进行控制的控制程序。通常Service本体是一个console程序,而控制程序则是一个普通的Win32应用程序(当然,用户不用控制程序而通过控制面板也可对Service进行启、停,但不能进行添加、删除操作。)
---- 首先,我们来编写Service本体。对于Service本体来说,它一般又由以下三部分组成:main()、ServiceMain()、Handler(),下面是main()的源代码:(注:由于篇幅的关系,大部分程序都没进行错误处理,读者可以自己添上)
int main(int argc, char **argv)
SERVICE_TABLE_ENTRY ste[2]; //一个Service进程可以有多个线程,这是每个线程的入口表
ste[0].lpServiceName="W.Z.SERVICE"; //线程名字
ste[0].lpServiceProc=ServiceMain; //线程入口地址
ste[1].lpServiceName=NULL; //最后一个必须为NULL
ste[1].lpServiceProc=NULL;
StartServiceCtrlDispatcher(ste);
return 0;
---- main()是Service的主线程。当servie control manager开始一个Service进程时,它总是等待这个Service去调用StartServiceCtrlDispatcher()函数。main(
)作为这个进程的主线程应该在程序开始后尽快调用StartServiceCtrlDispatcher()。StartServiceCtrlDispatcher()在被调用后并不立即返回,它把本Service的主线程连接到service
control manager,从而让service control manager通过这个连接发送开始、停止等控制命令给主线程。主线程在这时就扮演了一个命令的转发器的角色,它或者调用Handle(
)去处理停止、继续等控制要求,或者产生一个新线程去执行ServiceMain。StartServiceCtrlDispatcher()在整个Service结束时才返回。
---- ServiceMain()是Service真正的入口点,必须在main()中进行了正确的定义。ServiceMain(
)的两个参数是由StartService()传递过来的。下面是ServiceMain()的源代码:
void WINAPI ServiceMain(DWORD dwArgc,LPTSTR *lpszArgv)
ssh=RegisterServiceCtrlHandler
("W.Z.SERVICE",Handler);
ss.dwServiceType=SERVICE_WIN32_OWN
_PROCESS|SERVICE_INTERACTIVE_PROCESS;
ss.dwCurrentState=SERVICE_START_PENDING;
file://如用户程序的代码比较多
(执行时间超过1秒),这儿要设成SERVICE_
START_PENDING,待用户程序完成后再设为SERVICE_RUNNING。
ss.dwControlsAccepted=SERVICE_ACCEPT_
STOP;//表明Service目前能接受的命令是停止命令。
ss.dwWin32ExitCode=NO_ERROR;
ss.dwCheckPoint=0;
ss.dwWaitHint=0;
SetServiceStatus(ssh, &ss);
file://必须随时更新数据库中Service的状态。
Mycode(); file://这儿可放入用户自己的代码
ss.dwServiceType=SERVICE_WIN32_OWN_
PROCESS|SERVICE_INTERACTIVE_PROCESS;
ss.dwCurrentState=SERVICE_RUNNING;
ss.dwControlsAccepted=SERVICE_ACCEPT_STOP;
ss.dwWin32ExitCode=NO_ERROR;
ss.dwCheckPoint=0;
ss.dwWaitHint=0;
SetServiceStatus(ssh,&ss);
Mycode();// 这儿也可放入用户自己的代码
在ServiceMain()中应该立即调用
RegisterServiceCtrlHandler()注册一个Handler
去处理控制程序或控制面板对Service的控制要求。
Handler()被转发器调用去处理要求,
下面是Handler()的源代码:
void WINAPI Handler(DWORD Opcode)
switch(Opcode)
case SERVICE_CONTROL_STOP: file://停止Service
Mycode();//这儿可放入用户自己的相关代码
ss.dwWin32ExitCode = 0;
ss.dwCurrentState =SERVICE_STOPPED;
file://把Service的当前状态置为STOP
ss.dwCheckPoint = 0;
ss.dwWaitHint = 0;
SetServiceStatus (ssh,&ss);
/必须随时更新数据库中Service的状态
break;
case SERVICE_CONTROL_INTERROGATE:
SetServiceStatus (ssh,&ss);
/必须随时更新数据库中Service的状态
break;
---- 好了,Service本体程序已基本完成,我们接着来看一下Service的控制程序:
---- 控制程序是一个标准的window程序,上面主要有四个按纽:Create Service、Delete
Service、start、stop,分别用来产生、删除、开始和停止Service。下面是它们的部分源代码:
1. 产生Service
void __fastcall TForm1::CreateBtnClick
(TObject *Sender)
scm=OpenSCManager(NULL,NULL,
SC_MANAGER_CREATE_SERVICE);
if (scm!=NULL)
svc=CreateService(scm,
"W.Z.SERVICE","W.Z.SERVICE",//Service名字
SERVICE_ALL_ACCESS,
SERVICE_WIN32_OWN_PROCESS
|SERVICE_INTERACTIVE_PROCESS,
SERVICE_AUTO_START,
file://以自动方式开始
SERVICE_ERROR_IGNORE,
"C:\\ntservice.exe", file://Service本体程序路径,
必须与具体位置相符
NULL,NULL,NULL,NULL,NULL);
if (svc!=NULL)
CloseServiceHandle(svc);
CloseServiceHandle(scm);
2. 删除Service
void __fastcall TForm1::DeleteBtnClick
(TObject *Sender)
scm=OpenSCManager(NULL,NULL,
SC_MANAGER_CONNECT);
if (scm!=NULL)
svc=OpenService(scm,"W.Z.SERVICE",
SERVICE_ALL_ACCESS);
if (svc!=NULL)
QueryServiceStatus(svc,&ServiceStatus);
if (ServiceStatus.dwCurrentState==
SERVICE_RUNNING)//删除前,先停止此Service.
ControlService(svc,
SERVICE_CONTROL_STOP,&ServiceStatus);
DeleteService(svc);
CloseServiceHandle(svc);
file://删除Service后,最好再调用CloseServiceHandle
file://以便立即从数据库中移走此条目。
CloseServiceHandle(scm);
3. 开始Service
void __fastcall TForm1::StartBtnClick(TObject *Sender)
scm=OpenSCManager(NULL,NULL,SC_MANAGER_CONNECT);
if (scm!=NULL)
svc=OpenService(scm,"W.Z.SERVICE",SERVICE_START);
if (svc!=NULL)
StartService(svc,0,NULL);//开始Service
CloseServiceHandle(svc);
CloseServiceHandle(scm);
4.停止Service
void __fastcall TForm1::StopBtnClick
(TObject *Sender)
scm=OpenSCManager(NULL,NULL,
SC_MANAGER_ALL_ACCESS);
if (scm!=NULL)
svc=OpenService(scm,"W.Z.SERVICE",
SERVICE_STOP|SERVICE_QUERY_STATUS);
if (svc!=NULL)
QueryServiceStatus(svc,&ServiceStatus);
if (ServiceStatus.dwCurrentState==
SERVICE_RUNNING)
ControlService(svc,
SERVICE_CONTROL_STOP,&ServiceStatus);
CloseServiceHandle(svc);
CloseServiceHandle(scm);
---- 本程序在C++Builder 和Windows NT 4.0下编译通过。
参考资料:C++Builder WINNT 编制 Service
参考技术A 我也想知道,在这里等答案。。 参考技术B 利用Windows API做成自己启动的,具体怎么实现不太清楚怎么把文件名以参数的方式传入python脚本
我有一个文件夹都是json档文件 我怎么可以让脚本去自动处理这些文件 并把数据收集到一个文件内
作为一名从小就看篮球的球迷,会经常逛虎扑篮球及湿乎乎等论坛,在论坛里面会存在很多精美图片,包括NBA球队、CBA明星、花边新闻、球鞋美女等等,如果一张张右键另存为的话真是手都点疼了。作为程序员还是写个程序来进行吧!
所以我通过Python+Selenium+正则表达式+urllib2进行海量图片爬取。
运行效果:
http://photo.hupu.com/nba/tag/马刺
http://photo.hupu.com/nba/tag/陈露
源代码:
# -*- coding: utf-8 -*-
"""
Crawling pictures by selenium and urllib
url: 虎扑 马刺 http://photo.hupu.com/nba/tag/%E9%A9%AC%E5%88%BA
url: 虎扑 陈露 http://photo.hupu.com/nba/tag/%E9%99%88%E9%9C%B2
Created on 2015-10-24
@author: Eastmount CSDN
"""
import time
import re
import os
import sys
import urllib
import shutil
import datetime
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import selenium.webdriver.support.ui as ui
from selenium.webdriver.common.action_chains import ActionChains
#Open PhantomJS
driver = webdriver.PhantomJS(executable_path="G:\\phantomjs-1.9.1-windows\\phantomjs.exe")
#driver = webdriver.Firefox()
wait = ui.WebDriverWait(driver,10)
#Download one Picture By urllib
def loadPicture(pic_url, pic_path):
pic_name = os.path.basename(pic_url) #删除路径获取图片名字
pic_name = pic_name.replace(\'*\',\'\') #去除\'*\' 防止错误 invalid mode (\'wb\') or filename
urllib.urlretrieve(pic_url, pic_path + pic_name)
#爬取具体的图片及下一张
def getScript(elem_url, path, nums):
try:
#由于链接 http://photo.hupu.com/nba/p29556-1.html
#只需拼接 http://..../p29556-数字.html 省略了自动点击"下一张"操作
count = 1
t = elem_url.find(r\'.html\')
while (count <= nums):
html_url = elem_url[:t] + \'-\' + str(count) + \'.html\'
#print html_url
\'\'\'
driver_pic.get(html_url)
elem = driver_pic.find_element_by_xpath("//div[@class=\'pic_bg\']/div/img")
url = elem.get_attribute("src")
\'\'\'
#采用正则表达式获取第3个<div></div> 再获取图片URL进行下载
content = urllib.urlopen(html_url).read()
start = content.find(r\'<div class="flTab">\')
end = content.find(r\'<div class="comMark" style>\')
content = content[start:end]
div_pat = r\'<div.*?>(.*?)<\\/div>\'
div_m = re.findall(div_pat, content, re.S|re.M)
#print div_m[2]
link_list = re.findall(r"(?<=href=\\").+?(?=\\")|(?<=href=\\\').+?(?=\\\')", div_m[2])
#print link_list
url = link_list[0] #仅仅一条url链接
loadPicture(url, path)
count = count + 1
except Exception,e:
print \'Error:\',e
finally:
print \'Download \' + str(count) + \' pictures\\n\'
#爬取主页图片集的URL和主题
def getTitle(url):
try:
#爬取URL和标题
count = 0
print \'Function getTitle(key,url)\'
driver.get(url)
wait.until(lambda driver: driver.find_element_by_xpath("//div[@class=\'piclist3\']"))
print \'Title: \' + driver.title + \'\\n\'
#缩略图片url(此处无用) 图片数量 标题(文件名) 注意顺序
elem_url = driver.find_elements_by_xpath("//a[@class=\'ku\']/img")
elem_num = driver.find_elements_by_xpath("//div[@class=\'piclist3\']/table/tbody/tr/td/dl/dd[1]")
elem_title = driver.find_elements_by_xpath("//div[@class=\'piclist3\']/table/tbody/tr/td/dl/dt/a")
for url in elem_url:
pic_url = url.get_attribute("src")
html_url = elem_title[count].get_attribute("href")
print elem_title[count].text
print html_url
print pic_url
print elem_num[count].text
#创建图片文件夹
path = "E:\\\\Picture_HP\\\\" + elem_title[count].text + "\\\\"
m = re.findall(r\'(\\w*[0-9]+)\\w*\', elem_num[count].text) #<a href="http://www.aspku.com/tech/jiaoben/%3Ca%20href=" tech="" jiaoben="" python="" 270537.html"="" style="background: none; border: none; padding: 0px; margin: 0px; text-decoration: none; font-family: Tahoma, Geneva, sans-serif; color: rgb(59, 89, 152);">python/95398.html">爬虫图片张数
nums = int(m[0])
count = count + 1
if os.path.isfile(path): #Delete file
os.remove(path)
elif os.path.isdir(path): #Delete dir
shutil.rmtree(path, True)
os.makedirs(path) #create the file directory
getScript(html_url, path, nums) #visit pages
except Exception,e:
print \'Error:\',e
finally:
print \'Find \' + str(count) + \' pages with key\\n\'
#Enter Function
def main():
#Create Folder
basePathDirectory = "E:\\\\Picture_HP"
if not os.path.exists(basePathDirectory):
os.makedirs(basePathDirectory)
#Input the Key for search str=>unicode=>utf-8
key = raw_input("Please input a key: ").decode(sys.stdin.encoding)
print \'The key is : \' + key
#Set URL List Sum:1-2 Pages
print \'Ready to start the Download!!!\\n\\n\'
starttime = datetime.datetime.now()
num=1
while num<=1:
#url = \'http://photo.hupu.com/nba/tag/%E9%99%88%E9%9C%B2?p=2&o=1\'
url = \'http://photo.hupu.com/nba/tag/%E9%A9%AC%E5%88%BA\'
print \'第\'+str(num)+\'页\',\'url:\'+url
#Determine whether the title contains key
getTitle(url)
time.sleep(2)
num = num + 1
else:
print \'Download Over!!!\'
#get the runtime
endtime = datetime.datetime.now()
print \'The Running time : \',(endtime - starttime).seconds
main()
如果是从外部类似
$ xxx.py xxx.xx这种命令, 可以使用sys.argv实现, 如果是遍历过滤可以使用os模块
def get_data_from_dir(self, encoding='utf-8'):'''
从json文件目录提取数据
:param encoding:
:return:
'''
# 获取json_path目录下的文件(夹)
json_file_list = os.listdir(json_path)
# 遍历所有文件
for json_file in json_file_list:
# 检测文件拓展名是不是json(注意需要加 点)
# 此处可能需要增加对文件夹的检测
if os.path.splitext(os.path.join(json_path, json_file))[-1] != '.json':
continue
f = open(os.path.join(json_path, json_file), 'r', encoding=encoding)
d = json.loads(f.read())
f.close()
#d为json内容, 可进行其他操作
# ...
return
以上是关于怎么把程序作为服务来运行?的主要内容,如果未能解决你的问题,请参考以下文章