从16.0.8625.2121
Office 版本开始(使用 Word 和 Excel 测试) - 当您在资源管理器中选择多个文档并按 Enter 打开它们时,您最终会得到先前选择的文档的实例数。
要重现,请执行以下步骤:
- 在您的机器上的任意位置创建 2 个空的 Excel 工作簿
- 选择这两个文件
- 按 Enter 键
- 检查任务管理器,你会看到 2 个 Excel 实例
在早期版本中,16.0.8625.2121
您最终只会得到 1 个实例。
经过测试
16.0.4266.1003
- 我们有一个相当旧的图像,然后我们更新到较新的版本officec2rclient.exe /update user updatetoversion=16.0.xxxx.yyyy
使用这些新版本逐步重新测试:
- 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 的东西
- Windows 8 + Office 2013 - 不会发生
- Windows 7 + Office 2010 - 不会发生
- Windows 10 + Office 2010 - 不会发生
- Windows 10 + Office 2013 - 不会发生
答案1
如果我在整篇文章中重复我的解释,我深感抱歉,但我发现这个问题非常复杂,所以我试图确保读者能够理解其含义:
虽然可能不知道这是一个错误还是有意为之,但我们可以使用动态数据交换协议 (DDE) 强制它在“同一”实例中打开,方法是创建 DDE 消息,而不是指向该实例在执行文件时要打开的文件的硬参数“%1”。 (尽管,即使使用硬参数,DDE 也会使用)。
在这种情况下,DDE 消息用于告诉程序打开文件。对于执行的每个文件,它实际上每次都会创建一个新实例。但是当使用 DDE 协议时,它首先检查是否已创建实例,如果是,它会将 DDE 消息转发给找到的第一个实例并退出,从而给人一种所有文件都在单个实例中打开的错觉,因为它是即时的。
猜测
在多个实例中打开文件的问题可能与调用另一个实例时单个实例已加载了多少文件有关。第一个实例和第二个实例的执行时间差趋势是,随着执行间隔时间的增加,它倾向于产生一个实例,而随着执行间隔时间的减少,它倾向于产生两个实例。这表明,如果执行另一个文件,第一个实例必须已加载或“准备好”在同一个实例中打开新文件,如果没有,它将用自己打开文件。
似乎当文件路径作为程序的参数时,它似乎仅遵循这种趋势:
- Word 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();
}
}
如果没有程序员的通知,我们就无法判断这是否是一个错误或者是因为某种原因而故意隐藏起来。
解决方案
我们希望调整某些文件扩展名的执行,不再将正在执行的文件的文件路径(“%1”)作为参数发送,而是告诉正在执行的程序执行 DDE 消息的内容,其中包含打开文件的请求,如果存在,它将把它转发给已经存在的实例,如果不存在,它将自己使用它。据推测,如果使用文件路径的参数,这将绕过这些应用程序对另一个实例被视为“就绪”的模糊要求。
这些都是与 Class 键相关的文件扩展名,应替换为x
:
对于 Word
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
对于 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
* 最重要/最常见的文件扩展名,应至少完成。主观。
† 应至少完成的次要最重要的/最常见的文件扩展名。主观。
这些列表可以通过命令行复制:用官方缩写名称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")]
- (针对 Word)[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)
。
我正在考虑将其设为社区 wiki,因为它非常具有推测性,而且尚未完成(如果 Word 和 Excel 不是唯一的)。请对此发表意见。
答案2
除了@El8tedN8te 的出色回答之外,我还想指出,对于 Excel 来说,不需要修改ddeexec
注册表项。
将项目的值设置(Default)
为:
"C:\Program Files (x86)\Microsoft Office\Root\Office16\EXCEL.EXE" /dde "%1"
通过我的测试,这可以确保只执行一个 Excel 实例。
答案3
在这页面报道称“Excel 中没有 MDI 兼容性选项。“
“MDI”代表多文档界面,并已被 SDI(单文档界面)取代,因此没有错误。这是 Excel 现在的工作方式。
您可以按 来循环浏览工作簿Ctrl+TAB
,然后Ctrl+Shift+TAB
按 来向后循环。如果您愿意,可以安装将此功能添加到整个 Office 套件的应用程序。检查以下两个选项:
不幸的是我现在无法测试这些软件。