zoukankan      html  css  js  c++  java
  • 用python & bat写软件安装脚本 + HM NIS Edit自动生成软件安装脚本

    2019-03-11更新:原来NSIS脚本也可以禁用64位文件操作重定向的!

    1、在安装脚本的开始处定义 LIBRARY_X64。

    !include "MUI.nsh"
    !include "Library.nsh"

    ;如果做32位安装包就把下句注释。
    !define LIBRARY_X64

    2、在调用涉及目标机器上系统目录(即$SYSDIR)的函数前用 ${DisableX64FSRedirection}。

    在安装包的第一个Section中调用一次即可。
    !ifdef  LIBRARY_X64
     ${DisableX64FSRedirection}
    !endif 

    之前问题主要在于64位重定向问题,所以自己用python写了个脚本。找到了NSIS禁用重定向方法就可以无论32位还是64位都可以使用NSIS来写脚本了。

    原文:

    前些天自己做了一年多的软件成功交付客户,客户提出些完善意见,其中一条就是要一个软件安装脚本。

    这个之前也尝试python做过,只不过当时有更紧急的任务,最后就没深入尝试。

    这次我就捡起了之前的python工程,继续做做。

    整个过程很简单:

    1,把软件解压到客户选择的目录

    2,把一个dll程序复制到windowssystem32目录

    3,创建一个桌面快捷方式

    因为就这么几步,所以我以为很容易搞,就选择了久违的python自己写,而没有选择一些成熟的自动生成脚本工具。

    首先肯定要有个界面吧,主要是要用户选择安装目录。我用Tkinter写了个简陋的界面,这个不多说。

    解压压缩包的话,python有很好的库zipfile:

    def unzip(zipFilePath, destDir):
        zfile = zipfile.ZipFile(zipFilePath)
        for name in zfile.namelist():
            (dirName, fileName) = os.path.split(name)
            if fileName == '':
                # directory
                newDir = destDir + '/' + dirName
                if not os.path.exists(newDir):
                    os.mkdir(newDir)
            else:
                # file
                fd = open(destDir + '/' + name, 'wb')
                fd.write(zfile.read(name))
                fd.close()
        zfile.close()

    创建桌面快捷方式python肯定也有库,但我最后选择了使用bat脚本。

    set Program=这里要写快捷方式对应的程序目录,且必须是绝对路径。

    在python里将这个路径填写上,然后程序里运行bat脚本即可。

    @ echo off
    
    set Program=
     
    set LnkName=manager software
     
    set WorkDir=
     
    set Desc=soft
     
    if not defined WorkDir call:GetWorkDir "%Program%"
    (echo Set WshShell=CreateObject("WScript.Shell"^)
    echo strDesKtop=WshShell.SpecialFolders("DesKtop"^)
    echo Set oShellLink=WshShell.CreateShortcut(strDesKtop^&"\%LnkName%.lnk"^)
    echo oShellLink.TargetPath="%Program%"
    echo oShellLink.WorkingDirectory="%WorkDir%"
    echo oShellLink.WindowStyle=1
    echo oShellLink.Description="%Desc%"
    echo oShellLink.Save)>makelnk.vbs
    echo SUCCESS
    makelnk.vbs
    del /f /q makelnk.vbs
    exit
    goto :eof
    :GetWorkDir
    set WorkDir=%~dp1
    set WorkDir=%WorkDir:~,-1%
    goto :eof

    上面都算顺利,最后竟然在本以为很简单的复制文件到系统目录上出了问题。

    不管怎样努力,都没法将文件复制到windowssystem32目录下。

    一开始本以为是权限问题。

    在程序开始前加入这样的代码:

    def is_admin():
        try:
            return ctypes.windll.shell32.IsUserAnAdmin()
        except:
            return False
    
    
    if is_admin():
        #主程序代码
    else:
        # Re-run the program with admin rights
        ctypes.windll.shell32.ShellExecuteW(None, u"runas", unicode(sys.executable), unicode(__file__), None, 1)

    这样在运行前就会弹窗要求获取管理员权限。

    按道理这样程序就已经有了管理员权限了,可还是没有复制到system32目录下。

    后来在同事帮我看这个问题,他弄了一会,发现其实是64位系统下,系统自动重定向到C:WindowsSysWOW64目录下了!

    所以一定要在复制操作前,禁止重定向。

        with disable_file_system_redirection():
            shutil.copy2('sdfp_lib.dll',os.getenv("SystemDrive")+'\windows\system32')

    上述,便是用python写我的软件自动安装脚本的全过程,后面会附上我的全部代码。

    我先再讲下要实现这种软件自动安装脚本需求 最常用最合适的实现方法。

    其实用工具自动生成就好了!

    这个HM NIS Edit工具。

    点击文件,选择新建脚本向导。

    然后按照向导一般的安装,基本的安装需求都可以简单实现。

    重点是这一步:

    左边可以添加分组,右边可以给每个分组添加安装指令,可以给组添加单独的文件,也可以给组添加主程序目录。每个组再配置安装目标目录。这个目标目录有很多选择,包括系统目录、用户选择目录…………不赘述。

    这个工具编译好脚本,就生成了一个Setup.exe文件。这就是安装程序。要安装的软件文件都包含在这个exe里了,很厉害。

    按道理,只要用这个工具就可以完成我的需求了,但在64位系统还有些问题,那就是依然会有系统重定向现象。本来要复制到system32目录下的dll还是会被复制到C:WindowsSysWOW64下。

    最后我就决定,做两个版本。

    32位的安装程序用HM NIS Edit工具自动生成。

    64位我自己用python写。

    另外,python转化成exe文件的写法,之前文章介绍过:

    https://www.cnblogs.com/rixiang/p/7274026.html

    附上py完整代码:

    # -*- coding: utf-8 -*-
    from __future__ import print_function
    from Tkinter import *
    import os
    import sys
    import subprocess
    import shutil
    
    reload(sys)
    defaultencoding = 'utf-8'
    import ctypes
    
    import tkFileDialog as filedialog
    import zipfile
    from shutil import copyfile
    
    
    class disable_file_system_redirection:
        _disable = ctypes.windll.kernel32.Wow64DisableWow64FsRedirection
        _revert = ctypes.windll.kernel32.Wow64RevertWow64FsRedirection
        def __enter__(self):
            self.old_value = ctypes.c_long()
            self.success = self._disable(ctypes.byref(self.old_value))
        def __exit__(self, type, value, traceback):
            if self.success:
                self._revert(self.old_value)
                
    
    def unzip(zipFilePath, destDir):
        zfile = zipfile.ZipFile(zipFilePath)
        for name in zfile.namelist():
            (dirName, fileName) = os.path.split(name)
            if fileName == '':
                # directory
                newDir = destDir + '/' + dirName
                if not os.path.exists(newDir):
                    os.mkdir(newDir)
            else:
                # file
                fd = open(destDir + '/' + name, 'wb')
                fd.write(zfile.read(name))
                fd.close()
        zfile.close()
    
    def choose_directory():
        global dir_choosen
        global dir_choosen2
        dir_choosen = filedialog.askdirectory(initialdir='C:')
        # unzip my program to directory choosen
        dir_choosen2 = dir_choosen
        dir_choosen = dir_choosen + '/tgsoft'
        if not os.path.exists(dir_choosen):
            os.makedirs(dir_choosen)
        entryText.set(dir_choosen)
    
    def install():
        if dir_choosen2.strip()=='' or dir_choosen.strip()=='':
            return -1
        unzip('tgsoft.zip',dir_choosen)
        with disable_file_system_redirection():
            shutil.copy2('sdfp_lib.dll',os.getenv("SystemDrive")+'\windows\system32')
        str_bat = ''
        f = open('CREATE_SHORTCUT.bat', 'r')
        line = f.readline()             
        while line:
            str_bat+=line
            line = f.readline()
        f.close()
        nPos=str_bat.index('=')+1
        str_bat = str_bat[:nPos]+dir_choosen2+"\tgsoft\ManagerSoftware.exe"+str_bat[nPos:]
        f = open('CREATE_SHORTCUT2.bat', 'w') # 若是'wb'就表示写二进制文件
        f.write(str_bat)
        f.close()
        child = subprocess.Popen('CREATE_SHORTCUT2.bat',shell=False)
        # reset the window
        file_label.destroy()
        file_entry.destroy()
        file_btn.destroy()
        b2.destroy()
        w = Label(master, text="安装成功
    感谢使用")
        w.grid(row=0)    
    
    def is_admin():
        try:
            return ctypes.windll.shell32.IsUserAnAdmin()
        except:
            return False
    
    
    if is_admin():
        global master
        master = Tk()
        master.title('指静脉注册软件安装程序')
        master.geometry('400x100')
        global file_label
        file_label = Label(master, text="选择软件安装路径")
        file_label.grid(row=0)
        global entryText
        entryText = StringVar()
        global file_entry
        file_entry = Entry(master,textvariable=entryText)
        file_entry.grid(row=0, column=1)
        global file_btn
        file_btn = Button(master, text='点击选择路径', command=choose_directory)
        file_btn.grid(row=0,column=2)
        global b1
        b1 = Button(master, text='  退 出  ', command=master.quit)
        b1.grid(row=1,column=0)
        global b2
        b2 = Button(master, text='  确 定  ', command=install)
        b2.grid(row=1,column=1)
        mainloop()
    else:
        # Re-run the program with admin rights
        ctypes.windll.shell32.ShellExecuteW(None, u"runas", unicode(sys.executable), unicode(__file__), None, 1)
  • 相关阅读:
    线性时不变系统的频率选择滤波器
    Why Programmmers dont have a High Social Status?
    Oracle 的DBA考证
    基于matlab的FIR加窗滤波器设计
    奥本海姆《离散时间信号处理》第二版书评
    Altium Designer 发现的机密——摘自CRAZY BINGO
    sql server 2008 添加登录名
    Silverlight listbox横向排列
    IOS 摄像头使用(1)UIImagePickerController
    ios 如何获得系统时间和日期
  • 原文地址:https://www.cnblogs.com/rixiang/p/10313014.html
Copyright © 2011-2022 走看看