Packaging a ClickOnce Server Deployment
Recently, I have helped one of our clients with ClickOnce deployment. Part of the system that our client sells consists of application servers that have a .Net web service, and a smart client application. We helped our client improve the architecture of the web service and the Smart Client application to support extensibility for customizations specific to their customers. These customizations are out of the scope of this post, but during this re-architecture, the technology base was upgraded from VS 2003 & .Net 1.1 to VS 2005 and .Net 2.0.
The extensibility, plug-in and pipelining portions of the project went over very well. We did however face an unforeseen complexity: ClickOnce Deployment. We had done our fair share of reading on the benefits of ClickOnce and had read some of the deeper technical articles, that all focused on ClickOnce deployment from the web server to the client. The benefits seemed great on paper, even though there are issues with Xbrowser support that quickly rear their head. Also, there seems to be a lack of documentation around actually getting the server portion of ClickOnce installed, and that is what this post focuses on.
ClickOnce deployment has two very important files: the application manifest that defines what files make up the application and the deployment manifest that has the deployment version information and the path (the ProviderURL) for the ClickOnce application to look for updates from. These manifests are signed and this leads to a very tough issue to get around: how can you have a setup package that contains a deployment URL that you do not know ahead of time?
Our client sells their web applications to many customers. These servers are all imaged with Windows Server 2003 and the .NET framework 2.0. Then a deployment team installs the web service and ClickOnce application on the server via an MSI setup package. In the field, servers are always added to server farms to increase capacity. The need for quick setup of machines via a setup package is required.
The nature of the client application that we built, includes dynamically loaded assemblies in order to support customizations of the core application. This scenario is not covered by the “right click -> deploy†ClickOnce deployment method in Visual Studio or MSBuild. When using MSBuild /deploy, the ClickOnce setup package only consists of statically linked assemblies.
What we did was prepare a nAnt script for the build team to use, which creates the proper setup program, including dynamically linked files and additional files needed for server setup. The setup program itself is created via a Web Setup project in Visual Studio.
The nAnt script that we created performed the following steps:
- Get files from source safe and label
- Update all assembly info files with version information (remember that the assembly version is not what is used by ClickOnce to determine if there is a new version to download, the deployment manifest is used for that purpose)
- Build all of the assemblies that are statically and dynamically linked.
- Create the application manifest using Mage.exe, linking all of the statically and dynamically linked assemblies
- Create the deployment manifest and version it with Mage.exe – ensure that the deployment provider URL points to an invalid host name
- Sign the Deployment and Application manifests with a certificate that is not issued by a certificate authority
- Copy all of the files that are needed into a temporary build area with the Web Setup Project
- Update the default web page with version information
- Include a vbscript that is invoked during the install that will update the manifests (more on this later)
- Invoke the Web Setup package via devenv (you need Visual Studio 2005 installed on the build machine)
After the NAnt script is run, there will be a setup.exe and MSI install package for your ClickOnce server deployment. In this package a dummy certificate is included and a vbscript for updating the deployment and application manifests.
In the Web Setup project we configured the vbscript to run during the server install. The vbscript prompts the user to input the URL that will be used for ClickOnce client updates. This URL is then used to update the deployment manifest with the new deployment URL. The prompt also tells the installer that they should point to the load balanced hostname or as opposed to the specific server IP or hostname. The vbscript invokes mage.exe to perform this step. Mage.exe is part of the .Net Framework 2.0 SDK and must also be deployed with the setup project.
mage.exe -Update myclickonceapp.application -ProviderUrl http://myserverhostname/myclickonceapp
The installer then prompts for a path to the certificate to sign with. Customers can use their own certificate to sign the application manifests with, or they can use the unsecured dummy certificate that is included in the setup package.
mage.exe -Sign myclickonceapp.application -CertFile somecertificate.pfx -Password somepassword
Next the installer vbscript must update the bootstrapper that is used to install the prerequisites. This step is needed because the bootstrapper itself will invoke the ClickOnce application after it installs the prerequisites. Note, that there are even issues around this if the client browser is not IE. There are some very good work arounds for ClickOnce Firefox issues discussed over on the Microsoft Channel 9 forums.
setup.exe /url= http://myserverhostname/
After this is complete, the vbscript is left on the server in case if the user wants to update the deployment URL after setup.
This build and installation has worked very well for us, but you do need to at least provide instructions on the setup web page for Firefox users on how to properly install the application, or follow some of the work arounds detailed on the Microsoft Channel 9 Forums.
I hope this helps
-Jon
October 18th, 2006 at 8:07 am
Hi,
Very good post, I am struggling with the exact same setup.
To be able to run mage.exe on the client machine to update the manifest, do I have to include the whole .Net FW 2.0 SDK (~350MB) as a prerequisite? Or is there some other way to accomplish this?
Thanks!
October 18th, 2006 at 9:42 am
Hi Robert,
Thanks for your kind remarks.
I added the mage.exe file to my install, not the entire .Net Framework SDK 2.0. I need to check with MS on the redistribution terms on the mage.exe utility. I don’t see a reason why they would not allow it to be put in a setup program since it is needed to update the manifests.
Also, just to be clear, do you really mean running mage.exe on the client machine? I would think you would run it on the server.
Thanks,
Jon
October 18th, 2006 at 10:35 pm
Hi,
Thank you for your quick reply. Indeed, I meant to run
mage.exe on the server.
Hopefully I will get this resolved soon
Thanks again!
October 24th, 2006 at 1:30 am
I tried to update my clickonce url by executing following code and its not updaing. But same thing executing fine from command prompt (manually). Please tell me where I am wrong….Need Urent help!!!!
——————————————————
Private Function ConfigureclickOnce() As Boolean
Dim Prsc As Process
Dim startInfo As ProcessStartInfo
Dim StrFullPath, StrArgs As String
Try
StrFullPath = “C:\Program Files\Software Solutions Centre Ltd (SSC)\NaviCare\ClickOnce\mage.exe”
StrArgs = New StringBuilder(500).Append(” -Update NaviCareCore.application -ProviderUrl “).Append(ClickOnce_IISPath).Append(“/NaviCareCore.application”).ToString()
‘MsgBox(StrArgs)
startInfo = New ProcessStartInfo(StrFullPath)
startInfo.WindowStyle = ProcessWindowStyle.Hidden
startInfo.UseShellExecute = False
startInfo.RedirectStandardOutput = True
startInfo.RedirectStandardError = True
startInfo.Arguments = StrArgs
Prsc = New Process
Prsc.StartInfo = startInfo
Prsc.Start()
Prsc.WaitForExit()
Prsc.Close()
While Not Prsc.HasExited
End While
‘————————————————————————–
StrArgs = New StringBuilder(500).Append(” -Sign NaviCareCore.application -CertFile NaviCare.pfx -Password sscess”).ToString()
MsgBox(StrArgs)
startInfo = New ProcessStartInfo(StrFullPath)
startInfo.WindowStyle = ProcessWindowStyle.Hidden
startInfo.UseShellExecute = False
startInfo.RedirectStandardOutput = True
startInfo.RedirectStandardError = True
startInfo.Arguments = StrArgs
Prsc = New Process
Prsc.StartInfo = startInfo
Prsc.Start()
While Not Prsc.HasExited
End While
”————————————————————————–
StrArgs = New StringBuilder(500).Append(” /url=”).Append(ClickOnce_IISPath).Append(“\publish.htm”).ToString()
‘MsgBox(StrArgs)
startInfo = New ProcessStartInfo(StrFullPath)
startInfo.WindowStyle = ProcessWindowStyle.Hidden
startInfo.UseShellExecute = False
startInfo.RedirectStandardOutput = True
startInfo.RedirectStandardError = True
startInfo.Arguments = StrArgs
Prsc = New Process
Prsc.StartInfo = startInfo
Prsc.Start()
While Not Prsc.HasExited
End While
Return True
Catch ex As Exception
Throw ex
End Try
End Function
October 24th, 2006 at 8:09 am
What is your error?
Also try setting your working directory to where your application manifest is located (NaviCareCore.application).
Let us know,
Jon
November 20th, 2006 at 8:22 am
“Invoke the Web Setup package via devenv (you need Visual Studio 2005 installed on the build machine) ”
Could you please tell me how can I invoke the Web Setup package in NAnt script? I what to write a build script to build my web setup project which is created in VS2003.
Thanks,
Tracter
November 21st, 2006 at 8:38 am
First, make sure you can build your web setup package from the command line.
Doing that will look something like this:
C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\IDE>devenv /build release “C:\WebSetup1\WebSetup1.sln”
Once you get it to work, you can include the command to run devenv from the command line to your NAnt script like so:
Of course, I would recommend parameterizing your actual script.
Hope that helps,
Jon
December 15th, 2006 at 4:44 am
Hello,
Very good post! I used it to solve the same problem, but now I am still testing the mage commands to see if the republishing on another web server works with my scripts.
I published the application on the dev machine, then I used mage tool, as you adviced, to change the install url:
mage -Update App.application -ProviderUrl http://server/location/App.application
I created the pfx key (installed locally on the dev machine) and used it to resign the manifest:
mage -Sign App.application -CertFile MyKey.pfx -Password MyPass
Then I executed:
setup.exe /url= http://server/location
All the package was then moved to the location above and when I clicked on my installation link to execute the App.application manifest, I get this error:
ERROR DETAILS
Following errors were detected during this operation.
* [12/15/2006 11:10:08 AM] System.Deployment.Application.InvalidDeploymentException (ManifestParse)
- Exception reading manifest from http://server/location/App.application: the manifest may not be valid or the file could not be opened.
- Source: System.Deployment
- Stack trace:
at System.Deployment.Application.ManifestReader.FromDocument(String localPath, ManifestType manifestType, Uri sourceUri)
at System.Deployment.Application.DownloadManager.DownloadDeploymentManifestDirectBypass(SubscriptionStore subStore, Uri& sourceUri, TempFile& tempFile, SubscriptionState& subState, IDownloadNotification notification, DownloadOptions options, ServerInformation& serverInformation)
at System.Deployment.Application.DownloadManager.DownloadDeploymentManifestBypass(SubscriptionStore subStore, Uri& sourceUri, TempFile& tempFile, SubscriptionState& subState, IDownloadNotification notification, DownloadOptions options)
at System.Deployment.Application.ApplicationActivator.PerformDeploymentActivation(Uri activationUri, Boolean isShortcut)
at System.Deployment.Application.ApplicationActivator.ActivateDeploymentWorker(Object state)
— Inner Exception —
System.Deployment.Application.InvalidDeploymentException (SignatureValidation)
- Manifest XML signature is not valid.
- Source: System.Deployment
- Stack trace:
at System.Deployment.Application.Manifest.AssemblyManifest.ValidateSignature(Stream s)
at System.Deployment.Application.ManifestReader.FromDocument(String localPath, ManifestType manifestType, Uri sourceUri)
— Inner Exception —
System.Security.Cryptography.CryptographicException
- No signature was present in the subject.
- Source: System.Deployment
- Stack trace:
at System.Deployment.Internal.CodeSigning.SignedCmiManifest.Verify(CmiManifestVerifyFlags verifyFlags)
at System.Deployment.Application.Manifest.AssemblyManifest.ValidateSignature(Stream s)
January 15th, 2007 at 5:30 am
Hi,
I managed to do all this work, but my problem is that
setup.exe /url= http://myserverhostname/
prompts a confirmation message inside the installer. Did you find any way to automatically respond with yes?
February 8th, 2007 at 9:13 am
Great information! This has always been one of the biggest weak points with ClickOnce deployments for the kind of work that I do.
Great article, you made my day
March 8th, 2007 at 7:45 am
Hi,
After I publish the application, I want to specift the update path. While publishing the application I dont know the update path. When I am at client side I would know the update path. At that time I have only the Setup.exe and 2 XML (Deployment and Application manefest) files. How can I do this. So when the next time if I run my application then it should check in the update path for a new version.Can u please help me regarding this issue??
March 8th, 2007 at 4:47 pm
Suren,
I’m not sure what you mean by “When I am at client side I would know the update path. ” What is your definition of client? You confused me when you said you would have “Setup.exe and 2 XML” on the client side. Are you talking about a web distro or by CD rom, etc….
How is the application getting delivered to the client machine to begin with?
Please explain your exact usage scenario.
Jon
March 12th, 2007 at 12:40 am
I am installing using a CD ROM. Forget abt the client. I mean to say that when I am physically existing at client location. I have the installation CD with me. All the required files are there in the CD. So now how can i change the updates path?
March 12th, 2007 at 11:24 am
Suren,
Have you looked at using Mage.exe that is included in the .net 2.0 SDK? I have not done a CD based install, but I assume you still need to use Mage to update the deployment manifest for the update url.
Thanks,
Jon
April 9th, 2007 at 8:06 am
Hi guys,
Im new in clickonce and VS 2005, i read the articles above and its very informative. But can anyone tell me more about how security for clickonce application works? And what if the user does not have enough rights to install the application is there a code to bypass it? or a workaround to do this?
Thanks!!!
April 24th, 2007 at 9:07 am
I’ve been able to use this to get it working:
String sPath = ;
DeployManifest dManifest = (DeployManifest)ManifestReader.ReadManifest(“DeployManifest”, sPath , false);
string sNewUrl = @”http://”;
sNewUrl += Environment.MachineName + “/” + virtualDirectory;
sNewUrl += “/iToolBox/iToolbox.application”;
dManifest.DeploymentUrl = sNewUrl;
X509Certificate2 cert;
cert = new X509Certificate2(“x:\myKey.pfx”));
ManifestWriter.WriteManifest(dManifest);
SecurityUtilities.SignFile(cert, null, sPath);
I noticed that like Anil, using the mage process seemed to run successfully but the manifest was unchanged, but my using Microsoft.Build.Tasks.Deployment it worked.
April 24th, 2007 at 9:14 am
Eti, thanks for the post!
Seems this post is becoming a place to exchange our ClickOnce woes.
Cheers to the contributers!
Jon
April 25th, 2007 at 11:55 pm
Did someone say ClickOnce woes? I have been going crazy trying to solve the problem Mike Caito points out in this brief post:
http://geekswithblogs.net/thibbard/archive/2006/02/22/70375.aspx
We are trying to run a web distro with a CD rom option to install. Generally our users install from CD and recieve updates from our website. However, the first update after the CD install doesnt copy data files into the new version’s DataDirectory. Any data previously entered is lost. Argh.
Note: Our DB is created and updated dynamically by running sql scripts. It is not published with the app, which is a common cause of the ‘data being lost after update’ problem.
Has anyone got around this one before?
April 26th, 2007 at 9:04 pm
Hi Jimmy,
I actually never did a CD install. Does this happen after every version update or just the first update from the web?
Cheers,
Jon
June 24th, 2007 at 11:06 pm
Hi,
I am new to use OneClick deployment. I have developed a test project and published it using .net 2005 publishing wizard. It create a virtual directory named testOneClick. I can successfully use this functionality as far as I used the website where i published it.
But if I copy the contents of the published virtual directory to server and create a virtual directory with same namd and contents as on my system it does not work. infact I can browse the application deployed on server “http://Server001/OneClick/publish.htm” but when I click on install button it prompts the error and in details following details are shown:
ERROR DETAILS
Following errors were detected during this operation.
* [6/25/2007 1:27:25 PM] System.Deployment.Application.InvalidDeploymentException (ManifestParse)
- Exception reading manifest from http://localhost/OneClick/OneClick.application: the manifest may not be valid or the file could not be opened.
I have also changed the machine(system) name from localhost to server001 in all of these files “OneClick.application”, “OneClick_1_0_0_0.application”, “OneClick.exe.deploy”
I want to do this using .net 2005 publishing wizard not MageUI.
Sabir
gsabirmalik@hotmail.com
June 28th, 2007 at 6:51 pm
Hi Sabir,
If you modify the manifests manually, you will have to resign them using mage.
HTH,
Jon
October 4th, 2007 at 11:33 pm
Very good article, Thanks. This is similar how my publishing process works, although I am using msbuild instead of NAnt. How do you write the version numbers into files? Is there a task for this in NAnt?
October 19th, 2007 at 2:58 pm
gyurisc – For the manifest files I just use the generic execute NAnt task to invoke mage.exe and use mage to update the manifest versions.
March 31st, 2008 at 5:39 pm
What are the downsides to the customer if they use the dummy certificate?
April 1st, 2008 at 10:39 pm
I found a good answer, for .net 3.5 at least, here: http://msdn2.microsoft.com/en-us/library/bb384248.aspx It works better than the options available in .net 2.0 and 3.0
April 2nd, 2008 at 5:59 am
Hi John,
Thanks for posting the link, it seems to be a really helpful writeup of the trade-offs. This certainly did not exist when I took the steps that I took in my post. People who find my post I’m sure will be grateful for the link.
Cheers,
Jon
February 4th, 2009 at 11:21 am
I am also looking for the same feature Roxana needs
setup.exe /url= http://myserverhostname/
prompts a confirmation message inside the installer. Is there any way to automatically respond with yes?
March 10th, 2009 at 2:06 pm
Kumar, I’m Also looking for a solution for the same problem you and Roxana have run into regarding the following command.
setup.exe /url= http://myserverhostname/
I haven’t found a solution however I have found the following post on the Microsoft feedback site (see my comment at the bottom of the page).
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=390992&wa=wsignin1.0
I’ve also found the other following resources on the issue.
http://social.msdn.microsoft.com/Forums/en-US/winformssetup/thread/633a284c-6485-4266-a6c5-01055e43dcf4
http://social.msdn.microsoft.com/Forums/en-US/winformssetup/thread/65cf51bd-b9a6-4a7a-b8f0-d0a837055703
http://social.msdn.microsoft.com/Forums/en-US/winformssetup/thread/c3c5063c-9f5a-4754-81f7-990d82f5855c
http://social.msdn.microsoft.com/Forums/en-US/winformssetup/thread/6a2a299d-932b-4a1e-87e3-fe535a883fae
March 18th, 2009 at 6:59 pm
Hey all,
I wrote these scripts a long time ago for an old client of mine, so I don’t have the source anymore. I believe message boxes were ok in our setup because the person installing it would need to choose the correct deployment URL in popup no matter what (so answering other popups might have been ok). However, I would think there has to be a way for a vbscript or a powershell script even to somehow answer/suppress popups.
Please share your experiences, as thousands of people have come to this page for click once install help (so your experiences are helping people).
Cheers,
Jon
March 25th, 2009 at 9:15 pm
So far there doesn’t seem to be any way to suppress the prompt. What I have found is that if you answer Yes to the prompt that after subsequent attempts to change the URL you will no longer get the prompt. This still isn’t acceptable for automated build scripts though.
Supposedly this was supposed to be fixed in Visual Studio 2008 but I can still reproduce it and it seems that others are having the same issue still as well. In order to get some official response I’ve posted a new bug report on the Microsoft Feedback Center for Visual Studio. Please follow the link below and vote, comment, or just track the progress of this issue if you are interested in seeing this fixed.
I created a new bug report on the Microsoft Feedback Center site to help try to get the -url= parameter to work in a silent manner. If anybody is interested in voting in order to get this issue addressed or to just follow the progress please follow the link below.
The bootstrapper (setup.exe) created along with a ClickOnce application in Visual Studio 2008 is supposed to update without prompting
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=426896
December 8th, 2009 at 11:39 pm
Unfortunately it doesn’t appear that the problems with the bootstrapper that I mentioned above have been addressed yet in Visual Studio 2010 Beta 2.
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=519149