InvalidOperationException 类
TOC
折叠目录
展开目录
本文由机器翻译。若要查看英语原文,请勾选“英语”复选框。 也可将鼠标指针移到文本上,在弹出窗口中显示英语原文。
翻译
英语

InvalidOperationException 类

 

当方法调用对于对象的当前状态无效时引发的异常。

命名空间:   System
程序集:  mscorlib(mscorlib.dll 中)


[SerializableAttribute]
[ComVisibleAttribute(true)]
public class InvalidOperationException : SystemException

名称说明
System_CAPS_pubmethodInvalidOperationException()

初始化 InvalidOperationException 类的新实例。

System_CAPS_protmethodInvalidOperationException(SerializationInfo, StreamingContext)

用序列化数据初始化 InvalidOperationException 类的新实例。

System_CAPS_pubmethodInvalidOperationException(String)

用指定的错误消息初始化 InvalidOperationException 类的新实例。

System_CAPS_pubmethodInvalidOperationException(String, Exception)

使用指定的错误消息和对作为此异常原因的内部异常的引用来初始化 InvalidOperationException 类的新实例。

名称说明
System_CAPS_pubpropertyData

获取提供有关异常的其他用户定义信息的键/值对集合。(从 Exception 继承。)

System_CAPS_pubpropertyHelpLink

获取或设置指向与此异常关联的帮助文件链接。(从 Exception 继承。)

System_CAPS_pubpropertyHResult

获取或设置 HRESULT(一个分配给特定异常的编码数字值)。(从 Exception 继承。)

System_CAPS_pubpropertyInnerException

获取导致当前异常的 Exception 实例。(从 Exception 继承。)

System_CAPS_pubpropertyMessage

获取描述当前异常的消息。(从 Exception 继承。)

System_CAPS_pubpropertySource

获取或设置导致错误的应用程序或对象的名称。(从 Exception 继承。)

System_CAPS_pubpropertyStackTrace

获取调用堆栈上的即时框架字符串表示形式。(从 Exception 继承。)

System_CAPS_pubpropertyTargetSite

获取引发当前异常的方法。(从 Exception 继承。)

名称说明
System_CAPS_pubmethodEquals(Object)

确定指定的对象是否等于当前对象。(从 Object 继承。)

System_CAPS_protmethodFinalize()

在垃圾回收将某一对象回收前允许该对象尝试释放资源并执行其他清理操作。(从 Object 继承。)

System_CAPS_pubmethodGetBaseException()

当在派生类中重写时,返回 Exception,它是一个或多个并发的异常的根源。(从 Exception 继承。)

System_CAPS_pubmethodGetHashCode()

作为默认哈希函数。(从 Object 继承。)

System_CAPS_pubmethodGetObjectData(SerializationInfo, StreamingContext)

当在派生类中重写时,用关于异常的信息设置 SerializationInfo(从 Exception 继承。)

System_CAPS_pubmethodGetType()

获取当前实例的运行时类型。(从 Exception 继承。)

System_CAPS_protmethodMemberwiseClone()

创建当前 Object 的浅表副本。(从 Object 继承。)

System_CAPS_pubmethodToString()

创建并返回当前异常的字符串表示形式。(从 Exception 继承。)

名称说明
System_CAPS_proteventSerializeObjectState

当异常被序列化用来创建包含有关该异常的徐列出数据的异常状态对象时会出现该问题。(从 Exception 继承。)

InvalidOperationException 在情况下失败调用的方法由无效参数以外的原因时使用。 通常情况下,对象状态无法支持该方法调用时它将引发。 例如, InvalidOperationException 如由方法引发异常 ︰

System_CAPS_important重要事项

因为 InvalidOperationException 可能会引发异常在各种情况下,务必阅读异常消息通过返回 Message 属性。

本节内容:

InvalidOperationException 异常一些常见原因
      更新从非 UI 线程的 UI 线程
      更改循环访问它时收集
      强制转换为其基础类型为 null 的 Nullable < T > 
      对空集合调用了 System.Linq.Enumerable 方法
      如果没有一个元素序列调用后,Enumerable.Single 或 Enumerable.SingleOrDefault
      动态跨应用程序域字段访问权限
引发 InvalidOperationException 异常
杂项信息

下面几节介绍一些常见顺序中的情况下 InvalidOperationException 应用程序中引发异常。 如何处理问题取决于具体情况。 大多数情况下,但是,异常均由开发人员错误和 InvalidOperationException 可以预料并避免异常。

通常情况下,工作线程用于执行涉及收集数据,将在应用程序的用户界面中显示某些后台工作。 但是。 .NET Framework 中,如 Windows 窗体和 Windows Presentation Foundation (WPF) 的大多数 GUI (图形用户界面) 应用程序框架允许您仅从线程中创建和管理用户界面 (主数据连接或 UI 线程) 访问 GUI 对象。 InvalidOperationException 当您尝试从 UI 线程以外的线程访问 UI 元素时引发。 下表中显示异常消息的文本。

应用程序类型

消息

WPF 应用程序

调用线程无法访问此对象,因为另一个线程拥有它。

UWP 应用程序

应用程序为另一个线程调用封送的接口。

Windows 窗体应用程序

跨线程操作无效 ︰ 控件 TextBox1 在其创建的线程以外的线程访问。

.NET Framework 的 UI 框架实现 调度程序 模式,其中包括检查 UI 线程上是否正在执行对 UI 元素成员的调用的方法和其他用以计划对 UI 线程的调用的方法 ︰

  • 在 WPF 应用程序中调用 Dispatcher.CheckAccess 方法,以确定方法是否正在非 UI 线程上运行。 它将返回 true 方法是否在 UI 线程上运行和 false 否则为。 调用的重载之一 Dispatcher.Invoke 方法来计划在 UI 线程上的调用。

  • UWP 应用程序中调用 CoreDispatcher.HasThreadAccess 方法,以确定方法是否正在非 UI 线程上运行。 调用 CoreDispatcher.RunAsync 方法来执行更新 UI 线程的委托。

  • 在 Windows 窗体应用程序使用 Control.InvokeRequired 属性来确定方法是否正在非 UI 线程上运行。 调用的重载之一 Control.Invoke 方法来执行更新 UI 线程的委托。

以下示例说明了 InvalidOperationException 当你尝试更新从创建它的线程以外的线程的 UI 元素时引发的异常。 每个示例,您需要创建两个控件 ︰

  • 名为一个文本框控件 textBox1 在 Windows 窗体应用中,应设置其 Multiline 属性设置为 true

  • 一个名为的按钮控件 threadExampleBtn 本示例提供了一个处理程序, ThreadsExampleBtn_Click, ,为该按钮的 Click 事件。

每种情况下, threadExampleBtn_Click 事件处理程序调用 DoSomeWork 方法两次。 第一次调用以同步方式运行,并成功。 但第二次调用,因为它以异步方式运行,线程池线程上试图从非 UI 线程更新 UI。 这会导致 InvalidOperationException 异常。

WPF 和 UWP 应用
private async void threadExampleBtn_Click(object sender, RoutedEventArgs e)
{
    textBox1.Text = String.Empty;

    textBox1.Text = "Simulating work on UI thread.\n";
    DoSomeWork(20);
    textBox1.Text += "Work completed...\n";

    textBox1.Text += "Simulating work on non-UI thread.\n";
    await Task.Run( () => DoSomeWork(1000));
    textBox1.Text += "Work completed...\n";
}

private async void DoSomeWork(int milliseconds)
{
    // Simulate work.
    await Task.Delay(milliseconds);

    // Report completion.
    var msg = String.Format("Some work completed in {0} ms.\n", milliseconds);
    textBox1.Text += msg;
}

以下版本的 DoSomeWork 方法消除了 WPF 应用程序中的异常。

private async void DoSomeWork(int milliseconds)
{
    // Simulate work.
    await Task.Delay(milliseconds);

    // Report completion.
    bool uiAccess = textBox1.Dispatcher.CheckAccess();
    String msg = String.Format("Some work completed in {0} ms. on {1}UI thread\n",
                               milliseconds, uiAccess ? String.Empty : "non-");
    if (uiAccess)
        textBox1.Text += msg;
    else
        textBox1.Dispatcher.Invoke(() => { textBox1.Text += msg; });
}

以下版本的 DoSomeWork 方法消除了 UWP 应用程序中的异常。

private async void DoSomeWork(int milliseconds)
{
    // Simulate work.
    await Task.Delay(milliseconds);

    // Report completion.
    bool uiAccess = textBox1.Dispatcher.HasThreadAccess;
    String msg = String.Format("Some work completed in {0} ms. on {1}UI thread\n",
                               milliseconds, uiAccess ? String.Empty : "non-");
    if (uiAccess)
        textBox1.Text += msg;
    else
        await textBox1.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { textBox1.Text += msg; });
}
Windows 窗体应用
List<String> lines = new List<String>();

private async void threadExampleBtn_Click(object sender, EventArgs e)
{
    textBox1.Text = String.Empty;
    lines.Clear();

    lines.Add("Simulating work on UI thread.");
    textBox1.Lines = lines.ToArray();
    DoSomeWork(20);

    lines.Add("Simulating work on non-UI thread.");
    textBox1.Lines = lines.ToArray();
    await Task.Run(() => DoSomeWork(1000));

    lines.Add("ThreadsExampleBtn_Click completes. ");
    textBox1.Lines = lines.ToArray();
}

private async void DoSomeWork(int milliseconds)
{
    // simulate work
    await Task.Delay(milliseconds);

    // report completion
    lines.Add(String.Format("Some work completed in {0} ms on UI thread.", milliseconds));
    textBox1.Lines = lines.ToArray();
}

以下版本的 DoSomeWork 方法消除了在 Windows 窗体应用程序中的异常。

private async void DoSomeWork(int milliseconds)
{
    // simulate work
    await Task.Delay(milliseconds);

    // Report completion.
    bool uiMarshal = textBox1.InvokeRequired;
    String msg = String.Format("Some work completed in {0} ms. on {1}UI thread\n",
                               milliseconds, uiMarshal ? String.Empty : "non-");
    lines.Add(msg);

    if (uiMarshal) {
        textBox1.Invoke(new Action(() => { textBox1.Lines = lines.ToArray(); }));
    }
    else {
        textBox1.Lines = lines.ToArray();
    }
}

foreach C# 中的语句或 For Each 使用在 Visual Basic 中的语句循环访问集合的成员以读取或修改其自身的元素。 但是,它不能用于添加或从集合中移除项。 这样做将引发 InvalidOperationException 异常相似,一条消息"集合已修改;可能无法执行枚举操作。"

下面的示例循环的整数集合尝试向集合添加每个整数的平方。 本示例将引发 InvalidOperationException 与首次调用 List<T>.Add 方法。

using System;
using System.Collections.Generic;

public class Example
{
   public static void Main()
   {
      var numbers = new List<int>() { 1, 2, 3, 4, 5 };
      foreach (var number in numbers) {
         int square = (int) Math.Pow(number, 2);
         Console.WriteLine("{0}^{1}", number, square);
         Console.WriteLine("Adding {0} to the collection...\n", square);
         numbers.Add(square);
      }
   }
}
// The example displays the following output:
//    1^1
//    Adding 1 to the collection...
//    
//    
//    Unhandled Exception: System.InvalidOperationException: Collection was modified; 
//       enumeration operation may not execute.
//       at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
//       at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
//       at Example.Main()

具体取决于应用程序逻辑,可以消除两种方式之一中的异常 ︰

  • 如果必须将元素添加到集合中循环访问它时,可以循环它访问的索引使用 for 语句而不是 foreachFor Each 下面的示例使用语句添加到集合的集合中的数字的平方。

    using System;
    using System.Collections.Generic;
    
    public class Example
    {
       public static void Main()
       {
          var numbers = new List<int>() { 1, 2, 3, 4, 5 };
    
          int upperBound = numbers.Count - 1;
          for (int ctr = 0; ctr <= upperBound; ctr++) {
             int square = (int) Math.Pow(numbers[ctr], 2);
             Console.WriteLine("{0}^{1}", numbers[ctr], square);
             Console.WriteLine("Adding {0} to the collection...\n", square);
             numbers.Add(square);
          }
    
          Console.WriteLine("Elements now in the collection: ");
          foreach (var number in numbers)
             Console.Write("{0}    ", number);
       }
    }
    // The example displays the following output:
    //    1^1
    //    Adding 1 to the collection...
    //    
    //    2^4
    //    Adding 4 to the collection...
    //    
    //    3^9
    //    Adding 9 to the collection...
    //    
    //    4^16
    //    Adding 16 to the collection...
    //    
    //    5^25
    //    Adding 25 to the collection...
    //    
    //    Elements now in the collection:
    //    1    2    3    4    5    1    4    9    16    25
    

    请注意在循环访问集合可以通过将退出循环 appropraitely,通过向后循环的循环内的计数器之前,必须建立的迭代数从 Count -1 到 0,或该示例一样,通过分配给变量的数组中的元素数并使用它来建立循环的上限。 否则,如果一个元素添加到每个迭代上的集合,产生无限循环。

  • 如果不需要将元素添加到集合中循环访问它时,你可以存储临时集合中添加循环访问集合来完成时要添加的元素。 下面的示例使用此方法添加到临时的集合,集合中的数字的平方,然后将合并到单个数组对象的集合。

    using System;
    using System.Collections.Generic;
    
    public class Example
    {
       public static void Main()
       {
          var numbers = new List<int>() { 1, 2, 3, 4, 5 };
          var temp = new List<int>();
    
          // Square each number and store it in a temporary collection.
          foreach (var number in numbers) {
             int square = (int) Math.Pow(number, 2);
             temp.Add(square);
          }
    
          // Combine the numbers into a single array.
          int[] combined = new int[numbers.Count + temp.Count];
          Array.Copy(numbers.ToArray(), 0, combined, 0, numbers.Count);
          Array.Copy(temp.ToArray(), 0, combined, numbers.Count, temp.Count);
    
          // Iterate the array.
          foreach (var value in combined)
             Console.Write("{0}    ", value);
       }
    }
    // The example displays the following output:
    //       1    2    3    4    5    1    4    9    16    25
    

尝试进行强制转换 Nullable<T> 值,该值是 null 为其基础类型将引发 InvalidOperationException 异常并显示错误消息,"Null 的对象必须具有值。

下面的示例引发 InvalidOperationException 异常在尝试循环访问数组时,包括 Nullable(Of Integer) 值。

using System;
using System.Linq;

public class Example
{
   public static void Main()
   {
      var queryResult = new int?[] { 1, 2, null, 4 };
      var map = queryResult.Select(nullableInt => (int)nullableInt);

      // Display list.
      foreach (var num in map)
         Console.Write("{0} ", num);
      Console.WriteLine();   
   }
}
// The example displays the following output:
//    1 2
//    Unhandled Exception: System.InvalidOperationException: Nullable object must have a value.
//       at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
//       at Example.<Main>b__0(Nullable`1 nullableInt)
//       at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext()
//       at Example.Main()

要避免此异常 ︰

以下示例执行这两个避免 InvalidOperationException 异常。

using System;
using System.Linq;

public class Example
{
   public static void Main()
   {
      var queryResult = new int?[] { 1, 2, null, 4 };
      var numbers = queryResult.Select(nullableInt => (int)nullableInt.GetValueOrDefault());

      // Display list using Nullable<int>.HasValue.
      foreach (var number in numbers)
         Console.Write("{0} ", number);
      Console.WriteLine();   

      numbers = queryResult.Select(nullableInt => (int) (nullableInt.HasValue ? nullableInt : -1));
      // Display list using Nullable<int>.GetValueOrDefault.
      foreach (var number in numbers)
         Console.Write("{0} ", number);
      Console.WriteLine();   
   }
}
// The example displays the following output:
//       1 2 0 4
//       1 2 -1 4

Enumerable.Aggregate<TSource>, ,Enumerable.Average, ,Enumerable.First<TSource>, ,Enumerable.Last<TSource>, ,Enumerable.Max, ,Enumerable.Min, ,Enumerable.Single<TSource>, ,和 Enumerable.SingleOrDefault<TSource> 方法对序列执行操作并返回单个结果。 这些方法中的一些重载会引发 InvalidOperationException 异常序列为空,而其他重载返回时 null Enumerable.SingleOrDefault<TSource> 方法还会抛出 InvalidOperationException 序列中不包含多个元素时出现异常。

System_CAPS_note注意

引发的方法的大多数 InvalidOperationException 异常是重载。 请确保您了解您选择的重载的行为。

下表列出了来自的异常消息 InvalidOperationException 通过调用某些会引发的异常对象 System.Linq.Enumerable 方法。

方法

消息

Aggregate
Average
 Last
 Max
Min 

序列不包含任何元素

First

序列中不包含任何匹配元素

Single
SingleOrDefault

序列包含多个匹配元素

消除或处理的异常的方式取决于您的应用程序的假设以及您调用的特定方法。

示例提供了更多详细信息。

下面的示例使用 Enumerable.Average 方法来计算一个序列,其值是大于 4 的平均值。 由于原始数组中的任何值超过 4、 没有值包含在序列中,并且该方法将引发 InvalidOperationException 异常。

using System;
using System.Linq;

public class Example
{
   public static void Main()
   {
      int[] data = { 1, 2, 3, 4 };
      var average = data.Where(num => num > 4).Average();
      Console.Write("The average of numbers greater than 4 is {0}",
                    average);
   }
}
// The example displays the following output:
//    Unhandled Exception: System.InvalidOperationException: Sequence contains no elements
//       at System.Linq.Enumerable.Average(IEnumerable`1 source)
//       at Example.Main()

可通过调用来消除异常 Any<TSource> 方法,以确定是否序列不包含任何元素之前调用的方法处理序列中的,如以下示例所示。

using System;
using System.Linq;

public class Example
{
   public static void Main()
   {
       int[] dbQueryResults = { 1, 2, 3, 4 };
       var moreThan4 = dbQueryResults.Where(num => num > 4);

       if(moreThan4.Any())
           Console.WriteLine("Average value of numbers greater than 4: {0}:", 
                             moreThan4.Average());
       else
           // handle empty collection 
           Console.WriteLine("The dataset has no values greater than 4.");
   }
}
// The example displays the following output:
//       The dataset has no values greater than 4.

Enumerable.First<TSource> 方法返回的第一项中的序列或序列中满足指定的条件的第一个元素。 如果序列为空,因此不会将第一个元素,则会引发 InvalidOperationException 异常。

在下面的示例中, Enumerable.First<TSource>(IEnumerable<TSource>, Func<TSource, Boolean>) 方法抛出异常 InvalidOperationException 异常因为 dbQueryResults 数组不包含大于 4 的元素。

using System;
using System.Linq;

public class Example
{
   public static void Main()
   {
      int[] dbQueryResults = { 1, 2, 3, 4 };

      var firstNum = dbQueryResults.First(n => n > 4);

      Console.WriteLine("The first value greater than 4 is {0}", 
                        firstNum);
   }
}
// The example displays the following output:
//    Unhandled Exception: System.InvalidOperationException: 
//       Sequence contains no matching element
//       at System.Linq.Enumerable.First[TSource](IEnumerable`1 source, Func`2 predicate)
//       at Example.Main()

您可以调用 Enumerable.FirstOrDefault<TSource> 方法而不是 Enumerable.First<TSource> 以返回特定值或默认值。 如果该方法在序列中未找到第一个元素,则返回该数据类型的默认值。 默认值是 null 对于引用类型,零对于数值数据类型,和 DateTime.MinValueDateTime 类型。

System_CAPS_note注意

解释返回的值 Enumerable.FirstOrDefault<TSource> 类型的默认值可以是有效的值序列中的事实通常比较复杂方法。 在此情况下,您调用 Enumerable.Any<TSource>方法,以确定序列是否具有有效成员之前调用 Enumerable.First<TSource> 方法。

下面的示例调用 Enumerable.FirstOrDefault<TSource>(IEnumerable<TSource>, Func<TSource, Boolean>) 方法,以阻止 InvalidOperationException 在前面的示例引发异常。

using System;
using System.Linq;

public class Example
{
   public static void Main()
   {
      int[] dbQueryResults = { 1, 2, 3, 4 };

      var firstNum = dbQueryResults.FirstOrDefault(n => n > 4);

      if (firstNum == 0)
         Console.WriteLine("No value is greater than 4.");
      else   
         Console.WriteLine("The first value greater than 4 is {0}", 
                           firstNum);
   }
}
// The example displays the following output:
//       No value is greater than 4.

Enumerable.Single<TSource> 方法将返回一个序列的唯一元素或序列中满足指定的条件的唯一元素。 如果在序列中,没有任何元素,或者如果多个元素,该方法将引发 InvalidOperationException 异常。

您可以使用 Enumerable.SingleOrDefault<TSource> 方法以返回默认值而不是序列中不包含任何元素时引发异常。 但是, Enumerable.SingleOrDefault<TSource> 方法仍会引发 InvalidOperationException 序列中不包含多个元素时出现异常。

下表列出了来自的异常消息 InvalidOperationException 通过调用引发的异常对象 Enumerable.Single<TSource>Enumerable.SingleOrDefault<TSource> 方法。

方法

消息

Single 

序列中不包含任何匹配元素

Single
SingleOrDefault

序列包含多个匹配元素

在下面的示例中,对调用 Enumerable.Single<TSource> 方法抛出异常 InvalidOperationException 异常因为序列没有大于 4 的元素。

using System;
using System.Linq;

public class Example
{
   public static void Main()
   {
       int[] dbQueryResults = { 1, 2, 3, 4 };

       var singleObject = dbQueryResults.Single(value => value > 4);

       // Display results.
       Console.WriteLine("{0} is the only value greater than 4", singleObject);
   }
}
// The example displays the following output:
//    Unhandled Exception: System.InvalidOperationException: 
//       Sequence contains no matching element
//       at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source, Func`2 predicate)
//       at Example.Main()

下面的示例尝试阻止 InvalidOperationException 通过改为调用序列为空时引发的异常 Enumerable.SingleOrDefault<TSource> 方法。 但是,因为此序列将返回其值大于 2 的多个元素,它还会抛出 InvalidOperationException 异常。

using System;
using System.Linq;

public class Example
{
   public static void Main()
   {
       int[] dbQueryResults = { 1, 2, 3, 4 };

       var singleObject = dbQueryResults.SingleOrDefault(value => value > 2);

       if (singleObject != 0)
           Console.WriteLine("{0} is the only value greater than 2", 
                             singleObject);
       else
           // Handle an empty collection.
           Console.WriteLine("No value is greater than 2");
   }
}
// The example displays the following output:
//    Unhandled Exception: System.InvalidOperationException: 
//       Sequence contains more than one matching element
//       at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source, Func`2 predicate)
//       at Example.Main()

调用 Enumerable.Single<TSource> 方法假定的序列或满足指定的条件的序列包含仅有一个元素。 Enumerable.SingleOrDefault<TSource> 假定具有零个或一个结果,但没有更多的序列。 如果这种假定是一个故意对您来说并不满足这些条件,重新引发或捕捉生成 InvalidOperationException 适用。 否则为或者如果你预计这些无效的条件将以某种频率发生,应考虑使用其他一些 Enumerable 方法,如 FirstOrDefault<TSource>Where<TSource>

OpCodes.Ldflda Microsoft 中间语言 (MSIL) 指令将引发 InvalidOperationException 如果包含您尝试检索其地址的字段的对象不在执行代码时的应用程序域内的异常。 只能从它所在的应用程序域访问字段的地址。

您应该引发 InvalidOperationException 异常仅在由于某种原因您对象的状态不支持特定方法调用时。 即方法调用是在某些情况下或上下文中,有效,但在其他无效。

如果方法调用失败是由于无效的参数,则 ArgumentException 或其派生类之一 ArgumentNullExceptionArgumentOutOfRangeException, ,应改为引发。

InvalidOperationException 使用 HRESULT 返回 COR_E_INVALIDOPERATION,其值为 0x80131509。

有关实例的初始属性值的列表 InvalidOperationException, ,请参阅 InvalidOperationException 构造函数。

Universal Windows Platform
8 后可用
.NET Framework
1.1 后可用
Portable Class Library
受以下版本支持:portable .NET platforms
Silverlight
2.0 后可用
Windows Phone Silverlight
7.0 后可用
Windows Phone
8.1 后可用

此类型的任何公共静态(Visual Basic 中为 Shared)成员都是线程安全的。但不保证所有实例成员都是线程安全的。

返回页首
显示:
© 2016 Microsoft