{"id":10073,"date":"2011-07-25T07:00:00","date_gmt":"2011-07-25T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2011\/07\/25\/how-is-it-possible-to-run-wordpad-by-just-typing-its-name-even-though-it-isnt-on-the-path\/"},"modified":"2023-06-08T10:58:34","modified_gmt":"2023-06-08T17:58:34","slug":"20110725-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20110725-00\/?p=10073","title":{"rendered":"How is it possible to run Wordpad by just typing its name even though it isn&#8217;t on the PATH?"},"content":{"rendered":"<p>In a comment completely unrelated to the topic, Chris Capel asks <a title=\"Steamy coffee leaves grounds for concern\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20080919-01\/?p=20823\"> how Wordpad manages to run when you type its name into the Run dialog even though the command prompt can&#8217;t find it<\/a>. In other words, the Run dialog manages to find Wordpad even though it&#8217;s not on the <code>PATH<\/code>.<\/p>\n<p>Chris was unable to find anywhere I discussed this issue earlier, but it&#8217;s there, just <a title=\"How to find the Internet Explorer binary\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20040901-00\/?p=37993\"> with Internet Explorer as the application instead of Wordpad<\/a>.<\/p>\n<p>It&#8217;s through the magic of <code>App Paths<\/code>.<\/p>\n<p><code>App Paths<\/code> was introduced in Windows\u00a095 to address the <i>path pollution<\/i> problem. Prior to the introduction of <code>App Paths<\/code>, typing the name of a program without a fully-qualified path resulted in a search along the path, and if it wasn&#8217;t found, then that was the end of that. File not found. As a result, it became common practice for programs, as part of their installation, to edit the user&#8217;s <code>AUTOEXEC.BAT<\/code> and add the application&#8217;s installation directory to the path.<\/p>\n<p>This had a few problems.<\/p>\n<p>First of all, editing <code>AUTOEXEC.BAT<\/code> is decidedlly nontrivial since batch files can have control flow logic like <code>IF<\/code> and <code>CALL<\/code> and <code>GOTO<\/code>. Finding the right <code>SET PATH=...<\/code> or <code>PATH ...<\/code> command is an exercise in code coverage analysis, especially since MS-DOS 6 added multi-config support to <code>CONFIG.SYS<\/code>, so the value of the <code>CONFIG<\/code> environment variable is determined at runtime. If you wanted to avoid hanging your setup program, you would have to <a title=\"Never say never, part two\" href=\"https:\/\/ericlippert.com\/2011\/02\/24\/never-say-never-part-two\/\"> solve the Halting Problem<\/a>. (You can&#8217;t just stick at <code>PATH ...<\/code> at the beginning because it might get wiped out by a later <code>PATH<\/code> command, and you can&#8217;t just stick it at the end, because control might never reach last line of the batch file.)<\/p>\n<p>And of course, very few uninstall programs would take the time to undo the edits the installer performed, and even if they tried, there&#8217;s no guarantee that the undo would be successful, since the user (or another installer!) may have edited the <code>AUTOEXEC.BAT<\/code> file in the meantime.<\/p>\n<p>Even if you postulate the existence of the <i><code>AUTOEXEC.BAT<\/code> editing fairy<\/i> who magically edits your <code>AUTOEXEC.BAT<\/code> for you, you still run into the <code>PATH<\/code> length limit. The maximum length of a command line was 128 characters in MS-DOS, and if each program added itself to the <code>PATH<\/code>, it wouldn&#8217;t be long before the <code>PATH<\/code> reached its maximum length.<\/p>\n<p><b>Pre-emptive Yuhong Bao irrelevant detail that has no effect on the story<\/b>: Windows\u00a095 increased the maximum command line length, but the program being launched needed to know where to look for the &#8220;long command line&#8221;. And that didn&#8217;t help existing installers which were written against the old 128-character limit. Give them an <code>AUTOEXEC.BAT<\/code> with a line longer than 128 characters and you had a good chance that you&#8217;d hit a buffer overflow bug.<\/p>\n<p>On top of the difficulty of adding more directories to the <code>PATH<\/code>, there was the recognition that this was another case of <a title=\"Don't use global state to manage a local problem\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20081211-00\/?p=19873\"> using a global setting to solve a local problem<\/a>. It seemed wasteful to add a directory to the path just so you could find <i>one file<\/i>. Each additional directory on the path slowed down path sarching operations, even the ones unrelated to locating that one program.<\/p>\n<p>Enter <code>App Paths<\/code>. The idea here is that instead of adding your application directory to the path, you just create <a href=\"http:\/\/msdn.microsoft.com\/ee872121.aspx\"> an entry under the <code>App Paths<\/code> key<\/a> saying, &#8220;If somebody is looking to execute <code>contoso.exe<\/code>, I put it over here.&#8221; Instead of adding an entire directory to the path, you just add a single file, and it&#8217;s used only for application execution purposes, so it doesn&#8217;t slow down other path search operations like loading DLLs.<\/p>\n<p>(Note that <a href=\"http:\/\/msdn.microsoft.com\/en-us\/ms997545.aspx\"> the old documentation on App Paths<\/a> has been superseded by the new documentation linked above.)<\/p>\n<p>Now that there was a place to store information associated with a particular application, you may as well use it for other stuff as well. A secondary source of path pollution came from applications which added not only the application directory to the path, but also a helper directory where the application kept its DLLs. To address this, an additional <code>Path<\/code> value specified which directories your application wanted to be added to the path before it was executed. Over time, additional attributes were added to the <code>App Paths<\/code> key, such as <a title=\"The UseUrl attribute in the App Paths key indicates that your application can accept a URL on the command line\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20110630-00\/?p=10293\"> the <code>UseUrl<\/code> value<\/a> we saw some time ago.<\/p>\n<p>When you type the name of a program into the Run dialog (with no path), the <code>Shell\u00adExecute<\/code> function checks if the name corresponds to an application registered under <code>App Paths<\/code>. If so, then it uses the registration information to launch the application. Hooray, applications can be run by just typing their name without requiring them to modify the global path.<\/p>\n<p>Note that this extra lookup is performed only by the <code>Sh\u00adellExecute<\/code> family of functions, so if you use <code>Create\u00adProcess<\/code> or <code>Search\u00adPath<\/code>, you&#8217;ll still get <code>ERROR_<wbr \/>FILE_<wbr \/>NOT_<wbr \/>FOUND<\/code>.<\/p>\n<p>Now, the intent was that the registered full path to the application is the same as the registered short name, just with a full path in front. For example, <code>wordpad.exe<\/code> registers the full path of <code>%ProgramFiles%\\<wbr \/>Windows NT\\<wbr \/>Accessories\\<wbr \/>WORDPAD.EXE<\/code>. But there&#8217;s no check that the two file names match. The Pbrush folks took advantage of this by registering an application path entry for <code>pbrush.exe<\/code> with a full path of <code>%SystemRoot%\\<wbr \/>System32\\<wbr \/>mspaint.exe<\/code>: That way, when somebody types <code>pbrush<\/code> into the Run dialog, they get redirected to <code>mspaint.exe<\/code>.<\/p>\n<p>Sneaky.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>It&#8217;s in the App Paths.<\/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-10073","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>It&#8217;s in the App Paths.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/10073","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=10073"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/10073\/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=10073"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=10073"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=10073"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}