Java:如何从本地安装程序创建一个可运行的.exe文件?

4

我有一个程序,希望能够轻松地被任何用户安装,我认为创建本地安装程序是正确的方式。我使用了Netbeans 8.0的功能(项目属性 > 启用本地打包和打包为)。我成功地创建了一个可工作的本地安装程序。安装后,我有以下文件树:

+-- Project
    \+-- app
         \+--lib (containing all the jars used by the project)
         \---Project.jar
         \---package.cfg
    \+--runtime
        \+--jre (contains the current jre)
    \---Project.exe

从Project.jar中运行程序是可行的。但是,当我尝试启动Project.exe时,我会在弹出窗口上收到以下错误信息:没有任何细节

Failed due to exception from main class

为了确定问题的来源,我已经使用基本标准输出,并将它们重定向到文件中,感谢Windows命令提示符。我得到了输出,直到第一个对外部库的调用。因此,似乎库链接未完成。但是,这是我的package.cfg文件的内容:

app.mainjar=Project.jar
app.mainclass=project/Main
app.classpath=lib/firstlibrary.jar lib/secondlibrary.jar 

指定了classpath但似乎无法识别。我试着将反斜杠(\)倒过来,更改空格为;,但都没有成功。
有一种简单的方法可以复制这个错误(使用Netbeans 8.0和JDK 1.8u11):
  • 在Netbeans中创建一个新的Java项目。
  • 链接任何外部库(.jar):右键单击Libraries > Add jar/folder。
  • 在main方法中,实例化引用该库的对象。
  • 右键单击项目名称 > Properties > Deployment > Enable Native Packaging actions in project menu。
  • 右键单击项目 > Package as > EXE installer。
  • 构建完成后,转到创建安装程序的目录,并启动安装程序。
  • 在安装结束时,转到安装目录并启动.exe文件。应该会出现错误。
你有什么想法如何解决这个问题吗?

你能提供“库链接未完成”的完整输出吗? - Jean Waghetti
嗨@Koln,你能否解释一下你是如何做到这一点的:“为了找出问题的来源,我使用了基本标准输出,并通过Windows命令提示符将它们重定向到文件中”?我在命令行中没有看到任何东西,并且想追踪一个类似的问题。 - ZiglioUK
@ZiglioNZ 所有的解释都在先前的评论中。我在我知道错误可能发生的地方添加了大量的 System.out.println(),通过终端启动我的exe并重定向输出。类似于 file.exe > mystacktrace.txt - Koln
谢谢@koln,我已经弄清楚了:你可以使用/Debug选项运行可执行文件,它会打印出异常堆栈跟踪。当异常消失时,输出被打印出来并可以像你建议的那样导入到文件中。 - ZiglioUK
@Koln 我们能否以某种方式配置构建,使 file.exe 将其输出呈现到 stdout?我在这个主题上找到的信息很少,而且无法想象为什么首先会抑制 stdout?编辑:我自己回答,因为迄今为止还没有涵盖这一点。简短的答案是:不行,我几乎可以肯定永远不会得到支持。原因是链接期间指定了生成的 exe 文件为非控制台应用程序。获取 stdout 的唯一方法是将其重定向到文件。 - quantum
显示剩余2条评论
3个回答

2

我也遇到了同样的问题,使用以下工具:

  • Java SE版本8 Update 31(构建1.8.0_31-b13)
  • NetBeans 8.0.2(Patch 1)
  • Windows 7企业版SP1,Intel Core i7(64位)
  • Inno Setup Compiler 5.5.5(u)- Unicode

我通过重定向printlns来跟踪输出,第一次调用外部库时它就崩溃了。我以为我可能和matt1有同样的问题,所以我修复了所有相对路径,但没有成功。我最终发现安装程序没有创建带有外部jar文件的/app/lib文件夹。手动将lib文件夹复制到目标安装文件夹后,即使在未安装JRE的计算机上也可以正常工作(我正在开发一个独立应用程序)。

解决方法是在build.xml中添加一行:

   <target name="-post-jfx-deploy">
   <fx:deploy width="${javafx.run.width}" height="${javafx.run.height}" 
             nativeBundles="all"
             outdir="${basedir}/${dist.dir}" outfile="${application.title}">
      <fx:application name="${application.title}" mainClass="${javafx.main.class}"/>
      <fx:resources>
          <fx:fileset dir="${basedir}/${dist.dir}" includes="*.jar"/>
          <!--below is the magic bit that copies all the dependency jar files to the native package output-->
          <fx:fileset dir="${basedir}/${dist.dir}" includes="lib/*.jar"/>
      </fx:resources>
      <fx:info title="MyApp" vendor="MyVendor"/>
  </fx:deploy>          
</target>

第二个fileset指令强制从lib文件夹复制jar依赖项,这在部署为EXE或MSI时修复了所有问题。

嗨@TxF,你能解释一下你是如何通过重定向println输出来跟踪输出的吗?谢谢! - ZiglioUK
我明白了,只需要在命令行中使用重定向符号“>”即可,但它只会输出到文件中,有点不太方便。而且我看不到任何异常堆栈跟踪信息,也许我需要以某种方式重定向stderr。 - ZiglioUK
是的,但是这总比一无所获要好。 :) 您始终可以在catch块内执行“e.printStackTrace();”,将输出推送到标准输出流中,最终应该会进入重定向文件(如果需要,您可以强制引发错误条件)。 - TxF
谢谢,我已经发现如何打印异常堆栈跟踪:>可执行/Debug - ZiglioUK

2

我曾经遇到过一个本地打包的JavaFX应用程序类似的问题,出现了"Failed due to exception from main class"错误。我的package.cfg文件看起来和你的一样。

为了诊断这个问题,我只是在命令行中手动运行了jar文件(例如Project.jar),并查看了堆栈跟踪信息,例如如果你的Main类在org.project.Project中。

java -cp Project.jar org.project.Project

原来我使用的URL加载各种文件(例如JavaFX的FXML文件)封装在jar中时出现了问题 - 我使用的是相对URL(例如“./blah.ext”或“../foo.txt”等),但一旦我将URL更改为基于文件在jar中的布局的绝对路径,它就可以正常工作了(例如“/org/project/blah.ext”和“/org/foo.txt”)。


0

我认为你的主类没有正确设置到包中。 你可以使用exe4J从jar文件创建exe,而不是使用Netbeans。(请确保在 项目 > 属性 > 运行 > 主类 中设置主类)

这里下载exe4J。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接