Исключение COM-взаимодействия Excel при работе в фоновом режиме - PullRequest
1 голос
/ 01 марта 2012

Я пытаюсь применить некоторые стили к ячейкам в моей книге. И я хочу сделать это в фоновом режиме, чтобы мой графический интерфейс оставался отзывчивым. Эта работа должна занять несколько секунд, и если я нажму на какую-нибудь случайную ячейку в моем документе, я получу исключение. Вот мой код:

public void ApplyStyles()
{
    BackgroundWorker bw = new BackgroundWorker();
    bw.DoWork += DoWork;
    bw.RunWorkerAsync();

}

private void DoWork(object sender, DoWorkEventArgs e)
{
    try
    {
        foreach (ICell xcell in cells)
        {
            Microsoft.Office.Interop.Excel.Range cell = cellUtility.GetCell(xcell);
            if (styles.ContainsKey(styleIds[xcell.Style]))
            {
                Style s = styles[xcell.Style];
                cell.Style = s;
            }
        }
    }
    catch (Exception ex)
    {
        if (Logger.IsErrorEnabled)
        {
            Logger.Error(ex.ToString());
        }
        messageBox.ShowErrorMessage(localizationMessages.ApplyingErrorText, localizationMessages.ApplyingErrorCaption);
    }
}

Когда происходит исключение, это сообщение, которое я получаю;

System.Runtime.InteropServices.COMException (0x800AC472): Exception from HRESULT: 0x800AC472
   at System.RuntimeType.ForwardCallToInvokeMember(String memberName, BindingFlags flags, Object target, Int32[] aWrapperTypes, MessageData& msgData)
   at Microsoft.Office.Interop.Excel.Range.set_Style(Object value)
   at ABZ.ReportFactory.OfficeAddin.Excel.BatchLinking.BackgroundStyleApplier.DoWork() in C:\ABZ\ABZ ReportFactory Office Addin\ABZ.ReportFactory.OfficeAddin.Excel\BatchLinking\BackgroundStyleApplier.cs:line 86

Можно ли применить этот стиль, применяя операцию в фоновом потоке? И как мне это сделать?

Вторая проблема, с которой я столкнулся, заключается в том, что, пока применение этого стиля выполняется в фоновом режиме, мой курсор постоянно меняет состояние с занятого на обычное, пока эта операция не закончится. Я хотел бы, чтобы курсор был нормальным. Этот пользователь совершенно не знает об этой фоновой операции.

веселит, Владимир

1 Ответ

13 голосов
/ 01 марта 2012

Вы должны быть уверены, что получите корневой объект приложения Excel, а оттуда Range, из потока, в котором выполняется работа.Все COM-объекты Excel живут в однопоточной квартире (STA), поэтому вы не можете просто использовать их из какого-либо другого потока.

Вы не показываете, как «cellUtility.GetCell» фактически получает Range,но вероятная проблема заключается в том, что вы используете объект Application, который был первоначально получен из другого потока.

Выполнение такого рода работы в «фоновом потоке, чтобы ваш графический интерфейс мог оставаться отзывчивым», проблематично - все работают вВ конечном итоге Excel появляется в основном потоке Excel, поскольку Excel (по сути) является однопоточным.

Есть несколько способов решения этой проблемы:

  1. Часто вы найдетечто отключение ScreenUpdating и установка Calculation на Manual позволяет вам выполнять работу по редактированию намного быстрее, тогда вам не нужны другие потоки или что-либо еще.

  2. Запустите свою работу в главном потоке,но разбейте его на маленькие куски, а затем запланируйте следующий блок после передачи в Excel - вы можете создать Windows.Forms.Timer или, если вы можете запуститьмакрос Excel использует Application.OnTime для планирования следующей части работы.

  3. Если вы хотите выполнить работу из другого потока, вам нужно получить объект Application и дополнительный COM-объект наэта нить.Может быть сложно получить правильный экземпляр приложения Excel, но Эндрю Уайтчепел описывает хороший подход здесь: http://blogs.officezealot.com/whitechapel/archive/2005/04/10/4514.aspx. Однако вам все равно придется проверять ошибки на каждый вызов COM из другого потока., поскольку Excel может быть занят и может отклонить любой вызов COM из другого потока в любое время (особенно, если ваши пользователи взаимодействуют с этим «отзывчивым графическим интерфейсом». Необходимо по крайней мере проверить наличие исключений COMException с ошибками - каждый вызов COM может выдать одиниз них:

    • const uint RPC_E_SERVERCALL_RETRYLATER = 0x8001010A;
    • const uint VBA_E_IGNORE = 0x800AC472;

ExcelDNA (библиотека интеграции Excel / .NET, которую я разрабатываю) предоставляет доступ к объекту Application в потоке, который вы запускаете, вызывая ExcelDnaUtil.Application, но я все же рекомендую перенести все взаимодействия с объектной моделью Excel в основной поток Excel., возможно, используя вызов Application.Run, чтобы запустить Excel, чтобы запустить макрос.Вызов pplication.Run становится единой точкой, в которой COMException можно проверить и повторить из фонового потока.

...