Detect the Visual Studio version inside a VSPackage
Asked Answered
A

7

16

How can I check/detect which Visual Studio version is running under my VSPackage?

I cannot get from the registry because the computer could have several versions installed, so I guess there is an API that is able to get it.

Anybody knows how to get it from a managed Visual Studio package using C#?

Agentive answered 18/6, 2012 at 12:3 Comment(0)
A
10

Finally I wrote a class to detect the Visual Studio version. Tested and working:

public static class VSVersion
{
    static readonly object mLock = new object();
    static Version mVsVersion;
    static Version mOsVersion;

    public static Version FullVersion
    {
        get
        {
            lock (mLock)
            {
                if (mVsVersion == null)
                {
                    string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "msenv.dll");

                    if (File.Exists(path))
                    {
                        FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(path);

                        string verName = fvi.ProductVersion;

                        for (int i = 0; i < verName.Length; i++)
                        {
                            if (!char.IsDigit(verName, i) && verName[i] != '.')
                            {
                                verName = verName.Substring(0, i);
                                break;
                            }
                        }
                        mVsVersion = new Version(verName);
                    }
                    else
                        mVsVersion = new Version(0, 0); // Not running inside Visual Studio!
                }
            }

            return mVsVersion;
        }
    }

    public static Version OSVersion
    {
        get { return mOsVersion ?? (mOsVersion = Environment.OSVersion.Version); }
    }

    public static bool VS2012OrLater
    {
        get { return FullVersion >= new Version(11, 0); }
    }

    public static bool VS2010OrLater
    {
        get { return FullVersion >= new Version(10, 0); }
    }

    public static bool VS2008OrOlder
    {
        get { return FullVersion < new Version(9, 0); }
    }

    public static bool VS2005
    {
        get { return FullVersion.Major == 8; }
    }

    public static bool VS2008
    {
        get { return FullVersion.Major == 9; }
    }

    public static bool VS2010
    {
        get { return FullVersion.Major == 10; }
    }

    public static bool VS2012
    {
        get { return FullVersion.Major == 11; }
    }
}
Agentive answered 19/6, 2012 at 8:32 Comment(4)
This solution makes sense compared to DTE.Version, since sooner or later DTE will be deprecated from VS API (the same way VS addin technology got deprecated). The proposed code can be enhanced by using fvi.FileMajorPart and fvi.FileMinorPart that returns two integers, this avoids the string parsing part in the proposed code.Valle
+1 because this code retrieves the full version (e.g. 11.0.61030.00) that can be used to infer the update-level of VS. DTE.Version only returns e.g. "11.0"Grosvenor
Any reason to prefer msenv.dll over devenv.exe? Just curious. Also: wonder if there is really locking required?Cohen
Visual Studio shell based applications other than Visual Studio use a different executable than devenv.exe... they all use msenv.dll.Offcolor
P
17

You could try to get version via automation DTE object. In MPF you could get it in this way:

EnvDTE.DTE dte = (EnvDTE.DTE)Package.GetGlobalService(typeof(EnvDTE.DTE));

There are some other related things to retrieve DTE object - via Project.DTE, also read this thread if you're getting null for DTE.

Then you can get the version by using DTE.Version property.

Also useful information could be found on Carlos Quintero (VS addin ninja) website HOWTO: Detect installed Visual Studio editions, packages or service packs

Papyrology answered 18/6, 2012 at 15:0 Comment(2)
For Visual Studio 2010 this property returns "10.0" and "12.0" for Visual Studio 2013.Coley
Is it possible to get VS Update number (eg. Update 3) using DTE?Enchorial
A
10

Finally I wrote a class to detect the Visual Studio version. Tested and working:

public static class VSVersion
{
    static readonly object mLock = new object();
    static Version mVsVersion;
    static Version mOsVersion;

    public static Version FullVersion
    {
        get
        {
            lock (mLock)
            {
                if (mVsVersion == null)
                {
                    string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "msenv.dll");

                    if (File.Exists(path))
                    {
                        FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(path);

                        string verName = fvi.ProductVersion;

                        for (int i = 0; i < verName.Length; i++)
                        {
                            if (!char.IsDigit(verName, i) && verName[i] != '.')
                            {
                                verName = verName.Substring(0, i);
                                break;
                            }
                        }
                        mVsVersion = new Version(verName);
                    }
                    else
                        mVsVersion = new Version(0, 0); // Not running inside Visual Studio!
                }
            }

            return mVsVersion;
        }
    }

    public static Version OSVersion
    {
        get { return mOsVersion ?? (mOsVersion = Environment.OSVersion.Version); }
    }

    public static bool VS2012OrLater
    {
        get { return FullVersion >= new Version(11, 0); }
    }

    public static bool VS2010OrLater
    {
        get { return FullVersion >= new Version(10, 0); }
    }

    public static bool VS2008OrOlder
    {
        get { return FullVersion < new Version(9, 0); }
    }

    public static bool VS2005
    {
        get { return FullVersion.Major == 8; }
    }

    public static bool VS2008
    {
        get { return FullVersion.Major == 9; }
    }

    public static bool VS2010
    {
        get { return FullVersion.Major == 10; }
    }

    public static bool VS2012
    {
        get { return FullVersion.Major == 11; }
    }
}
Agentive answered 19/6, 2012 at 8:32 Comment(4)
This solution makes sense compared to DTE.Version, since sooner or later DTE will be deprecated from VS API (the same way VS addin technology got deprecated). The proposed code can be enhanced by using fvi.FileMajorPart and fvi.FileMinorPart that returns two integers, this avoids the string parsing part in the proposed code.Valle
+1 because this code retrieves the full version (e.g. 11.0.61030.00) that can be used to infer the update-level of VS. DTE.Version only returns e.g. "11.0"Grosvenor
Any reason to prefer msenv.dll over devenv.exe? Just curious. Also: wonder if there is really locking required?Cohen
Visual Studio shell based applications other than Visual Studio use a different executable than devenv.exe... they all use msenv.dll.Offcolor
S
2

I think the following code is better:

string version = ((EnvDTE.DTE) ServiceProvider.GlobalProvider.GetService(typeof(EnvDTE.DTE).GUID)).Version;
Storage answered 17/2, 2013 at 5:0 Comment(0)
A
2

To get the full semantic version that includes the patch numbers, like "15.9.6", neither DTE.Version nor the file versions give sufficient information.

I have found a solution in the new managed project system (https://github.com/dotnet/project-system) that seems to be working in VS2017 at least.

Basically, it is using the IVsAppId COM interface, that you need to declare, like in this file. (You can basically copy that file as it is.)

Once you did that, you need to get the IVsAppId implementation in the usual way through a service provider, and call the GetProperty method with VSAPropID.VSAPROPID_ProductSemanticVersion (the enum is also defined in the linked file):

var vsAppId = serviceProvider.GetService<IVsAppId>(typeof(SVsAppId));

vsAppId.GetProperty((int)VSAPropID.VSAPROPID_ProductSemanticVersion, out var semanticVersionObj);

The semanticVersionObj in the sample above will contain a string, in format, like 15.9.6+28307.344. Getting the part before + (sometimes -) gives you the semantic version: 15.9.6.

BTW: it is used in the managed project system here.

(It is so great that MS made the new project system code open source. It provides a good source of information and also you can find useful patterns in it.)

Arbitration answered 7/3, 2019 at 9:11 Comment(1)
Tested all answers and this one provides the closest VS version. For my VS2019 16.9.4 it is "16.9.4+31205.134". At the same time the DTE version is just 16.0. The shell version form the @TWT answer below is "16.0.31205.134 D16.9", The file version from the accepted Daniel Peñalba answer is "16.0.31201.295" for msenv.dll and "16.9.31205.134" for devenv.exe. No other versions contain anything related to the minor VS version. The only downside for me is that I could not find any info on IVsAppId interface, so it looks like an internal undocumented feature which may break in the future.Delvecchio
D
1

@gaspar-nagy answer provides the most precise version. However, it can be further enhanced. Like the answer advices you need to add IVsAppId/SVsAppId interfaces declarations and the code to get the version is also almost the same. However, you need to use the VSAPROPID_ProductDisplayVersion enum value instead of VSAPROPID_ProductSemanticVersion from the VSAPropID enum:

var vsAppId = serviceProvider.GetService<IVsAppId>(typeof(SVsAppId));
vsAppId.GetProperty((int)VSAPropID.VSAPROPID_ProductDisplayVersion, out var versionObj);

The semantic version from @gaspar-nagy answer for my VS 16.9.4 is "16.9.4+31205.134" which requires further parsing. But this display version is user friendly and its version is "16.9.4".

UPDATE: One important thing I've discovered recently is that in case of preview VS the version string in my answer will look something like 16.11.0 Preview 3.0. It may be important if you pass the obtained raw version string without any extra processing to some other APIs like System.Version constructor.

Delvecchio answered 10/5, 2021 at 19:55 Comment(0)
F
1

I believe IVsAppId is the correct way to go.

Semantic version Release version DisplayVersion
17.9.2+34622.214 17.9.34622.214 d17.9 17.9.2
17.9.0-pre.2.1+34414.90 17.9.34414.90 d17.9 17.9.0 Preview 2.1

Using Daniel Peñalba's VSVersion class reading of msenv.dll from msenv.dll

TWT answer agrees with the Release version obtained with IVsAppId ( with the property id that applies to IVsShell )

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace VsVersioning
{
    [Guid("1EAA526A-0898-11d3-B868-00C04F79F802")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [ComImport]
#pragma warning disable IDE1006 // Naming Styles
    public interface SVsAppId
#pragma warning restore IDE1006 // Naming Styles
    {
    }

    [Guid("1EAA526A-0898-11d3-B868-00C04F79F802")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [ComImport]
    public interface IVsAppId
    {
        [MethodImpl(MethodImplOptions.PreserveSig)]
        int SetSite(Microsoft.VisualStudio.OLE.Interop.IServiceProvider pSP);

        [MethodImpl(MethodImplOptions.PreserveSig)]
        int GetProperty(int propid, [MarshalAs(UnmanagedType.Struct)] out object pvar);

        [MethodImpl(MethodImplOptions.PreserveSig)]
        int SetProperty(int propid, [MarshalAs(UnmanagedType.Struct)] object var);

        [MethodImpl(MethodImplOptions.PreserveSig)]
        int GetGuidProperty(int propid, out Guid guid);

        [MethodImpl(MethodImplOptions.PreserveSig)]
        int SetGuidProperty(int propid, ref Guid rguid);

        [MethodImpl(MethodImplOptions.PreserveSig)]
        int Initialize();
    }

    internal class VsVersion
    {
        private readonly IServiceProvider serviceProvider;
        private string semanticVersion;
        private string releaseVersion;
        private string displayVersion;
        private string editionName;

        public VsVersion(
            IServiceProvider serviceProvider
        ) => this.serviceProvider = serviceProvider;


        public string GetSemanticVersion()
        {
            if (this.semanticVersion == null)
            {
                this.semanticVersion = this.GetAppIdStringProperty(-8642);
            }

            return this.semanticVersion;
        }

        public string GetReleaseVersion()
        {
            if (this.releaseVersion == null)
            {
                this.releaseVersion = this.GetAppIdStringProperty(-8597);
            }

            return this.releaseVersion;
        }

        public string GetDisplayVersion()
        {
            if (this.displayVersion == null)
            {
                this.displayVersion = this.GetAppIdStringProperty(-8641);
            }

            return this.displayVersion;
        }

        public string GetEditionName()
        {
            if (this.editionName == null)
            {
                this.editionName = this.GetAppIdStringProperty(-8620);
            }
            return this.editionName;
        }

        private string GetAppIdStringProperty(int propId)
        {
            var vsAppId = this.serviceProvider.GetService(typeof(SVsAppId)) as IVsAppId;
            _ = vsAppId.GetProperty(propId, out object v);
            return v as string;
        }
    }
}
Faucher answered 25/3 at 11:23 Comment(0)
D
0
var shell = _package.GetService(typeof(SVsShell)) as IVsShell;
shell.GetProperty((int)__VSSPROPID5.VSSPROPID_ReleaseVersion, out object ver);
return ver.ToString();
Doubler answered 7/10, 2019 at 9:25 Comment(1)
While this code may help with the question, it is better to include some context, explaining how it works and when to use it. Code-only answers tend to be less useful in the long run. See How do I write a good answer? for some more info.Libertylibia

© 2022 - 2024 — McMap. All rights reserved.