1. 在没有报异常的情况下触控屏获取触摸失效。
在做触摸屏笔书写的时候,我们WPF可以获取到笔的压感值,今天测试的时候突然发现压感失效了,笔迹的书写压感效果没有了。然后一致怀疑书写组件配置的问题或是实现的问题。测试了好久 。
最后通过控制变量一步步注释代码,使用最简单Demo测试。最后发现在调用Wmi方法会触发InvalidCastException导致获取压感触摸失效。最后改为通过Cim获取Sn就没有问题了。此问题只有在.Net Core和.net版本有,在 .NET Framework 框架是没问题的。
System.InvalidCastException: 没有注册接口at MS.Win32.Penimc.UnsafeNativeMethods.CoCreateInstance(Guid& clsid, Object punkOuter, Int32 context, Guid& iid)at MS.Win32.Penimc.UnsafeNativeMethods.CreatePimcManager()
下面是测试Demo
using System.Diagnostics;
using System.Management;
using System.Text;
using System.Windows;
using System.Windows.Annotations;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using H3C.Board.Core;
using Microsoft.Management.Infrastructure;namespace WpfApp1
{/// <summary>/// Interaction logic for MainWindow.xaml/// </summary>public partial class MainWindow : Window{public MainWindow(){AppDomain.CurrentDomain.FirstChanceException += (sender, args) =>{Debug.WriteLine(args.Exception);};InitializeComponent();//WmiGetSerialNumberInfo();//立即Window初始化的时候调用Wmi会抛出FirstChanceException,Grid_StylusDown就不会触发了Loaded += MainWindow_Loaded;}private void MainWindow_Loaded(object sender, RoutedEventArgs e){WmiGetSerialNumberInfo(); //Loaded之后调用不会有问题,}private void Grid_StylusDown(object sender, StylusDownEventArgs e){var pressure = GetPressureValue(e);var point = e.GetPosition(this);Console.WriteLine($"开始记录压感 - 位置: {point}, 压感: {pressure:F4}");}private void Grid_StylusMove(object sender, StylusEventArgs e){var pressure = GetPressureValue(e);var point = e.GetPosition(this);// 实时显示压感值DisplayPressureInfo(point, pressure);}private void Grid_StylusUp(object sender, StylusEventArgs e){var pressure = GetPressureValue(e);var point = e.GetPosition(this);}private double GetPressureValue(StylusEventArgs e){try{var stylusPoints = e.GetStylusPoints(this);if (stylusPoints.Count > 0){return stylusPoints[0].PressureFactor;}}catch (Exception ex){Console.WriteLine($"获取压感值失败: {ex.Message}");}return 0.0;}private void DisplayPressureInfo(Point point, double pressure){// 在UI上显示压感信息Console.WriteLine($"实时压感 - X: {point.X:F1}, Y: {point.Y:F1}, Pressure: {pressure:F4}");// 可以更新UI控件显示压感值// 更新文本显示PressureDisplay.Text = $"压感: {pressure:F4}";// 更新可视化指示器PressureIndicator.Visibility = Visibility.Visible;Canvas.SetLeft(PressureIndicator, point.X - 10);Canvas.SetTop(PressureIndicator, point.Y - 10);// 根据压感调整指示器大小和颜色var size = 10 + (pressure * 30); // 压感越大,圆圈越大PressureIndicator.Width = size;PressureIndicator.Height = size;// 根据压感调整颜色var colorIntensity = (byte)(pressure * 255);PressureIndicator.Fill = new SolidColorBrush(Color.FromRgb(255, (byte)(255 - colorIntensity), (byte)(255 - colorIntensity)));}/// <summary>/// 使用Cim不会触摸失效/// </summary>/// <returns></returns>public static string? GetComputerSn(){try{// 创建本地 CIM 会话using var session = CimSession.Create(null);// 查询 Win32_BIOS 类var instances = session.QueryInstances(@"root\cimv2", "WQL", "SELECT SerialNumber FROM Win32_BIOS");foreach (var instance in instances){var snProperty = instance.CimInstanceProperties["SerialNumber"];if (snProperty?.Value == null) continue;string? sn = snProperty.Value.ToString()?.Trim();if (string.IsNullOrEmpty(sn)) continue;// 检查是否为无效的默认值(常见于一些 OEM 厂商)if (sn.Contains("O.E.M", StringComparison.OrdinalIgnoreCase) ||sn.Contains("OEM", StringComparison.OrdinalIgnoreCase) ||sn.Equals("Default", StringComparison.OrdinalIgnoreCase) ||sn.Equals("Default string", StringComparison.OrdinalIgnoreCase) ||sn.Equals("To be filled by O.E.M.", StringComparison.OrdinalIgnoreCase)){continue; // 跳过无效的序列号}return sn;}}catch{//}return string.Empty;}private void WmiGetSerialNumberInfo(){try{// 创建 WMI 查询ManagementObjectSearcher searcher = new("SELECT SerialNumber FROM Win32_BIOS");// 遍历查询结果foreach (ManagementObject obj in searcher.Get()){string serialNumber = obj["SerialNumber"]?.ToString();if (!string.IsNullOrEmpty(serialNumber)){Console.WriteLine($"BIOS 序列号: {serialNumber}");return;}}Console.WriteLine("未找到 BIOS 序列号信息");}catch (ManagementException ex){Console.WriteLine($"WMI 查询错误: {ex.Message}");}catch (Exception ex){Console.WriteLine($"WMI 查询异常错误: {ex.Message}");}}private void ButtonBase_OnClick(object sender, RoutedEventArgs e){WmiGetSerialNumberInfo();}}}
<Windowx:Class="WpfApp1.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:core="clr-namespace:H3C.Board.Core;assembly=H3C.Board.Core"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:local="clr-namespace:WpfApp1"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"Title="MainWindow"Width="1000"Height="800"Background="Transparent"ShowInTaskbar="False"Stylus.IsPressAndHoldEnabled="False"Stylus.IsTapFeedbackEnabled="False"WindowChrome.ResizeGripDirection="None"WindowStartupLocation="CenterScreen"WindowStyle="None"mc:Ignorable="d"><GridBackground="LightGray"StylusDown="Grid_StylusDown"StylusMove="Grid_StylusMove"StylusUp="Grid_StylusUp"><!-- 压感值显示 --><TextBlockx:Name="PressureDisplay"Margin="30"HorizontalAlignment="Left"VerticalAlignment="Top"FontSize="16"FontWeight="Bold"Foreground="Blue" /><!-- 压感可视化指示器 --><Ellipsex:Name="PressureIndicator"Width="20"Height="20"HorizontalAlignment="Left"VerticalAlignment="Top"Fill="Red"Visibility="Collapsed" /><ButtonWidth="30"Height="30"Click="ButtonBase_OnClick" /></Grid>
</Window>
.net8使用WMI可能还有一些意想不到的坑,建议都替换成CIMI。
此问题lindexi已经报告给 WPF 官方,请看 https://github.com/dotnet/wpf/issues/9752
2. 在.Net8发布增加 PublishTrimmed 裁剪选项,调用WMI 的ManagementObject 异常
下面是转自wutangyuan的博客:https://www.cnblogs.com/wuty/p/18931865
最近在做OTA的功能,需要获取到sn做一些业务的逻辑。我们自己实现的库里边的,大部分都是调用 System.Management 的 ManagementObjectSearcher 获取 Bios 的序列号
如下所示:
private void BtnWmi_OnClick(object sender, RoutedEventArgs e){try{// 创建 WMI 查询ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT SerialNumber FROM Win32_BIOS");
// 遍历查询结果foreach (ManagementObject obj in searcher.Get()){string serialNumber = obj["SerialNumber"]?.ToString();
if (!string.IsNullOrEmpty(serialNumber)){Console.WriteLine($"BIOS 序列号: {serialNumber}");return;}}Console.WriteLine("未找到 BIOS 序列号信息");}catch (ManagementException ex){Console.WriteLine($"WMI 查询错误: {ex.Message}");}catch (Exception ex){Console.WriteLine($"WMI 查询异常错误: {ex.Message}");}}
由于我们项目现在新建的项目都是基于.Net 8 开发,而且为了兼容多种设备和系统,我们目前的打包方式都是以发布独立部署的方式
同时为了能减少输出文件的大小,我们会启用裁剪的方式 <PublishTrimmed>true</PublishTrimmed>
<PublishTrimmed>true</PublishTrimmed><_SuppressWpfTrimError>true</_SuppressWpfTrimError><BuiltInComInteropSupport>true</BuiltInComInteropSupport><TrimMode>partial</TrimMode>
以上准备工作做好,发布以上的程序,运行发现如下的错误:
查询了官网的资料,有如下的说明:剪裁选项 - .NET | Microsoft Learn
就是启用裁剪会禁用掉某些框架的功能。
解决方法:
参考:使用 C# 远程连接到 WMI - Win32 apps | Microsoft Learn
使用 Microsoft.Management.Infrastructure 的 CimSession 替换WMI 早期的版本
如下所示:
using Microsoft.Management.Infrastructure;private void BtnCim_OnClick(object sender, RoutedEventArgs e){try{// 创建本地CIM会话using (var session = CimSession.Create(null)){// 查询Win32_BIOS类var instances = session.QueryInstances(@"root\cimv2", "WQL", "SELECT SerialNumber FROM Win32_BIOS");foreach (var instance in instances){var serialNumber = instance.CimInstanceProperties["SerialNumber"].Value?.ToString();Console.WriteLine($"BIOS 序列号: {serialNumber}");}}}catch (Exception ex){Console.WriteLine($"错误: {ex.Message}");}}
不修改发布选项的情况下,运行如下:是可以获取得到Bios的sn的
总结:
1、推荐使用 Microsoft.Management.Infrastructure 的 CimSession 替换 WMI 旧版的 ManagementObject
2、裁剪的选项 PublishTrimmed 如果不介意应用程序的一点体积,是可以忽略不加
代码Demo 链接:wutangyuan/wutyDemo: Demo代码备份