Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disabled manifest generation for exes to allow ClickOnce deployment #1924

Closed
wants to merge 1 commit into from

Conversation

taylorjonl
Copy link
Contributor

Right now if you try to deploy one of the CefSharp executables in a ClickOnce deployment it will fail to download with this error:

Reference in the manifest does not match the identity of the downloaded assembly CefSharp.BrowserSubprocess.exe.

There are ways around it, e.g. you can deploy the files as data files instead, but with this change it should work by default.

@amaitland
Copy link
Member

The manifests are required, I don't have time to go into the details at the moment.

You'll need to look for another solution.

Surprisingly others use clickonce and I've never seen a report of the subprocess being a problem.

@amaitland amaitland closed this Feb 3, 2017
@taylorjonl
Copy link
Contributor Author

This is the exact problem I am having: #500

Unfortunately the fix they mention in that issue will not work for me because I have to use mage.exe to generate my manifest because the project cannot be published from Visual Studio or through MSBUILD. My only option is to manually edit the generated manifest which I am trying to avoid since this build script is used by multiple projects and it generic.

I will look into another option but if you were able to let me know why the manifest is required that may help me figure out what other options I have.

@amaitland
Copy link
Member

Is there a reason you need to add it as a reference? You can also build your own subprocess, it's a single c# file.

Go through the git history of the manifests, that will give you some clues.

If you distrib a subprocess without one, expect problems.

@taylorjonl
Copy link
Contributor Author

It is not added as a reference, I am using the NuGet package. Short answer is I am using mage.exe which scans a directory and builds the manifest from the files in the directory. It sees the CefSharp.BrowserSubprocess.exe as an assembly and adds it to the manifest as such.

The reason adding the file as content works for others is because that tells the MSBUILD to add the assembly as a file and not as an 'assembly` so it doesn't validate anything. I can't do this because the publish feature they use will not work in our solution, our projects are loosely coupled and don't have references so it will only deploy some of the files.

I don't consider deploying my own subprocess an option because it will just add a maintenance headache that I will never hear the end of from my fellow developers.

In the end I have to move on so I will just have to put a dirty hack in the build script until I have time to look into this further, sorry for wasting your time but thanks for the conversation, it will help me later when I try to find another solution to this problem.

@amaitland
Copy link
Member

I don't consider deploying my own subprocess an option because it will just add a maintenance headache that I will never hear the end of from my fellow developers.

It's a valid option, and shouldn't be dismissed out of hand. Maintenance burden is very small.

In the end I have to move on so I will just have to put a dirty hack in the build script until I have time to look into this further, sorry for wasting your time but thanks for the conversation, it will help me later when I try to find another solution to this problem.

Good luck 👍

@mkmorse
Copy link

mkmorse commented Feb 6, 2017

@taylorjonl We are running into the same issue, what was your "dirty hack"?

@taylorjonl
Copy link
Contributor Author

@mkmorse, if you are deploying through Visual Studio you should follow the steps in issue 500. If you are using MSBUILD to do the work you can fix it by moving the second .EXE to the "Files" list instead of the "Dependencies" list, e.g.:

<GenerateApplicationManifest AssemblyName="$(ApplicationName).exe"
                             AssemblyVersion="$(PackageVersion)"
                             Dependencies="@(Dependency)"
                             Files="@(File)"
                             EntryPoint="@(EntryPoint)"
                             IconFile="@(IconFile)"
                             ClrVersion="4.0.30319.1"
                             TargetFrameworkVersion="v4.5"
                             TargetFrameworkMoniker=".NETFramework,Version=v4.0"
                             OutputManifest="$(ClickOnceDir)\$(PackageVersion)\$(ApplicationName).exe.manifest">`

If you are using mage.exe like us you have to modify the application manifest after you create it and before you sign it. Here is our script we use to prepare our artifacts for ClickOnce deployment:

$basePath = Resolve-Path .

$applicationManifestPath = "$ClickOnceApplicationVersion\$ClickOnceApplicationExeName.manifest"
$deploymentManifestPath = "$ClickOnceApplicationAssemblyName.application"
$certificatFilePath = Resolve-Path "Certificates\ClickOnce.pfx"

Set-Location "$basePath\ClickOnce"
try
{
	# Copy the configuration file to the output directory
	Copy-Item -Path "$basePath\Configs\$ClickOnceApplicationConfigName" -Destination "$ClickOnceApplicationVersion\$ClickOnceApplicationExeName.config" | Out-Null

	# Create the application manifest
	Write-Host "Creating application manifest at '$applicationManifestPath'"
	& "$basePath\Tools\mage.exe" -New Application -ToFile "$applicationManifestPath" -Name "$ClickOnceApplicationAssemblyName" -Version "$ClickOnceApplicationVersion" -Processor amd64 -FromDirectory "$ClickOnceApplicationVersion" -TrustLevel FullTrust -Algorithm sha256RSA

	# Convert any 'dependency' nodes that are not the main executable but is an executable to 'file' nodes
	[System.Xml.XmlDocument]$applicationManifest = new-object System.Xml.XmlDocument
	$applicationManifest.Load((Join-Path (Resolve-Path .) "$applicationManifestPath"))
	$namespaceManager = New-Object System.Xml.XmlNamespaceManager($applicationManifest.NameTable)
	$namespaceManager.AddNamespace("asmv2","urn:schemas-microsoft-com:asm.v2")
	foreach($dependentAssemblyNode in $applicationManifest.SelectNodes("//asmv2:dependency/asmv2:dependentAssembly[substring(@codebase,string-length(@codebase)-3)='.exe' and @codebase!='$ClickOnceApplicationExeName']", $namespaceManager))
	{
		# Get attributes and nodes that will be used to create new node
		$attributes = $dependentAssemblyNode | select codeBase, size
		$hashNode = $dependentAssemblyNode.SelectSingleNode("asmv2:hash", $namespaceManager)

		# Remove the 'dependency' node
		$dependencyNode = $dependentAssemblyNode.ParentNode
		$dependencyNode.ParentNode.RemoveChild($dependencyNode) | Out-Null

		# Create the new 'file' node
		$fileNode = $applicationManifest.CreateElement("file", "urn:schemas-microsoft-com:asm.v2")
		$fileNode.SetAttribute("name", $attributes.codeBase)
		$fileNode.SetAttribute("size", $attributes.size)
		$fileNode.AppendChild($hashNode) | Out-Null

		# Add the new file node at the appropriate place
		foreach($siblingNode in $applicationManifest.SelectNodes("//asmv2:file", $namespaceManager))
		{
			$siblingAttributes = $siblingNode | select name
			if ([string]::Compare($attributes.codeBase, $siblingAttributes.name, $True) -lt 0)
			{
				break;
			}
		}
		$siblingNode.ParentNode.InsertAfter($fileNode, $siblingNode) | Out-Null
	}
	$applicationManifest.Save((Join-Path (Resolve-Path .) "$applicationManifestPath"))

	# Sign the application manifest
	Write-Host "Signing application manifest at 'applicationManifestPath'"
	& "$basePath\Tools\mage.exe" -Sign "$applicationManifestPath" -CertFile "$certificatFilePath"

	# Create the deployment manifest
	Write-Host "Creating deployment manifest at '$deploymentManifestPath'"
	& "$basePath\Tools\mage.exe" -New Deployment -ToFile "$deploymentManifestPath" -Name "$ClickOnceApplicationName" -Version "$ClickOnceApplicationVersion" -Processor amd64 -AppManifest "$applicationManifestPath" -Install false -UseManifestForTrust false -Publisher "$ClickOncePublisher" -ProviderURL "$ClickOnceProviderUrl" -AppCodeBase "$ClickOnceApplicationVersion\$ClickOnceApplicationExeName.manifest" -Algorithm sha256RSA

	# Modify deployment manifest to enable use of .deploy file extension so they can be served from IIS
	[System.Xml.XmlDocument]$deploymentManifest = new-object System.Xml.XmlDocument
	$deploymentManifest.Load((Join-Path (Resolve-Path .) "$deploymentManifestPath"))
	$deploymentNode = $deploymentManifest.SelectSingleNode('//*[local-name() = "deployment"]')
	$deploymentNode.SetAttribute('mapFileExtensions', 'true')
	$deploymentManifest.Save((Join-Path (Resolve-Path .) "$deploymentManifestPath"))

	# Sign the deployment manifest
	Write-Host "Signing deployment manifest at '$deploymentManifestPath'"
	& "$basePath\Tools\mage.exe" -Sign "$deploymentManifestPath" -CertFile "$certificatFilePath"

	# Rename files in output directory to include .deploy file extension
	Write-Host "Adding the *.deploy extension to all files"
	Get-ChildItem -Path "$ClickOnceApplicationVersion" -File -Recurse -Exclude @("*.manifest") | foreach { Rename-Item $_.FullName "$_.deploy" }
}
finally
{
	Set-Location "$basePath"
}

It isn't the perdiest script ever and doesn't have much error logic but it gets the job done. It is still a work in progress.

@mkmorse
Copy link

mkmorse commented Feb 7, 2017

@taylorjonl Thanks for the information, much appreciated.
Does your commit around not generating manifest files work for the mage.exe path?

@taylorjonl
Copy link
Contributor Author

No, as @amaitland stated it causes bad stuff and as soon as the client tries to startup the BrowserSubClient it immediately crashes.

@amaitland
Copy link
Member

From memory you specifically need the compatibility section to avoid crashing.

@mkmorse
Copy link

mkmorse commented Feb 15, 2017

@taylorjonl Just as an FYI we were able to disable manifest generation (your change) and deploy through ClickOnce without having to modify the manifests, so thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants