pyinstaller+python3.6搭建windows客户端的服务(二)

前文pyinstaller+python3.6搭建windows客户端的服务(一)描述了使用python3.6+django+windows服务的方法搭建带本地服务的客户端方法。主要存在如下缺点:

  • windows服务不完全稳定,有时可以访问正常,但偶尔又访问不正常;
  • 调试windows服务太困难,据本人所知,就只有事件查看器和自己打印日志去调试程序2种方法,如果在服务运行过程中出现服务自身的异常,可能并一定有日志,这种情况就非常难定位和分析。

经过努力,本项目又尝试了另外一种方法实现本地客户端,详见下文。

方案(二)Python3.6+pyinstaller+pythonw

一、开始

  在查找更好解决方案过程中,网上有人说使用pythonw.exe程序,可以启动一个无窗体的后台进程;而django自身在启动后,会一直监听端口,此进程是不会退出的。
  也就是说如果使用pythonw.exe工具,让django在无窗体的后台进程中运行,这样就和window服务的进程看进来没有区别了,同时代码运行的调试能力也方便很多,因为这个进程的所有日志都可以打印到日志文件中。

二、主程序打包过程

  上一篇文章已经建立好helloworld的django工程,本次把service.ini和service.py文件删除,同时删除【E:\helloworld\hello\settings.py】文件中变量INSTALLED_APPS的'django_windows_tools'。

2.1、准备工作

  • 将工程中manage.py更名为manage.pyw
    此时python能在打包后运行时使用pythonw.exe,而不是使用默认的python.exe去运行

2.2、hello.spec——文件夹形式

# -*- mode: python -*-

block_cipher = None
hide_imports = ['django.contrib.admin.apps', 'django.contrib.auth.apps', 'django.contrib.contenttypes.apps',
    'django.contrib.messages.apps', 'django.contrib.staticfiles.apps', 'django.contrib.sessions.models',
    'django.contrib.sessions.apps', 'django.contrib.messages.middleware', 'django.contrib.auth.middleware',
    'django.contrib.sessions.middleware', 'django.contrib.sessions.serializers','django.core.mail.backends',
    'django.views.defaults', 'django.core.mail.backends.smtp',]

a = Analysis(['manage.pyw'], pathex=['E:\\LayaProj\\hello'], binaries=[],
    datas=[('logo.ico', '.')], hiddenimports=hide_imports,
    hookspath=[], runtime_hooks=[], excludes=[], win_no_prefer_redirects=False,
    win_private_assemblies=False, cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(pyz, a.scripts, exclude_binaries=True, name='Hello', debug=False, strip=False, upx=True, console=False, icon='logo.ico')
coll = COLLECT(exe, a.binaries, a.zipfiles, a.datas, strip=False, upx=True, name='Hello')

  其中logo.ico为应用的图标,使用命令【pyinstaller -y hello.spec】编译工程,会在dist\Hello\目录下存放生成好的所有文件。
  在cmd命令行中执行【dist\Hello\Hello.exe runserver --noreload 0.0.0.0:8800】,应会创建Hello.exe进程,此进程会创建另外一个Hello.exe进程,此两个进程即为django的服务器的监听和处理进程。
  但,此时只是进行了打包,还是手工执行命令启动服务,而常规的windows客户端程序是点击桌面的快捷方式启动程序,还需进一步改进。

三、实现类桌面应用的网页版程序

3.1、新增一个启动工程entry

此工程主要有2个文件:entry.py和entry.spec。
本项目将其中的配置相关放在Configs.py文件中,LoggerHelper.py为记录日志的类,视情况参考。

  • 源文件entry\Configs.py:
import os, sys

# This is my base path
BASE_PATH = os.path.dirname(os.path.abspath(__file__))
# all tmp dir dirs
APP_TMP_DIR = os.path.join(BASE_PATH, 'tmp')

# determine if application is a script file or frozen exe
if getattr(sys, 'frozen', False):
    BASE_PATH = sys._MEIPASS
    APP_TMP_DIR = os.path.join(BASE_PATH, '..', 'tmp')
if not BASE_PATH in sys.path:
    sys.path.append(BASE_PATH)

DJANGO_PORT = 18800
DJANGO_ARGS = 'runserver --noreload 0.0.0.0:%d' % DJANGO_PORT
DJANGO_PROCESS_ID = 'Hello.exe'

APP_LOGGING = {
    'version': 1,
    'disable_existing_loggers': True,
    'formatters': {  # 格式器
        'standard': {  # 详细
            'format': '[%(asctime)s|%(levelname)s|%(processName)s|%(module)s|%(lineno)s]: %(message)s',
            #'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s',
            'datefmt': '%Y-%m-%d %H:%M:%S',
        },
    },
    'handlers': {
        'start': {
            'level': 'DEBUG',
            'class': 'logging.handlers.TimedRotatingFileHandler',
            'filename': os.path.join(APP_TMP_DIR, 'start.log'),
            'when': 'D',
            'interval': 1,
            'backupCount': 3,
            'formatter': 'standard',
        },
    },
    'loggers': {
        'start': {
            'handlers': ['start'],
            'level': 'DEBUG',
            'propagate': True,
        },
    },
}

ROOT_URL = 'http://localhost:8800/'
# 主程序中django的主页的url
INDEX_URL = '%sstatic/index.html' % ROOT_URL
  • 源文件entry\entry.py:
#!/usr/bin/env python
import os, time
import traceback
import subprocess
from subprocess import Popen
from LoggerHelper import logger
from Configs import BASE_PATH, DJANGO_ARGS, DJANGO_PROCESS_ID, ROOT_URL, INDEX_URL, DJANGO_PORT
import webbrowser
import requests
import win32api, win32con

# 检查服务进程是否启动,默认尝试1次
def check_server(retry=1):
    print('检查服务是否启动 ...')
    ret = False
    while retry > 0 and not ret:
        try:
            r = requests.get(ROOT_URL, timeout=2)
            ret = (r.status_code == requests.codes.ok)
        except requests.HTTPError as exp:
            logger.error('服务器返回了异常:%s' % exp)
            ret = False
        except (requests.Timeout, requests.ConnectionError) as exp:
            logger.error('网页连接超时或连接失败:%s' % exp)
            ret = False
        except (requests.RequestException, Exception) as exp:
            logger.error('发现未处理的异常:%s' % exp)
            ret = False
        retry = retry - 1
    print('%s' % ret)
    return ret

# 启动服务进程
def start_server():
    ret = None
    try:
        print('检查进程是否存在 ...')
        ret = os.system('netstat -an | find "%d"' % DJANGO_PORT)
        if ret == 0:
            ret = os.system('tasklist | find "%s"' % DJANGO_PROCESS_ID)
            if ret == 0:
                print('强制结束老进程 ...')
                os.system('TASKKILL /F /IM %s' % DJANGO_PROCESS_ID)
                time.sleep(0.5)
        url = os.path.join(BASE_PATH, '..', 'Hello', r'Hello.exe')
        logger.info('开始启动服务 ... %s' % url)
        print('开始启动服务 ... ')
        startupinfo = subprocess.STARTUPINFO()
        startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
        ret = Popen('%s %s' % (url, DJANGO_ARGS), startupinfo=startupinfo)
    except Exception as exp:
        logger.error('Error : %s, Detail : %s' % (exp, traceback.format_exc()))
    return ret

# 打开主页,为后续实现双击exe可执行文件能自动打开浏览器网页作准备
def start_url(url):
    ret = None
    try:
        logger.info('start url ... %s' % url)
        ret = webbrowser.open(url, new=2, autoraise=True)
        time.sleep(2)
        maxWindow()
    except Exception as exp:
        logger.error('Error : %s, Detail : %s' % (exp, traceback.format_exc()))
    return ret

# 浏览器中全屏,使用按键进行模拟
def maxWindow():
    win32api.keybd_event(122,0,0,0) #F11
    win32api.keybd_event(122, 0, win32con.KEYEVENT_KEYUP,0) #Realize the F11 button

if __name__ == "__main__":
    logger.info('start server ...')
    if check_server():
        start_url(INDEX_URL)
    elif start_server() is not None:
        if check_server(30):
            start_url(INDEX_URL)
        else:
            logger.error('ERROR : 服务未成功启动,请手工杀死进程LotServer.exe,再尝试启动!')
            print('ERROR : 服务未成功启动,请手工杀死进程LotServer.exe,再尝试启动!')
    else:
        print('ERROR : 服务启动过程中发生异常,请先手工清理相关进程,再尝试启动!')
        start_url(os.path.join(BASE_PATH, 'error.html'))
    logger.info('end start!')
  • 源文件entry\entry.spec:
# -*- mode: python -*-

block_cipher = None

a = Analysis(['entry.py'],
             pathex=['E:\\entry'],
             binaries=[],
             datas=[('error.html','.')],
             hiddenimports=[],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          a.scripts,
          exclude_binaries=True,
          name='entry',
          debug=False,
          strip=False,
          upx=True,
          console=True, icon='logo.ico')
coll = COLLECT(exe,
               a.binaries,
               a.zipfiles,
               a.datas,
               strip=False,
               upx=True,
               name='entry')
  • 源文件entry\LoggerHelper.py(记录日志):
import sys, os
import logging
from logging.config import dictConfig
from Configs import APP_TMP_DIR, APP_LOGGING

## config the logging
#log_dir = os.path.join(APP_TMP_DIR, 'logs') ## os.getenv('SYSTEMROOT') + '\\..\\LOTS'
try:
    if not os.path.exists(APP_TMP_DIR):
        os.makedirs(APP_TMP_DIR)
except Exception as exp:
    print('ERROR : Create Dir %s failed.' % APP_TMP_DIR)
    sys.exit(1)

try:
    dictConfig(APP_LOGGING)
except Exception as exp:
    print('%s' % exp)
logger = logging.getLogger('start')

global __FIST_TIME
__FIST_TIME = True

if __FIST_TIME:
    #sys.stderr.write = lambda s: logger.error(s)
    #sys.stdout.write = lambda s: logger.info(s)
    __FIST_TIME = False

if __name__ == '__main__':
    logger.error("test-error")
    logger.info("test-info")
    logger.warn("test-warn")
  • 源文件entry\error.html文件随意写,主要为提示用户当程序启动不了时应如何处理。

3.2、编译/启动工程entry

E:\entry>pyinstaller -y entry.spec
E:\entry>dist\entry\entry.exe

便可启动服务进程了。

四、最后的安装包Inno Setup Compiler

  前面已经将helloworld(django工程)和entry(普通工程)打包成相应的hello和entry目录,剩下的就是使用Inno setup将这些可执行文件统一打包并进行安装。

4.1、安装向导setup.iss:

4.2、中文的安装向导:

需将Inno Setup安装目录(如D:\Program Files (x86)\Inno Setup 5\Languages)里添加一个ChineseSimplified.isl文件(此文件内容来自网络):

; *** Inno Setup version 5.5.3+ Chinese (Simplified) messages ***
;   By Qiming Li (qiming at clault.com)
;
; To download user-contributed translations of this file, go to:
;   http://www.jrsoftware.org/files/istrans/
;
; Note: When translating this text, do not add periods (.) to the end of
; messages that didn't have them already, because on those messages Inno
; Setup adds the periods automatically (appending a period would result in
; two periods being displayed).

[LangOptions]
; The following three entries are very important. Be sure to read and 
; understand the '[LangOptions] section' topic in the help file.
LanguageName=<4E2D><6587><FF08><7B80><4F53><FF09>
LanguageID=$0804
LanguageCodePage=936
; If the language you are translating to requires special font faces or
; sizes, uncomment any of the following entries and change them accordingly.
DialogFontName=宋体
;DialogFontSize=8
;WelcomeFontName=Verdana
;WelcomeFontSize=12
;TitleFontName=Arial
;TitleFontSize=29
;CopyrightFontName=Arial
;CopyrightFontSize=8

[Messages]

; *** Application titles
SetupAppTitle=安装向导
SetupWindowTitle=安装向导 - %1
UninstallAppTitle=卸载向导
UninstallAppFullTitle=%1卸载向导

; *** Misc. common
InformationTitle=信息
ConfirmTitle=确认
ErrorTitle=错误

; *** SetupLdr messages
SetupLdrStartupMessage=安装向导将在您的电脑上安装%1。确定要继续吗?
LdrCannotCreateTemp=无法创建临时文件。安装已终止
LdrCannotExecTemp=无法运行临时文件夹中的文件。安装已终止

; *** Startup error messages
LastErrorMessage=%1.%n%n错误 %2: %3
SetupFileMissing=安装目录中缺失文件%1。请解决该问题,或重新获取一份程序拷贝。
SetupFileCorrupt=安装文件已被损坏。请重新获取一份程序拷贝。
SetupFileCorruptOrWrongVer=安装文件已被损坏,或与本安装向导版本不兼容。请解决该问题,或重新获取一份程序拷贝。
InvalidParameter=无效命令行参数:%n%n%1
SetupAlreadyRunning=安装程序已经运行。
WindowsVersionNotSupported=程序不支持您电脑上运行的Windows版本。
WindowsServicePackRequired=程序要求%1 Service Pack %2或更新版本。
NotOnThisPlatform=程序不可在%1上运行。
OnlyOnThisPlatform=程序必须在%1上运行。
OnlyOnTheseArchitectures=程序只能在为以下处理器架构所设计的Windows版本上安装:%n%n%1
MissingWOW64APIs=您所使用的Windows版本没有包含进行64位安装所需的功能。请安装Service Pack %1解决此问题。
WinVersionTooLowError=程序要求%2版本或以上的%1。
WinVersionTooHighError=程序不可安装的%2或更高版本的%1上。
AdminPrivilegesRequired=您必须登录为管理员才能安装此程序。
PowerUserPrivilegesRequired=您必须登录为管理员或高权限用户才能安装此程序。
SetupAppRunningError=安装向导检测到%1正在运行。%n%n请关闭其所有窗口并点击“确定”继续,或点击“取消”退出安装。
UninstallAppRunningError=卸载向导检测到%1正在运行。%n%n请关闭其所有窗口,然后点击“确定”继续,或点击“取消”退出。

; *** Misc. errors
ErrorCreatingDir=安装向导无法创建文件夹“%1”
ErrorTooManyFilesInDir=由于文件夹“%1”中文件过多,无法在其中创建文件

; *** Setup common messages
ExitSetupTitle=退出安装向导
ExitSetupMessage=安装尚未完成。如果现在退出,程序将不会被安装。 %n%n您可以下次再运行安装向导来完成程序的安装。%n%n确定退出安装向导吗?
AboutSetupMenuItem=关于安装向导(&A)…
AboutSetupTitle=关于安装向导
AboutSetupMessage=%1版本%2%n%3%n%n%1主页:%n%4
AboutSetupNote=
TranslatorNote=

; *** Buttons
ButtonBack=< 上一步(&B)
ButtonNext=下一步(&N) >
ButtonInstall=安装(&I)
ButtonOK=确定
ButtonCancel=取消
ButtonYes=是(&Y)
ButtonYesToAll=全选是(&A)
ButtonNo=否(&N)
ButtonNoToAll=全选否(&O)
ButtonFinish=结束(&F)
ButtonBrowse=浏览(&B)…
ButtonWizardBrowse=浏览(&R)…
ButtonNewFolder=创建文件夹(&M)

; *** "Select Language" dialog messages
SelectLanguageTitle=选择语言
SelectLanguageLabel=选择安装时使用语言:

; *** Common wizard text
ClickNext=点击“下一步”继续,或“取消”退出安装向导。
BeveledLabel=
BrowseDialogTitle=浏览选择文件夹
BrowseDialogLabel=在以下列表中选取一个文件夹,并点击“确定”。
NewFolderName=新建文件夹

; *** "Welcome" wizard page
WelcomeLabel1=欢迎使用[name]安装向导
WelcomeLabel2=本向导将在您的电脑上安装[name/ver]%n%n建议您在继续之前关闭其他所有应用程序。

; *** "Password" wizard page
WizardPassword=密码
PasswordLabel1=本安装程序由密码保护。
PasswordLabel3=请输入密码,并点击“下一步”。密码区分大小写。
PasswordEditLabel=密码(&P):
IncorrectPassword=您输入的密码不正确。请重试。

; *** "License Agreement" wizard page
WizardLicense=许可协议
LicenseLabel=请阅读以下重要信息,然后再进入下一步。
LicenseLabel3=请阅读以下许可协议。您必须接受此协议的条款,然后才能继续安装。
LicenseAccepted=我接受协议(&A)
LicenseNotAccepted=我不接受协议(&D)

; *** "Information" wizard pages
WizardInfoBefore=信息
InfoBeforeLabel=请阅读以下重要信息再进入下一步。
InfoBeforeClickLabel=准备好继续安装后,点击“下一步”。
WizardInfoAfter=信息
InfoAfterLabel=请阅读以下重要信息再进入下一步。
InfoAfterClickLabel=准备好继续安装后,点击“下一步”。

; *** "User Information" wizard page
WizardUserInfo=用户信息
UserInfoDesc=请输入您的信息
UserInfoName=用户名称(&U):
UserInfoOrg=机构名称(&O):
UserInfoSerial=序列号码(&S):
UserInfoNameRequired=必须输入用户名

; *** "Select Destination Location" wizard page
WizardSelectDir=选择安装位置
SelectDirDesc=将[name]安装到何处?
SelectDirLabel3=安装向导将把[name]安装到以下文件夹中。
SelectDirBrowseLabel=点击“下一步”继续。如果您要选择不同的文件夹,请点击“浏览”。
DiskSpaceMBLabel=必须至少有[mb]兆字节(MB)的闲置磁盘空间。
CannotInstallToNetworkDrive=无法安装至网络驱动器。
CannotInstallToUNCPath=无法安装至UNC路径。
InvalidPath=您必须输入包括盘符的完整路径,例如:%n%nC:\应用程序%n%n或如下格式的UNC路径:%n%n\\服务器名\共享目录名
InvalidDrive=您选择的驱动器或UNC共享不存在或不可访问。请另选一个。
DiskSpaceWarningTitle=磁盘空间不足
DiskSpaceWarning=必须至少有%1千字节(KB)的闲置空间才可安装,但所选驱动器仅有%2千字节(KB)可用空间。%n%n您确定要继续吗?
DirNameTooLong=文件夹名称或路径太长。
InvalidDirName=文件夹名称无效。
BadDirName32=文件夹名称不能包含下列字符:%n%n%1
DirExistsTitle=文件夹已存在
DirExists=文件夹%n%n%1%n%n已存在。您确定要安装到该文件夹吗?
DirDoesntExistTitle=文件夹不存在
DirDoesntExist=文件夹%n%n%1%n%n不存在。您要创建该文件夹吗?

; *** "Select Components" wizard page
WizardSelectComponents=选择组件
SelectComponentsDesc=要安装哪些组件?
SelectComponentsLabel2=请选择要安装的组件,清除不要安装的组件。准备好后点击“下一步”。
FullInstallation=全部安装
; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language)
CompactInstallation=简洁安装
CustomInstallation=自定义安装
NoUninstallWarningTitle=组件已存在
NoUninstallWarning=安装向导检测到已经安装下列组件:%n%n%1%n%n取消选定不会卸载这些组件。%n%n您确定要继续安装吗?
ComponentSize1=%1千字节(KB)
ComponentSize2=%1兆字节(MB)
ComponentsDiskSpaceMBLabel=目前所选组件要求至少[mb]兆字节(MB)磁盘空间。

; *** "Select Additional Tasks" wizard page
WizardSelectTasks=选择附加任务
SelectTasksDesc=要执行哪些附加任务?
SelectTasksLabel2=请选择安装[name]时需要执行的附加任务,然后点击“下一步”。

; *** "Select Start Menu Folder" wizard page
WizardSelectProgramGroup=选择开始菜单文件夹
SelectStartMenuFolderDesc=把程序快捷方式放到哪里?
SelectStartMenuFolderLabel3=安装向导将在以下开始菜单文件夹中创建程序快捷方式。
SelectStartMenuFolderBrowseLabel=点击“下一步”继续。如要选择另一个文件夹,点击“浏览”。
MustEnterGroupName=您必须输入文件夹名称
GroupNameTooLong=文件夹名称或路径太长。
InvalidGroupName=文件夹名称无效。
BadGroupName=文件夹名称不能包含下列字符:%n%n%1
NoProgramGroupCheck2=不要创建开始菜单文件夹(&D)

; *** "Ready to Install" wizard page
WizardReady=安装准备完毕
ReadyLabel1=安装向导已准备完毕,将开始在您的电脑上安装[name]。
ReadyLabel2a=点击“安装”开始安装。如要确认或更改设置请点击“上一步”。
ReadyLabel2b=点击“安装”开始安装。
ReadyMemoUserInfo=用户信息:
ReadyMemoDir=安装位置:
ReadyMemoType=安装类型:
ReadyMemoComponents=所选组件:
ReadyMemoGroup=开始菜单文件夹:
ReadyMemoTasks=附加任务:

; *** "Preparing to Install" wizard page
WizardPreparing=准备安装
PreparingDesc=安装向导正在准备在您的电脑上安装[name]。
PreviousInstallNotCompleted=上次程序安装/卸载未能完成。您需要重启电脑来完成上次安装。%n%n电脑重启之后,请重新运行安装向导来安装[name]。 
CannotContinue=安装无法继续。请点击“取消”退出。
ApplicationsFound=安装向导需要更新的文件被下列应用程序占用。建议允许安装向导自动关闭这些应用程序。
ApplicationsFound2=安装向导需要更新的文件被下列应用程序占用。建议允许安装向导自动关闭这些应用程序。安装完成后,安装向导将尝试重新启动这些应用程序。 
CloseApplications=自动关闭应用程序(&A)
DontCloseApplications=不自动关闭应用程序(&D)
ErrorCloseApplications=安装向导无法自动关闭所有的应用程序。在进入下一步之前,建议您关闭那些占用安装向导需要更新文件的应用程序。

; *** "Installing" wizard page
WizardInstalling=正在安装
InstallingLabel=请稍候,安装向导正在您的电脑上安装[name]。

; *** "Setup Completed" wizard page
FinishedHeadingLabel=[name]安装完成
FinishedLabelNoIcons=安装向导已在您的电脑上安装[name]。
FinishedLabel=安装向导已在您的电脑上安装[name]。可以通过已安装的快捷方式来打开此应用程序。
ClickFinish=点击“结束”退出安装。
FinishedRestartLabel=为了完成[name]的安装,安装向导必须重启您的电脑。要立即重启吗?
FinishedRestartMessage=为了完成[name]的安装,安装向导必须重启您的电脑。%n%n要立即重启吗?
ShowReadmeCheck=是,我要阅读自述文件
YesRadio=是,立即重启电脑(&Y)
NoRadio=否,稍后我再重启电脑(&N)
; used for example as 'Run MyProg.exe'
RunEntryExec=运行%1
; used for example as 'View Readme.txt'
RunEntryShellExec=查阅%1

; *** "Setup Needs the Next Disk" stuff
ChangeDiskTitle=安装向导需要下一张磁盘
SelectDiskLabel2=请插入磁盘%1 并点击“确定”。%n%n如果该磁盘中的文件并不在以下所示文件夹中,请输入正确的路径或点击“浏览”。
PathLabel=路径(&P):
FileNotInDir2=文件“%1”不在“%2”中。请插入正确的磁盘或选择其它文件夹。
SelectDirectoryLabel=请指定下一张磁盘的位置。

; *** Installation phase messages
SetupAborted=安装未能完成。%n%n请解决问题后再重新运行安装向导。
EntryAbortRetryIgnore=点击“重试”重新尝试,点击“忽略”继续安装,或点击“中止”取消安装。

; *** Installation status messages
StatusClosingApplications=正在关闭应用程序…
StatusCreateDirs=正在创建文件夹…
StatusExtractFiles=正在取出文件…
StatusCreateIcons=正在创建快捷方式…
StatusCreateIniEntries=正在创建INI条目…
StatusCreateRegistryEntries=正在创建注册表条目…
StatusRegisterFiles=正在创建注册表项目…
StatusSavingUninstall=正在保存卸载信息…
StatusRunProgram=正在结束安装…
StatusRestartingApplications=正在重启应用程序…
StatusRollback=正在撤销更改…

; *** Misc. errors
ErrorInternal2=内部错误:%1
ErrorFunctionFailedNoCode=%1失败
ErrorFunctionFailed=%1失败,错误码%2
ErrorFunctionFailedWithMessage=%1失败,错误码%2。%n%3
ErrorExecutingProgram=无法运行程序:%n%1

; *** Registry errors
ErrorRegOpenKey=打开注册表键时出错:%n%1\%2
ErrorRegCreateKey=创建注册表键时出错:%n%1\%2
ErrorRegWriteKey=写入注册表键时出错:%n%1\%2

; *** INI errors
ErrorIniEntry=在文件“%1”中创建INI条目时出错。

; *** File copying errors
FileAbortRetryIgnore=点击“重试”重新尝试,点击“忽略”跳过此文件(不推荐这样做),或点击“中止”取消安装。
FileAbortRetryIgnore2=点击“重试”重新尝试,点击“忽略”继续安装(不推荐这样做),或点击“中止”取消安装。
SourceIsCorrupted=源文件已损坏
SourceDoesntExist=源文件“%1”不存在
ExistingFileReadOnly=现有文件被标记为只读。%n%n点击“重试”移除其只读属性并重新尝试,点击“忽略”跳过此文件,或点击“中止”取消安装。
ErrorReadingExistingDest=读取现有文件时出错:
FileExists=文件已存在。%n%n让安装向导覆盖它吗?
ExistingFileNewer=现有文件比安装向导试图安装的还要新。建议保留现有文件。%n%n您要保留现有文件吗?
ErrorChangingAttr=更改现有文件属性时出错:
ErrorCreatingTemp=在目的文件夹中创建文件时出错:
ErrorReadingSource=读取源文件时出错:
ErrorCopying=复制文件时出错:
ErrorReplacingExistingFile=替换现有文件时出错:
ErrorRestartReplace=重启替换失败:
ErrorRenamingTemp=为目的文件夹中文件重命名时出错:
ErrorRegisterServer=无法注册动态库或控件(DLL/OCX):%1
ErrorRegSvr32Failed=运行RegSvr32失败,其返回值为:%1
ErrorRegisterTypeLib=无法注册类型库:%1

; *** Post-installation errors
ErrorOpeningReadme=打开自述文件时出错。
ErrorRestartingComputer=安装向导无法重启电脑。请手动重启。

; *** Uninstaller messages
UninstallNotFound=文件“%1”不存在。无法卸载。
UninstallOpenError=无法打开文件“%1”。无法卸载
UninstallUnsupportedVer=此版本的卸载向导无法识别卸载日志文件“%1”的格式。无法卸载
UninstallUnknownEntry=在卸载日志中遇到未知条目 (%1)
ConfirmUninstall=您是否确定要完全删除%1及其所有组件?
UninstallOnlyOnWin64=此安装只能在64位Windows上卸载。
OnlyAdminCanUninstall=此安装只能由具备管理员权限的用户卸载。
UninstallStatusLabel=请稍候,正在删除%1。
UninstalledAll=已成功地从您的电脑中删除%1。
UninstalledMost=%1卸载完毕。%n%n某些项目无法在卸载过程中删除。可以手动删除这些项目。
UninstalledAndNeedsRestart=若要完成%1的卸载,必须重启电脑。%n%n要立即重启吗?
UninstallDataCorrupted=文件“%1”已损坏。无法卸载

; *** Uninstallation phase messages
ConfirmDeleteSharedFileTitle=删除共享文件吗?
ConfirmDeleteSharedFile2=系统显示没有任何程序使用以下共享文件。要删除该共享文件吗?%n%n如果有程序使用该文件,当它被删除后这些程序可能无法正常运行。如果不确定,请选择“否”。留下该文件不会对系统造成任何危害。
SharedFileNameLabel=文件名:
SharedFileLocationLabel=位置:
WizardUninstalling=卸载状态
StatusUninstalling=正在卸载%1…

; *** Shutdown block reasons
ShutdownBlockReasonInstallingApp=正在安装%1。
ShutdownBlockReasonUninstallingApp=正在卸载%1。

; The custom messages below aren't used by Setup itself, but if you make
; use of them in your scripts, you'll want to translate them.

[CustomMessages]

NameAndVersion=%1版本%2
AdditionalIcons=附加快捷方式:
CreateDesktopIcon=创建桌面快捷方式(&D)
CreateQuickLaunchIcon=创建快速启动栏快捷方式(&Q)
ProgramOnTheWeb=%1网站
UninstallProgram=卸载%1
LaunchProgram=运行%1
AssocFileExtension=将%1与%2文件扩展名关联(&A)
AssocingFileExtension=正在将%1与%2文件扩展名关联…
AutoStartProgramGroupDescription=启动:
AutoStartProgram=自动启动%1
AddonHostProgramNotFound=在您所选文件夹中找不到%1。%n%n是否仍然继续?

完成!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,658评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,482评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,213评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,395评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,487评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,523评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,525评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,300评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,753评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,048评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,223评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,905评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,541评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,168评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,417评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,094评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,088评论 2 352

推荐阅读更多精彩内容