{"id":108477,"date":"2023-07-24T07:00:00","date_gmt":"2023-07-24T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=108477"},"modified":"2023-07-24T06:49:48","modified_gmt":"2023-07-24T13:49:48","slug":"20230724-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230724-00\/?p=108477","title":{"rendered":"Why does <CODE>IAsyncAction<\/CODE> or <CODE>IAsyncOperation.<WBR>GetResults()<\/CODE> produce a <CODE>E_<WBR>ILLEGAL_<WBR>METHOD_<WBR>CALL<\/CODE> exception?"},"content":{"rendered":"<p>For expository purposes, let&#8217;s look at the code we wrote some time ago which <a title=\"Obtaining network usage information from the Windows Runtime\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210520-00\/?p=105232\"> obtains network usage information<\/a>.<\/p>\n<pre>using System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Threading.Tasks;\r\nusing Windows.Networking.Connectivity;\r\n\r\nclass Program\r\n{\r\n    static async Task DoIt()\r\n    {\r\n        var now = DateTime.Now;\r\n        var states = new NetworkUsageStates\r\n        { Roaming = TriStates.DoNotCare, Shared = TriStates.DoNotCare };\r\n\r\n        var profiles = NetworkInformation.GetConnectionProfiles();\r\n        foreach (var profile in profiles)\r\n        {\r\n            var usages = await profile.GetNetworkUsageAsync(\r\n                now.AddDays(-1), now, DataUsageGranularity.PerDay,\r\n                states);\r\n            var usage = usages[0];\r\n            if (usage.ConnectionDuration &gt; TimeSpan.Zero)\r\n            {\r\n                Console.WriteLine(profile.ProfileName);\r\n                Console.WriteLine($\"BytesReceived = {usage.BytesReceived}\");\r\n                Console.WriteLine($\"BytesSent = {usage.BytesSent}\");\r\n                Console.WriteLine($\"ConnectionDuration = {usage.ConnectionDuration}\");\r\n                Console.WriteLine($\"------------------\");\r\n            }\r\n        }\r\n\r\n    }\r\n    static void Main()\r\n    {\r\n        DoIt().GetAwaiter().GetResult();\r\n    }\r\n}\r\n<\/pre>\n<p>But instead of wrapping the calculations inside a <code>DoIt<\/code> helper method, why not just grab the results directly?<\/p>\n<pre>using System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Threading.Tasks;\r\nusing Windows.Networking.Connectivity;\r\n\r\n<span style=\"border: solid 1px currentcolor;\">[STAThread]<\/span>\r\nstatic void Main()\r\n{\r\n    var now = DateTime.Now;\r\n    var states = new NetworkUsageStates\r\n    { Roaming = TriStates.DoNotCare, Shared = TriStates.DoNotCare };\r\n\r\n    var profiles = NetworkInformation.GetConnectionProfiles();\r\n    foreach (var profile in profiles)\r\n    {\r\n        var usages = <span style=\"border: solid 1px currentcolor;\">\/* <span style=\"text-decoration: line-through;\">await<\/span> *\/<\/span> profile.GetNetworkUsageAsync(\r\n            now.AddDays(-1), now, DataUsageGranularity.PerDay,\r\n            states)<span style=\"border: solid 1px currentcolor;\">.GetResults()<\/span>;\r\n        var usage = usages[0];\r\n        if (usage.ConnectionDuration &gt; TimeSpan.Zero)\r\n        {\r\n            Console.WriteLine(profile.ProfileName);\r\n            Console.WriteLine($\"BytesReceived = {usage.BytesReceived}\");\r\n            Console.WriteLine($\"BytesSent = {usage.BytesSent}\");\r\n            Console.WriteLine($\"ConnectionDuration = {usage.ConnectionDuration}\");\r\n            Console.WriteLine($\"------------------\");\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<p>Unfortunately, if you try this, you discover that the <code>GetResults()<\/code> call always fails with the exception <code>E_<wbr \/>ILLEGAL_<wbr \/>METHOD_<wbr \/>CALL<\/code>. What&#8217;s going on?<\/p>\n<p>There is a difference between <code>TaskAwaiter.GetResult()<\/code> and <code>IAsyncAction.<wbr \/>GetResult()<\/code> (and its buddies like <code>IAsyncOperation&lt;T&gt;.<wbr \/>GetResult()<\/code>).<\/p>\n<p>The <code>TaskAwaiter.<wbr \/>GetResult()<\/code> method waits for the task to complete before producing the results. On the other hand, <code>IAsyncAction.<wbr \/>GetResult()<\/code> does not wait. It gives you the result if the asynchronous activity has completed, or it throws <code>E_<wbr \/>ILLEGAL_<wbr \/>METHOD_<wbr \/>CALL<\/code> if the activity has not yet run to completion. You are expected to wait for the <code>Completed<\/code> callback before retrieving the result.\u00b9<\/p>\n<p>Now in this case, the situation is compounded by the fact that the program also marked the <code>Main<\/code> method as <code>[STAThread]<\/code>. Single-threaded apartments must pump messages while waiting; otherwise, you may run into deadlocks. So maybe it&#8217;s a good thing that this doesn&#8217;t work. Otherwise, you&#8217;d be tempted to use it in an incorrect way.<\/p>\n<p>\u00b9 You can find the code that enforces this in the method <code>Check\u00adValid\u00adState\u00adFor\u00adResults\u00adCall<\/code>. We looked at this method <a title=\"Cancelling a Windows Runtime asynchronous operation, part 7: WRL-generated asynchronous operations\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200709-00\/?p=103970\"> some time ago<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>You&#8217;re asking too soon.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-108477","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>You&#8217;re asking too soon.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108477","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=108477"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108477\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=108477"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=108477"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=108477"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}