错误处理(一)—— 被呼叫方拒绝接收呼叫。 (异常来自 HRESULT:0x80010001 (RPC_E_CALL_REJECTED))

——————————————————————————

亲测方案2可用

——————————————————————————

转载自:http://blog.csdn.net/hyman_c/article/details/53780431

最近在进行Word相关的开发时遇到了这个错误,在网上找了很多资料,发现就那么几个解决方案(方案3和方案4),但是都没有很好的解决这个问题。后面在stackoverflow看到一个歪果仁提供的一个思路(方案1),完美解决了这个问题。而后无意中浏览到了MSDN,发现原来微软早就提供了这个问题的解决方案(方案2),可以从根本上避免此类异常的发生,可恶的微软隐藏的这么深。下面特意把这些方案整理出来。


方案1 捕捉异常,利用委托(delegate)和泛型重复执行异常的方法。

     这个方案可以很完美的解决该问题,主要的思路就是捕获异常。如果获取的异常为该异常重复调用发生异常的方法,知道能够顺利调用完成为止。这个方案定义了两类泛型函数,函数的参数是委托。我们调用时就是把我们可能抛出异常的方法放到委托中通过下面类似的泛型函数进行调用。

        利用委托Action进行泛型的定义:
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);  
       }  

利用委托Func进行泛型的定义:

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

方案2 引入IOleMessageFilter,从根本上杜绝这种异常情况的出现

MSDN地址: https://msdn.microsoft.com/zh-cn/library/ms228772(v=vs.120).aspx

按照微软的意思,这个异常产生的根本原因是我们自己写的程序和外部的WORD程序间线程征用所致,它定义了一个MessageFilter类继承自IOleMessageFilter来进行这些线程的管理。在它的示例代码中,还引入并打开了IDE,我想它只是为了演示MessageFilter的用法,真正能解决问题的只是在你程序的开始和结束位置分别进行MessageFilter.Register()和MessageFilter.Revoke()就可以了

另外贴上自己使用的代码:

public void PrintWord(String dirPath, String fileName)
{
	try
	{
		MessageFilter.Register();
		object oTemplate = dirPath + @"\" + fileName;
		Microsoft.Office.Interop.Word._Document oDoc = oWord.Documents.Add(ref oTemplate, ref oMissing, ref oMissing, ref oMissing);
		object oTrue = true;
		oDoc.PrintOut(ref oTrue);
		object doNotSaveChanges = Microsoft.Office.Interop.Word.WdSaveOptions.wdDoNotSaveChanges;
		oDoc.Close(ref doNotSaveChanges, ref oMissing, ref oMissing);
		MessageFilter.Revoke();=
	}
	catch (Exception e)
	{
		FileHelper.ErrorLog(e.Message, "PrintWord", Data.UsersId);
		throw e;
	}
}

方案3 目录权限问题

        若是权限问题,按如下步骤:

步骤1打开dcomcnfg 

        在运行里面输入: 
msdtc -resetlog  

然后再开启:Distributed Transaction Coordinator 。接着关闭服务组件窗体 ,并重新打开,这个时候每个组建就有属性了。然后在命令行中输入:

dcomcnfg  

步骤2 进行DCOM 配置

        打开“组件服务->计算机->我的电脑->DCOM 配置”,找到“Microsoft Word文档”,单击右键,选择“属性”。在“属性”对话框中单击“标识”选项卡,选择“交互式用户””,关闭“组件服务”管理器。 

         PS:这个方案没有仔细研究。不过操作Word和Excel应该用到的是COM接口,这里却要进行DCOM相关配置,有些让人费解。反正这个方案在我电脑上是无效的。

方案4 操作频繁问题,在代码中加入延时。

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还会造成界面的无响应状态。

Github位置:
https://github.com/HymanLiuTS/OfficeTestByC-
克隆本项目:
git clone [email protected]:HymanLiuTS/OfficeTestByC-.git
获取本文源代码:
git checkout L16


你可能感兴趣的:(C#,(RPC_E_CALL,错误处理(一)——,被呼叫方拒绝接收呼叫)