代码之家  ›  专栏  ›  技术社区  ›  Nathan Wheeler

mage.exe部署问题

  •  6
  • Nathan Wheeler  · 技术社区  · 15 年前

    我有一个配置文件需要根据每台服务器进行更改,这样一旦服务器上安装了我们的软件,客户端安装程序的配置文件就会设置为与服务器的特定设置相匹配,然后复制到Web上的公用文件夹进行部署。

    因为我要更改配置文件,所以我还必须重建*.manifest和*.application文件,据我所知,我唯一的选择是使用win7 sdk中的mage.exe。为了使用修改后的配置文件中的正确哈希修复*.manifest文件,我运行:

    mage-新应用程序-fd“.\application files \<appname>\u 1 \u 0 \u 0 \u 0”-tofile“.\application files \u 1 \u 0 \u 0 \<appname>.exe.manifest”-name“<appname>”-version“1.0.0.0”-certfile“key.pfx”-password“<password>”

    然后,要使用修改后的*.manifest文件中的正确哈希修复*.application文件,我运行:

    mage-新部署-i t-t“<appname>.application”-v“1.0.0.0”-appmanifest“.\application files\<appname>&u 1\u 0\u 0\<appname>.exe.manifest”-pu“http://lt;hostaddress>/<path>/application files/<appname>&u 1\u 0\u 0/<appname>.exe.manifest”-certfile“key.pfx”-密码”

    现在,一切都正常了,我收到消息,文件已成功签名。但是,当我尝试安装客户机应用程序时,很明显当我收到一个错误日志并显示以下消息时出现了问题:

    + Deployment manifest is not semantically valid.
    + Deployment manifest requires <deployment> section.
    

    在查看*.application文件时,它在“deployment”节点下提供了一些附加信息,而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在部署节点中不带额外信息的情况下创建文件,从而使其正常工作?

    编辑:作为一个临时解决方案,我将把文件加载到一个XML文档中,并修改它们以适应需要,然后重新对文件进行签名。另外,我现在面临的问题是,还不能确定如何将图标添加到部署中,所以“开始”菜单项会得到一个普通图标以外的图标。

    2 回复  |  直到 10 年前
        1
  •  2
  •   Nathan Wheeler    15 年前

    这是我的实现。我花了很多时间在这一小段代码上,但我仍然没有找到让mage在不干预的情况下处理所有生成的.application文件的所有正确选项。我要说的是,可能会对这段代码进行很多优化。然而,这仍然可以作为跳板来帮助别人。

    为了使以下方法工作,您必须从vs中的clickOnce至少部署一次,然后只保留.application文件不进行部署。必须删除Deploy文件夹中的.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);
            }
        }
    }
    
        2
  •  1
  •   The Muffin Man    10 年前

    如果您的目标是在环境之间修改应用程序清单,我不知道为什么要创建新清单。只需修改当前的一个。我正在发布一个PowerShell脚本,该脚本可以满足您的需要,而且更多…在我的例子中,我有一个安装引导程序,但是您需要的相关代码是在底部。

    对于安装引导程序,您不能放弃已签名的引导程序,因此我必须找到第三方dll来取消对它的签名。(德尔切特) http://forum.xda-developers.com/showthread.php?t=416175 我让那个母亲在源代码管理中,以防有一天它从网络上消失。)

    查找分区 #Begin Resigning various Manifests

    $root = "$PSScriptRoot"
    $ToolsPath = "C:\Tools"
    $CertFile = $ToolsPath + "\my cert.pfx"
    $CertPassword = "wouldn't you like to know"
    
    #Update the setup.exe bootstrappers update url
    Start-Process "$PSScriptRoot\setup.exe" -ArgumentList "-url=`"$ClickOnceUpdateUrl`"" -Wait
    
    #The bootstrappers signature is now invalid since we updated the url
    #We need to remove the old signature
    Start-Process 'C:\Tools\delcert.exe' -ArgumentList "`"$root\setup.exe`"" -Wait
    
    Write-Host "$root [writeline]"
    #Resign with signtool
    Invoke-Expression 'C:\Tools\signtool.exe sign /d "My Company" /f "$CertFile" /p "$CertPassword" "$root\setup.exe"'
    
    #update config properties
    $CodeBasePath = Convert-Path "$PSScriptRoot\Application Files\MyProduct_*"
    $ConfigPath = $CodeBasePath + "\MyProduct.dll.config.deploy"
    [xml] $xml = Get-Content $ConfigPath
    
    $Endpoint = $xml.SelectSingleNode('/configuration/appSettings/add[@key="MailCheckerEndpoint"]')
    $Endpoint.value = $MailCheckerEndpoint
    
    $ApiEndpoint = $xml.SelectSingleNode('/configuration/appSettings/add[@key="MyApi:ApiBaseUrl"]')
    $ApiEndpoint.value = $MyProductApiEndpoint
    $xml.Save($ConfigPath)  
    
    #Begin Resigning various Manifests
    $AppManifestPath = Convert-Path "Application Files\MyCompany_*\MyCompany.dll.manifest"
    
    #Need to resign the application manifest, but before we do we need to rename all the files back to their original names (remove .deploy)
    Get-ChildItem "$CodeBasePath\*.deploy" -Recurse | Rename-Item -NewName { $_.Name -replace '\.deploy','' }
    
    #Resign application manifest
    Invoke-Expression 'C:\Tools\mage.exe -update "$CodeBasePath\MyCompany.dll.manifest" -certFile "$CertFile" -password "$CertPassword" -if "Application Files\MyCompany_1_2_35_0\Resources\ID.ico"'
    
    #Regisn deployment manifests in root and versioned folder
    Invoke-Expression 'C:\Tools\mage.exe -update "$CodeBasePath\MyCompany.vsto" -certFile "$CertFile" -password "$CertPassword" -appManifest "$AppManifestPath" -pub "My Company" -ti "http://timestamp.globalsign.com/scripts/timstamp.dll"'
    Invoke-Expression 'C:\Tools\mage.exe -update "$root\MyComapny.vsto" -certFile "$CertFile" -password "$CertPassword" -appManifest "$AppManifestPath" -pub "My company" -ti "http://timestamp.globalsign.com/scripts/timstamp.dll"'
    
    #Rename files back to the .deploy extension, skipping the files that shouldn't be renamed
    Get-ChildItem -Path "Application Files\*"  -Recurse | Where-Object {!$_.PSIsContainer -and $_.Name -notlike "*.manifest" -and $_.Name -notlike "*.vsto"} | Rename-Item -NewName {$_.Name + ".deploy"}
    
    推荐文章