繁体   English   中英

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

[英]How to detect installed version of MS-Office?

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

检查安装的 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

另一种不使用特定注册表项的选项是使用此处所述的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 版本的内容。

HKEY_CLASSES_ROOT\\Word.Application\\CurVer 怎么样?

如果您在 64 位计算机上安装了 32 位 Office,您可能需要检查是否存在“SOFTWARE\\Wow6432Node\\Microsoft\\Office\\12.0\\”,用适当的版本替换 12.0。 安装在 64 位 Windows 7 上的 Office 2007 肯定就是这种情况。

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

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

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;
    }



}
}

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

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

对这种方法有什么批评吗?

尽管这个问题很久以前就得到了回答,但我发现了一些与上述答案相关的有趣事实要补充。

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

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

所以,我的逻辑如下:

案例1 :如果电脑安装了MSOffice 2016,则Licensing下没有子项。

情况 2 :如果计算机安装了 MSOffice 2019,则有值的名称(Office 产品 ID 之一)。 (例如Standard2019Volume

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

此处提供可能的 productId。

为了区分这三个,我只是打开了钥匙,看看是否失败。 如果打开失败,其Office 2016 . 然后我枚举LicensingNext并尝试查看是否有任何名称具有前缀o365 ,如果找到它,则它的O365 如果没有,那么它的Office 2019

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

希望这对感兴趣的人有所帮助。

如果我可以检测到已安装的特定版本的 Excel,那将是一个奖励。

我知道这个问题很久以前就有人问过和回答过,但同样的问题让我很忙,直到我做出这个观察:

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

在 64 位 Windows 上,您需要在SOFTWAREMicrosoft crumbs 之间插入Wow6432Node ,而不管 Office 安装的位数。

在我的机器上,这给出了最初安装版本的版本信息。 例如,对于 Office 2010,数字与此处列出的数字匹配,并且它们与File > Help报告的版本不同,后者反映了修补程序应用的补丁。

        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;
            }
        }

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

我刚刚检测到“Microsoft Office”的安装路径,然后从“Office”应用程序(在我的示例“Word”中)的 exe 文件中声明了一个 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”然后单击“属性”时获得的信息。 请参阅: winword.exe 的详细信息

对于可能关心的任何人,这是我的版本,用于检查 Office 95-2019 和 O365,在 32 位和 64 位系统上均支持基于 MSI 和 ClickAndRun(在未安装 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 输入在注册表功能和办公功能之间是不同的,因为我在发现 pypy/python2 不喜欢打字之前编写了注册表...
  • 任何改进都非常受欢迎

试试这个(适用于所有 Office 版本):

            ManagementObjectSearcher LicenseSearcher =
                       new ManagementObjectSearcher("root\\CIMV2",
                       "SELECT name, LicenseStatus,Description,PartialProductKey  FROM SoftwareLicensingProduct where name like '%office%'");

            foreach (ManagementObject LSObj in LicenseSearcher.Get())
            {
                String prod_name = LSObj["Name"].ToString();
                String prod_description = LSObj["Description"].ToString();

                String prod_key = "";
                try
                {
                    prod_key = LSObj["PartialProductKey"].ToString();
                }
                catch
                {
                    prod_key = "";
                }
 
                
                String prod_lic_sta= LSObj["LicenseStatus"].ToString();

        
                switch (prod_lic_sta)
                {
                    case "0": 
                        prod_lic_sta = "Unlicensed";
                        break;
                    case "1": 
                        prod_lic_sta = "Licensed";
                        break;
                    case "2": 
                        prod_lic_sta = "OOBGrace";
                        break;
                    case "3":
                        prod_lic_sta = "OOTGrace";
                        break;
                    case "4":
                        prod_lic_sta = "NonGenuineGrace";
                        break;
                    case "5":
                        prod_lic_sta = "Notification";
                        break;
                    case "6":
                        prod_lic_sta = "ExtendedGrace";
                        break;
                }

                
            }
        

Output:

办公室 19

Office19HomeBusiness2019R_零售版

密钥:XXXXX

状态:许可

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM