Вопрос
У меня есть настольное приложение C# WPF, которое печатает счета на термальный принтер с использованием Пользовательской очереди печати (печать в стиле ESC/POS). Проблема:
Если приложение бездействует или свернуто ~10 минут, печать перестает работать:
Задание печати создано успешно.
Оно появляется в очереди печати
Задание никогда не печатается (не приостановлено, нет ошибок) — Показать ожидание.
Исключение не создается. приложение
Если я перезапущу приложение, печать заработает немедленно
Сам принтер работает нормально и печатает нормально из других приложений. Что работает:
Перезапуск приложения (не требуется перезагрузка системы или принтера)
Что НЕ помогает:
Возврат приложения на передний план
Повторная попытка печати
Принтер подключен к сети и реагирует
Среда:
Рабочий стол Windows
WPF (.NET)
Термопринтер
Приложение продолжает работать (не приостановлено и не закрыто)
Вопросы:
Что могло привести к зависанию заданий печати после того, как приложение было бездействующим/свернутым?
Временно Я исправил это, запустив в фоновом режиме. Плохая ли практика поддерживать работу приложения с помощью таймера/пульса?
Рекомендуется ли подход к:
Повторной инициализации объектов принтера перед каждой печатью?
Повторному созданию службы печати после бездействия?
Избегайте повторного использования полностью связанные с принтером объекты?
Я ищу лучшие практики для обеспечения надежности печати в долго работающих приложениях WPF, особенно с термопринтерами.
[b]Вопрос[/b] У меня есть [b]настольное приложение C# WPF[/b], которое печатает счета на [b]термальный принтер[/b] с использованием [b]Пользовательской очереди печати[/b] (печать в стиле ESC/POS). [b]Проблема:[/b]
Если приложение [b]бездействует или свернуто ~10 минут[/b], печать перестает работать: [list] [*]Задание печати создано успешно.
[*]Оно появляется в [b]очереди печати[/b]
[*]Задание никогда не печатается (не приостановлено, нет ошибок) — Показать ожидание.
[*]Исключение не создается. приложение
[*]Если я [b]перезапущу приложение[/b], печать заработает немедленно
[/list] Сам принтер работает нормально и печатает нормально из других приложений. [b]Что работает:[/b] [list] [*]Перезапуск приложения (не требуется перезагрузка системы или принтера) [/list] [b]Что НЕ помогает:[/b] [list] [*]Возврат приложения на передний план
[*]Повторная попытка печати
[*]Принтер подключен к сети и реагирует
[/list] [b]Среда:[/b] [list] [*]Рабочий стол Windows
[*]WPF (.NET)
[*]Термопринтер
[*]Приложение продолжает работать (не приостановлено и не закрыто)
[/list] [b]Вопросы:[/b] [list] [*]Что могло привести к зависанию заданий печати после того, как приложение было бездействующим/свернутым?
[*]Временно Я исправил это, запустив в фоновом режиме. Плохая ли практика поддерживать работу приложения с помощью таймера/пульса?
[*]Рекомендуется ли подход к: [list] Повторной инициализации объектов принтера перед каждой печатью?
[*]Повторному созданию службы печати после бездействия?
[*]Избегайте повторного использования полностью связанные с принтером объекты?
[/list]
[/list] Я ищу [b]лучшие практики[/b] для обеспечения надежности печати в долго работающих приложениях WPF, особенно с термопринтерами. [code]public class PrintQueueProcessor : IDisposable { private readonly IDbContextFactory _contextFactory; private readonly ThermalPrinterService _thermalPrinterService; private Timer? _processingTimer; private Timer? _cleanupTimer; private Timer? _keepAliveTimer; private readonly object _lock = new(); private bool _isProcessing; private bool _isRunning; private CancellationTokenSource? _cts; private Task? _currentProcessingTask;
public PrintQueueProcessor( IDbContextFactory contextFactory, ThermalPrinterService thermalPrinterService) { _contextFactory = contextFactory ?? throw new ArgumentNullException(nameof(contextFactory)); _thermalPrinterService = thermalPrinterService ?? throw new ArgumentNullException(nameof(thermalPrinterService)); Log.Information("PrintQueueProcessor initialized"); }
public void Start() { lock (_lock) { if (_isRunning) { Log.Warning("Print queue processor already running"); return; }
_isRunning = true; _cts = new CancellationTokenSource();
_processingTimer = new Timer( ProcessPendingJobsCallback, null, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(3));
_cleanupTimer = new Timer( CleanupCallback, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(5));
_keepAliveTimer = new Timer( KeepAliveCallback, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(2));
Log.Information("✅ Print Queue Processor STARTED (with keep-alive)"); } }
[DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)] private static extern bool GetPrinter(IntPtr hPrinter, int Level, IntPtr pPrinter, int cbBuf, out int pcbNeeded);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] private struct PRINTER_INFO_2 { public string pServerName; public string pPrinterName; public string pShareName; public string pPortName; public string pDriverName; public string pComment; public string pLocation; public IntPtr pDevMode; public string pSepFile; public string pPrintProcessor; public string pDatatype; public string pParameters; public IntPtr pSecurityDescriptor; public uint Attributes; public uint Priority; public uint DefaultPriority; public uint StartTime; public uint UntilTime; public uint Status; public uint cJobs; public uint AveragePPM; }
private bool PingPrinter(string printerName) { IntPtr hPrinter = IntPtr.Zero; try { if (!OpenPrinter(printerName, out hPrinter, IntPtr.Zero)) { Log.Warning("⚠️ Cannot open printer: {Printer}", printerName); return false; }
// Get printer info - this keeps connection alive GetPrinter(hPrinter, 2, IntPtr.Zero, 0, out int needed);
if (needed > 0) { IntPtr pPrinterInfo = Marshal.AllocHGlobal(needed); try { if (GetPrinter(hPrinter, 2, pPrinterInfo, needed, out _)) { var info = Marshal.PtrToStructure(pPrinterInfo); Log.Debug("🖨️ Printer '{Printer}' alive - Jobs: {Jobs}, Status: {Status}", printerName, info.cJobs, info.Status); return true; } } finally { Marshal.FreeHGlobal(pPrinterInfo); } }