我有一个需要在每台服务器上更改的配置文件,因此一旦服务器上安装了我们的软件,客户端安装程序的配置文件就会设置为匹配该服务器的特定设置,然后复制到公共(public)Web 上用于部署的文件夹。
由于我要更改配置文件,我还必须重建 *.manifest 和 *.application 文件,据我了解,我唯一真正的选择是使用 Win7 SDK 中的 Mage.exe。为了使用修改后的配置文件中的正确哈希修复 *.manifest 文件,我运行:
mage -new Application -fd ".\Application Files\<appName>_1_0_0_0" -ToFile ".\Application Files\_1_0_0_0\<appName>.exe.manifest" -Name "<appName>" -Version "1.0.0.0" -CertFile "key.pfx" -password "<password>"
然后,要使用修改后的 *.manifest 文件中的正确哈希来修复 *.application 文件,我运行:
mage -new Deployment -I t -t "<appName>.application" -v "1.0.0.0" -appManifest ".\Application Files\<appName>_1_0_0_0\<appName>.exe.manifest" -pu "http://<hostaddress>/<path>/Application Files/<appName>_1_0_0_0/<appName>.exe.manifest" -CertFile "key.pfx" -password ""
现在,这一切正常,我收到文件已成功签名的消息。但是,当我尝试安装客户端应用程序时,当我收到带有消息的错误日志时,很明显出现了问题:
+ Deployment manifest is not semantically valid.
+ Deployment manifest requires <deployment> section.
在查看 *.application 文件时,它在“部署”节点下有一些附加信息,而直接来自 VS2008 的发布功能的相同文件没有:
<deployment install="true">
<subscription>
<update>
<expiration maximumAge="0" unit="days" />
</update>
</subscription>
<deploymentProvider codebase="http://<hostaddress>/<path>/Application Files/<appName>_1_0_0_0/<appName>.exe.manifest" />
</deployment>
VS2008 发布版本简单的有:
<deployment install="true" />
当我删除附加信息并将部署节点设置为自终止节点,然后重新签署文件时,一切都按预期工作。
这是一个已知问题吗?有没有办法让 Mage 在没有部署节点中的额外信息的情况下创建文件,以便它能够正常工作?
编辑:作为临时解决方案,我将文件加载到 XmlDocument 并修改它们以适应,然后重新签署文件。此外,我现在还面临无法确定如何向部署添加图标的问题,因此“开始”菜单项获得的图标不是通用图标。
最佳答案
这是我的实现。我在这段代码上花了很多时间,但我仍然没有找到让 Mage 在不干预的情况下处理所有 .application 文件的生成的所有正确选项。我要说的是,可能有很多可以对这段代码进行优化的地方。但是,这仍然可以用作帮助某人的跳板。
为了使以下方法起作用,您必须至少从 VS 中的 ClickOnce 部署一次,然后只保留该部署中的 .application 文件。您必须删除部署文件夹中的 .application 和 .manifest。
在我将所有应用程序文件移动到 Config.Instance.ServerSettings.ClientLocation + "<AppName>_<version>"
之后:
DirectoryInfo filedir = new DirectoryInfo(Config.Instance.ServerSettings.ClientLocation);
if (filedir.Exists)
{
FileInfo[] files = filedir.GetFiles();
// Find the current .application file.
FileInfo appinfo = null;
foreach (FileInfo fi in files)
{
if (fi.Name == "<AppName>.application")
{
appinfo = fi;
break;
}
}
if (appinfo != null)
{
XmlDocument applocinfo = new XmlDocument();
applocinfo.Load(appinfo.FullName);
// Get the location of the files from the .application file.
string codebase = applocinfo["asmv1:assembly"]["dependency"]["dependentAssembly"].Attributes["codebase"].Value.Replace("AppName.exe.manifest", "");
XmlDocument xDoc = new XmlDocument();
xDoc.Load(Path.Combine(Path.Combine(filedir.FullName, codebase), "AppName.exe.config"));
foreach (XmlNode xn in xDoc["configuration"]["appSettings"].ChildNodes)
{
if (xn.Attributes != null && xn.Attributes["key"] != null && xn.Attributes["key"].Value == "Clnt_Host")
{
// Here is where I'm modifying my config file, the whole purpose in this wretched deployment process.
xn.Attributes["value"].Value = Config.Instance.ClientSettings.Host;
break;
}
}
xDoc.Save(Path.Combine(Path.Combine(filedir.FullName, codebase), "<AppName>.exe.config"));
Process p = new Process();
p.StartInfo = new ProcessStartInfo(Path.Combine(filedir.FullName, "Mage.exe"));
p.StartInfo.WorkingDirectory = filedir.FullName;
FileInfo fi = new FileInfo(Path.Combine(Path.Combine(filedir.FullName, codebase.TrimStart('.')), "<AppName>.exe.manifest"));
if (fi.Exists)
fi.Delete();
// Write a new .manifest file as an Application file. (-new Application -ToFile ".\codebase\<AppName.exe.manifest")
// Include the files from the codebase directory in the manifest (-fd ".\codebase\")
// Give the application a name to use in the start menu (-name "<AppName>")
// Assign a version number to the deployment (-Version "<version>")
// Give the application an icon to use in the start menu (-IconFile "64x64.ico")
// Sign the manifest (-CertFile "<KeyName>.pfx -Password <password>)
p.StartInfo.Arguments = "-new Application -fd \".\\" + codebase.TrimEnd('\\') + "\" -ToFile \".\\" + Path.Combine(codebase, "<AppName>.exe.manifest") + "\" -Name \"<AppName>\" -Version \"" + codebase.Substring(codebase.IndexOf('_') + 1, codebase.Length - (codebase.IndexOf('_') + 1)).Replace('_', '.').TrimEnd('\\') + "\" -CertFile \"<KeyName>.pfx\" -Password <Password> -IconFile \"64x64.ico\"";
while (p.StartInfo.Arguments.Contains(".\\.\\"))
p.StartInfo.Arguments = p.StartInfo.Arguments.Replace(".\\.\\", ".\\");
Logger.Instance.LogInfo("Starting application: " + p.StartInfo.FileName + "\n\tWith arguments: " + p.StartInfo.Arguments, Logger.InfoType.Information);
p.Start();
while (!p.HasExited)
{
Thread.Sleep(100);
}
// Make a new deployment manifest (-new Deployment -t "<AppName>.application")
// Make the application available offline (-I t)
// Use the files from the .manifest we just made (-AppManifest ".\codebase\<AppName>.exe.manifest")
p.StartInfo.Arguments = "-new Deployment -I t -t \"<AppName>.application\" -v \"" + codebase.Substring(codebase.IndexOf('_') + 1, codebase.Length - (codebase.IndexOf('_') + 1)).Replace('_', '.').TrimEnd('\\') + "\" -AppManifest \".\\" + codebase + "<AppName>.exe.manifest\" -pu \"http://" + Config.Instance.ClientSettings.Host + "/client/" + codebase.Replace('\\', '/') + "<AppName>.exe.manifest\"";
while (p.StartInfo.Arguments.Contains(".\\.\\"))
p.StartInfo.Arguments = p.StartInfo.Arguments.Replace(".\\.\\", ".\\");
Logger.Instance.LogInfo("Starting application: " + p.StartInfo.FileName + "\n\tWith arguments: " + p.StartInfo.Arguments, Logger.InfoType.Information);
p.Start();
while (!p.HasExited)
{
Thread.Sleep(100);
}
xDoc = new XmlDocument();
xDoc.Load(Path.Combine(filedir.FullName, "<AppName>.application"));
// Add to the Deployment manifest (.application) to make the application
// have a minimum required version of the current version,and makes a
// subscription so that the application will always check for updates before
// running.
if (xDoc["asmv1:assembly"]["deployment"]["subscription"] != null)
{
xDoc["asmv1:assembly"]["deployment"].RemoveChild(xDoc["asmv1:assembly"]["deployment"]["subscription"]);
xDoc["asmv1:assembly"]["deployment"].RemoveChild(xDoc["asmv1:assembly"]["deployment"]["deploymentProvider"]);
XmlAttribute node = xDoc.CreateAttribute("minimumRequiredVersion");
node.Value = codebase.Substring(codebase.IndexOf('_') + 1, codebase.Length - (codebase.IndexOf('_') + 1)).Replace('_', '.').TrimEnd('\\');
xDoc["asmv1:assembly"]["deployment"].Attributes.Append(node);
xDoc["asmv1:assembly"]["deployment"].InnerXml = "<subscription><update><beforeApplicationStartup /></update></subscription>";
}
xDoc.Save(Path.Combine(filedir.FullName, "<AppName>.application"));
// Sign the deployment manifest (.application) (-Sign "\<AppName>.application" -CertFile "<AppName>.key" -Password <password>
p.StartInfo.Arguments = "-Sign \"<AppName>.application\" -CertFile \"<AppName>.pfx\" -Password <password>";
while (p.StartInfo.Arguments.Contains(".\\.\\"))
p.StartInfo.Arguments = p.StartInfo.Arguments.Replace(".\\.\\", ".\\");
Logger.Instance.LogInfo("Starting application: " + p.StartInfo.FileName + "\n\tWith arguments: " + p.StartInfo.Arguments, Logger.InfoType.Information);
p.Start();
while (!p.HasExited)
{
Thread.Sleep(100);
}
}
}
关于deployment - Mage.exe 部署问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2313658/