Пакетная печать отчетов в MS Dynamics AX 2012 при помощи XMLExcelReport_RU

Класс  XMLExcelReport_RU наследник RunBaseBatch, соответственно должен работать в пакете, но для  отправки отчета сразу на печать используется COM объект, который может работать не стабильно при больших объемах в пакете. Можно использовать библиотеки Microsoft.Office.Interop, которые чаще используются в пакетном режиме. Также в XMLExcelReport_RU используется WinAPI, его тоже не совсем корректно использовать при работе на сервере, потому что, к примеру, WinAPI::deleteFile() используется в методе processInExcel() без учета возвращаемого значения и при не удачной попытке выполнить команду на сервере продолжает работу приложения, при этом не выводя сообщений, а временные файлы отчетов продолжают копиться. Ниже приведен код для запуска печати на сервере в MS Dynamics AX 2012 (метод XMLExcelReport_RU.processInExcel()).

		case SRSPrintMediumType::Printer:
			if (isRunningOnServer())
			{
				this.excelPrintOnServer(_filename);
			}
			else
			{

Печать выведена в отдельный метод.

private void excelPrintOnServer(Filename _filename)
{
    Microsoft.Office.Interop.Excel.ApplicationClass     excel;
    Microsoft.Office.Interop.Excel.Workbooks            workBooks;
    Microsoft.Office.Interop.Excel._Workbook            workbook;
    System.Type                                         type = System.Type::GetType('System.Reflection.Missing');
    System.Reflection.FieldInfo                         info = type.GetField('Value');
    System.Object                                       missing = info.GetValue(null);
    //________
    void deleteFile()
    {
        boolean             fileExists;
        new InteropPermission(InteropKind::ClrInterop).assert();
        fileExists = System.IO.File::Exists(_filename);
        CodeAccessPermission::revertAssert();
        if (fileExists)
        {
            XmlExcelReport_RU::removeFileServer(_filename);
        }
    }
    //_______
    void errorCatched()
    {
        new InteropPermission(InteropKind::ClrInterop).assert();
        if (excel)
        {
            excel.Quit();
            excel = null;
            System.GC::Collect();
            System.GC::WaitForPendingFinalizers();
        }
        else
        {
            error(strFmt("Ошибка создания объекта Microsoft.Office.Interop.Excel на '%1'", xGlobal::computerName()));
        }

        CodeAccessPermission::revertAssert();
        deleteFile();

        throw error(strFmt("Ошибка при печати файла %1 на сервере %2", _filename, xGlobal::computerName()));
    }
    //______
    boolean checkPrinterName()
    {
        System.Drawing.Printing.PrinterSettings printerSettings;

        if (!printDestinationSettings.printerName())
        {
            return checkFailed("Имя принтера не указано");
        }

        printerSettings = new System.Drawing.Printing.PrinterSettings();
        printerSettings.set_PrinterName(printDestinationSettings.printerName());
        if (!printerSettings.get_IsValid())
        {
            return checkFailed(strFmt("Имя принтера '%1' не допустимо для печати на '%2'", printDestinationSettings.printerName(), xGlobal::computerName()));
        }

        return true;
    }
    boolean openFile()
    {
        // BP deviation documented
        workbook = workBooks.Open(_fileName, missing, missing, missing, missing, missing, missing, missing, missing, missing, missing, missing, missing, missing, missing);
        // BP deviation documented
        return !CLRInterop::isNull(workbook);
    }
    //======

    setPrefix(strFmt("Печать на принтер '%1' на сервере '%2'", printDestinationSettings.printerName(), xGlobal::computerName()));

    if (!checkPrinterName())
    {
        deleteFile();
        throw Exception::Error;
    }

    try
    {
        CodeAccessPermission::revertAssert();
        new InteropPermission(InteropKind::ClrInterop).assert();
        excel       = new Microsoft.Office.Interop.Excel.ApplicationClass();
        workBooks   = excel.get_Workbooks();
        
        if (openFile())
        {
            workbook.PrintOut(missing,
                              missing,
                              CLRInterop::getObjectForAnyType(1),
                              False,
                              CLRInterop::getObjectForAnyType(printDestinationSettings.printerName()),
                              false,
                              true,
                              '');
            workbook.Close(false, missing, missing);
            workBook = null;
            workBooks.Close();
            workBooks = null;
            excel.Quit();
            excel = null;
            System.GC::Collect();
            System.GC::WaitForPendingFinalizers();
            CodeAccessPermission::revertAssert();
        }
        else
        {
            CodeAccessPermission::revertAssert();
            throw error(strFmt('Не удалось открыть файл "%1".', _filename));
        }
    }
    catch (Exception::CLRError)
    {
        error(AifUtil::getClrErrorMessage());
        errorCatched();
    }
    catch
    {
        errorCatched();
    }
}

Проверка наименования принтера классом System.Drawing.Printing.PrinterSettings была добавлена, потому что если было передано пустое имя принтера или наименование не установленного на данном сервере принтера, то печать зависала. Также зависало выполнение пакетного задания если вовремя не завершались процессы Excel. Для этого дополнительно вызывается сборщик мусора System.GC и присваиваются объектам Excel значение null. Также дополнительно освобождалась переменная excelDocument в методе XMLExcelReport_RU.run()

            if (excelDocument)
            {
                if (excelDocument.getComDocument())
                {
                    excelDocument.visible(true);
                    excelDocument.finalize();
                }
                else
                {
                    excelDocument.quitApplication(false);
                    excelDocument.finalize();
                }

                excelDocument = null;
                System.GC::Collect();
                System.GC::WaitForPendingFinalizers();
            }

 

Comments

So empty here ... leave a comment!

Добавить комментарий

Sidebar