最近在进行Word相关的开发时遇到了这个错误,在网上找了很多资料,发现就那么几个解决方案(方案3和方案4),但是都没有很好的解决这个问题。后面在stackoverflow看到一个歪果仁提供的一个思路(方案1),完美解决了这个问题。而后无意中浏览到了MSDN,发现原来微软早就提供了这个问题的解决方案(方案2),可以从根本上避免此类异常的发生,可恶的微软隐藏的这么深。下面特意把这些方案整理出来。
这个方案可以很完美的解决该问题,主要的思路就是捕获异常。如果获取的异常为该异常重复调用发生异常的方法,知道能够顺利调用完成为止。这个方案定义了两类泛型函数,函数的参数是委托。我们调用时就是把我们可能抛出异常的方法放到委托中通过下面类似的泛型函数进行调用。
public static void RunWithOutRejected(Action action, T t)
{
bool hasException;
do
{
try
{
action(t);
hasException = false;
}
catch (System.Runtime.InteropServices.COMException e)
{
if (e.ErrorCode == -2147418111)
{
hasException = true;
}
else
{
throw;
}
}
catch (Exception)
{
throw;
}
} while (hasException);
}
public static T RunWithOutRejected(Func func)
{
var result = default(T);
bool hasException;
do
{
try
{
result = func();
hasException = false;
}
catch (System.Runtime.InteropServices.COMException e)
{
if (e.ErrorCode == -2147418111)
{
hasException = true;
}
else
{
throw;
}
}
catch (Exception)
{
throw;
}
} while (hasException);
return result;
}
public void ClearWordRange(Word.Range range)
{
Action action = ExceptionHandlerInnerClearWordRange;
ExceptionHandler.RunWithOutRejected(action, range);
}
private void ExceptionHandlerInnerClearWordRange(Word.Range range)
{
range.Text = "";
range = null;
}
MSDN地址:https://msdn.microsoft.com/zh-cn/library/ms228772(v=vs.120).aspx
按照微软的意思,这个异常产生的根本原因是我们自己写的程序和外部的WORD程序间线程征用所致,它定义了一个MessageFilter类继承自IOleMessageFilter来进行这些线程的管理。在它的示例代码中,还引入并打开了IDE,我想它只是为了演示MessageFilter的用法,真正能解决问题的只是在你程序的开始和结束位置分别进行MessageFilter.Register()和MessageFilter.Revoke()就可以了。
若是权限问题,按如下步骤:
dcomcnfg
在“服务”里找到这三个服务:
Distributed Transaction Coordinator、
Remote Procedure Call (RPC) 、
Security Accounts Manager ,找到之后都去启动。
如果 Distributed Transaction Coordinator 启动不了,
就先在运行里面输入:
msdtc -resetlog
然后再开启:Distributed Transaction Coordinator 。
接着关闭服务组件窗体 ,并重新打开,这个时候每个组建就有属性了。
然后在命令行中输入:
dcomcnfg
打开“组件服务->计算机->我的电脑->DCOM 配置”,找到“Microsoft Word文档”,单击右键,选择“属性”。在“属性”对话框中单击“标识”选项卡,选择“交互式用户””,关闭“组件服务”管理器。
PS:这个方案没有仔细研究。不过操作Word和Excel应该用到的是COM接口,这里却要进行DCOM相关配置,有些让人费解。反正这个方案在我电脑上是无效的。
int m_iErrCnt=0;
while( true )
{
try
{
bm0.Range.Text=bandvalue;
break;
}
catch(SystemException err)
{
m_iErrCnt++;
if( m_iErrCnt<10 )
{
System.Threading.Thread.Sleep(1000);
}
else
{ throw err;}
}
}
PS:这个方案可以解决问题,但是不是很完美,因为中间Sleep的时间不好把控,而且Sleep会影响程序的效率。若是在界面主程序中Sleep还会造成界面的无响应状态。