【问题标题】:Exception thrown: 'System.InvalidOperationException' in WindowsBase.dll抛出异常:WindowsBase.dll 中的“System.InvalidOperationException”
【发布时间】:2018-05-06 02:44:53
【问题描述】:

在我的 WPF UI 中更新某些内容时遇到问题。抛出异常的同一部分代码在另一个函数中工作得很好。现在,由于某种原因,我无法弄清楚它不仅抛出异常,而且也没有像我想要的那样更新。我希望 OnCPUDetEvent() 函数中的 UI 元素根据我设置的计时器进行更新。

这是我的代码:

using System;
using System.Collections.Generic;
using System.Management.Instrumentation;
using System.Management;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using System.Runtime.Serialization;
using System.Windows;
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;

namespace HWDetCS
{
    /// <summary>
    /// Interaction logic for CPUBase.xaml
    /// </summary>
    public partial class CPUBase : Page
    {

        // lists are better than arrays, fite me!
        public List<string> names = new List<string>();
        public List<string> values = new List<string>();
        public int i = 0;

        // Set up a timer to be enabled later
        public Timer CPUDetRefreshTimer;


        public CPUBase()
        {
            // Auto generated stuff, don't touch!
            InitializeComponent();

            // Actually run all the detection stuff
            CPUDet();

            // Start up the Timer, and get it ready
            CPUDetRefreshTimer = new Timer();
            CPUDetRefreshTimer.AutoReset = true;
            CPUDetRefreshTimer.Interval = 500;
            CPUDetRefreshTimer.Enabled = true;
            CPUDetRefreshTimer.Elapsed += OnCPUDetEvent;

        }

        // This thing does all the work
        public void CPUDet()
        {
            // Get the CPU Management class, this makes it the CPU we get info off of rather than nothing, because if it wasnt set to the CPU, it would error and break and cry a lot... dont change it.
            ManagementClass CPUClass = new ManagementClass("Win32_Processor");
            CPUClass.Options.UseAmendedQualifiers = true;

            // Set up a data collection to get the data off of, this and the next thing SHOULD NEVER BE IN A LOOP! IT WILL BREAK YOUR CPU LIKE A BALLOON!
            PropertyDataCollection dataCollection = CPUClass.Properties;

            // Get the instance of the class, for some reason this is required to work, dont touch AND DONT PUT IT IN A LOOP WHY CANT YOU LISTEN!?
            ManagementObjectCollection instanceCollection = CPUClass.GetInstances();



            // This is a loop, its very fragile, dont touch it, it gets the list of data we are collecting
            foreach (PropertyData property in dataCollection)
            {

                // adds the names into one nice readable-ish list!
                names.Add(property.Name);

                // loop through all the instances and grabs the actual data off of it
                foreach (ManagementObject instance in instanceCollection)
                {
                    // makes sure we dont get null reference errors, I HATE THOSE SO MUCH! I KNOW ITS NULL JUST SHUT UP!
                    if (instance.Properties[property.Name.ToString()].Value == null)
                    {
                        // if its null, dont add the actual property data, INSTEAD, add a string that says null so we know not to mess with it
                        values.Add("null");
                    }
                    else
                    {
                        // otherwise, go right ahead
                        values.Add(instance.Properties[property.Name.ToString()].Value.ToString());
                    }
                }
                // counting....
                i++;

            }



            // Debug stuff, dont release uncommented!
            // TODO: COMMENT THIS OUT!
            for (int x = 0; x < names.Count - 1; x++)
            {
                Console.WriteLine(x.ToString());
                Console.WriteLine(names[x]);
                Console.WriteLine(values[x]);
            }

            // Get the name
            CPUNameText.Content = values[29];
            // Get the manufacturer
            CPUManuText.Content = values[27];
            // Get the number of CORES (NOT THREADS!)
            CPUCoreCountText.Content = values[30];
            // Get the Family
            CPUFamilyText.Content = values[18];

        }

        public void OnCPUDetEvent(Object obj, ElapsedEventArgs args)
        {
            //Console.WriteLine("Event Fire!");

            // Get the current clock speed
            CPUClockSpeedText.Content = values[10] + "MHz";
            // Get the current Voltage
            CPUCVoltageText.Content = (Convert.ToDouble(values[11]) / 10).ToString() + " Volts";
        }
    }


}

这是实际 UI 页面的 XAML:

<Page x:Class="HWDetCS.CPUBase"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      xmlns:local="clr-namespace:HWDetCS"
      mc:Ignorable="d" 
      d:DesignHeight="450" d:DesignWidth="800"
      Title="CPUBase" MinWidth="1280" MinHeight="720">

    <Grid Background="Gray">
        <DockPanel>
            <UniformGrid Rows="6" Columns="2">
                <Label x:Name="CPUNameLabel" Content="CPU Name:" FontSize="36" FontWeight="Bold" Foreground="#FF007ACC" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"/>
                <Label x:Name="CPUNameText" Content="Label" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontSize="24" FontWeight="Bold" Foreground="#FF007ACC"/>
                <Label x:Name="CPUManuLabel" Content="CPU Manufacturer:" FontSize="36" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontWeight="Bold" Foreground="#FF007ACC"/>
                <Label x:Name="CPUManuText" Content="Label" Foreground="#FF007ACC" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontSize="24" FontWeight="Bold"/>
                <Label x:Name="CPUClockSpeedLabel" Content="CPU Clock Speed:" Foreground="#FF007ACC" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontSize="36" FontWeight="Bold"/>
                <Label x:Name="CPUClockSpeedText" Content="Label" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontSize="24" FontWeight="Bold" Foreground="#FF007ACC"/>
                <Label x:Name="CPUCoreCountLabel" Content="CPU Core Count:" Foreground="#FF007ACC" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontSize="36" FontWeight="Bold"/>
                <Label x:Name="CPUCoreCountText" Content="Label" Foreground="#FF007ACC" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontSize="24" FontWeight="Bold"/>
                <Label x:Name="CPUFamilyLabel" Content="CPU Family:" FontSize="36" FontWeight="Bold" Foreground="#FF007ACC" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"/>
                <Label x:Name="CPUFamilyText" Content="Label" Foreground="#FF007ACC" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontSize="24" FontWeight="Bold"/>
                <Label x:Name="CPUCVoltageLabel" Content="CPU Current Voltage:" FontSize="36" FontWeight="Bold" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Foreground="#FF007ACC"/>
                <Label x:Name="CPUCVoltageText" Content="Label" Foreground="#FF007ACC" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontSize="24" FontWeight="Bold"/>
            </UniformGrid>
        </DockPanel>
    </Grid>
</Page>

【问题讨论】:

  • 您应该添加一个 try-catch 块以查看实际发生的情况。我们无法仅通过 InvalidOperationException 为您提供帮助
  • 也提供window.xaml。如果您在 app.xaml 中有特定的说明,请描述或添加。
  • 并在此处发表评论以通知我们
  • 我在抛出异常的 2 行(如果包含 cmets 为 4 行)上使用了 try-catch 块,它所做的只是我在 Visual Studio 的输出窗口中收到的垃圾邮件数量的两倍
  • >> "引发异常的同一部分代码在另一个函数中工作得很好。"我认为我们无法分辨出代码的哪一部分正在抛出以及哪个函数对您来说可以正常工作。请详细说明。此外,请考虑共享异常详细信息,包括堆栈、消息任何内部异常。

标签: c# .net wpf xaml


【解决方案1】:

您收到的错误是“拒绝访问”错误的结果。您的函数在与 GUI 本身不同的线程中开始其生命周期,因此不允许更改 GUI 元素。

在这个链接中有描述:A method running on a non-UI thread updates the UI

以上链接提供了逃避问题的方法。但是,您的问题没有单一的解决方案。以下只是其中之一。它会逐步进行您需要应用的更改。 (编辑:我在下一个答案中添加了另一个解决方案)


using System.ComponentModel; 添加到您的标题中。这是使用具有更改属性的绑定。如果需要,添加参考 dll 文件。

现在为你的班级使用这个标题:

public partial class CPUBase : Page,INotifyPropertyChanged

下一步是添加通知器。代码原样用于.NET v4.0,其他版本使用方法请咨询INotifyPropertyChanged Interface

public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
    if (PropertyChanged != null)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(info));
    }
}

现在创建两个属性字段来保存您不断变化的值。其他版本请参考以上链接。

string speed;
public string Speed
{
    get
    {
        return speed;
    }
    set
    {
        speed= value;
        NotifyPropertyChanged(nameof(Speed));
    }
}
string volt;
public string Volt
{
    get
    {
        return volt;
    }
    set
    {
        volt = value;
        NotifyPropertyChanged(nameof(Volt));
    }
}

现在剩下的就容易多了。只需在函数中更改这些属性(额外的i 是为了显示它正在工作,因为值不会立即显示更改)(编辑:添加工作但我认为代码不会激活 values,我尝试降低CPU频率,结果没有改变)

Speed = values[10] + "MHz"+i;
Volt = (Convert.ToDouble(values[11]) / 10).ToString() + " Volts"+i;
i++;

现在通过绑定到这些属性为您的页面命名并更新标签。

<Page x:Name="CPUBaseMain ...
...
<Label x:Name="CPUClockSpeedText" Content="{Binding Speed, ElementName=CPUBaseMain}" ...
<Label x:Name="CPUCVoltageText" Content="{Binding Volt, ElementName=CPUBaseMain}" ...

【讨论】:

  • @JustMATT,如果这个答案解决了你的问题并且没有其他人给出另一个答案,请你将其标记为接受吗?
  • 哇,谢谢,我一定要试试这个,一会儿就回来!
  • @JustMATT, NotifyPropertyChanged 方法在 .NET v4.5 上略有不同,所以如果您在项目中使用该链接或任何其他版本,请参阅我提供的链接。
  • 效果很好,谢谢,现在我只需要找到一种方法来更新我的值列表而不破坏我的 CPU
  • @JustMATT,很高兴知道它适合您的需求。快乐编码:)
【解决方案2】:

在我之前的回答中,我提到这个问题没有单一的解决方案。这是另一个比前一个简单得多的。请记住,每种不同的解决方案都有其优点和缺点。


此解决方案使用 Windows 类本身的 Threading 方法。子进程使用调度程序方法与 GUI 通信,否则系统不允许这样做。调度程序用于获取/设置外部方法中 GUI 元素的属性。缺点是您需要为您所做的每一个单独/独立工作编写一个新的调度程序行。

解决方法很简单:

首先,为Windows类的Threading方法添加命名空间。

using System.Windows.Threading;

然后,创建一个Action。为此,只需将与 GUI 相关的 getter/setter 代码片段包装在新的 Action 定义中。此操作可以在您的方法可以访问的任何地方以及任何地方定义。

Action dispatcherAction = new Action(() =>
  {
    // Get the current clock speed
    CPUClockSpeedText.Content = values[10] + "MHz";
    // Get the current Voltage
    CPUCVoltageText.Content = (Convert.ToDouble(values[11]) / 10).ToString() + " Volts";
  });

最后,在OnCPUDetEventmethod 中发送您的操作。就是这样。

this.Dispatcher.Invoke(dispatcherAction, DispatcherPriority.Normal);

如果需要调度的代码只有几行,那么就不需要在外部创建新的操作。但是通过这样做,您将操作绑定到调用它的方法。

this.Dispatcher.Invoke(
    new Action (() =>
        {
            // Get the current clock speed
            CPUClockSpeedText.Content = values[10] + "MHz";
            // Get the current Voltage
            CPUCVoltageText.Content = (Convert.ToDouble(values[11]) / 10).ToString() + " Volts";
        }),
    DispatcherPriority.Normal                
    );

【讨论】:

    猜你喜欢
    • 2019-11-20
    • 2020-12-21
    • 2013-05-24
    • 2018-06-08
    • 1970-01-01
    • 2015-11-07
    • 2018-03-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多