{"id":91791,"date":"2015-08-18T07:00:00","date_gmt":"2015-08-18T21:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20150818-00\/?p=91791\/"},"modified":"2019-03-13T12:18:36","modified_gmt":"2019-03-13T19:18:36","slug":"20150818-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20150818-00\/?p=91791","title":{"rendered":"If you are going to call Marshal.GetLastWin32Error, the function whose error you&#8217;re retrieving had better have SetLastError=true"},"content":{"rendered":"<p>A customer reported that their p\/invoke to a custom DLL was failing, and the error code made no sense. <!--more--><\/p>\n<pre>\n\/\/ C#\nusing System;\nusing System.Runtime.InteropServices;\nusing System.Diagnostics;\n\nclass Program\n{\n  [DllImport(\"contoso.dll\", CallingConvention=CallingConvention.Cdecl)]\n  public static extern int Fribble();\n\n  public static void Main()\n  {\n    Console.WriteLine(\"About to call Fribble\");\n\n    var result = Fribble();\n    if (result &gt;= 0) {\n      Console.WriteLine(\"succeeded {0}\", result);\n    } else {\n      Console.WriteLine(\"failed {0}, last error = {1}\",\n                        result, Marshal.GetLastWin32Error());\n    }\n  }\n}\n\n\/\/ C++\n\nint __cdecl Fribble()\n{\n HANDLE hEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE,\n                           TEXT(\"FribbleEvent\"));\n if (hEvent == nullptr)\n  return -1;\n }\n\n if (!SetEvent(hEvent)) {\n  CloseHandle(hEvent);\n  return -2;\n }\n\n CloseHandle(hEvent);\n return 1;\n}\n<\/pre>\n<p>The customer reported that their <code>Fribble<\/code> function was returning &minus;1, indicating a failure to open the event, but the error code returned by <code>Marshal.Get&shy;Last&shy;Win32&shy;Error<\/code> is 87, &#8220;The parameter is incorrect.&#8221; But all of the parameters to <code>Open&shy;Event<\/code> look correct. Why are we getting this strange error code? <\/p>\n<p>My psychic powers tell me that if the customer had taken the time to troubleshoot their problem by writing a C++ program that calls the <code>Fribble<\/code> function, <code>Get&shy;Last&shy;Error<\/code> would have returned the more reasonable <a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2010\/01\/27\/9953807.aspx\">error 2<\/a>, meaning that the event does not exist. <\/p>\n<p>That&#8217;s because <code>Get&shy;Last&shy;Error<\/code> is working fine. The last error code is&nbsp;2. <\/p>\n<p>The problem is with the p\/invoke declaration. <\/p>\n<p>The documentation for the <code>Marshal.Get&shy;Last&shy;Win32&shy;Error<\/code> function says as its very first line <\/p>\n<blockquote CLASS=\"q\"><p>Returns the error code returned by the last unmanaged function that was called using platform invoke <i>that has the DllImportAttribute.SetLastError flag set<\/i>. <\/p><\/blockquote>\n<p>(Emphasis mine.) <\/p>\n<p>This reminder about <code>Dll&shy;Import&shy;Attribute.Set&shy;Last&shy;Error<\/code> is repeated in the Remarks. <\/p>\n<blockquote CLASS=\"q\"><p>You can use this method to obtain error codes only if you apply the System.Runtime.Interop&shy;Services.Dll&shy;Import&shy;Attribute<\/code> to the method signature and set the <code>Set&shy;Last&shy;Error<\/code> field to <b>true<\/b>. <\/p><\/blockquote>\n<p>Observe that the <code>Set&shy;Last&shy;Error<\/code> field was not set in the p\/invoke declaration. Therefore, what you are actually getting when you call <code>Marshal.Get&shy;Last&shy;Win32&shy;Error<\/code> is whatever error was lying around after the previous call to a p\/invoke function that <i>did<\/i> specify <code>Set&shy;Last&shy;Error = true<\/code>. <\/p>\n<p>Changing the p\/invoke to <\/p>\n<pre>\n[DllImport(\"contoso.dll\", <font COLOR=\"blue\">SetLastError=true<\/font>,\n           CallingConvention=CallingConvention.Cdecl)]\npublic static extern int Fribble();\n<\/pre>\n<p>fixed the problem. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Or it won&#8217;t get anything.<\/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-91791","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Or it won&#8217;t get anything.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/91791","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=91791"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/91791\/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=91791"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=91791"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=91791"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}