摘要:当Java桌面程序开发完成做产品的时候,面对未知的安装环境,通常是编写一些预安装检测脚本/程序,让程序傻瓜化安装以便减少分发出去的产品带来

的未知工作量(安装答疑,操作系统问题引起安装失败等),当然你也可以把安装过程中出现的问题写入文档手册。本文写作的背景是

1.不想让客户知道产品是用Java编写的 

2. 客户懒的帮助文档都不想看

  截至本文编写的时候,JDK官方最新版本为jdk 8u144,本文测试环境在虚拟机Windows XP,使用的版本为jdk-8u144-windows-i586.exe

       Java 安装是使用 Microsoft Window Installer (MSI) 2.0 技术构建的。MSI 包含对静默或无人参与安装的内置支持。

  从Google搜索"java silent install"总结出了一些安装参数  

 /lang=<语言代码> 安装特定语言包

  支持的语言包如下

 

语言   语言代码
英语 (en) 1033
法语 (fr) 1036            
德语 (de) 1031
意大利语 (it) 1040
日语 (ja) 1041
韩语 (ko) 1042
西班牙语 (es) 1034
瑞典语 (sv) 1053
简体中文 (zh) 2052
繁体中文 (zh_TW) 1028

经过笔者实测,设置语言代码为1041,即日语。安装命令:

jdk-8u144-windows-i586.exe /lang=1041

安装包界面如下

做对日外包的同学应该习惯于看到这个界面。这样就以日语语言环境安装JDK了。

WEB_JAVA = 1|0 Java浏览器支持 1启用 0禁用
IEXPLORER = 1|0 IE浏览器支持 1启用 0 禁用(jdk1.8命令行实测无效)
SPONSORS= 1|0 绕过赞助商,如浏览器插件,实测发现完整安装了JDK
WEB_JAVA_SECURITY_LEVEL = VH|H|M|L 浏览器中运行的未签名 Java 应用程序的安全级别 非常高|高|中|低
AUTO_UPDATE= 1|0 JDK自动检测更新 1启用 0禁用
NOSTARTMENU= 1|0 创建开始菜单 1禁用 0 启动

当然你也可以安装后在控制面板找到Java设置安全级别

以上为JDK 7u10 release版本起的安装特性,Java早期的时候有一些应用就是通过Web Applet方式发布的,笔者工作早些年的时候一

家做ERP的单位他就是通过浏览器方式发布项目的,现今已经很少看到这样的企业应用,鲜见一些社区里的Demo程序会有Web Applet的示例程序。

 

/L install.log 记录安装日志

常规情况下Oracle公司已经帮我考虑到安装过程中遇到的问题,并且他也有自己的解决方法,这些都封装在安装包里,当然有时候如果需要查看java安装过程的操作,

或者在特殊情况下(静默安装)他都做了什么,或者是为了排错,我们需要记录日志。/L参数便应运而生。

/s 静默安装

此参数不需要赋值,直接带入安装命令行即可,启动后将在后台默默的安装直至安装完成结束。

ADDLOCAL = [[ToolsFeature],[SourceFeature],[PublicjreFeature]] 选择附加组件包各个参数可独立使用也可以组合使用
ADDLOCAL = ["ToolsFeature,SourceFeature,PublicjreFeature"] 安装开发工具/源代码/JRE
ADDLOCAL = ["ToolsFeature,SourceFeature"] 安装开发工具和源代码

 除了以上命令还有很多配置方式或者参数,笔者没有一一测试,笔者最终的安装命令如下:

jdk-8u144-windows-i586.exe /lang=2052 /s /L c:\jdk-install.log INSTALLDIR=D:\develop\Java\jdk1.8.0_144 ADDLOCAL="ToolsFeature,SourceFeature,,PublicjreFeature" WEB_JAVA=0 AUTO_UPDATE=0 NOSTARTMENU=1 /INSTALLDIRPUBJRE=D:\develop\Java\jre1.8.0_144

 


命令解释:笔者选择了简体中文静默安装,记录了安装日志到c:\jdk-install.log,JDK安装目录c:\\embededJDK1.8,另外还安装了开发工具和源代码组件包、jre,禁用浏览Java插件,关闭JAVA自动更新,屏蔽了java开始菜单的创建。

安装后效果图如下:

 

从IE浏览器“工具”--"管理加载项"找不到java浏览器插件

开始菜单也没了Java的踪迹

windows注册表启动项也没有java的影子

 

 

 当然文字不是到这里就结束了。

本文最终目的是要发送福利,在下面,笔者用C#写了简单的程序来静默安装JDK。

首先从java官方获得JDK的x86和x64版本分别改名为sdk-x86.bin和sdk-x64.bin

下面奉上代码(考虑客户端实际安装环境,为兼容Windows XP SP及更高版本,代码采用C#2.0 .NET Framework 2.0)

using System;
using System.IO;
using System.ComponentModel;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.Threading;

namespace Starter
{
    class Enterance
    {
        static string JVM_Pattern = string.Empty;
        static bool hasFoundJVM = false;
        static string Target_JDK_FIlE = string.Empty;
        public static void Main(string[] args)
        {
            
            Console.WriteLine("按任意键开始安装xxx综合平台...");
            Console.ReadKey(true);
            Console.Clear();
            
            if(isFileMissed())
            {
                Console.WriteLine("安装文件丢失请联系技术人员");
            }else{                
                doPreInstallCheck();
                if(hasFoundJVM){
                    installJVM(Target_JDK_FIlE);
                }
            }
            
            Console.ReadKey(true);
        }
        
        /// <summary>
        /// 判断主安装程序是否丢失
        /// </summary>
        /// <returns></returns>
        static bool isFileMissed()
        {        
            return !File.Exists("setup.exe");
        }
        
        static String getArch()
        {
             return System.Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE", 
                                                             EnvironmentVariableTarget.Machine);
        }
        
        /// <summary>
        /// 安装预检测
        /// </summary>
        static void doPreInstallCheck(){
            String arch =getArch();
            Architecture type= (Architecture)Enum.Parse(typeof(Architecture),arch);            
            switch(type){
                case Architecture.AMD64:
                case Architecture.IA64:
                case Architecture.x64:
                        Console.WriteLine("发现{0}",EnumUtil.GetEnumDescription(type));
                        JVM_Pattern = "sdk.*x64.*";
                    break;
                case Architecture.x86:
                    Console.WriteLine("发现{0}",EnumUtil.GetEnumDescription(type));
                    JVM_Pattern = "sdk.*x86.*";
                    break;
            }
            string []files = Directory.GetFiles(Environment.CurrentDirectory,"sdk-*.bin");                
            Action<string> action = new Action<string>(matchJVM);
            Array.ForEach(files, action);            
        }
        
         private static void matchJVM(string jdkfile)
        {
             FileInfo fileInfo = new FileInfo(jdkfile);
             bool isMatched = Regex.IsMatch(fileInfo.Name,JVM_Pattern);
             if(isMatched) {
                 hasFoundJVM = true;
                 Target_JDK_FIlE = jdkfile;
             }
             if(Debugger.IsAttached)
                 Console.WriteLine("{0}与当前操作系统匹配的JVM:{1}",fileInfo.Name,isMatched);
        }        
        
        /// <summary>
        /// 安装JVM
        /// </summary>
        /// <param name="fileName">JDK文件名</param>
        static void installJVM(String fileName)
        {
            Console.WriteLine("即将安装程序,等待时间取决你电脑的性能");
            //https://stackoverflow.com/questions/3360555/how-to-pass-parameters-to-threadstart-method-in-thread
            Thread thread =new Thread(new ParameterizedThreadStart(DoSlientInstall));
            thread.Start(fileName);
            Thread.Sleep(5000);//等待安装进程启动
            FileInfo fileInfo = new FileInfo(fileName);
            InstallState state =new InstallState();
            state.processName =fileInfo.Name;
            
            
            Timer timer =new Timer(waitForProcessExitCallBack,state,0,1000);                
            state.tmr = timer;
        
        }
        
        public static void waitForProcessExitCallBack(object target){
            InstallState state = target as InstallState;
            Timer timer = state.tmr;
            Process []allProcceses = Process.GetProcesses();
            bool isJDKProcExit  = true;            
            foreach(Process proc in allProcceses)
            {
                string procName = String.Format("{0}",proc.ProcessName);                
                if(Debugger.IsAttached)
                    Console.WriteLine("{0},{1}, {2}",procName,state.processName,procName.Equals(state.processName));
                if(procName.Equals(state.processName)){                                                    
                    isJDKProcExit = false;
                }                                
            }
            if(isJDKProcExit){
                Console.WriteLine("\r\n安装所需软件使用时间:{0}秒",state.counter);
                timer.Dispose();
            }else{
                Console.Write(state.delims);
                state.counter++;                            
                //Thread.Sleep(1000);
                //
                //timer.Change(1000,1000);
                //timer.Dispose();
            }            
    
        }
        
        //https://stackoverflow.com/questions/24918768/progress-bar-in-console-application

        //https://stackoverflow.com/questions/12354883/how-do-i-gracefully-stop-a-system-threading-timer
        static void DoSlientInstall(object fileName)
        {
            Console.WriteLine("安装进行中,请勿退出...");
            ProcessStartInfo pStartInfo = new ProcessStartInfo();
            pStartInfo.FileName = (String)fileName;
            pStartInfo.Verb = "runas";
            pStartInfo.Arguments = "/lang=2052 /s /L c:\\jdk-install.log INSTALLDIR=c:\\embededJDK1.8 ADDLOCAL=\"ToolsFeature,SourceFeature\" WEB_JAVA=0 AUTO_UPDATE=0 NOSTARTMENU=1";
            pStartInfo.UseShellExecute = false;            
            System.Diagnostics.Process.Start(pStartInfo);
            //https://stackoverflow.com/questions/6050478/how-do-i-create-edit-a-manifest-file
            //System.Diagnostics.Process.Start((String)fileName,"/s");
        }
    }
    //http://www.gnu.org/software/dotgnu/pnetlib-doc/System/Threading/Timer.html
    class InstallState{
        /// <summary>
        /// 时间计数器
        /// </summary>
         public int counter = 0;
         /// <summary>
         /// 进程名
         /// </summary>
         public String processName;
         /// <summary>
         /// 分隔符
         /// </summary>
         public string delims="*";

         public Timer tmr;
    }
}
using System;
using System.ComponentModel;
using System.Reflection;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Data;

namespace Starter
{
    /// <summary>
    /// 操作系统架构枚举类
    /// </summary>
    //https://msdn.microsoft.com/en-us/library/aa384274.aspx
    //https://ss64.com/nt/syntax-64bit.html
    public enum Architecture{        
        [Description("64位操作系统")]
        x64 =0,
        [Description("AMD 64位操作系统")]
        AMD64=1,
        [Description("Intel64位操作系统")]
        IA64=2,
        [Description("x86架构操作系统")]
        x86=3
    }
    class EnumUtil{
        public static string GetEnumDescription(Enum enumValue)
        {
            string enumValueAsString = enumValue.ToString();
        
            Type type = enumValue.GetType();
            FieldInfo fieldInfo = type.GetField(enumValueAsString);
            object[] attributes = fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
        
            if (attributes.Length > 0)
            {
                DescriptionAttribute attribute = (DescriptionAttribute)attributes[0];
                return attribute.Description;
            }
        
            return enumValueAsString;
        }
    }
}

安装效果图

 

参考资料

Installing With a Configuration File

Java 8 SE Documentation

How do I arrange a silent (unattended) Java installation?

Language IDs

Installing the JDK Silently

posted on 2017-08-25 16:14  你不知道的浪漫  阅读(2654)  评论(1编辑  收藏  举报