How to display ClickOnce Version number on Windows Forms
Asked Answered
G

10

70

I have a windows forms application that is deployed to two different locations.

  • Intranet - ClickOnce
  • Internet - Installed on a citrix farm through Windows installer

I display ClickOnce version number for click-once deployed versionApplicationDeployment.IsNetworkDeployed.

if (ApplicationDeployment.IsNetworkDeployed)
        return ApplicationDeployment.CurrentDeployment.CurrentVersion;

But for the non-click application, I am not sure how to retrieve clickonce version unless I hardcode the version number in assembly info.

Is there an automatic way of retrieve ClickOnce version number for non-clickonce deployed version?

Goer answered 8/7, 2009 at 13:33 Comment(2)
I have always wondered how Microsoft decided two versions for a program would be useful.. and then give no way to tie the two together. Oy...Lodged
It's quite beautiful really, as unintentional as it is. Cpg's answer has become so popular, because it answer's the questions people visiting this page after typing their own question into a search engine, whereas JaredPar's answer has been marked the best answer, yet still isn't very popular at all.Hypocorism
O
15

No I do not believe that there is a way. I believe the ClickOnce information comes from the manifest which will only be available in a ClickOnce deployment. I think that hard coding the version number is your best option.

Overtask answered 8/7, 2009 at 13:40 Comment(2)
@JaredPar: Thank you and I will actually try to leave the question open to see if anyone has encountered the similar situation and was able to come up with some work-arounds.Goer
+1 Because cpg's answer is just reiterating the 'solution' from the question (albeit with some more details).Disgusting
J
110
  1. Add an assembly reference to System.Deployment to your project.

  2. Import the namespace in your class file:

    VB.NET:

    Imports System.Deployment.Application
    

    C#:

    using System.Deployment.Application;
    
  3. Retrieve the ClickOnce version from the CurrentVersion property.

    You can obtain the current version from the ApplicationDeployment.CurrentDeployment.CurrentVersion property. This returns a System.Version object.

    Note (from MSDN):

    CurrentVersion will differ from UpdatedVersion if a new update has been installed but you have not yet called Restart. If the deployment manifest is configured to perform automatic updates, you can compare these two values to determine if you should restart the application.

    NOTE: The CurrentDeployment static property is only valid when the application has been deployed with ClickOnce. Therefore before you access this property, you should check the ApplicationDeployment.IsNetworkDeployed property first. It will always return a false in the debug environment.

    VB.NET:

    Dim myVersion as Version
    
    If ApplicationDeployment.IsNetworkDeployed Then
       myVersion = ApplicationDeployment.CurrentDeployment.CurrentVersion
    End If
    

    C#:

    Version myVersion;
    
    if (ApplicationDeployment.IsNetworkDeployed)
       myVersion = ApplicationDeployment.CurrentDeployment.CurrentVersion;
    
  4. Use the Version object:

    From here on you can use the version information in a label, say on an "About" form, in this way:

    VB.NET:

    versionLabel.Text = String.Concat("ClickOnce published Version: v", myVersion)
    

    C#:

    versionLabel.Text = string.Concat("ClickOnce published Version: v", myVersion);
    

    (Version objects are formatted as a four-part number (major.minor.build.revision).)

Joannejoannes answered 24/5, 2012 at 9:24 Comment(3)
How is this upvoted [currently] 106 times when it doesn't even answer the original question? The original question already says it knows about ApplicationDeployment.CurrentDeployment.CurrentVersion, and this answer just says to use that. The asker very clearly wants to know how to get the version when running as a non-ClickOnce app. The only way I know how to do that is during development by manually parsing a relative path to the .csproj or using `Microsoft.Build.BuildEngine.Project'.Musky
This doesn't work with .NET Core (.NET 5 and above) since there is no System.Deployment. Is there an update way to get the ClickOnce version that doesn't use System.Deployment?Quirita
@Quirita I have added an answer for .net CoreRabbi
O
15

No I do not believe that there is a way. I believe the ClickOnce information comes from the manifest which will only be available in a ClickOnce deployment. I think that hard coding the version number is your best option.

Overtask answered 8/7, 2009 at 13:40 Comment(2)
@JaredPar: Thank you and I will actually try to leave the question open to see if anyone has encountered the similar situation and was able to come up with some work-arounds.Goer
+1 Because cpg's answer is just reiterating the 'solution' from the question (albeit with some more details).Disgusting
H
8

I would simply make the assembly version of the main assembly the same as the CLickOnce version every time you put out a new version. Then when it runs as a non-clickonce application, just use Reflection to pick up the assembly version.

Horribly answered 21/8, 2009 at 10:46 Comment(3)
why not use the version of the main assembly when run as clickonce as well?Factorial
You have to change the ClickOnce version, or it won't install as an update. I generally try to keep the assembly version and ClickOnce version the same, or at least in the same minor version, depending on what our release versions are going to be.Horribly
The problem with using the AssemblyInfo is that it won't automatically keep the assembly version the same as the ClickOnce version. This will be a manual change that all the developers in the team will have to make each time someone deploys.Pharmacopoeia
R
7

Solution for .NET (Core) 7 and higher

On .net Core, you can read the version number from the environment variable ClickOnce_CurrentVersion.

string versionString = Environment.GetEnvironmentVariable("ClickOnce_CurrentVersion") ?? "0.0.0.0";
Version version= Version.Parse(versionString);
MessageBox.Show(version.ToString());

See documentation

Rabbi answered 27/1, 2023 at 20:48 Comment(1)
I was able to use this code on a .NET Core 3.1 app as wellBock
P
3

not that it matters three years later, but I ended up just parsing the manifest file with xml reader.

Padre answered 5/12, 2012 at 22:12 Comment(1)
Why not use cpg's solution?Recrystallize
S
3

Try thread verification:

if (ApplicationDeployment.IsNetworkDeployed)
{
    if (ApplicationDeployment.CurrentDeployment.CurrentVersion != ApplicationDeployment.CurrentDeployment.UpdatedVersion)
    {
        Application.ExitThread();
        Application.Restart();
    }
}
Slotter answered 8/9, 2014 at 18:14 Comment(0)
D
3

To expand on RobinDotNet's solution:

Protip: You can automatically run a program or script to do this for you from inside the .csproj file MSBuild configuration every time you build. I did this for one Web application that I am currently maintaining, executing a Cygwin bash shell script to do some version control h4x to calculate a version number from Git history, then pre-process the assembly information source file compiled into the build output.

A similar thing could be done to parse the ClickOnce version number out of the project file i.e., Project.PropertyGroup.ApplicationRevision and Project.PropertyGroup.ApplicationVersion (albeit I don't know what the version string means, but you can just guess until it breaks and fix it then) and insert that version information into the assembly information.

I don't know when the ClickOnce version is bumped, but probably after the build process so you may need to tinker with this solution to get the new number compiled in. I guess there's always /*h4x*/ +1.

I used Cygwin because *nix scripting is so much better than Windows and interpreted code saves you the trouble of building your pre-build program before building, but you could write the program using whatever technology you wanted (including C#/.NET). The command line for the pre-processor goes inside the PreBuildEvent:

<PropertyGroup>
  <PreBuildEvent>
    $(CYGWIN_ROOT)bin\bash.exe --login -c refresh-version
  </PreBuildEvent>
</PropertyGroup>

As you'd imagine, this happens before the build stage so you can effectively pre-process your source code just before compiling it. I didn't want to be automatically editing the Properties\AssemblyInfo.cs file so to play it safe what I did was create a Properties\VersionInfo.base.cs file that contained a text template of a class with version information and was marked as BuildAction=None in the project settings so that it wasn't compiled with the project:

using System.Reflection;
using EngiCan.Common.Properties;

[assembly: AssemblyVersion("0.$REVNUM_DIV(100)$.$REVNUM_MOD(100)$.$DIRTY$")]
[assembly: AssemblyRevisionIdentifier("$REVID$")]

(A very dirty, poor-man's placeholder syntax resembling Windows' environment variables with some additional h4x thrown in was used for simplicity's/complexity's sake)

AssemblyRevisionIdentifierAttribute was a custom attribute that I created to hold the Git SHA1 since it is much more meaningful to developers than a.b.c.d.

My refresh-version program would then copy that file to Properties\VersionInfo.cs, and then do the substitution of the version information that it already calculated/parsed (I used sed(1) for the substitution, which was another benefit to using Cygwin). Properties\VersionInfo.cs was compiled into the program. That file can start out empty and you should ignore it by your version control system because it is automatically changing and the information to generate it is already stored elsewhere.

Dietetic answered 25/11, 2014 at 20:50 Comment(0)
S
3

One possible method is to extract the information from the application's manifest file

string version = "N/A";
    
Assembly assembly = Assembly.GetEntryAssembly();
string manifestPath = assembly.Location + ".manifest";

XmlDocument manifestXml = new XmlDocument();
manifestXml.Load(manifestPath);

XmlNamespaceManager namespaceManager = new 
XmlNamespaceManager(manifestXml.NameTable);
namespaceManager.AddNamespace("asmv1", "urn:schemas-microsoft-com:asm.v1");

XmlNode 
versionNode = 
manifestXml.SelectSingleNode("//asmv1:assembly/asmv1:assemblyIdentity/@version", namespaceManager);

if (versionNode != null)
{
version = versionNode.Value;
}
Soap answered 15/6, 2023 at 13:36 Comment(1)
it seems rather silly that this is the best solution so far. I had to resort to the same after giving up on all the suggestions found on the internet.Smattering
S
2

Hard code, or... Keep track on your versions (File, Assembly, Deploy) in a database. Make a call to the database with your Assembly and get the Deploy version.

This assumes that you are incrementing your versions in a logical way such that each version type has a relationship. It's a lot of work for such a minor problem. I'd personally go with Jared's solution; although I hate hard coding anything.

Salinger answered 8/7, 2009 at 14:16 Comment(2)
@Coov: " I hate hard coding anything" That is exactly why I was thinking about if there was a way to get around this. I have not thought about database to keep track of version numbers. Thank you for another way to solve this issue.Goer
It makes little sense to me to store it in the database. It is similar to hard coding it. Why not utilize the manifest, which increments on each publish?Englishism
C
2

Using a build component, you could read the click-once version from the project file and write it automatically to the assembly info so both of them are in sync.

Chisolm answered 10/12, 2009 at 0:57 Comment(1)
Do you have an example of this?Rogatory

© 2022 - 2025 — McMap. All rights reserved.