2012-06-21 12 views
5

Estoy trabajando en la automatización del instalador para una aplicación .NET 4.0 ClickOnce WPF, que necesita algunos elementos para establecerse en el archivo app.config. He pasado por el espinoso proceso de encontrar pasos específicos que debo seguir usando Mage.exe (es decir, actualizar y volver a firmar los manifiestos de implementación y aplicación) y ahora estoy tratando de automatizarlo para la instalación.Actualizando el manifiesto de despliegue para una aplicación ClickOnce programmatically resulta en un elemento faltante <compatibleFrameworks>, requerido en 4.0

he optado por utilizar .deploy extensión a minimizar los problemas con IIS/Internet   mecanismos de seguridad Explorer, por lo que en esencia el algoritmo es el siguiente (basado en Signing and re-signing manifests in ClickOnce (Saurabh Bhatia) y Update Configuration of a ClickOnce WPF Application Using Mage or MageUI, como fuentes primarias entre otros):

  1. Ir a la carpeta \Application Files\App_%HighestVersion%\
  2. Retire .deploy ext ensión de los archivos que tienen que
  3. Run   mage -u %app%.exe.manifest -cf cert.pfx
  4. Restaurar .deploy extensión
  5. Run   mage -u %app%.application -appm %app%.exe.manifest -cf cert.pfx
  6. %app%.application Copia 2 niveles arriba (a ..\.. - raíz de implementación)

que las obras perfectamente si se hace manualmente. Puedo ejecutar un archivo .cmd, personalizado para detalles del entorno (rutas, etc.), pero luego tendría que incluir mage.exe en la implementación, y si Microsoft nos permite hacer eso es una pregunta abierta para mí. Por lo tanto, estoy tratando de llevar a cabo acciones similares en la clase Installer:

X509Certificate2 ct = new X509Certificate2(sPathCert); 

// .. Remove .deploy extension (for files in the sPathApp folder). 

sPathMft = Directory.GetFiles(sPathApp, "*.exe.manifest")[0]; 
ApplicationManifest am = ManifestReader.ReadManifest("ApplicationManifest", sPathMft, false) as ApplicationManifest; 
if (am == null) 
    throw new ArgumentNullException("AppManifest"); 
am.ResolveFiles(); 
am.UpdateFileInfo(); 
ManifestWriter.WriteManifest(am, sPathMft); 
SecurityUtilities.SignFile(ct, null, sPathMft); 

// .. Restore .deploy extensions to files touched above. 

sPathMft = Directory.GetFiles(sPathApp, "*.application")[0]; 
DeployManifest dm = ManifestReader.ReadManifest("DeployManifest", sPathMft, false) as DeployManifest; 
if (dm == null) 
    throw new ArgumentNullException("DplManifest"); 
dm.ResolveFiles(); 
dm.UpdateFileInfo(); 
ManifestWriter.WriteManifest(dm, sPathMft); 
SecurityUtilities.SignFile(ct, null, sPathMft); 

File.Copy(sPathMft, sPathBin + "\\" + dm.AssemblyIdentity.Name, true); 

Ahora, aquí viene lo bueno. Todo funciona perfectamente con excepción del paso 5. Cuando la aplicación se descarga a la máquina del usuario hay un problema con el manifiesto de implementación:

  • El manifiesto de implementación no es semánticamente válido.
  • manifiesto de implementación falta < compatibleFrameworks>.

De hecho, esta sección ya no está presente (Sin embargo, fue en la aplicación original%% .Application!). Se describe un resultado similar en ClickOnce - .NET 4.0 errors: "Deployment manifest is not semantically valid" and "Deployment manifest is missing <compatibleFrameworks>", pero es el resultado de un proceso diferente (msbuild). Esta sección es nueva (y obligatoria) para los manifiestos 4.0, así que mi única suposición es que, de alguna forma, cuando ManifestWriter persiste en los cambios al disco, ¿lo hace de forma 3.5? Comprueba tres veces que se utiliza una biblioteca correcta (C: \ Archivos de programa (x86) \ Reference Assemblies \ Microsoft \ Framework.NETFramework \ v4.0 \ Microsoft.Build.Tasks.v4.0.dll). ¿Qué ofrece?

En lugar de una respuesta hasta ahora tratado de agregar la sección que falta manualmente:

dm.CompatibleFrameworks.Clear(); // Unnecessary as dm.CompatibleFrameworks.Count == 0 indeed! 
CompatibleFramework cf = new CompatibleFramework(); 
cf.Version= "4.0"; 
cf.SupportedRuntime = "4.0.30319"; 
cf.Profile= "Client"; 
dm.CompatibleFrameworks.Add(cf); 
cf = new CompatibleFramework(); 
cf.Version = "4.0"; 
cf.SupportedRuntime = "4.0.30319"; 
cf.Profile = "Full"; 
dm.CompatibleFrameworks.Add(cf); 

Pero eso no tiene ningún efecto, no importa dónde colocar este código, antes de dm.ResolveFiles() , dm.UpdateFileInfo() o ManifestWriter.WriteManifest (..)!

Mi resultado es similar al de la pila de desbordamiento preguntas MageUI.exe removes compatibleFrameworks element o Why does Mage.exe not generate a compatibleFrameworks attribute? o MageUI.exe is not including a compatibleFrameworks element, pero no estoy usando mageui, mage o incluso msbuild en absoluto!

¿Qué está pasando?

+0

Como solución, ¿ha intentado manipular directamente el manifiesto XML en lugar de utilizar las API? Funcionó para nosotros en el pasado: usted tiene control total sobre los contenidos, hace lo que quiera, y luego vuelve a firmar el archivo una vez que haya terminado. –

+0

Astrogator: ¿hay alguna posibilidad de que puedas compartir tus scripts? Estoy a punto de comenzar un proceso muy similar. ¡Gracias! –

+0

¡Hola @ErickT! Bueno, el algoritmo está bastante establecido aquí. Este código es ejecutado por msiexec.exe a través de una acción personalizada, que especifico en el proyecto de instalación de Visual Studio 2010 - no hay 'scripts' per se. Avíseme, si eso no es lo suficientemente claro, y trataré de exponer más. – Astrogator

Respuesta

8

Lo encontré yo mismo. El culpable es ManifestReader.ReadManifest ("DeployManifest", sPathMft, true).

MSDN dice, [argumento preserveStream] "especifica si desea conservar el flujo de entrada en la propiedad InputStream del objeto manifiesto resultante. Utilizado por ManifestWriter para reconstituir de entrada que no está representado en la representación de objeto".

redacción a un lado, el establecimiento de cierto no es suficiente por sí mismo: dm.CompatibleFrameworks.Count todavía será 0, pero ahora la adición de CompatibleFramework artículos tendrá un efecto!

para otra persona en el mismo barco, yo que antes dm.ResolveFiles():

if( dm.CompatibleFrameworks.Count <= 0 ) 
{ 
    CompatibleFramework cf= new CompatibleFramework(); 
    cf.Profile= "Client";  cf.Version= "4.0";  cf.SupportedRuntime= "4.0.30319"; 
    dm.CompatibleFrameworks.Add(cf);    // cf= new CompatibleFramework(); 
    cf.Profile= "Full";  // cf.Version= "4.0";  cf.SupportedRuntime= "4.0.30319"; 
    dm.CompatibleFrameworks.Add(cf);    /// no need for separate object 
} 

@davidair, gracias por su sugerencia!   De acuerdo, aunque prefiero trabajar con objetos API (en comparación con XML).
Otra alternativa es llamar al mage (directamente o desde un archivo .cmd), ya que parece que are allowed lo redistribuiremos.


También añadirá la parte siguiente, que no tiene un impacto en la pregunta misma, pero puede ser muy importante para cualquier persona siguiendo el mismo camino (/cliente es la raíz del despliegue, y puede haber personalizado):

dm.DeploymentUrl= string.Format("http://{0}/{1}/client/{1}.application", 
         Dns.GetHostName(), Context.Parameters[ scTokVirtDir ]); 
dm.UpdateMode=  UpdateMode.Background; 
dm.UpdateUnit=  UpdateUnit.Weeks; 
dm.UpdateInterval= 1; 
dm.UpdateEnabled= true; 
1

También necesitaba agregar CompatibleFrameworks. También probé se suman los CompatibleFrameworks como esto (que no funciona)

dm.CompatibleFrameworks.Add(...); 

Mi solución fue establecer:

dm.TargetFrameworkMoniker = ".NETFramework,Version=v4.0";     

Después de esto la generación Manifiesto era correcta.

Tenga cuidado Si establece TargetFrameworkMoniker antes de WriteManifest, tiene el <Frameworks compatible > dos veces y el archivo de la aplicación está dañado. Aquí está mi solución para esto:

DeployManifest dm = ManifestReader.ReadManifest("DeployManifest", applicationFileName, false) as DeployManifest; 
dm.ResolveFiles(); 
//doing stuff.. 
dm.UpdateFileInfo(); 
ManifestWriter.WriteManifest(dm, applicationFileName); 
dm.TargetFrameworkMoniker = ".NETFramework,Version=v4.0"; 
ManifestWriter.WriteManifest(dm, applicationFileName); 
Cuestiones relacionadas