代码之家  ›  专栏  ›  技术社区  ›  code4life

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

  •  56
  • code4life  · 技术社区  · 14 年前

    有人知道检测安装了哪个版本的Office的最佳方法是什么吗?另外,如果安装了多个版本的Office,我想知道它们是什么版本。如果我能检测到安装了(/are)的特定版本的Excel,那就有好处了。

    8 回复  |  直到 14 年前
        1
  •  80
  •   Dirk Vollmar    6 年前

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

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

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

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

    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
    

    MSIEnumProducts here .

    另外,微软不支持不同Office版本的并行安装。它们确实起了一定的作用,但可能会得到不希望的效果和不一致性。

    从Office 2019开始,基于MSI的安装程序不再可用,单击运行是现在部署Office的唯一方法。随着对定期更新的Office 365的更改,Office的主要/次要版本号也不再更新(至少目前是这样)。这意味着即使对于office2019,注册表项中使用的值和 Application.Version 16.0 .

    就目前而言,没有文件证明的方法来区分2016年办公室和2019年办公室。线索可能是winword.exe的文件版本;但是,对于修补过的Office 2016版本,此版本也会增加(请参阅下面@antonio的评论)。

    // 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版本更大的版本。

        2
  •  24
  •   BobL2112    10 年前

    HKEY\U CLASSES\u ROOT\Word.Application\CurVer怎么样?

        3
  •  17
  •   Hugh W    12 年前

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

        4
  •  8
  •   zee    13 年前
    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
  •  6
  •   Community Tales Farias    7 年前

    为什么不检查 HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\[office.exe] [office.exe] winword.exe , excel.exe 等。 在这里您可以得到可执行文件的路径并检查该文件的版本。

    in C++ /在 C#

        6
  •  5
  •   vaska11    5 年前

    尽管这个问题早就有答案了,但我还是发现了一些与上述答案相关的有趣的事实。

    正如Dirk提到的,从office365/2019开始,MS的版本控制似乎有一种奇怪的方式。通过查看可执行路径,您无法区分这三个路径(2016、2019、O365)。就像他自称的那样,把查看可执行文件的构建作为判断哪个是什么的手段,也不是很有效。

    经过一番研究,我找到了一个可行的解决办法。解决方案位于注册表子项下 Computer\HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Common\Licensing\LicensingNext

    所以,我的逻辑如下:

    案例1 Licensing .

    案例2 Standard2019Volume )

    案例3 o365bussinessretail (这也是一个产品ID)以及其他一些值。

    提供了可能的productID here

    为了区分这三个,我打开钥匙,看看是否失败了。如果打开失败,它的 LicensingNext 看看有没有名字有前缀 o365 ,如果它找到了,那么它的 O365型 . 如果它没有,那么它的 2019年办公室

    坦率地说,我没有足够的时间在不同的环境下测试逻辑。所以请注意。

    希望这对任何人都有帮助。

        7
  •  2
  •   Chris    7 年前

    如果我能检测到安装了(/are)的特定版本的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上,需要插入 Wow6432Node SOFTWARE Microsoft 面包屑,不管办公室设备有多小。

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

        8
  •  1
  •   Rakesh Ravi G    8 年前
            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;
                }
            }
    
        9
  •  0
  •   Orsiris de Jong    4 年前

    无论是谁,这里是我的版本,检查Office 95-2019&O365,基于MSI和ClickAndRun在32位和64位系统上都受支持(未安装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年
    • Python类型在注册表函数和office函数之间是不同的,因为我在发现pypy/python2不喜欢类型之前编写了注册表函数。。。在这些python解释器上,您可以完全删除键入
    • 任何改进都是非常受欢迎的