问题描述
我试图通过从Java执行schtasks将计划的任务XML导入Windows Task Scheduler。在我的程序运行schtasks命令之前,它会在<Command>
元素中插入一个文件路径。任务计划程序命令在我编辑XML之前 起作用,但之后不起作用。它一直给我同样的错误:
ERROR: The task XML is malformed.
(1,2)::ERROR: incorrect document Syntax
有趣的是,taskschd.msc
毫不费力地导入了我的任务。仅仅是schtasks给我带来麻烦。如果我使用taskchd.msc手动输入文件路径,然后将其导出,则它与程序输出的XML完全匹配,但是schtasks接受它。这使我相信它与文件编码有关。话虽这么说,我使用taskchd.msc生成的UTF-16格式编码文件,因此我也怀疑它可能与javax.xml
库处理文档的方式有关。我在互联网上四处寻找答案,但是我只能找到有缺陷的Powershell脚本和实际上格式错误的XML文档的结果。那么到底是什么导致了这个问题,我该如何解决呢?
这是我的程序编辑XML文档和执行schtasks的方式:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.parse(f2); // f2 is the XML file
Node n = document.getElementsByTagName("Command").item(0);
// f is the target executable that this task will execute
n.setTextContent("\"" + f.getAbsolutePath() + "\"");
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setoutputProperty(OutputKeys.INDENT,"yes");
transformer.setoutputProperty(OutputKeys.METHOD,"xml");
transformer.setoutputProperty(OutputKeys.ENCODING,"UTF-16");
Result output = new StreamResult(f2);
Source input = new DOMSource(document);
transformer.transform(input,output);
ProcessBuilder builder = new ProcessBuilder("cmd","/c","schtasks","/Create","/TN",taskName,"/XML","\"" + f2.getAbsolutePath() + "\"");
Process p = builder.start();
这是传递给schtasks的最终XML文件:
<?xml version="1.0" encoding="UTF-16" standalone="no"?>
<Task xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task" version="1.2">
<RegistrationInfo>
<Date>2020-09-11T16:33:30</Date>
<Author>CardinalSystem</Author>
<URI>\LOTHStartupScheduler</URI>
</RegistrationInfo>
<Triggers>
<logonTrigger>
<StartBoundary>2020-09-11T16:33:00</StartBoundary>
<Enabled>true</Enabled>
</logonTrigger>
</Triggers>
<Principals>
<Principal id="Author">
<GroupId>S-1-5-32-545</GroupId>
<RunLevel>LeastPrivilege</RunLevel>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<disallowStartIfOnBatteries>false</disallowStartIfOnBatteries>
<StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
<AllowHardTerminate>true</AllowHardTerminate>
<StartWhenAvailable>false</StartWhenAvailable>
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
<IdleSettings>
<StopOnIdleEnd>true</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
<AllowStartondemand>true</AllowStartondemand>
<Enabled>true</Enabled>
<Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<WaketoRun>false</WaketoRun>
<ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
<Priority>7</Priority>
</Settings>
<Actions Context="Author">
<Exec>
<Command>"C:\Users\Cardinal System\Desktop\TaskScheduler.exe"</Command>
<Arguments>-startup 1</Arguments>
</Exec>
</Actions>
</Task>
解决方法
我认为this MSDN page可以提供有价值的信息来解决您的问题。
正如您还指出的那样,该帖子暗示该问题与XML文件的编码有关(请参阅最新答案):
即使任务计划程序将任务文件导出为UTF-16编码,它也会拒绝读取它,除非它是UTF-8。使用文本编辑器(如TextPad或Notepad ++)将XML保存为UTF-8编码的文件,任务计划程序将正确导入该文件(即使主XML标记显示的编码为“ UTF-16”)。
在您的代码中,您将UTF-16
指示为所需的输出编码。在装有macOS的机器中,Transformer
将在macOS中使用UTF-16 Big Endian生成文件。
如上所述,该编码似乎不受支持:
是的,这些编码可与schtasks导入xml文件一起使用(编码为
Notepad
来命名它们):Unicode Ansi,而不能:Unicode big endian,UTF-8
不幸的是,最后一个答案并没有提供太多帮助。请参阅this帖子。
不过,您可以尝试修改程序以输出UTF-8
。
为此,显然,您只需要在代码中更改此行:
transformer.setOutputProperty(OutputKeys.ENCODING,"UTF-8");
再次不幸的是,它也可能是tricky。
在这种情况下,您可以尝试以下操作:
String taskName = "taskname";
File f = new File("C:\\Users\\Cardinal System\\Desktop\\TaskScheduler.exe");
File f2 = new File("C:\\Users\\Cardinal System\\Desktop\\input.xml");
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.parse(f2); // f2 is the XML file
Node n = document.getElementsByTagName("Command").item(0);
// f is the target executable that this task will execute
n.setTextContent("\"" + f.getAbsolutePath() + "\"");
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT,"yes");
transformer.setOutputProperty(OutputKeys.METHOD,"xml");
transformer.setOutputProperty(OutputKeys.ENCODING,"UTF-8");
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION,"yes");
File fout = new File("/Users/jccampanero/Desktop/output.xml");
Writer out = null;
try {
out = new OutputStreamWriter(new FileOutputStream(fout),Charset.forName("UTF-8"));
out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
Source input = new DOMSource(document);
Result output = new StreamResult(out);
transformer.transform(input,output);
out.flush();
} finally {
if (out != null) {
try {
out.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
ProcessBuilder builder = new ProcessBuilder("cmd","/c","schtasks","/Create","/TN",taskName,"/XML","\"" + fout.getAbsolutePath() + "\"");
Process p = builder.start();
如果还是不起作用,则可以尝试使用其他编码方式,例如计算机上的默认编码方式,或者为document
使用不同的序列化方法:
Writer writer = new FileWriter("C:\\Users\\Cardinal System\\Desktop\\output.xml");
XMLSerializer xml = new XMLSerializer(writer,null);
xml.serialize(document);
请注意,该代码段在幕后使用FileWriter
,这反过来会将默认字符编码应用于XML。请参阅this stack overflow question了解更多信息。
因此,我最好的客人是,可能以一种或另一种方式应用计算机的默认编码将达到目的。
,我遇到了类似的问题,发现 SCHTASKS
除了它认为是“正确的”编码之外,不尊重任何编码:ANSI 和 UTF16 Little Endian。 xmllint
是否说没关系。
无论您在 encoding=<your encoding>
中放入什么,都不会使 SCHTASKS
接受带或不带 BOM、UTF-16 Big Endian 的 UTF8 或除其两种首选编码之外的任何其他编码。
两个修复:
- 如果您只有 ANSI 字符,请删除编码属性(只需使用记事本将其保存为 ANSI)
- 将其另存为 UTF-16 LE 并将编码设置为“UTF-16”。