{"id":93555,"date":"2016-06-01T07:00:00","date_gmt":"2016-06-01T21:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/?p=93555"},"modified":"2019-03-13T11:50:30","modified_gmt":"2019-03-13T18:50:30","slug":"20160601-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20160601-00\/?p=93555","title":{"rendered":"Why does SHGetKnownFolderPath fail when impersonating?"},"content":{"rendered":"<p>A customer was having trouble with the <code>SHGet&shy;Known&shy;Folder&shy;Path<\/code> function. <\/p>\n<blockquote CLASS=\"q\">\n<p>We are calling the <code>SHGet&shy;Known&shy;Folder&shy;Path<\/code> function. from a service while impersonating a user, but it returns <code>E_ACCESS&shy;DENIED<\/code>. It&#8217;s failing both for <code>FOLDERID_Program&shy;Data<\/code> and <code>FOLDERID_Roaming&shy;App&shy;Data<\/code>: <\/p>\n<pre>\nhr = SHGetKnownFolderPath(\n        FOLDERID_ProgramData, \/\/ folder ID\n        0,                    \/\/ flags\n        nullptr,              \/\/ token\n        &amp;path);           \/\/ result\n<\/pre>\n<\/blockquote>\n<p>Before we could reply, the customer followed up: <\/p>\n<blockquote CLASS=\"q\">\n<p>We found that if we save the token that is being used for impersonation and pass it to the <code>SHGet&shy;Known&shy;Folder&shy;Path<\/code> function. then it works. But we are not sure why is it working now since the call was already being made while impersonating, and the documentation for the <code>SHGet&shy;Known&shy;Folder&shy;Path<\/code> function says that if you want to get the known folder path for the current user, then you pass <code>NULL<\/code> as the token. <\/p>\n<\/blockquote>\n<p>Remember that <a HREF=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20110928-00\/?p=9533\">the default answer to &#8220;Does this work while impersonating?&#8221; is No<\/a>. When the <code>SHGet&shy;Known&shy;Folder&shy;Path<\/code> function says &#8220;the current user&#8221;, it roughly means &#8220;the user under whose identity the process is running&#8221;, but more importantly, it means the user whose registry is mapped as <code>HKEY_CURRENT_USER<\/code>, which, as we saw earlier, <a HREF=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20141121-00\/?p=43563\">is a very tricky proposition for a service that impersonates<\/a>. <\/p>\n<p>In this case, what&#8217;s probably happening is that the service is running as something like SYSTEM, and <code>HKEY_CURRENT_USER<\/code> points to SYSTEM&#8217;s registry, and the impersonated user does not have access to SYSTEM&#8217;s registry, hence <code>E_ACCESS&shy;DENIED<\/code>. <\/p>\n<p>The backstory is that the <code>SHGet&shy;Known&shy;Folder&shy;Path<\/code> function (and its close relatives like <code>SHGet&shy;Special&shy;Folder&shy;Path<\/code>) are overwhelmingly used by normal applications that do no impersonation, so that&#8217;s the use case they are optimized for. if you pass <code>NULL<\/code> as the token, then the functions will read from <code>HKEY_CURRENT_USER<\/code> and cache the results. If the thread is impersonating, but you fail to pass the impersonation token to the <code>SHGet&shy;Known&shy;Folder&shy;Path<\/code> function, then it might return the (wrong) cached value, or it might try to read the value from the (wrong) <code>HKEY_CURRENT_USER<\/code> hive. Whatever happens, it&#8217;s probably going to be wrong. To say, &#8220;No, don&#8217;t use any of your fancy optimizations for normal applications, because I&#8217;m not a normal application. Get the known folder path for this specific user by reading from that user&#8217;s registry.&#8221; <\/p>\n<p>You might say, &#8220;Well, the <code>SHGet&shy;Known&shy;Folder&shy;Path<\/code> function should auto-detect whether it is being called when impersonating and compensate accordingly.&#8221; Maybe, but that means introducing an expensive test to a hot code path to cover a rare case. that people shouldn&#8217;t expect to work anyway because <a HREF=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20110928-00\/?p=9533\">the default answer to &#8220;Does this work while impersonating?&#8221; is No<\/a>. Instead, the extra work of dealing with impersonation is transferred to the people who want to get this information while impersonating. <\/p>\n<p><b>Bonus chatter<\/b>: Even if you pass the correct token to the <code>SHGet&shy;Known&shy;Folder&shy;Path<\/code> function, you may still run afoul of other requirements. In particularly, note the sentence <\/p>\n<blockquote CLASS=\"q\"><p>In addition to passing the user&#8217;s <i>hToken<\/i>, the registry hive of that specific user must be mounted. <\/p><\/blockquote>\n<p>If the registry hive is not mounted, then there is no way to look up the information in the registry because, well, it&#8217;s not in the registry. You can use the <code>Load&shy;User&shy;Profile<\/code> function to load the profile into the registry. Just remember to call <code>Unload&shy;User&shy;Profile<\/code> when you&#8217;re done. (See the documentation for details.) <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Because there&#8217;s a special way to call it for impersonation.<\/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-93555","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Because there&#8217;s a special way to call it for impersonation.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/93555","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=93555"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/93555\/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=93555"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=93555"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=93555"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}