示例工程【免费下载链接】Windows-universal-samplesAPI samples for the Universal Windows Platform.项目地址https://gitcode.com/gh_mirrors/wi/Windows-universal-samples点击查看免费下载本文围绕 Windows-universal-samples 仓库中的 POS 打印机示例PosPrinter README展开系统讲解基于Windows.Devices.PointOfService.PosPrinterAPI 在 UWP 应用中完成收据打印机接入的完整链路从设备枚举查找、认领Claim与启用Enable到提交打印作业文本、位图、条码、处理切纸时序再到多应用竞争同一台打印机时的保留/释放仲裁。读者读完本文后将能够独立实现一个可用的 UWP 收据打印功能模块并理解 PointOfService 设备访问模型的核心机制。示例概览它演示了什么该示例是 UWP 功能示例大集合中的一员专门演示Windows.Devices.PointOfService.PosPrinterAPI 的典型用法。按 README 的说明它聚焦四个能力点查找Find发现系统上可用的 POS 打印机设备认领Claim与启用Enable独占式认领收据打印机并使其进入可用状态打印Print向已认领的收据打印机提交打印作业切纸保护Paper Cutter确保切纸器不会切到小票上已打印的内容竞争认领管理Competing Claims处理多客户端多个应用实例同时对同一台打印机发起认领的竞争场景。示例工程同时提供了C#Samples/PosPrinter/cs与C/CXSamples/PosPrinter/cpp两个实现版本两者的界面 XAML 是共享的Samples/PosPrinter/shared业务逻辑一一对应方便对照学习两种语言下 WinRT 异步 API 的写法差异。从代码结构看整个示例由一个共享的主页面状态容器 三个独立场景页面构成场景C# 实现C 实现共享 XAML演示内容场景一Scenario1_FindClaimEnable.xaml.csScenario1_FindClaimEnable.xaml.cppScenario1_FindClaimEnable.xaml查找、认领、启用收据打印机场景二Scenario2_PrintReceipt.xaml.csScenario2_PrintReceipt.xaml.cppScenario2_PrintReceipt.xaml打印文本、整张小票、位图与条码场景三Scenario3_MultipleClaims.xaml.csScenario3_MultipleClaims.xaml.cppScenario3_MultipleClaims.xaml对同一打印机发起第二次认领主页面C# 版 SampleConfiguration.cs、C 版 SampleConfiguration.cpp在MainPage上集中保存了跨场景共享的运行状态PosPrinter Printer、ClaimedPosPrinter ClaimedPrinter、DeviceInformation deviceInfo以及是否为重要交易标志IsAnImportantTransaction三个场景页通过它协调工作。系统要求与构建运行按 README 的约定示例的硬性环境要求是Windows 10可从清单文件进一步确认C# 与 C 的 Package.appxmanifest 均声明TargetDeviceFamily为Windows.UniversalMinVersion10.0.15063.0即最低支持 Windows 10 创意者更新。开发构建需要Visual Studio。构建步骤与仓库中其他 UWP 示例一致如果以 ZIP 方式下载了整个示例集合务必解压整个压缩包而不是只解压目标示例所在的文件夹——示例之间共享SharedContent等公共依赖解压不完整会导致编译失败启动 Visual Studio选择File → Open → Project/Solution进入示例的Samples\PosPrinter子目录再进入你偏好的语言子目录cs或cpp双击其中的解决方案文件PosPrinter.sln打开工程按CtrlShiftB或选择Build → Build Solution编译。运行阶段分两种情况仅部署选择Build → Deploy Solution部署并运行按F5或Debug → Start Debugging以调试方式运行按CtrlF5或Debug → Start Without Debugging则免调试运行。需要说明的是POS 打印机示例的完整功能需要在接入真实收据打印机或支持 PointOfService 协议的模拟设备的环境中验证仅靠模拟器无法观察到真实的打印与切纸行为。场景一查找、认领与启用收据打印机这是其余所有场景的前置条件界面说明Scenario1_FindClaimEnable.xaml明确写着该场景是其他场景的前提。页面提供五个控件Find receipt printer查找、Claim and enable认领并启用、Retain device复选框重要交易时保留设备、Release claim释放认领、Release printer释放打印机。第一步用 DevicePicker 查找设备查找过程使用系统级设备选择器DevicePicker而不是自行枚举设备列表// 选择一个 POS 打印机设备C#见 Scenario1_FindClaimEnable.xaml.cs 的 FindPrinter_Click DevicePicker devicePicker new DevicePicker(); devicePicker.Filter.SupportedDeviceSelectors.Add(PosPrinter.GetDeviceSelector()); // 将选择器锚定在 Find 按钮上使其弹出位置贴近按钮 GeneralTransform ge FindButton.TransformToVisual(Window.Current.Content as UIElement); Rect rect ge.TransformBounds(new Rect(0, 0, FindButton.ActualWidth, FindButton.ActualHeight)); DeviceInformation deviceInfo await devicePicker.PickSingleDeviceAsync(rect);关键 API 是PosPrinter.GetDeviceSelector()——它返回一个 AQSAdvanced Query Syntax选择器字符串只筛选出 POS 打印机类设备。拿到DeviceInformation.Id后通过PosPrinter.FromIdAsync(deviceInfo.Id)建立设备对象。值得注意的细节查找动作之前会先调用rootPage.ReleaseAllPrinters()释放上一次持有的打印机保证每次查找都从干净状态开始如果用户取消了选择器deviceInfo null则不会创建打印机对象。第二步校验设备能力示例并不直接接受任意 POS 设备而是先校验其能力if (printer ! null printer.Capabilities.Receipt.IsPrinterPresent) { rootPage.Printer printer; rootPage.NotifyUser(Found receipt printer., NotifyType.StatusMessage); } else { printer?.Dispose(); // 清理无法使用的设备对象 rootPage.NotifyUser(Please select a device whose printer is present., NotifyType.ErrorMessage); }Capabilities.Receipt.IsPrinterPresent是PosPrinterCapabilities中收据子模块的能力标志表明该设备确实带有一个收据打印单元。能力校验失败的设备会被立即Dispose()释放——这是 WinRT 设备对象的标准清理方式防止句柄泄漏。C/CX 版本中对应delete printer;见 Scenario1_FindClaimEnable.xaml.cpp。第三步认领并启用POS 打印机的访问模型是独占认领应用必须通过ClaimPrinterAsync()获得独占所有权后才能提交打印作业认领成功后还需EnableAsync()将设备置为可用状态rootPage.ClaimedPrinter await rootPage.Printer.ClaimPrinterAsync(); if (rootPage.ClaimedPrinter null) { rootPage.NotifyUser(Unable to claim printer, NotifyType.ErrorMessage); } else { // 注册 ReleaseDeviceRequested 事件以便在其他客户端想抢走打印机时得到通知 rootPage.SubscribeToReleaseDeviceRequested(); if (await rootPage.ClaimedPrinter.EnableAsync()) { rootPage.NotifyUser(Enabled printer., NotifyType.StatusMessage); } else { rootPage.NotifyUser(Could not enable printer, NotifyType.ErrorMessage); rootPage.ReleaseClaimedPrinter(); } }认领失败返回null或启用失败时示例都会向用户报错并在启用失败时主动释放认领避免残留无效状态。认领后的界面状态机场景一的UpdateButtons()方法C# 与 C 实现一致根据是否忙碌、是否已找到打印机、是否已认领三组状态动态启用/禁用五个按钮直观展示了认领状态流转状态FindClaim and enableRelease claimRelease printer忙碌中异步操作进行中禁用禁用禁用禁用未找到打印机启用禁用禁用禁用已找到、未认领禁用启用禁用启用已认领禁用禁用启用启用竞争通知ReleaseDeviceRequested 与保留设备认领成功后示例立即订阅ClaimedPosPrinter.ReleaseDeviceRequested事件见 SampleConfiguration.cs。该事件在其他客户端试图认领这台打印机时触发收到通知的持有者必须迅速决策保留或让出。// 如果勾选了 Retain device重要交易保留设备否则让出认领 private async void ClaimedPrinter_ReleaseDeviceRequested(ClaimedPosPrinter sender, PosPrinterReleaseDeviceRequestedEventArgs args) { if (IsAnImportantTransaction) { await sender.RetainDeviceAsync(); } else { await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () { NotifyUser(Lost printer claim., NotifyType.ErrorMessage); ReleaseClaimedPrinter(); }); } }这里体现了 POS 设备共享的核心仲裁机制RetainDeviceAsync()让当前持有者在指定时间内响应并保留设备适合正在打印重要交易小票的场景若持有者选择不保留则立即在 UI 线程上释放认领并提示Lost printer claim.。C/CX 版本通过create_task(sender-RetainDeviceAsync())与Dispatcher-RunAsync完成同样的逻辑SampleConfiguration.cpp。释放逻辑同样有讲究ReleaseClaimedPrinter()先退订ReleaseDeviceRequested事件再Dispose()并置空引用ReleaseAllPrinters()则在释放认领之后进一步Dispose()掉PosPrinter对象本身SampleConfiguration.cs。每次状态变化都会触发StateChanged事件驱动场景页刷新按钮状态。场景二向已认领的收据打印机提交打印作业场景二提供四种打印能力Scenario2_PrintReceipt.xaml打印单行文本、打印示例小票、打印位图Logo、打印 UPCA 条码。所有入口的第一步都是IsPrinterClaimed()守卫——若尚未认领打印机提示Use scenario 1 to find, claim, and enable a receipt printer.。打印的基本模型是从ClaimedPrinter.Receipt创建ReceiptPrintJob作业对象 → 向作业追加打印指令 → 调用ExecuteAsync()一次性执行。打印单行文本ReceiptPrintJob job rootPage.ClaimedPrinter.Receipt.CreateJob(); job.PrintLine(PrintLineTextBox.Text); await ExecuteJobAndReportResultAsync(job);PrintLine()打印一行文本并自动换行是最基础的指令。打印完整小票并处理切纸时序示例的打印示例小票演示了更接近真实收银场景的流程一次作业包含两份小票商户联与顾客联并且在切纸前补足足够的空行确保切纸器不会切到已打印的内容string receiptString \n | Sample Header |\n \n Item Price\n ----------------------\n Books 10.40\n Games 9.60\n ----------------------\n Total 20.00\n; ReceiptPrintJob job rootPage.ClaimedPrinter.Receipt.CreateJob(); PrintLineFeedAndCutPaper(job, receiptString GetMerchantFooter()); PrintLineFeedAndCutPaper(job, receiptString GetCustomerFooter()); await ExecuteJobAndReportResultAsync(job);切纸保护的实现是本节的核心技巧Scenario2_PrintReceipt.xaml.csprivate void PrintLineFeedAndCutPaper(ReceiptPrintJob job, string receipt) { // 将多行字符串一次性传给 Print比多次调用 PrintLine 走纸更平滑 string feedString ; for (uint n 0; n rootPage.ClaimedPrinter.Receipt.LinesToPaperCut; n) { feedString \n; } job.Print(receipt feedString); if (rootPage.Printer.Capabilities.Receipt.CanCutPaper) { job.CutPaper(); } }要点有三LinesToPaperCutClaimedReceiptPrinter暴露的、从最后一打印行到切纸器之间所需的空行数。示例按该值生成等量换行符把内容送过切纸器刀口后再执行切割多行字符串用Print()而非多次PrintLine()源码注释明确说明这样走纸更平滑CanCutPaper能力门控只有设备能力报告支持切纸时才调用CutPaper()避免对无切纸器的设备发送无效指令。打印位图打印位图前先开启信函质量模式再从应用包资源加载图片解码为BitmapFrame以居中对齐方式打印rootPage.ClaimedPrinter.Receipt.IsLetterQuality true; ReceiptPrintJob job rootPage.ClaimedPrinter.Receipt.CreateJob(); BitmapFrame logoFrame await LoadLogoBitmapAsync(); job.PrintBitmap(logoFrame, PosPrinterAlignment.Center); await ExecuteJobAndReportResultAsync(job);LoadLogoBitmapAsync()展示了从ms-appx:///Assets/coffee-logo.png读取应用内图片的完整链路StorageFile.GetFileFromApplicationUriAsync→OpenReadAsync→BitmapDecoder.CreateAsync→GetFrameAsync(0)取第一帧。打印条码job.PrintBarcode(BarcodeText.Text, BarcodeSymbologies.Upca, 60, 3, PosPrinterBarcodeTextPosition.Below, PosPrinterAlignment.Center);PrintBarcode()的参数依次为条码内容、条码符号体系此处为BarcodeSymbologies.UpcaUPC-A 标准、条码高度60、条码宽度3、可读文本位置Below表示打印在条码下方、对齐方式Center。场景界面中条码文本框默认内容为01234567891212 位 UPC-A 码。作业失败的诊断ExecuteJobAndReportResultAsync()在所有打印路径中共用。执行失败时它借助ClaimedReceiptPrinter的一组设备状态属性给出具体原因Scenario2_PrintReceipt.xaml.cs状态属性含义提示信息IsCartridgeEmpty色带/墨盒已空请更换墨盒IsCartridgeRemoved墨盒缺失请安装墨盒IsCoverOpen机盖打开请关闭机盖IsHeadCleaning打印头正在清洗请等待清洗完成IsPaperEmpty纸卷用尽请装入新纸卷以上皆否未知原因无法打印这份诊断清单直接复用了 WinRT 设备状态 API是生产级 POS 应用处理打印故障的常见做法。C/CX 版本Scenario2_PrintReceipt.xaml.cpp使用create_task(...).then(...)链式写法实现同样的异步流程逻辑完全一致。场景三管理同一台打印机的竞争认领场景三验证的是同一台打印机能否被第二个客户端认领这一竞争语义Scenario3_MultipleClaims.xaml.csusing (var printer await PosPrinter.FromIdAsync(rootPage.Printer.DeviceId)) { using (var claimedPrinter await printer.ClaimPrinterAsync()) { if (claimedPrinter ! null) { ClaimResultText.Text Claimed the printer.; } else { ClaimResultText.Text Did not claim the printer.; } } }该场景通过PosPrinter.FromIdAsync基于场景一已认领打印机的DeviceId重新创建第二个PosPrinter实例并尝试再次ClaimPrinterAsync()。认领的成败完全取决于场景一的决策若场景一勾选了Retain device重要交易场景一的持有者调用RetainDeviceAsync()保留设备场景三的第二次认领失败返回null若场景一未勾选持有者主动释放认领场景三的第二次认领成功。两个using语句块C 版通过 RAII 析构保证即使认领失败临时创建的PosPrinter与ClaimedPosPrinter对象也会被正确Dispose。这个场景直观演示了 PointOfService 的独占性设计同一时刻只有一方能持有打印机的有效认领且认领是否让渡由持有者的事件响应决定。关键实现细节与底层机制设备能力声明pointOfServiceC# 与 C 两份 Package.appxmanifest 都声明了访问 POS 设备所需的系统能力Capabilities Capability NameinternetClient / DeviceCapability NamepointOfService / /CapabilitiespointOfService设备能力是使用Windows.Devices.PointOfService命名空间下各类设备收据打印机、磁条阅读器、钱箱、条码扫描器等的前提缺失该声明会导致运行时无法访问设备。示例工程在MinVersion10.0.15063.0、MaxVersionTested10.0.22621.0的范围内测试通过。认领生命周期管理整个示例对设备对象的生命周期管理非常严格总结为三条原则认领前校验FromIdAsync之后先检查Capabilities.Receipt.IsPrinterPresent不符合即Dispose()认领后订阅ClaimPrinterAsync成功后立即SubscribeToReleaseDeviceRequested()订阅竞争通知确保任何时刻都能响应被抢占请求释放时退订ReleaseClaimedPrinter()先退订事件、再Dispose()、再置空引用并通知 UI 刷新杜绝悬挂事件与僵尸对象。跨语言对照示例的 C# 与 C/CX 版本在 API 调用上完全同构仅在异步写法上不同C# 使用async/awaitC/CX 使用create_task(...).then(...)任务链对象清理上 C# 用Dispose()配合usingC/CX 用delete。对照阅读两份源码如 C# 场景二 与 C 场景二是学习两种语言 WinRT 异步编程的良好教材。相关资源与延伸阅读历史版本本仓库的 archived/PosPrinter 目录保留了该示例的JavaScriptHTML/JS旧版实现README 中将其列为相关示例可作为对照参考API 参考示例围绕Windows.Devices.PointOfService命名空间展开核心类型包括PosPrinter、ClaimedPosPrinter、ClaimedReceiptPrinter、ReceiptPrintJob、PosPrinterCapabilities以及本示例中实际用到的PosPrinterBarcodeTextPosition、PosPrinterAlignment、BarcodeSymbologies等枚举构建环境整个 Windows-universal-samples 集合要求 Visual Studio 与 Windows 10 环境构建前需完整解压以获取共享依赖本示例依赖SharedContent目录下的公共模板与资源。小结PosPrinter 示例以三个递进场景完整覆盖了 UWP 收据打印的实战路径查找 → 认领 → 启用 → 打印文本/位图/条码→ 切纸保护 → 竞争仲裁。其中LinesToPaperCut驱动的切纸时序、ReleaseDeviceRequestedRetainDeviceAsync的让渡机制以及失败时的设备状态诊断都是真实收银系统中高频使用的工程模式。结合本仓库的 C# 与 C 双版本源码逐行研读即可将这套模式迁移到自己的 UWP 收银、自助终端或票据打印应用中。赞分享示例工程【免费下载链接】Windows-universal-samplesAPI samples for the Universal Windows Platform.项目地址https://gitcode.com/gh_mirrors/wi/Windows-universal-samples点击查看免费下载相关推荐Windows-universal-samples 之 Printing 示例UWP 应用打印支持完整实战指南Windows universal samples 之 Printing 示例UWP 应用打印支持完整实战指南 导读 本文以 Windows universa示例工程UWP 应用数据存储实战Windows-universal-samples 之 ApplicationData 示例深度解析UWP 应用数据存储实战Windows universal samples 之 ApplicationData 示例深度解析 导读 本文以 Windows u示例工程Windows-universal-samples 之 Compression 示例UWP 数据压缩与解压实战Windows universal samples 之 Compression 示例UWP 数据压缩与解压实战 导读 本文围绕 Windows univers示例工程上一篇Wazuh 漏洞扫描器 TC-005 测试用例深度解析软件包扫描、数据清理与全量重扫机制下一篇京东抢购助手终极指南免费开源工具实现自动化抢单创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
