打开多个 Office 文档时的多个实例

Ran*_*dom 9 microsoft-office microsoft-office-2016 microsoft-excel-2016 microsoft-word-2016

16.0.8625.2121Office版本开始(使用 Word 和 Excel 测试) - 当您在资源管理器中选择多个文档并按 Enter 键打开它们时,您最终会得到与先前选择的文档数量相同的实例。

要重现执行以下步骤:

  • 在您的机器上的任何位置创建 2 个空的 Excel 工作簿
  • 选择这两个文件
  • 按回车键
  • 检查任务管理器,您将看到 2 个 Excel 实例

在早期版本中,16.0.8625.2121您最终只会得到 1 个实例。

测试过

使用这些新版本逐步重新测试:

  • 16.0.8431.2094
  • 16.0.8431.2107
  • 16.0.8528.2139
  • 16.0.8528.2147

在提到明显的之前,没有DisableMergeInstance设置。

这是一个新的“功能”还是一个错误?我相信这是一个错误。

有办法解决吗?

更多信息:

我们使用(总是最新版本)测试了这种行为

  • Windows 7 + Office 2016 - 发生不当行为
  • Windows 10 + Office 2016 - 发生不当行为

还检查了较旧的 Office 版本以确保这是 Office 2016 Thing

  • Windows 8 + Office 2013 - 不会发生
  • Windows 7 + Office 2010 - 不会发生
  • Windows 10 + Office 2010 - 不会发生
  • Windows 10 + Office 2013 - 不会发生

El8*_*dN8 6

如果我在整个过程中重申我的解释,我深表歉意,但我发现这个问题非常复杂,所以我试图确保它在上下文中对读者有意义:

虽然可能不知道这是错误还是有意为之,但我们可以通过使用动态数据交换协议 (DDE) 创建 DDE 消息而不是硬参数“%”来强制它在“相同”实例中打开1" 指向要在执行文件时打开该实例的文件。(尽管,即使是硬参数,也使用 DDE)。

在这种情况下,DDE 消息用于告诉程序打开文件。对于执行的每个文件,它实际上每次都会创建一个新实例。但是当使用 DDE 协议时,它首先检查是否已经创建了一个实例,如果是,它会将 DDE 消息中继到找到的第一个实例并退出,从而产生所有文件在单个实例中打开的错觉,因为它是即时的。

猜测

在多个实例中打开文件的问题可能与调用另一个实例时单个实例已经加载了多少有关。第一个和第二个实例的执行时间差异之间的趋势是,随着执行之间的时间增加,它往往会产生一个实例,而随着时间的减少,它往往会产生两个实例。这表明如果另一个文件被执行,第一个实例必须被加载或“准备好”在同一个实例中打开一个新文件,如果不是,它应该用自己打开文件。

似乎当文件路径被用作程序的参数时,它似乎只遵循这个趋势:

  • 词 2016
  • Excel 2016

如果第一个实例准备好(或者如果非第一个实例看到它准备好了),当用作创建第一个实例之外的实例的参数时,非第一个实例似乎能够将参数作为 DDE 消息传递给第一个实例。

但是,如果我们执行程序并使用 DDE 消息打开文件,无论第一个实例是否准备好通过参数接受 DDE 消息,它似乎都立即遵循 DDE 协议。第一个实例是否准备好可能取决于非第一个实例是否将第一个实例视为就绪,如果没有,它不会将 DDE 消息发送到第一个实例,这似乎仅在通过参数打开时发生. 非第一个认为第一个未“准备好”或“不存在”的推测是由以下事实表明的,即 DDE 消息(来自非第一个)在以下情况下被第一个接受:非第一个不通过以下方式执行参数连接“%1”;它被告知通过 DDE 消息打开。

因此,我的推测是:这些应用程序的代码使用一些晦涩的方法来确定另一个实例是否“就绪”,如果是,则在使用参数时使用 DDE 协议。这似乎使用了与仅在接收 DDE 协议以确定是否将其发送到另一个实例时不同的方法。看起来实际上伪代码是:

if(argrument.wasUsed()){
    // Office's obscure condition
    if(Office.thinksInstanceIsReady(anotherInstance)){
        // Use DDE Protocol
        if(anotherInstance.exists()){ // already knew that
            sendDDEmessage(anotherInstance);
            exitThisInstance();
        }
    } else {
        selfFollowDDEmessage(); // Leave open this instance
    }
if(givenDDEMessage()){
    // Use DDE Protocol
    if(anotherInstance.exists()){
        sendDDEmessage(anotherInstance);
        exitThisInstance();
    } else {
        selfFollowDDEmessage();
    }
}
Run Code Online (Sandbox Code Playgroud)

如果没有程序员通知我们,就无法判断这是一个错误还是出于某种原因而有意隐瞒。

决议

我们要调整某些文件扩展名的执行,不再将正在执行的文件的文件路径(“%1”)作为参数发送,而是告诉正在执行的程序执行 DDE 消息的内容,其中包含打开文件的请求,如果存在,则将其中继到已经存在的实例,如果不使用它本身。如果使用文件路径的参数,这将绕过这些应用程序的模糊要求,使另一个实例被视为“就绪”。

这些都是与 Class 键相关的文件扩展名,应替换为x

FILEEXT          CLASS NAME (x)
 .doc*           Word.Document.8
 .docm†    Word.DocumentMacroEnabled.12
 .docx*         Word.Document.12
 .dot            Word.Template.8
 .dotm†    Word.TemplateMacroEnabled.12
 .dotx†         Word.Template.12
 .odt        Word.OpenDocumentText.12
 .rtf†             Word.RTF.8
 .wbk             Word.Backup.8
 .wiz             Word.Wizard.8
 .wll             Word.Addin.8
Run Code Online (Sandbox Code Playgroud)

对于 Excel

FILEEXT             CLASS NAME (x)
 .csv*                Excel.CSV
 .ods       Excel.OpenDocumentSpreadsheet.12
 .slk                 Excel.SLK
 .xla                Excel.Addin
 .xlam†        Excel.AddInMacroEnabled
 .xld                Excel.Dialog
 .xlk                Excel.Backup
 .xll                 Excel.XLL
 .xlm              Excel.Macrosheet
 .xls*              Excel.Sheet.8
 .xlsb†     Excel.SheetBinaryMacroEnabled.12
 .xlshtml           Excelhtmlfile
 .xlsm†       Excel.SheetMacroEnabled.12
 .xlsx*             Excel.Sheet.12
 .xlt†             Excel.Template.8
 .xlthtml          Excelhtmltemplate
 .xltm†        Excel.TemplateMacroEnabled
 .xltx†             Excel.Template
 .xlw               Excel.Workspace
 .xlxml               Excelxmlss
Run Code Online (Sandbox Code Playgroud)

* 最重要/最常见的文件扩展名,应该至少完成。主观。

† 次要的最重要/最常见的文件扩展名,应至少完成。主观。

这些列表可以通过命令行复制:assoc | findstr Word替换Word为官方缩写名称(区分大小写)。

如果您认为有必要,您可以选择执行所有这些操作。如果您想做的越多,您可能需要遵循我将提供的可选步骤,这将减少所需的工作。

对于下面的每个注册表项,您应遵循以下说明替换x为您选择的相应类:

  • HKEY_CLASSES_ROOT\x\shell\Open
  • HKEY_CLASSES_ROOT\x\shell\OpenAsReadOnly

(例如:HKEY_CLASSES_ROOT\Excel.Sheet.12\shell\Open

再一次,OpenAsReadOnly密钥是可选的,这将在文件被执行时准备好,以便它是只读的。

一个小预防措施 - 备份

为了最好地记住修改前的注册表值,您可能需要右键单击密钥分支HKEY_CLASSES_ROOT,然后在上下文菜单下单击“导出”并将注册文件保存到某个位置。如果 Doc Brown 说“我们需要返回”,您可以通过执行它并按照说明导入注册表项。

或者,您也可以运行它,以便您记住command用于修复小错误的值和类名称:

assoc>>fileexts.txt 可以过滤使用 type fileexts.txt | findstr Word

ftype>>classnames.txt 可以过滤使用 type classnames.txt | findstr Word

指示

上面列出的每个键值都应遵循这些,如您所愿。

进入您最喜欢的注册表编辑器,或者regedit转到您要修改的类。

输入名为 的键command,右键单击该(Default)值,然后单击上下文菜单下的“修改”。

当前设置的应该是执行的内容 ftype | findstr Word

更改它以删除值末尾的直接参数,包括空格,变为:

  • "C:\Program Files\Microsoft Office\Root\Office16\EXCEL.EXE"
    (对于 Excel 64 位)
  • "C:\Program Files\Microsoft Office\Root\Office16\WINWORD.EXE"
    (对于 Word 64 位)
  • "C:\Program Files (x86)\Microsoft Office\Root\Office16\WINWORD.EXE"
    (对于 Word 32 位)
  • "C:\Program Files (x86)\Microsoft Office\Root\Office16\EXCEL.EXE"
    (对于 Excel 32 位)

输入键ddeexec旁边的command键(如果不存在,则创建键),右键单击该(Default)值,然后单击上下文菜单下的“修改”,将值设置为:

  • [REM _DDE_Direct][FileOpen("%1")] - (词)
  • [open("%1")] - (Excel)

在下面ddeexec创建一个名为的新键topic(如果它不存在),右键单击该(Default)值,然后单击上下文菜单下的“修改”,并将该值设置为system(如果尚未存在)。

修改后,您可能必须在对注册表进行这些更改后,使用提升的命令提示符或 shell 运行它来刷新 shell32.dll:

regsvr32 /i shell32.dll

这已经在 Windows 10 Office 2016 版本 16.0.8625.2127 上进行了测试

替代快捷方式

您还可以转到文件扩展名(例如HKEY_CLASSES_ROOT\.xlsx)的键并将“(默认)”值修改为单个类,如果遵循此方法,则可以将多个文件扩展名指向您所使用的同一个类值(例如Excel.Sheet.12)只需使用 DDE 消息修改该类一次。如果您这样做,还应该重命名该注册表分支内所有重复的类名称。但是,不推荐这种方式,因为它很容易损坏,如果您要进行所有文件扩展以节省时间,则应该这样做。

旁注:

/o参数是 URL 的参数,因此丢失此功能并不是一个大问题,因为它很少被传递。但是,如果您愿意,可以尝试在调整(Default)值时保留这部分参数。

我正在考虑把它变成一个社区维基,因为它非常具有推测性而且还没有完成(如果 Word 和 Excel 不是唯一的)。请就此发表意见。