如何检测MS-Office的已安装版本?

时间:2010-07-16 15:53:52

标签: c# excel .net-3.5 ms-office

有谁知道检测哪个版本的Office的最佳方法是什么?另外,如果安装了多个版本的Office,我想知道它们是什么版本。如果我能够检测到安装的Excel的特定版本,我将获得奖励。

11 个答案:

答案 0 :(得分:69)

检查已安装的Office版本的一种方法是检查感兴趣的Office应用程序的InstallRoot注册表项。

例如,如果要检查是否安装了Word 2007,则应检查是否存在以下注册表项:

HKLM\Software\Microsoft\Office\12.0\Word\InstallRoot::Path

此条目包含可执行文件的路径。

将12.0(对于Office 2007)替换为相应的版本号:

Office 97   -  7.0
Office 98   -  8.0
Office 2000 -  9.0
Office XP   - 10.0
Office 2003 - 11.0
Office 2007 - 12.0
Office 2010 - 14.0 (sic!)
Office 2013 - 15.0
Office 2016 - 16.0
Office 2019 - 16.0 (sic!)

其他应用程序具有类似的键:

HKLM\Software\Microsoft\Office\12.0\Excel\InstallRoot::Path
HKLM\Software\Microsoft\Office\12.0\PowerPoint\InstallRoot::Path

或者您可以检查所有应用程序的公共根路径:

HKLM\Software\Microsoft\Office\12.0\Common\InstallRoot::Path

另一个不使用特定注册表项的选项是使用here所述的MSIEnumProducts API查询MSI数据库。

另外,Microsoft并未正式支持不同Office版本的并行安装。它们确实有些工作,但是你可能会产生不良影响和不一致。

更新:Office 2019和Office 365

从Office 2019开始,基于MSI的设置不再可用,“即点即用”是现在部署Office的唯一方法。连同对定期更新的Office 365的此更改,Office的主要/次要版本号也不再更新(至少目前为止)。这意味着 - 即使对于Office 2019 - 注册表项中使用的值和Application.Version返回的值(例如在Word中)仍为16.0

目前,没有记录的方法可以区分Office 2016和Office 2019.线索可能是winword.exe的文件版本;但是,对于修补后的Office 2016版本,此版本也会增加(请参阅下面的@antonio评论)。

如果您需要以某种方式区分Office版本,例如要确保存在某个功能或安装了最低版本的Office,可能是查看其中一个主要Office应用程序的文件版本的最佳方式:

// Using the file path to winword.exe
// Retrieve the path e.g. from the InstallRoot Registry key
var fileVersionInfo = FileVersionInfo.GetVersionInfo(@"C:\Program Files (x86)\Microsoft Office\root\Office16\WINWORD.EXE");
var version = new Version(fileVersionInfo.FileVersion);

// On a running instance using the `Process` class
var process = Process.GetProcessesByName("winword").First();
string fileVersionInfo = process.MainModule.FileVersionInfo.FileVersion;
var version = Version(fileVersionInfo);

Office 2019的文件版本是16.0.10730.20102,因此,如果您看到的内容大于您处理Office 2019或当前Office 365版本的任何内容。

答案 1 :(得分:23)

HKEY_CLASSES_ROOT \ Word.Application \ CurVer怎么样?

答案 2 :(得分:16)

如果您在64位计算机上安装了32位Office,则可能需要检查是否存在“SOFTWARE \ Wow6432Node \ Microsoft \ Office \ 12.0 \”,并使用相应的版本替换12.0。对于安装在64位Windows 7上的Office 2007,情况确实如此。

请注意,Office 2010(== 14.0)是第一个存在64位版本的Office。

答案 3 :(得分:9)

我发现这个CodeProject帮助我解决了这个问题:http://www.codeproject.com/Articles/26520/Getting-Office-s-Version

答案 4 :(得分:8)

namespace Software_Info_v1._0
{
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Office.Interop;

public class MS_Office
{
    public string GetOfficeVersion()
    {
        string sVersion = string.Empty;
        Microsoft.Office.Interop.Word.Application appVersion = new Microsoft.Office.Interop.Word.Application();
        appVersion.Visible = false;
        switch (appVersion.Version.ToString())
        {
            case "7.0":
                sVersion = "95";
                break;
            case "8.0":
                sVersion = "97";
                break;
            case "9.0":
                sVersion = "2000";
                break;
            case "10.0":
                sVersion = "2002";
                break;
            case "11.0":
                sVersion = "2003";
                break;
            case "12.0":
                sVersion = "2007";
                break;
            case "14.0":
                sVersion = "2010";
                break;
            default:
                sVersion = "Too Old!";
                break;
        }
        Console.WriteLine("MS office version: " + sVersion);
        return null;
    }



}
}

答案 5 :(得分:5)

为什么不选中HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\[office.exe],其中[office.exe]代表特定的办公产品exe-filename,例如winword.exeexcel.exe等 在那里,您将获得可执行文件的路径并检查该文件的版本。

如何检查文件的版本:in C++ / C#

对这种做法有任何批评吗?

答案 6 :(得分:2)

  

如果我能够检测到安装的Excel的特定版本,我将获得奖励。

我知道很久以前这个问题已被提出并得到解答,但同样的问题一直让我忙碌直到我做出这样的观察:

要获取内部版本号(例如15.0.4569.1506),请调查HKLM\SOFTWARE\Microsoft\Office\[VER]\Common\ProductVersion::LastProduct,其中[VER]是主要版本号(Office 2007为12.0,Office 2010为14.0,Office 2013为15.0) )。

在64位Windows上,无论Office安装的位数如何,都需要在Wow6432NodeSOFTWARE面包屑之间插入Microsoft

在我的计算机上,这会提供最初安装版本的版本信息。例如,对于Office 2010,这些数字与列出的here匹配,它们与File > Help中报告的版本不同,后者反映了修补程序应用的修补程序。

答案 7 :(得分:1)

        public string WinWordVersion
        {
            get
            {
                string _version = string.Empty;
                Word.Application WinWord = new Word.Application();   

                switch (WinWord.Version.ToString())
                {
                    case "7.0":  _version = "95";
                        break;
                    case "8.0": _version = "97";
                        break;
                    case "9.0": _version = "2000";
                        break;
                    case "10.0": _version = "2002";
                        break;
                    case "11.0":  _version = "2003";
                        break;
                    case "12.0": _version = "2007";
                        break;
                    case "14.0": _version = "2010";
                        break;
                    case "15.0":  _version = "2013";
                        break;
                    case "16.0": _version = "2016";
                        break;
                    default:                            
                        break;
                }

                return WinWord.Caption + " " + _version;
            }
        }

答案 8 :(得分:1)

当您想检测是否安装了“Office 2016”或“Office 2019”时,我找到了一种检测“Microsoft Office 版本”的优雅方法,这可能也有效。

我刚刚检测到“Microsoft Office”的安装路径,然后从“Office”应用程序的 exe 文件(在我的“Word”示例中)声明了 System.Diagnostics.FileVersionInfo 并从这个 FileVersionInfo 中获得了我需要的一切.

这里以一个非常小的控制台应用程序的完整代码为例:

using System;
using System.IO;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace Sandbox_Console
{
    public class Program
    {
        static void Main(string[] args)
        {
            string installpath = GetOfficeInstallPath();
            OfficeVersionInfo info = new OfficeVersionInfo(installpath);
            Console.WriteLine("Full Office Version Number: " + info.FullVersionNumber);
            Console.WriteLine("Full Office Name: " + info.FullOfficeVersionName);
            Console.Write("Press any key to end this program...");
            Console.ReadKey();
        }

        public class OfficeVersionInfo
        {
            private string _FullVersionNumber = "";
            private string _FullOfficeVersionName = "";

            public string FullVersionNumber { get { return _FullVersionNumber; } }
            public string FullOfficeVersionName { get { return _FullOfficeVersionName; } }

            public OfficeVersionInfo(string installPath)
            {
                string filepath = installPath + "\\winword.exe"; //For Excel, PowerPoint or others use the exe-files for them.
                if (File.Exists(filepath))
                {
                    FileVersionInfo info = FileVersionInfo.GetVersionInfo(filepath);
                    _FullVersionNumber = info.ProductVersion;
                    _FullOfficeVersionName = info.ProductName;
                }
            }
        }

        public static string GetOfficeInstallPath()
        {
            string result = "";
            try
            {
                Type appType = Type.GetTypeFromProgID("Word.Application"); //Also works with Excel, PowerPoint or others.
                dynamic appInstance = Activator.CreateInstance(appType);
                result = appInstance.Path;
                appInstance.quit();
                Marshal.ReleaseComObject(appInstance);
            } catch (Exception exc)
            {
                Debug.Print(exc.Message);
            }
            return result;
        }  
    }
}

代码示例中存储在“OfficeVersionInfo”中的信息是您左键单击“winword.exe”然后单击“属性”时获得的信息。看到这个: Detailes of winword.exe

答案 9 :(得分:0)

尽管这个问题早已得到回答,但我发现有一些有趣的事实与上述答案有关。

正如Dirk所述,从Office 365/2019开始,MS似乎存在一种奇怪的版本控制方式。您无法再通过查看可执行路径来区分这三个(2016、2019,O365)。就像他自言自语那样,看着可执行文件的构建,以此来告诉这是什么,也不是很有效。

经过研究,我找到了可行的解决方案。该解决方案位于注册表子项error.ejs下。

所以,我的逻辑如下:

案例1 :如果计算机安装了MSOffice 2016,则Computer\HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Common\Licensing\LicensingNext下没有子项。

案例2 :如果计算机安装了MSOffice 2019,则存在值的名称(这是Office产品ID之一)。 (例如Licensing

情况3 :如果计算机安装了Office365,则存在一个名为Standard2019Volume(也是产品ID)的值以及一些其他值。

here提供了可能的productId。

要区分这三个,我只是打开了钥匙,看是否失败。如果打开失败,则其 Office 2016 。然后,我枚举o365bussinessretail并尝试查看是否有任何名称带有前缀LicensingNext的前缀(如果找到),然后为其 O365 。如果不是,则其 Office 2019

坦白说,我没有足够的时间在变化的环境下测试逻辑。所以请注意。

希望这会帮助任何感兴趣的人。

答案 10 :(得分:0)

对于可能关心的人,这是我的版本,用于检查32和64位系统上是否都支持基于MSI和ClickAndRun的Office 95-2019和O365(如果未安装64位版本,则返回32位) )。

用Python 3.5编写,但是您当然总是可以使用该逻辑来用另一种语言编写自己的代码:

from winreg import *
from typing import Tuple, Optional, List

# Let's make sure the dictionnary goes from most recent to oldest
KNOWN_VERSIONS = {
    '16.0': '2016/2019/O365',
    '15.0': '2013',
    '14.0': '2010',
    '12.0': '2007',
    '11.0': '2003',
    '10.0': '2002',
    '9.0': '2000',
    '8.0': '97',
    '7.0': '95',
}


def get_value(hive: int, key: str, value: Optional[str], arch: int = 0) -> str:
    """
    Returns a value from a given registry path

    :param hive: registry hive (windows.registry.HKEY_LOCAL_MACHINE...)
    :param key:  which registry key we're searching for
    :param value: which value we query, may be None if unnamed value is searched
    :param arch: which registry architecture we seek (0 = default, windows.registry.KEY_WOW64_64KEY, windows.registry.KEY_WOW64_32KEY)
                 Giving multiple arches here will return first result
    :return: value
    """

    def _get_value(hive: int, key: str, value: Optional[str], arch: int) -> str:
        try:
            open_reg = ConnectRegistry(None, hive)
            open_key = OpenKey(open_reg, key, 0, KEY_READ | arch)
            value, type = QueryValueEx(open_key, value)
            # Return the first match
            return value
        except (FileNotFoundError, TypeError, OSError) as exc:
            raise FileNotFoundError('Registry key [%s] with value [%s] not found. %s' % (key, value, exc))

    # 768 = 0 | KEY_WOW64_64KEY | KEY_WOW64_32KEY (where 0 = default)
    if arch == 768:
        for _arch in [KEY_WOW64_64KEY, KEY_WOW64_32KEY]:
            try:
                return _get_value(hive, key, value, _arch)
            except FileNotFoundError:
                pass
        raise FileNotFoundError
    else:
        return _get_value(hive, key, value, arch)


def get_keys(hive: int, key: str, arch: int = 0, open_reg: HKEYType = None, recursion_level: int = 1,
             filter_on_names: List[str] = None, combine: bool = False) -> dict:
    """
    :param hive: registry hive (windows.registry.HKEY_LOCAL_MACHINE...)
    :param key: which registry key we're searching for
    :param arch: which registry architecture we seek (0 = default, windows.registry.KEY_WOW64_64KEY, windows.registry.KEY_WOW64_32KEY)
    :param open_reg: (handle) handle to already open reg key (for recursive searches), do not give this in your function call
    :param recursion_level: recursivity level
    :param filter_on_names: list of strings we search, if none given, all value names are returned
    :param combine: shall we combine multiple arch results or return first match
    :return: list of strings
    """
    def _get_keys(hive: int, key: str, arch: int, open_reg: HKEYType, recursion_level: int, filter_on_names: List[str]):
        try:
            if not open_reg:
                open_reg = ConnectRegistry(None, hive)
            open_key = OpenKey(open_reg, key, 0, KEY_READ | arch)
            subkey_count, value_count, _ = QueryInfoKey(open_key)

            output = {}
            values = []
            for index in range(value_count):
                name, value, type = EnumValue(open_key, index)
                if isinstance(filter_on_names, list) and name not in filter_on_names:
                    pass
                else:
                    values.append({'name': name, 'value': value, 'type': type})
            if not values == []:
                output[''] = values

            if recursion_level > 0:
                for subkey_index in range(subkey_count):
                    try:
                        subkey_name = EnumKey(open_key, subkey_index)
                        sub_values = get_keys(hive=0, key=key + '\\' + subkey_name, arch=arch,
                                              open_reg=open_reg, recursion_level=recursion_level - 1,
                                              filter_on_names=filter_on_names)
                        output[subkey_name] = sub_values
                    except FileNotFoundError:
                        pass

            return output

        except (FileNotFoundError, TypeError, OSError) as exc:
            raise FileNotFoundError('Cannot query registry key [%s]. %s' % (key, exc))

    # 768 = 0 | KEY_WOW64_64KEY | KEY_WOW64_32KEY (where 0 = default)
    if arch == 768:
        result = {}
        for _arch in [KEY_WOW64_64KEY, KEY_WOW64_32KEY]:
            try:
                if combine:
                    result.update(_get_keys(hive, key, _arch, open_reg, recursion_level, filter_on_names))
                else:
                    return _get_keys(hive, key, _arch, open_reg, recursion_level, filter_on_names)
            except FileNotFoundError:
                pass
        return result
    else:
        return _get_keys(hive, key, arch, open_reg, recursion_level, filter_on_names)


def get_office_click_and_run_ident():
    # type: () -> Optional[str]
    """
    Try to find the office product via clickandrun productID
    """
    try:
        click_and_run_ident = get_value(HKEY_LOCAL_MACHINE,
                                                 r'Software\Microsoft\Office\ClickToRun\Configuration',
                                                 'ProductReleaseIds',
                                                 arch=KEY_WOW64_64KEY |KEY_WOW64_32KEY,)
    except FileNotFoundError:
        click_and_run_ident = None
    return click_and_run_ident


def _get_used_word_version():
    # type: () -> Optional[int]
    """
    Try do determine which version of Word is used (in case multiple versions are installed)
    """
    try:
        word_ver = get_value(HKEY_CLASSES_ROOT, r'Word.Application\CurVer', None)
    except FileNotFoundError:
        word_ver = None
    try:
        version = int(word_ver.split('.')[2])
    except (IndexError, ValueError, AttributeError):
        version = None
    return version


def _get_installed_office_version():
    # type: () -> Optional[str, bool]
    """
    Try do determine which is the highest current version of Office installed
    """
    for possible_version, _ in KNOWN_VERSIONS.items():
        try:
            office_keys = get_keys(HKEY_LOCAL_MACHINE,
                                               r'SOFTWARE\Microsoft\Office\{}'.format(possible_version),
                                               recursion_level=2,
                                               arch=KEY_WOW64_64KEY |KEY_WOW64_32KEY,
                                               combine=True)

            try:
                is_click_and_run = True if office_keys['ClickToRunStore'] is not None else False
            except:
                is_click_and_run = False

            try:
                is_valid = True if office_keys['Word'] is not None else False
                if is_valid:
                    return possible_version, is_click_and_run
            except KeyError:
                pass
        except FileNotFoundError:
            pass
    return None, None


def get_office_version():
    # type: () -> Tuple[str, Optional[str]]
    """
    It's plain horrible to get the office version installed
    Let's use some tricks, ie detect current Word used
    """

    word_version = _get_used_word_version()
    office_version, is_click_and_run = _get_installed_office_version()

    # Prefer to get used word version instead of installed one
    if word_version is not None:
        office_version = word_version

    version = float(office_version)
    click_and_run_ident = get_office_click_and_run_ident()

    def _get_office_version():
        # type: () -> str
        if version:
            if version < 16:
                try:
                    return KNOWN_VERSIONS['{}.0'.format(version)]
                except KeyError:
                    pass
            # Special hack to determine which of 2016, 2019 or O365 it is
            if version == 16:
                if isinstance(click_and_run_ident, str):
                    if '2016' in click_and_run_ident:
                        return '2016'
                    if '2019' in click_and_run_ident:
                        return '2019'
                    if 'O365' in click_and_run_ident:
                        return 'O365'
                return '2016/2019/O365'
        # Let's return whatever we found out
        return 'Unknown: {}'.format(version, click_and_run_ident)

    if isinstance(click_and_run_ident, str) or is_click_and_run:
        click_and_run_suffix = 'ClickAndRun'
    else:
        click_and_run_suffix = None

    return _get_office_version(), click_and_run_suffix

您可以使用类似于以下示例的代码:

office_version, click_and_run = get_office_version()
print('Office {} {}'.format(office_version, click_and_run))

备注

  • 虽然没有在Office <2010进行测试
  • 在注册表函数和Office函数之间,Python的键入有所不同,因为我在发现pypy / python2不喜欢键入之前编写了注册表,在这些python解释器上,您可能只是完全删除了键入
  • 任何改进都是值得欢迎的