家里有臺很多年前買的電腦,CMOS電池殘廢了,經(jīng)常遇到開機后系統(tǒng)時間被重置的情況,老媽向我反映用起來很不方便。于是身為一個程序員的我想到寫個小工具來幫老媽排憂解難。話不多說,小工具需求如下:
功能需求 -- 電腦開機后自動執(zhí)行時間同步
非功能需求 -- 安裝執(zhí)行簡單,無需安裝額外環(huán)境
一、代碼實現(xiàn)
基于以上需求,思路如下:訪問網(wǎng)絡(luò)獲取北京時間,然后調(diào)用命令行來設(shè)置系統(tǒng)時間。程序?qū)懗蒞indows Service,并設(shè)置為開機自動運行。正好前段時間在學(xué)習(xí)Python,所以打算用Python來寫這個工具。具體代碼如下:
獲取網(wǎng)絡(luò)時間
1 def getBeijinTime(): 2 """ 3 獲取北京時間 4 """ 5 try: 6 conn = httplib.HTTPConnection("www.beijing-time.org") 7 conn.request("GET", "/time.asp") 8 response = conn.getresponse() 9 print response.status, response.reason 10 if response.status == 200: 11 #解析響應(yīng)的消息 12 result = response.read() 13 logging.debug(result) 14 data = result.split("\r\n") 15 year = data[1][len("nyear")+1 : len(data[1])-1] 16 month = data[2][len("nmonth")+1 : len(data[2])-1] 17 day = data[3][len("nday")+1 : len(data[3])-1] 18 #wday = data[4][len("nwday")+1 : len(data[4])-1] 19 hrs = data[5][len("nhrs")+1 : len(data[5])-1] 20 minute = data[6][len("nmin")+1 : len(data[6])-1] 21 sec = data[7][len("nsec")+1 : len(data[7])-1] 22 23 beijinTimeStr = "%s/%s/%s %s:%s:%s" % (year, month, day, hrs, minute, sec) 24 beijinTime = time.strptime(beijinTimeStr, "%Y/%m/%d %X") 25 return beijinTime 26 except: 27 logging.exception("getBeijinTime except") 28 return None
同步本地系統(tǒng)時間
1 def syncLocalTime(): 2 """ 3 同步本地時間 4 """ 5 logging.info("current local time is: %d-%d-%d %d:%d:%d" % time.localtime()[:6]) 6 7 beijinTime = getBeijinTime() 8 if beijinTime is None: 9 logging.info("get beijinTime is None, will try again in 30 seconds...") 10 timer = threading.Timer(30.0, syncLocalTime) 11 timer.start(); 12 else: 13 logging.info("get beijinTime is: %d-%d-%d %d:%d:%d" % beijinTime[:6]) 14 15 tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec = beijinTime[:6] 16 import os 17 os.system("date %d-%d-%d" % (tm_year, tm_mon, tm_mday)) #設(shè)置日期 18 os.system("time %d:%d:%d.0" % (tm_hour, tm_min, tm_sec)) #設(shè)置時間 19 logging.info("syncLocalTime complete, current local time: %d-%d-%d %d:%d:%d \n" % time.localtime()[:6])
二、部署安裝
為了讓Python程序能以Windows服務(wù)的方式運行,需要用到py2exe(用來把Python程序編譯成exe)和Python Win32 Extensions 。(py2exe把Python代碼編譯成Winodws服務(wù)時依賴此組件)下載并安裝這兩個組件。安裝完畢后,在Python的安裝目錄下找到py2exe的Windows Service示例({PythonRoot}\Lib\site-packages\py2exe\samples\advanced\MyService.py)。然后仿照這個示例將上面的代碼完善一下。
Windows服務(wù)示例
1 import win32serviceutil 2 import win32service 3 import win32event 4 import win32evtlogutil 5 6 class SynctimeService(win32serviceutil.ServiceFramework): 7 _svc_name_ = "Synctime" 8 _svc_display_name_ = "Synctime" 9 _svc_description_ = "Synchronize local system time with beijin time" 10 _svc_deps_ = ["EventLog"] 11 12 def __init__(self, args): 13 win32serviceutil.ServiceFramework.__init__(self, args) 14 self.hWaitStop = win32event.CreateEvent(None, 0, 0, None) 15 16 def SvcStop(self): 17 self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) 18 win32event.SetEvent(self.hWaitStop) 19 20 def SvcDoRun(self): 21 import servicemanager 22 23 # Write a 'started' event to the event log... 24 win32evtlogutil.ReportEvent(self._svc_name_, 25 servicemanager.PYS_SERVICE_STARTED, 26 0, # category 27 servicemanager.EVENTLOG_INFORMATION_TYPE, 28 (self._svc_name_, '')) 29 30 # wait for beeing stopped... 31 win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE) 32 33 # and write a 'stopped' event to the event log. 34 win32evtlogutil.ReportEvent(self._svc_name_, 35 servicemanager.PYS_SERVICE_STOPPED, 36 0, # category 37 servicemanager.EVENTLOG_INFORMATION_TYPE, 38 (self._svc_name_, '')) 39 40 if __name__ == '__main__': 41 # Note that this code will not be run in the 'frozen' exe-file!!! 42 win32serviceutil.HandleCommandLine(SynctimeService)
之后,再編寫一個steup.py文件用來生成安裝文件。
Setup.py
1 from distutils.core import setup 2 import py2exe 3 4 setup( 5 # The first three parameters are not required, if at least a 6 # 'version' is given, then a versioninfo resource is built from 7 # them and added to the executables. 8 version = "0.0.1", 9 description = "Synchroniz local system time with beijin time", 10 name = "sysctime", 11 12 # targets to build 13 # console = ["synctime.py"], 14 service=["synctime"] 15 )
編譯生成windows程序,如下圖:
然后在控制臺中運行:setup.py py2exe ,一切順利的話會在當(dāng)前目錄下生成build和dist目錄。
控制臺目錄切換到dist目錄,找到synctime.exe,在命令行中運行:
synctime.exe –install (-remove) 安裝或移除時間同步服務(wù)。
現(xiàn)在可以運行services.msc查看服務(wù)運行情況
可以看到服務(wù)并沒有啟動,而且啟動方式為手動。在這里可以右擊服務(wù)選擇屬性手動把服務(wù)啟動起來,并且設(shè)置為服務(wù)自動啟動。
好吧,我承認(rèn)。這樣操作跟上面的需求有點出入了,略顯麻煩。為了解決這個問題,自然想到的是用批處理來做。在dist目錄下分別建兩個批處理文件:
installservice.bat
1 @echo off 2 3 :: 安裝windows服務(wù) 4 echo 正在安裝服務(wù),請稍候... 5 synctime.exe -install 6 7 :: 設(shè)置服務(wù)自動啟動 8 echo 正在啟動服務(wù)... 9 sc config Synctime start= AUTO 10 11 :: 啟動服務(wù) 12 sc start Synctime 13 14 echo 服務(wù)啟動成功, 按任意鍵繼續(xù)... 15 pause
removeserivce.bat
1 @echo off
2
3 :: 停止服務(wù)
4 echo 正在停止服務(wù),請稍候...
5 sc stop Synctime
6
7 echo 正在卸載服務(wù)...
8 :: 刪除windows服務(wù)
9 synctime.exe -remove
10
11 echo 服務(wù)卸載完成,請按任意鍵繼續(xù)剩余卸載...
12 pause
好了,現(xiàn)在可以把dist打個包發(fā)給老媽用了。但是,這樣發(fā)個一個壓縮包,看起來也太不專業(yè)了。解決的辦法是打一個安裝包,把bat腳本打到安裝包里,在安裝程序時由安裝包調(diào)用。這里我用的是NISI(使用HM VNISEdit打包向?qū)砩纱虬_本非常方便)。
三、最終安裝效果圖
四、結(jié)尾
遺留的問題:
1、從上面的截圖可以看到,安裝程序在調(diào)用批處理時會顯示出控制臺窗口。這個問題我在網(wǎng)上查找資料,NSIS有相關(guān)的插件可以隱藏控制臺窗口調(diào)用bat文件。
2、我源代碼中有寫日志文件的操作,但是以Windows服務(wù)的方式運行后,日志文件不能寫了,不知道有沒有好的解決辦法。
3、360 ...真是要人命啊....Orz..
時間同步工具及源代碼:http://elephantinaurance.com/soft/56421.html
編譯方法:
第一步: 安裝Python環(huán)境(什么?還沒有Python環(huán)境?... - -!)
第二步: 安裝依賴組件
1、py2exe (目前只支持到python2.7)
2、Python Win32 Extensions
第三步(可選):安裝Nsis環(huán)境,用來編譯腳本
第四步:將synctime.py編譯成windows程序
1、在當(dāng)前目錄下運行"setup.py py2exe",順利的話會在當(dāng)前目錄下生成dist和build目錄
第五步: 運行,有兩種運行方式:
1、將installservice.bat 和 removeservice.bat 拷貝到dist中運行即可
2(依賴第三步)、使用Nsis編譯Synctime.nsi腳本,生成安裝包, 安裝后運行(推薦)