{"id":92951,"date":"2016-01-29T07:00:00","date_gmt":"2016-01-29T22:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/?p=92951"},"modified":"2019-03-13T10:29:27","modified_gmt":"2019-03-13T17:29:27","slug":"20160129-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20160129-00\/?p=92951","title":{"rendered":"Does the thread pool have different handle access privileges? Why am I getting ERROR_INVALID_HANDLE?"},"content":{"rendered":"<p>A customer was observing strange behavior in their application with handles and the thread pool. <\/p>\n<blockquote CLASS=\"q\">\n<p>We have a service that spawns a child process, and under certain conditions, we need to terminate that child process. If I try to terminate the process immediately upon the condition being met, then everything works. But now we want to wait a little while before terminating the child process. To do that, we create a thread pool timer and terminate the process from the thread pool. <\/p>\n<p>Here&#8217;s the code that runs when we detect that the condition is met. No errors are detected except where noted.<\/p>\n<pre>\nPTP_TIMER timerTask = CreateThreadpoolTimer(\n                            DelayTerminate, \n                            static_cast&lt;PVOID&gt;(ProcHandle),\n                            &amp;m_CallBackEnviron);\n\nif (NULL == timerTask) { ... }\n\n\/\/ Set the timer to fire after a little while.\n\nulDueTime.QuadPart = (ULONGLONG)(-TimeOutIn100Nanoseconds);\nFileDueTime.dwHighDateTime = ulDueTime.HighPart;\nFileDueTime.dwLowDateTime = ulDueTime.LowPart;\n\nSetThreadpoolTimer(timerTask, &amp;FileDueTime, 0, 0);\n\n\/\/ if we set the debugging flag, then the TerminateProcess call succeeds.\nif (DebuggingFlag) {\n  if (!TerminateProcess(ProcHandle, 1)) { ... }\n}\n<\/pre>\n<p>Here is our callback function: <\/p>\n<pre>\nVOID\nCALLBACK\nDelayTerminate(\n    PTP_CALLBACK_INSTANCE Instance,\n    PVOID                 Parameter,\n    PTP_TIMER             Timer\n    )\n{\n  \/\/ This call to TerminateProcess fails\n  if (!TerminateProcess((HANDLE)Parameter, 1)) {\n    Log(GetLastError()); \/\/ ERROR_INVALID_HANDLE\n  }\n  CloseThreadpoolTimer(Timer);\n}\n<\/pre>\n<p>Does the thread pool thread run with different access privileges from the main thread? <\/p>\n<p>We verified that the handle is the same in the main thread and in the callback. It is our understanding that <code>Duplicate&shy;Handle<\/code> is not needed to share handles between threads of a single process. Is there some other special thing that has to be done in order to share the handle between threads? <\/p>\n<\/blockquote>\n<p>I asked, &#8220;Is it possible that somebody closed the handle in the meantime?&#8221; After all, if the problem were due to access, then you would expect the error to be <code>ERROR_ACCESS_DENIED<\/code>. Since the error is <code>ERROR_INVALID_HANDLE<\/code>, the most likely reason is, um, an invalid handle. <\/p>\n<p>A clue that something strange is going on is the <code>static_cast&lt;PVOID&gt;(ProcHandle)<\/code>. This suggests that <code>ProcHandle<\/code> is not itself a <code>HANDLE<\/code>, but is rather some sort of RAII class that manages a process handle and which has an implicit conversion to <code>HANDLE<\/code>. (Because if <code>ProcHandle<\/code> were a <code>HANDLE<\/code>, then you wouldn&#8217;t need to cast it to <code>PVOID<\/code>.) <\/p>\n<p>The customer eventually wrote back, <\/p>\n<blockquote CLASS=\"q\">\n<p>Yes, that was it. We found that the handle was being closed before the thread pool tried to use it. Thanks. <\/p>\n<\/blockquote>\n","protected":false},"excerpt":{"rendered":"<p>The most common reason for getting ERROR_INVALID_HANDLE is that you have an invalid handle.<\/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-92951","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>The most common reason for getting ERROR_INVALID_HANDLE is that you have an invalid handle.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/92951","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=92951"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/92951\/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=92951"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=92951"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=92951"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}