引自强大而免费的微信小程序:皆我百晓生。
问题描述
在使用C#线程操作PowerPoint时,出现异常"The message filter indicated that the application is busy. (0x8001010A)"。当需要新增PPT页数比较多时,此异常偶发出现。
解决方案
出现此异常的原因是因为PowerPoint应用程序忙于处理其他操作,无法立即响应新的请求。为了解决这个问题,可以使用消息过滤器来确保PowerPoint应用程序能够处理后台线程的请求。
以下是解决方案的思路和步骤:
1. 引入消息过滤器
首先,我们需要引入消息过滤器以确保PowerPoint应用程序能够处理后台线程的请求。可以使用消息过滤器包装我们的PPT生成逻辑。
using System.Runtime.InteropServices;
...
public class MessageFilter : IOleMessageFilter
{
// 定义常量
private const int SERVERCALL_ISHANDLED = 0;
private const int SERVERCALL_RETRYLATER = 2;
private const int PENDINGMSG_WAITDEFPROCESS = 2;
// 实现接口方法
public int HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo)
{
return SERVERCALL_ISHANDLED;
}
public int RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType)
{
if (dwRejectType == SERVERCALL_RETRYLATER && dwTickCount < 10000)
{
// 等待100毫秒再重试
return 100;
}
return -1;
}
public int MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType)
{
return PENDINGMSG_WAITDEFPROCESS;
}
// 注册消息过滤器
public static void Register()
{
MessageFilter newFilter = new MessageFilter();
CoRegisterMessageFilter(newFilter, out IOleMessageFilter oldFilter);
}
// 取消注册消息过滤器
public static void Revoke()
{
CoRegisterMessageFilter(null, out IOleMessageFilter oldFilter);
}
// DllImport
[DllImport("ole32.dll")]
private static extern int CoRegisterMessageFilter(IOleMessageFilter newFilter, out IOleMessageFilter oldFilter);
}
// IOleMessageFilter接口
[ComImport, Guid("00000016-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IOleMessageFilter
{
[PreserveSig]
int HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo);
[PreserveSig]
int RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType);
[PreserveSig]
int MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType);
}
2. 注册和取消注册消息过滤器
在多线程操作之前,需要在主线程中注册消息过滤器,操作完成后再取消注册。
MessageFilter.Register();
// 执行多线程操作
// ...
MessageFilter.Revoke();
3. 修改生成PPT逻辑
在生成PPT的逻辑中,调用PPT操作前先使用MessageFilter.Register()
注册消息过滤器,操作完成后再使用MessageFilter.Revoke()
取消注册。
MicsoftOffice.Interop.PowerPoint.Application PPT = new MicsoftOffice.Interop.PowerPoint.Application();
MessageFilter.Register(); // 注册消息过滤器
Presentation presentation = PPT.Presentations.Open(sSource, MoTriState.mso.False,MoTriState.mso.False, MoTriState.mso.False);
GeneratePage(presentation);
presentation.SaveAs(sSavePath, PpSaveAsDefault,MoTriState.mso.False);
presentation.Close();
PPT.Quit();
MessageFilter.Revoke(); // 取消注册消息过滤器
GC.Collect();
完整代码示例
下面是一个完整的示例代码,展示了如何在C#中使用多线程生成多个PPT时,避免出现"The message filter indicated that the application is busy."异常。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MicsoftOffice.Interop.PowerPoint;
using MoTriState = MicsoftOffice.Core.MsoTriState;
namespace PowerPointThread
{
class Program
{
static void Main(string[] args)
{
// 注册消息过滤器
MessageFilter.Register();
// 创建线程
Thread thread1 = new Thread(GeneratePPT);
Thread thread2 = new Thread(GeneratePPT);
// 启动线程
thread1.Start();
thread2.Start();
// 等待线程完成
thread1.Join();
thread2.Join();
// 取消注册消息过滤器
MessageFilter.Revoke();
Console.WriteLine("Finished generating PPTs");
Console.ReadKey();
}
static void GeneratePPT()
{
MicsoftOffice.Interop.PowerPoint.Application PPT = new MicsoftOffice.Interop.PowerPoint.Application();
Presentation presentation = null;
try
{
string sSource = "template.pptx";
string sSavePath = $"output_{Guid.NewGuid()}.pptx";
presentation = PPT.Presentations.Open(sSource, MoTriState.msoFalse, MoTriState.msoFalse, MoTriState.msoFalse);
GeneratePage(presentation);
presentation.SaveAs(sSavePath, PpSaveAsDefault, MoTriState.msoFalse);
}
catch (Exception ex)
{
Console.WriteLine($"Error generating PPT: {ex.Message}");
}
finally
{
if (presentation != null)
{
presentation.Close();
}
PPT.Quit();
GC.Collect();
}
}
static void GeneratePage(Presentation presentation)
{
// 生成PPT页面的逻辑
// ...
}
// 消息过滤器的实现代码
// ...
}
}
使用上述代码,你可以在多个线程中同时生成多个PPT,每个线程以PPT模板生成新的PPT。同时,使用消息过滤器将确保PowerPoint应用程序能够处理后台线程的请求,避免出现"The message filter indicated that the application is busy."异常。