{"id":43723,"date":"2014-10-31T07:00:00","date_gmt":"2014-10-31T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2014\/10\/31\/the-case-of-the-file-that-wont-copy-because-of-an-invalid-handle-error-message\/"},"modified":"2014-10-31T07:00:00","modified_gmt":"2014-10-31T07:00:00","slug":"the-case-of-the-file-that-wont-copy-because-of-an-invalid-handle-error-message","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20141031-00\/?p=43723","title":{"rendered":"The case of the file that won&#039;t copy because of an Invalid Handle error message"},"content":{"rendered":"<p>\nA customer reported that they had a file that was &#8220;haunted&#8221;\non their machine:\nExplorer was unable to copy the file.\nIf you did a copy\/paste, the copy dialog displayed an error.\n<\/p>\n<table BORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\" STYLE=\"border: solid 3px #ebebeb;font-family: Segoe UI, sans-serif;color: black;background-color: white\">\n<tr>\n<td ALIGN=\"center\" STYLE=\"background-color: #ebebeb;padding: 5px\">1 Interrupted Action<\/td>\n<\/tr>\n<tr>\n<td STYLE=\"border: solid 1px #dadada;padding: 10px\">\n<p>Invalid file handle<\/p>\n<table BORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\">\n<tr>\n<td VALIGN=\"middle\" STYLE=\"font-size: 40px\">&#x26bf;&nbsp;<\/td>\n<td>Contract Proposal<br \/>\n    Size: 110 KB<br \/>\n    Date modified: 10\/31\/2013 7:00 AM<\/td>\n<\/tr>\n<\/table>\n<\/td>\n<\/tr>\n<\/table>\n<p>\nOkay, time to roll up your sleeves and get to work.\nThis investigation took several hours, but you&#8217;ll be able to read\nit in ten minutes\nbecause I&#8217;m deleting all the dead ends and red herrings,\nand because I&#8217;m skipping over a lot of horrible grunt work,\nlike tracing a variable in memory backward in time to see where\nit came from.&sup1;\n<\/p>\n<p>\nThe <i>Invalid file handle<\/i> error was most likely coming from\nthe error code\n<code>ERROR_INVALID_HANDLE<\/code>.\nSome tracing of handle operations\nshowed that a call to\n<code>Get&shy;File&shy;Information&shy;By&shy;Handle<\/code>\nwas being passed <code>INVALID_FILE_HANDLE<\/code>\nas the file handle,\nand as you might expect,\nthat results in the invalid handle error code.\n<\/p>\n<p>\nOkay, but why was Explorer&#8217;s file copying code getting confused\nand trying to get information from an invalid handle?\n<\/p>\n<p>\nCode inspection showed that the handle in question is normally\nset to a valid handle during the file copying operation.\nSo the new question is,\n&#8220;Why <i>wasn&#8217;t<\/i> this variable set to a valid handle?&#8221;\n<\/p>\n<p>\nDebugging why something didn&#8217;t happen is harder than debugging\nwhy it <i>did<\/i> happen,\nbecause you can&#8217;t set a breakpoint of the form\n&#8220;Break when X doesn&#8217;t happen.&#8221;\nInstead\nyou have to set a breakpoint in the code that you&#8217;re\npretty sure is being executed,\nthen trace forward to see where execution strays from the intended path.\n<\/p>\n<p>\nThe heavy lifting of the file copy is done by the\n<code>Copy&shy;File2<\/code> function.\nExplorer uses the\n<code>Copy&shy;File2&shy;Progress&shy;Routine<\/code> callback\nto get information about the copy operation.\nIn particular, it gets a handle to the destination file by\nmaking a duplicate of the\n<code>hDestination&shy;File<\/code> in the\n<code>COPY&shy;FILE2_MESSAGE<\/code> structure.\nThe question is now,\n&#8220;Why wasn&#8217;t Explorer told about the destination file that\nwas the destination of the file copy?&#8221;\n<\/p>\n<p>\nTracing through the file copy operation showed that the file\ncopy operation actually <i>failed<\/i>\nbecause the destination file already exists.\nThe failure would normally be reported as\n<code>ERROR_FILE_EXISTS<\/code>,\nand the offending\n<code>Get&shy;File&shy;Information&shy;By&shy;Handle<\/code>\nwould never have taken place.\nSomehow the file copy was being treated as having succeeded\neven though it failed.\nThat&#8217;s why we&#8217;re using an invalid handle.\n<\/p>\n<p>\nThe <code>Copy&shy;File2<\/code> function goes roughly like this:\n<\/p>\n<pre>\nHRESULT CopyFile2()\n{\n BOOL fSuccess = FALSE;\n HANDLE hSource = OpenTheSourceFile(); \/\/ calls SetLastError() on failure\n if (hSource != INVALID_HANDLE_VALUE)\n {\n  HANDLE hDest = CreateTheDestinationFile(); \/\/ calls SetLastError() on failure\n  if (m_hDest != INVALID_HANDLE_VALUE)\n  {\n   if (CopyTheStuff(hSource, hDest)) \/\/ calls SetLastError() on failure\n   {\n    fSuccess = TRUE;\n   }\n   CloseHandle(hDest);\n  }\n  CloseHandle(hSource);\n }\n return fSuccess ? S_OK : HRESULT_FROM_WIN32(GetLastError());\n}\n<\/pre>\n<p>\n<b>Note<\/b>: This is not the actual code,\nso don&#8217;t go whining about the coding style or the inefficiencies.\nBut it gets the point across for the purpose of this story.\n<\/p>\n<p>\nThe <code>Create&shy;The&shy;Destination&shy;File<\/code> function\nfailed because the file already existed,\nand it called <code>Set&shy;Last&shy;Error<\/code> to set the\nerror code to\n<code>ERROR_FILE_EXISTS<\/code>,\nexpecting the error code to be picked up when it returned to the\n<code>Copy&shy;File2<\/code> function.\n<\/p>\n<p>\nOn the way out,\nthe <code>Copy&shy;File2<\/code> function\nmakes two calls to <code>Close&shy;Handle<\/code>.\n<code>Close&shy;Handle<\/code> on a valid handle is not supposed\nto modify the thread error state,\nbut somehow stepping over the <code>Close&shy;Handle<\/code>\ncall showed that the error code set by\n<code>Create&shy;The&shy;Destination&shy;File<\/code> was\nbeing reset back to <code>ERROR_SUCCESS<\/code>.\n(Mind you, this was a poor design on the part of the\n<code>Copy&shy;File2<\/code> function to leave the error code\nlying around for an extended period,\nsince the error code is highly volatile, and you would be best\nserved to get it while it&#8217;s still there.)\n<\/p>\n<p>\nCloser inspection showed that the\n<code>Close&shy;Handle<\/code> function\n<i>had been hooked by some random DLL that had been\ninjected into Explorer<\/i>.\n<\/p>\n<p>\nThe hook function was somewhat complicated\n(more time spent trying to reverse-engineer the hook function),\nbut in simplified form, it went something like this:\n<\/p>\n<pre>\nBOOL Hook_CloseHandle(HANDLE h)\n{\n HookState *state = (HookState*)TlsGetValue(g_tlsHookState);\n if (!state || !state-&gt;someCrazyFlag) {\n  return Original_CloseHandle(h);\n }\n ... crazy code that runs if the flag is set ...\n}\n<\/pre>\n<p>\nWhatever that crazy flag was for,\nit wasn&#8217;t set on the current thread,\nso the intent of the hook was to have no effect in that case.\n<\/p>\n<p>\nBut it <i>did<\/i> have an effect.\n<\/p>\n<p>\nThe\n<a HREF=\"http:\/\/msdn.microsoft.com\/library\/ms686812\">\n<code>Tls&shy;Get&shy;Value<\/code> function<\/a>\nmodifies the thread error state,\neven on success.\nSpecifically, if it successfully retrieves the thread local storage,\nit sets the thread error state to\n<code>ERROR_SUCCESS<\/code>.\n<\/p>\n<p>\nOkay, now you can put the pieces together.\n<\/p>\n<ul>\n<li>The file copy failed because the destination already exists.\n<li>The <code>Create&shy;The&shy;Destination&shy;File<\/cODE>\n    function called\n    <code>Set&shy;Last&shy;Error(ERROR_FILE_EXISTS)<\/code>.<\/p>\n<li>The file copy function did some cleaning up before retrieving\n    the error code.<\/p>\n<li>The cleanup functions are not expected to alter the thread error state.\n<li>But the cleanup function had been patched by a rogue DLL,\n    and the hook function <i>did<\/i> alter the thread error state.<\/p>\n<li>This alteration caused the file copy function to think that the\n    file was successfully copied even though it wasn't.<\/p>\n<li>In particular, the caller of the file copy function expects\n    to have received a handle to the copy during one of the copy callbacks,\n    but the callback never occurred because the file was never copied.<\/p>\n<li>The variable that holds the handle therefore remains uninitialized.\n<li>This generates an invalid handle error when the code tries to use\n    that handle.<\/p>\n<li>This error is shown to the user.\n<\/ul>\n<p>\nAn injected DLL that patched a system call\nresulted in Explorer looking like an idiot.\n(As Alex and Gaurav well know,\nExplorer is perfectly capable of looking like an idiot without any help.)\n<\/p>\n<p>\nWe were quite fortunate that the error\nmanifested itself as a failure to copy the file.\nImagine if Explorer didn't use\n<code>Get&shy;File&shy;Information&shy;By&shy;Handle<\/code>\nto get information about the file that was copied.\nThe <code>Copy&shy;File2<\/code> function returns <code>S_OK<\/code>\n<i>even though it actually failed and no file was copied<\/i>.\nExplorer would have happily reported,\n\"Congratulations, your file was copied successfully!\"\n<\/p>\n<p>\nStop and think about that for a second.\n<\/p>\n<p>\nA rogue DLL injected into Explorer patches a system call incorrectly\nand ends up causing all calls to\n<code>Copy&shy;File2<\/code> to report success even if they failed.\nThe user then deletes the original, thinking that the file was safely\nat the destination,\nthen later discovers that, oops, looks like the file was not copied\nafter all.\nSorry, it looks like that rogue DLL (which I'm sure had the best\nof intentions) had a subtle bug that caused you to <i>lose all your data<\/i>.\n<\/p>\n<p>\nThis is why, as a general rule,\nWindows considers DLL injection and API hooking to be unsupported.\nIf you hook an API, you not only have to emulate all the documented\nbehavior, you also have to emulate all the <i>undocumented<\/i> behavior\nthat applications unwittingly rely on.\n<\/p>\n<p>\n(Yes, we contacted the vendor of the rogue DLL.\nIdeally, they would get rid of their crazy DLL injection and API hooking\nbecause, y'know, <i>unsupported<\/i>.\nBut my guess is that they are going to stick with it.\nAt least we can try to get them to fix their bug.)\n<\/p>\n<p>\n&sup1;\nTo do this, you identify the variable\nand set a breakpoint when that variable is allocated.\n(This can be tricky if the variable belongs to a class with\nhundreds of instances;\nyou have to set the breakpoint on the correct instance!)\nWhen that breakpoint is hit,\nyou set a write breakpoint on the variable,\nthen resume execution.\nThen you hope that the breakpoint gets hit.\nWhen it does,\nyou can see who set the value.\n\"Oh, the value was copied from that other variable.\"\nNow you repeat the exercise with that other variable,\nand so on.\nThis is very time-consuming but largely uninteresting\nso I've skipped over it.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A customer reported that they had a file that was &#8220;haunted&#8221; on their machine: Explorer was unable to copy the file. If you did a copy\/paste, the copy dialog displayed an error. 1 Interrupted Action Invalid file handle &#x26bf;&nbsp; Contract Proposal Size: 110 KB Date modified: 10\/31\/2013 7:00 AM Okay, time to roll up your [&hellip;]<\/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-43723","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>A customer reported that they had a file that was &#8220;haunted&#8221; on their machine: Explorer was unable to copy the file. If you did a copy\/paste, the copy dialog displayed an error. 1 Interrupted Action Invalid file handle &#x26bf;&nbsp; Contract Proposal Size: 110 KB Date modified: 10\/31\/2013 7:00 AM Okay, time to roll up your [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/43723","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=43723"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/43723\/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=43723"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=43723"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=43723"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}