When my code tries to create a new instance of Microsoft.Office.Interop.PowerPoint.Application
, I sometimes get the following exception:
System.Runtime.InteropServices.COMException (0x80010001): Retrieving the COM class factory for component with CLSID {91493441-5A91-11CF-8700-00AA0060263B} failed due to the following error: 80010001 Call was rejected by callee. (Exception from HRESULT: 0x80010001 (RPC_E_CALL_REJECTED)).
at System.Runtime.Remoting.RemotingServices.AllocateUninitializedObject(RuntimeType objectType)
at System.Runtime.Remoting.Activation.ActivationServices.CreateInstance(RuntimeType serverType)
at System.Runtime.Remoting.Activation.ActivationServices.IsCurrentContextOK(RuntimeType serverType, Object[] props, Boolean bNewObj)
at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)
at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache)
at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipVisibilityChecks, Boolean skipCheckThis, Boolean fillCache)
at System.Activator.CreateInstance(Type type, Boolean nonPublic)
I say sometimes because it doesn't happen consistently even given the same input. Furthermore, it also occurs (with the same lack of consistency) in other parts of my code where I also interact with the PowerPoint automation API.
I have tried out this solution from MSDN itself which seems to be the most recommended solution out there. However, it doesn't seem to have any impact as I still observe the same behavior.
My questions are:
I am using C#, .NET 4, and PowerPoint 2007.
I have run into this before and Paul B is indeed correct. It depends on whether you are calling the Powerpoint OM from the main thread (i.e. This_AddIn
) or not. If you are, ppt should not throw these exceptions. However, if you are calling ppt from another thread, you must implement IMessageFilter
to handle these windows message pump errors efficiently since ppt prioritizes main thread calls to the OM over calls from other threads, hence the call rejections.
There is another caveat that requires further boiler plate code to handle additional COMException
s such as 0x800AC472 (VBA_E_IGNORE)
. There is an example here.
So, the full solution is both to implement IMessageFilter
and to use something like sepp2k's code in which to wrap your OM calls in order to handle other types of COMException
that may be thrown.
So, for wrapping code like his:
private void TryUntilSuccess(Action action)
{
bool success = false;
while (!success)
{
try
{
action();
success = true;
}
catch (System.Runtime.InteropServices.COMException e)
{
if ((e.ErrorCode & 0xFFFF) == 0xC472)
{ // Excel is busy
Thread.Sleep(500); // Wait, and...
success = false; // ...try again
}
else
{ // Re-throw!
throw e;
}
}
}
}
which you could call with lamdas like this:
TryUntilSuccess(() =>
{
RegisterFilter(); // register this thread for IMessageFilter use
ppt_app.DoSomething();
UnRegisterFilter(); // unregister this thread for IMessageFilter use
};)
The reason for this two-pronged approach is that the IMessageFilter
strategy is more efficient than throwing exceptions and will more times than not be able to handle the busy messages coming from the app. However, at other times you will have to handle exceptions so you have to do both...
See here for the IMessageFilter implementation which includes wrappers
Hth!