using Microsoft.SqlServer.Server; using System; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; namespace linesider_screen_tool { public sealed class BartenderPrintHelper : IDisposable { private const string BarTenderProgId = "BarTender.Application"; private object? _btApp; // 声明为可空类型 private int _disposedValue; public BartenderPrintHelper() { // 延迟初始化,在首次使用时创建 Bartender 应用实例 } /// /// 打印单个标签(同步) /// public bool PrintLabel( string templatePath, Dictionary subStringValues, int copies = 1, int serializedLabels = 1) { ValidateParameters(templatePath, subStringValues); return ExecuteBartenderAction(format => { SetPrintSettings(format, copies, serializedLabels); SetSubStringValues(format, subStringValues); InvokeMethod(format, "PrintOut", false, false); return true; }, templatePath); } public Task> GetNamedSubStrings(string templatePath) { dynamic format =null; dynamic btApp = null; try { btApp = Activator.CreateInstance(Type.GetTypeFromProgID(BarTenderProgId)); format = btApp.Formats.Open( templatePath, false, null ); format.PrintSetup.IdenticalCopiesOfLabel = 1; format.PrintSetup.NumberSerializedLabels = 1; List subStrings = new List(); dynamic namedSubStrings = format.NamedSubStrings; foreach (var iteam in namedSubStrings) { subStrings.Add(iteam.Name); } return Task.FromResult(subStrings); } catch (Exception ex) { Console.WriteLine($"发生错误: {ex.Message}"); return null; } finally { // 清理资源 if (format != null) { Marshal.ReleaseComObject(format); } if (btApp != null) { btApp.Quit(0); Marshal.ReleaseComObject(btApp); } } } /// /// 批量打印标签(高性能实现) /// public bool PrintBatchLabels( string templatePath, IEnumerable> labelsData, int copiesPerLabel = 1, int serializedLabels = 1) { ValidateParameters(templatePath, labelsData); return ExecuteBartenderAction(format => { SetPrintSettings(format, copiesPerLabel, serializedLabels); bool allSuccess = true; foreach (var data in labelsData) { try { SetSubStringValues(format, data); InvokeMethod(format, "PrintOut", false, false); } catch (Exception ex) { allSuccess = false; LogError($"打印标签时出错: {ex.Message}"); } } return allSuccess; }, templatePath); } private T ExecuteBartenderAction(Func action, string templatePath) { if (IsDisposed) throw new ObjectDisposedException(nameof(BartenderPrintHelper)); EnsureBartenderInitialized(); object? format = null; try { format = OpenFormat(templatePath); return action(format!); } catch (Exception ex) { LogError($"执行 Bartender 操作失败: {ex.Message}"); throw new InvalidOperationException("Bartender 操作失败", ex); } finally { ReleaseFormat(format); } } private void EnsureBartenderInitialized() { if (_btApp == null) { try { _btApp = Activator.CreateInstance(Type.GetTypeFromProgID(BarTenderProgId)); SetProperty(_btApp, "Visible", false); // 确保应用程序不可见 } catch (Exception ex) { LogError($"初始化 Bartender 失败: {ex.Message}"); throw new InvalidOperationException("无法初始化 Bartender 应用程序", ex); } } } private object OpenFormat(string path) { EnsureBartenderInitialized(); try { var formats = GetProperty(_btApp!, "Formats"); return InvokeMethod(formats, "Open", path, false, null); } catch (Exception ex) { LogError($"打开标签模板失败: {path}, 错误: {ex.Message}"); throw new FileNotFoundException($"无法打开标签模板: {path}", ex); } } private void SetPrintSettings(object format, int copies, int serializedLabels) { var printSetup = GetProperty(format, "PrintSetup"); SetProperty(printSetup, "IdenticalCopiesOfLabel", copies); SetProperty(printSetup, "NumberSerializedLabels", serializedLabels); } private void SetSubStringValues(object format, Dictionary values) { if (values == null) return; foreach (var kv in values) { try { InvokeMethod(format, "SetNamedSubStringValue", kv.Key, kv.Value); } catch (Exception ex) { LogError($"设置标签变量失败: {kv.Key}={kv.Value}, 错误: {ex.Message}"); throw new ArgumentException($"无法设置标签变量: {kv.Key}", ex); } } } private void ReleaseFormat(object? format) { if (format == null) return; try { InvokeMethod(format, "Close", 0); // 0 = BarTender.BtSaveOptions.btDoNotSaveChanges } catch (Exception ex) { LogError($"关闭标签格式失败: {ex.Message}"); } finally { ReleaseComObject(format); } } private void ReleaseComObject(object obj) { if (obj == null || !Marshal.IsComObject(obj)) return; try { // 循环调用直到引用计数为 0 int refCount; while ((refCount = Marshal.ReleaseComObject(obj)) > 0) { LogError($"COM 对象引用计数: {refCount}"); } } catch (Exception ex) { LogError($"释放 COM 对象失败: {ex.Message}"); } } private void ValidateParameters(string templatePath, object data = null) { if (string.IsNullOrEmpty(templatePath)) throw new ArgumentNullException(nameof(templatePath), "标签模板路径不能为空"); if (!File.Exists(templatePath)) throw new FileNotFoundException("指定的标签模板文件不存在", templatePath); if (data is IEnumerable> labelsData && labelsData == null) throw new ArgumentNullException(nameof(labelsData), "标签数据不能为空"); if (data is Dictionary singleLabelData && singleLabelData == null) throw new ArgumentNullException(nameof(singleLabelData), "标签数据不能为空"); } private void LogError(string message) { // 这里可以添加实际的日志记录逻辑 Console.WriteLine($"[ERROR] {message}"); } private bool IsDisposed => Interlocked.CompareExchange(ref _disposedValue, 0, 0) != 0; public void Dispose() { // 使用 Interlocked.Exchange 确保线程安全 if (Interlocked.Exchange(ref _disposedValue, 1) != 0) return; try { // 使用临时变量确保线程安全 var btApp = Interlocked.Exchange(ref _btApp, null); if (btApp != null && Marshal.IsComObject(btApp)) { try { // 使用反射调用 Quit 方法 InvokeMethod(btApp, "Quit", 0); } catch (Exception ex) { LogError($"退出 Bartender 应用程序失败: {ex.Message}"); } finally { ReleaseComObject(btApp); } } } finally { GC.SuppressFinalize(this); } } ~BartenderPrintHelper() => Dispose(); // 反射辅助方法 private object GetProperty(object obj, string propertyName) { return obj.GetType().InvokeMember( propertyName, BindingFlags.GetProperty, null, obj, null )!; } private void SetProperty(object obj, string propertyName, object value) { obj.GetType().InvokeMember( propertyName, BindingFlags.SetProperty, null, obj, new[] { value } ); } private object InvokeMethod(object obj, string methodName, params object?[]? parameters) { return obj.GetType().InvokeMember( methodName, BindingFlags.InvokeMethod, null, obj, parameters ?? Array.Empty() )!; } } }