HRESULT异常:0x800401E3(MK_E_UNAVAILABLE)解决方法

19

从以下调用中

Marshal.GetActiveObject("Excel.Application")

我遇到了一个问题:

操作不可用(来自 HRESULT 的异常:0x800401E3 (MK_E_UNAVAILABLE))

我认为这个错误是因为我的应用程序和 Excel 之间的用户权限不匹配导致的。

我想知道是否有一种解决方法,可以在 Excel 被打开的情况下访问它,而不管它是如何被打开的。我可以将我想要从中访问 Excel 的程序作为管理员打开。

此外,我想知道如何查看进程使用的权限。我一直在使用 ProcessExplorer 查看 UserProfile(在两个应用程序中相同)和 Owner(也是相同的 BUILTIN\Administrators)。

背景 我有一个程序通过调用 NUnit-console-x86 运行不同的测试。被测试的应用程序打开了一个 Excel 表单,这是我想要从中读取数据的表单。无论我以管理员身份运行还是不运行我的程序,我都会遇到这些错误。我也尝试向启动 NUnit 的程序中添加 Process.StartInfo.Verb = "runas";,但仍然会遇到这些错误。

看起来安装 Visual Studio 可以解决这个问题,但我不想在每台计算机上都安装 Visual Studio。有人能给我解释一下吗?


2
我刚刚经历了Excel的权限噩梦,一旦我将Excel的兼容模式设置为以管理员身份运行,并将调用应用程序也作为管理员运行,一切都正常工作了。 - MisterIsaak
我遇到过这个问题很多次,我发现如果我遇到错误并在Skype和Excel之间来回切换,它就可以解决问题... - James Heffer
4个回答

7

查看 Microsoft Support Information,0x800401e3 似乎是在 Excel(或整个 Office)未处于活动状态或未在运行对象表中时生成的。 在调用此函数之前,您必须打开 Excel。 要么是您尚未在代码中打开 Excel,要么是其尚未完全注册。 这可能是问题的原因吗?


1
不,它正在运行。但不知何故,我以非管理员身份运行了Visual Studio。一旦我以管理员身份重新运行它,一切都开始正常工作了。 - BrainSlugs83
啊哈。没错,那也可以解决问题。很高兴你能让它正常工作。 - Dylan Corriveau
2
根据您提供的文章:“当Office应用程序启动时,它不会立即注册其运行对象。这优化了应用程序的启动过程。 Office应用程序在失去焦点后才将其运行对象注册到ROT中,而不是在启动时注册。” 我发现需要将焦点放在另一个窗口上才能完成此操作(仅最小化无法实现...),有点hacky,我想知道是否有更好的方法 :( - David Rogers
@DavidRogers 这对我有用,老实说它应该是一个独立的答案。但作为解决方案也太愚蠢了。 - Mox
@DavidRogers 这对我有用,老实说,它应该是一个独立的答案。但作为一个解决方案,它也是愚蠢的。 - undefined

2

延伸@DylanCorriveau所说的,我发现你需要采取以下方法之一来避免出现此问题。

方法一

我发现在某些情况下(Excel 2010),先打开Excel可以解决此问题。您需要调整路径并等待以适应您的需求和版本。

注:Original Answer翻译成“最初的回答”

string pathToTheVersionOfExcel = @"C:\Program Files (x86)\Microsoft Office\Office14\EXCEL.EXE";
System.Diagnostics.Process.Start(pathToTheVersionOfExcel);
Thread.Sleep(5000); //"WaitForInputIdle" waits for way too long, generally it takes 5 seconds to start for me

方法二

过去我采用的另一种方法是以不同的方式调用Excel:

最初的回答:

var oExcelApp = new Microsoft.Office.Interop.Excel.Application();

方法三

最终在我的应用程序中(适用于Excel 2010和2016),我使用了一个变通方法:

[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

//In your method...
string pathToTheVersionOfExcel = @"C:\Program Files (x86)\Microsoft Office\root\Office16\EXCEL.EXE";
Application oExcelApp = null;

Process process = new Process();
process.StartInfo.FileName = pathToTheVersionOfExcel;
process.Start();

Thread.Sleep(5000);

//Opening a closing notepad seems to "register" excel 2016, not needed for excel 2010 though...
Process processNotepad = new Process();
processNotepad.StartInfo.FileName = @"C:\Windows\system32\notepad.exe";
processNotepad.Start();

ShowWindow(process.MainWindowHandle, 2); //Minimize
ShowWindow(process.MainWindowHandle, 3); //Maximize

Thread.Sleep(5000);

processNotepad.CloseMainWindow();

oExcelApp = (_Application)Marshal.GetActiveObject("Excel.Application");

如果您在RDP会话中的Excel 2016中运行此代码,它可能非常挑剔。我发现您需要自定义RDP以忽略其最小化状态。我发现这篇文章非常有帮助。
如果您正在尝试通过某种自动化构建/发布平台(如TFS / Azure DevOps)在远程服务器上调用Excel,则需要尝试使用autologin。我自己还没有使其正常工作

2
这是Visual Studio和Excel之间权限不匹配的问题。虽然微软的文档没有提到,但这确实存在。更严重的问题是,在旧版本的Excel中(如2007版),有时会抛出异常,有时则不会,因为它们不了解管理员权限。当出现这种情况时,您应该将权限设置为管理员-管理员或无-无。即使Excel的权限是无,Visual Studio的权限是管理员也可能无法正常工作。"最初的回答"

1

只添加"Microsoft.Office.Interop.Excel.dll"的引用。

try
{
    //This requires excel app (excel.exe*32) to be running means any excel sheet should be open. If excel not running then it will throw error.
    excelApp = (Excel.Application)Marshal.GetActiveObject("Excel.Application");
    excelApp.Visible = false;
}
catch
{
    //create new excel instance
    excelApp = new Excel.Application(); 
    excelApp.Visible = false;
}

这对我很有帮助。
优点:无需将Microsoft.Office.Interop.Excel.dll复制到已安装的文件夹中。由于MS Excel已安装,因此它将从GAC中获取。
我也使用了Office.dll,但不确定是否真正需要。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接