{"id":93274,"date":"2016-04-08T07:00:00","date_gmt":"2016-04-08T21:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/?p=93274"},"modified":"2019-03-13T11:01:46","modified_gmt":"2019-03-13T18:01:46","slug":"20160408-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20160408-00\/?p=93274","title":{"rendered":"Why does FindExecutable behave erratically for files with extensions longer than three characters? (And what can you do about it?)"},"content":{"rendered":"<p>The <code>Find&shy;Executable<\/code> function looks up the executable responsible for launching a particular file. This is a dubious undertaking, because it assumes that the thing that launches a file is an executable. There are other things capable of launching a file, such as <a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2007\/02\/26\/1763683.aspx\">a DDE command<\/a>, <a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2004\/09\/20\/231739.aspx\">a context menu shell extension<\/a>, or a <a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2010\/05\/03\/10006065.aspx\">custom drop target<\/a>. What should <code>Find&shy;Executable<\/code> return in those cases? <\/p>\n<p>Okay, so if <code>Find&shy;Executable<\/code> is based upon a flawed assumption, why does it even exist? <\/p>\n<p>Because at the time it was originally introduced, the assumption was valid. <\/p>\n<p>The <code>Find&shy;Executable<\/code> function comes from 16-bit Windows, and back in those days, there were no context menu shell extensions or custom drop targets. (There was DDE, but that&#8217;s okay, because programs still have to register an executable to be used in the fallback case when nobody responds to the DDE message.) <\/p>\n<p>In the port to 32-bit Windows, the <code>Find&shy;Executable<\/code> function remains, but it works only in the case where files were registered in the 16-bit way; that is, with a command line executable. It so happens that most file types are still registered that way, so the <code>Find&shy;Executable<\/code> function basically still works. <\/p>\n<p>Since the <code>Find&shy;Executable<\/code> function is basically a throwback to 16-bit Windows, there is another attempt to accommodate the 16-bit world that is not as obvious: The <code>Find&shy;Executable<\/code> function takes the thing you pass and converts it into a short file name before trying to look up the handler. <\/p>\n<p>The effect of the conversion to a short file name depends on a bunch of things. <\/p>\n<p>If the volume does not have short file name autogeneration enabled, then the conversion to a short file name has no effect. But if the volume does have short file name autogeneration enabled, then the net effect is that the extension gets truncated to three characters. <code>foo.abcde<\/code> becomes <code>foo~1.abc<\/code>. And then <code>Find&shy;Executable<\/code> looks up and returns the handler for the <code>.abc<\/code> extension instead of the <code>.abcde<\/code> extension. <\/p>\n<p>Back in the days before long file names, all file extensions were truncated to 3 characters. <a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2007\/12\/17\/6785519.aspx\">if you asked for <code>foo.abcde<\/code>, you got <code>foo.abc<\/code><\/a>. The <code>Find&shy;Executable<\/code> function tries to maintain this compatibility with older applications. Newer applications shouldn&#8217;t be using <code>Find&shy;Executable<\/code> anyway, seeing as the handler for a file type may not even be an executable. <\/p>\n<p><b>I accept that the concept of finding the executable associated with a file is flawed in the face of handlers that do not take the form of an executable, but I still want to get the executable associated with a file, if possible, with the understanding that the answer may be incorrect.<\/b> <\/p>\n<p>You can use the <code>Assoc&shy;Query&shy;String<\/code> function to get the executable associated with the default verb of a file extension, if one exists. <\/p>\n<pre>\nHRESULT FindExecutableAssociatedWithFileExtension(\n    _In_ PCWSTR extension,\n    _Out_ PWSTR resultBuffer,\n    _In_ DWORD bufferLength)\n{\n return AssocQueryString(ASSOCF_INIT_INGORENUNKNOWN,\n                         ASSOCSTR_EXECUTABLE,\n                         fullPath,\n                         nullptr,\n                         resultBuffer,\n                         &amp;bufferLength);\n}\n<\/pre>\n<p>The <code>ASSOCF_INIT_UNKNOWN<\/code> flag says that if the file extension has no handler, don&#8217;t return the &#8220;Open unknown file&#8221; handler. <\/p>\n<p>This is not exactly the same as <code>Find&shy;Executable<\/code> because that function has special-case code for when you pass in, for example, <code>excel.exe<\/code>. In those cases, the <code>Find&shy;Executable<\/code> function just returns the file itself, since executables are their own handlers. <\/p>\n<p>The <code>ASSOCF_INIT_UNKNOWN<\/code> flag was added in Windows&nbsp;7. What do you do for older versions of Windows? Well, you&#8217;re in luck. Older versions of Windows didn&#8217;t have the &#8220;Open unknown file&#8221; handler, so if there is no registered handler, the call will simply fail. (Indeed, the introduction of the &#8220;Open unknown file&#8221; handler is what most likely prompted the creation of the <code>ASSOCF_INIT_UNKNOWN<\/code> flag in the first place.) As a second mark of good fortune, the flag is ignored by older versions of Windows, so you can go ahead and pass the flag unconditionally: On versions of Windows that support it, it does what you want. And on versions of Windows that don&#8217;t support it, they already behave the way you want by default. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Short file names strike again.<\/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-93274","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Short file names strike again.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/93274","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=93274"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/93274\/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=93274"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=93274"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=93274"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}