私は、C# で Excel ワークブックを自動化するという最も恐ろしい仕事をしています。プロセスの一部は、実行に約 2 分かかるブック内のマクロを呼び出すことです。これはすべて Windows サービスで発生し、最初から最後まで問題なく実行されます。現在、マクロを呼び出す直前とマクロの実行が終了したときに、イベントをデータベース テーブルに書き込みます。マクロ コード内で多くの計算を行い、データをテキスト ファイルにエクスポートします。
非常に時間がかかるため、プロセスのさまざまな部分で通知を受け取ることができるかどうか、ユーザーから問い合わせがありました。
予約に関する私の最初の考えは、System.Timers.Timer を使用してマクロを実行している間に更新される Application.StatusBar を定期的にポーリングすることでした。これには何らかのスレッドの問題があるのではないかと思いました.StatusBarを取得するためのタイマーからの呼び出しがかなり長い時間(数十秒)返されない/完了しないために発生していると思います.
ワークブックを次のクラスにまとめて、Excel が正しく閉じられ、マクロが実行されるようにします。
internal class myWorkbook : IDisposable
{
private Microsoft.Office.Interop.Excel.Application app = null;
private Microsoft.Office.Interop.Excel.Workbook myWorkbook = null;
private string _myWorkbookUri;
public myWorkbook(string myWorkbookUri, string)
{
_myWorkbookUri = myWorkbookUri;
}
public string Export(DateTime date)
{
app = new Microsoft.Office.Interop.Excel.Application();
app.Visible = false;
app.DisplayAlerts = false;
app.Interactive = false;
app.AskToUpdateLinks = false;
myWorkbook = app.Workbooks.Open(_myWorkbookUri, 0, true, 5, "", "", false, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "", true, false, 0, true, true, false);
return (string)app.Run("GenerateTextFile", date);
}
/// <summary>
/// Disposes the object instance and all unmanaged resources.
/// </summary>
void IDisposable.Dispose()
{
if (myWorkbook != null)
{
myWorkbook.Close(false);
System.Runtime.InteropServices.Marshal.ReleaseComObject(myWorkbook);
}
if (app != null)
{
app.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(app);
}
}
public string Status
{
get
{
if (myWorkbook == null)
return string.Empty;
else
{
return myWorkbook.Application.StatusBar.ToString();
}
}
}
}
次に、レポート処理クラスで次のネストされた/内部クラスを使用して監視を試みました。
private class MyMonitor : System.Timers.Timer
{
private MyWorkbook _wb;
private ReportGeneratorProcess _parent;
private string _lastStatus = string.Empty;
private bool handlingTimer = false;
public MyMonitor(MyWorkbook wb, ReportGeneratorProcess parent)
{
_wb = wb;
_parent = parent;
this.AutoReset = true;
this.Elapsed += new System.Timers.ElapsedEventHandler(this.timer_Elapsed);
}
private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (_wb != null && !handlingTimer)
{
try
{
handlingTimer = true;
string status = _wb.Status;
if (status != _lastStatus && status.ToLower() != "false")
_parent.AddEvent(MSG_TITLE_RUN_My, status);
_lastStatus = status;
}
finally
{
handlingTimer = false;
}
}
}
}
これはすべて、ReportGeneratorProcess クラスで次のコードを実行することによってトリガーされます (不要なコードは省略しました)。
string outputFilename = null;
MyMonitor monitor = null;
try
{
using (MyWorkbook wb = new MyWorkbook(_MyWorkbookUri))
{
monitor = new MyMonitor(wb, this);
monitor.Start();
outputFilename = wb.Export(month);
monitor.Stop();
}
AddEvent("My Complete", "Generated file " + outputFilename);
return outputFilename;
}
catch (Exception ex)
{
AddEvent("", "failed");
throw ex;
}
finally
{
if (monitor != null)
{
monitor.Stop();
}
}
AddEvent は、メイン クラスを使用して新しいイベントをデータベースに追加するだけです。
現時点では、別の解決策/これを回避する良い方法を考えようとして、私は敗北したままです. ヒントはありますか?
残念ながら、プロセスはそのまま動作する必要があります。Excel から何かを移動する余地はありません。