{"id":92952,"date":"2016-02-01T07:00:00","date_gmt":"2016-02-01T22:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/?p=92952"},"modified":"2019-03-13T10:29:48","modified_gmt":"2019-03-13T17:29:48","slug":"20160201-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20160201-00\/?p=92952","title":{"rendered":"A brief tour of the console alias functions"},"content":{"rendered":"<p>Today&#8217;s Little Program exercises the console alias functions. These functions let you define console aliases which are active when a target program reads a line of text from the console. The alias is recognized when it is entered at the start of a line. (Therefore, a way to defeat an alias is to put a space in front of it.) More details about console aliases can be found in <a HREF=\"https:\/\/technet.microsoft.com\/en-us\/library\/bb490894.aspx\">the documentation for <code>DOSKEY<\/code><\/a>. <\/p>\n<p>The program we&#8217;ll write has five commands: <\/p>\n<dl>\n<dt><code>add program.exe alias \"value\"<\/code> <\/p>\n<dd>This defines a console alias for the specified program. <\/p>\n<dt><code>delete program.exe alias<\/code> <\/p>\n<dd>This deletes a console alias definition. <\/p>\n<dt><code>show program.exe alias<\/code> <\/p>\n<dd>This shows the current definition of an alias. <\/p>\n<dt><code>showall program.exe<\/code> <\/p>\n<dd>This shows all aliases defined for the specified program. <\/p>\n<dt><code>showexes<\/code> <\/p>\n<dd>This shows all programs that have aliases defined. <\/dl>\n<p>Let&#8217;s dive in. <\/p>\n<pre>\n#define UNICODE\n#define _UNICODE\n#include &lt;windows.h&gt;\n#include &lt;iostream&gt;\n#include &lt;vector&gt;\n\nvoid do_add(int argc, wchar_t **argv);\nvoid do_delete(int argc, wchar_t **argv);\nvoid do_show(int argc, wchar_t **argv);\nvoid do_showall(int argc, wchar_t **argv);\nvoid do_showexes(int argc, wchar_t **argv);\n\nint __cdecl wmain(int argc, wchar_t **argv)\n{\n  auto command = argv[1];\n\n  if (wcscmp(command, L\"add\") == 0) {\n    do_add(argc, argv);\n  } else if (wcscmp(command, L\"delete\") == 0) {\n    do_delete(argc, argv);\n  } else if (wcscmp(command, L\"show\") == 0) {\n    do_show(argc, argv);\n  } else if (wcscmp(command, L\"showall\") == 0) {\n    do_showall(argc, argv);\n  } else if (wcscmp(command, L\"showexes\") == 0) {\n    do_showexes(argc, argv);\n  }\n  return 0;\n}\n<\/pre>\n<p>The main program looks at the first command line argument and dispatches the rest of the work to the appropriate handler function. Now let&#8217;s look at each of the handlers. Remember, Little Programs do little to no error checking. <\/p>\n<pre>\nvoid do_add(int argc, wchar_t **argv)\n{\n  auto program = argv[2];\n  auto alias = argv[3];\n  auto value = argv[4];\n  if (AddConsoleAlias(alias, value, program)) {\n    std::wcout &lt;&lt; alias &lt;&lt; L\"=\" &lt;&lt; value &lt;&lt; std::endl;\n  } else {\n    std::wcout &lt;&lt; L\"Failed to add alias\" &lt;&lt; std::endl;\n  }\n}\n<\/pre>\n<p>To add an alias, we call <code>Add&shy;Console&shy;Alias<\/code> with the alias, the value, and the program it should be applied to. An example alias might be <\/p>\n<pre>\nscratch add cmd.exe proj\n  \"cd \/D \\\"%USERPROFILE%\\Documents\\Visual Studio 2015\\Projects\\$*\\\"\"\n<\/pre>\n<p>(All one line; split into two for expository purposes.) <\/p>\n<p>This lets you type <code>proj<\/code> to go to your Visual Studio projects directory, and <code>proj scratch<\/code> to go to the <code>scratch<\/code> project. Note that we had to quote the value twice, once to get it past the scratch program&#8217;s command line parser, and a second time to get it past <code>cmd.exe<\/code>&#8216;s command line parser. <\/p>\n<p>Next is deletion: To delete an alias, you set it to a null pointer. <\/p>\n<pre>\nvoid do_delete(int argc, wchar_t **argv)\n{\n  auto program = argv[2];\n  auto alias = argv[3];\n  if (AddConsoleAlias(alias, nullptr, program)) {\n    std::wcout &lt;&lt; alias &lt;&lt; L\" deleted\" &lt;&lt; std::endl;\n  } else {\n    std::wcout &lt;&lt; L\"Failed to delete alias\" &lt;&lt; std::endl;\n  }\n}\n<\/pre>\n<p>Continuing our example, if you get bored of the <code>proj<\/code> alias, you can delete it by saying <code>scratch delete cmd.exe proj<\/code>. <\/p>\n<p>The next command is for showing the value of an alias, <\/p>\n<pre>\nvoid do_show(int argc, wchar_t **argv)\n{\n  auto program = argv[2];\n  auto alias = argv[3];\n  wchar_t value[8192];\n  if (GetConsoleAlias(alias, value, sizeof(value), program)) {\n    std::wcout &lt;&lt; alias &lt;&lt; L\"=\" &lt;&lt; value &lt;&lt; std::endl;\n  } else {\n    std::wcout &lt;&lt; L\"Cannot show (maybe it isn't defined)\" &lt;&lt; std::endl;\n  }\n}\n<\/pre>\n<p>There is no way to query the length of an alias&#8217;s value, but since <a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2003\/12\/10\/56028.aspx\">the maximum command line length supported by <code>cmd.exe<\/code> is 8192<\/a>, a buffer size of 8192 is a safe bet for now. (This is a Little Program and doesn&#8217;t need to worry itself with pesky things like forward compatibility.) <\/p>\n<p>The last two commands are for showing all the aliases defined for a specific program, and for showing the programs that have aliases defined. The two functions are very similar, so we present them together. First, a simple version that is subtly defective: <\/p>\n<pre>\n<i>\/\/ code in italics is wrong\nvoid do_showall(int argc, wchar_t **argv)\n{\n  auto program = argv[2];\n  auto bytes = GetConsoleAliasesLength(program);\n  std::vector&lt;wchar_t&gt; buffer(\n    (bytes + sizeof(wchar_t) + 1) \/ sizeof(wchar_t));\n  if (GetConsoleAliases(buffer.data(), bytes, program)) {\n    for (auto current = buffer.data();\n         current &lt; buffer.data() + buffer.size();\n         current += wcslen(current) + 1) {\n      std::wcout &lt;&lt; current &lt;&lt; std::endl;\n    }\n  }\n}\n\nvoid do_showexes(int argc, wchar_t **argv)\n{\n  auto bytes = GetConsoleAliasExesLength();\n  std::vector&lt;wchar_t&gt; buffer(\n    (bytes + sizeof(wchar_t) + 1) \/ sizeof(wchar_t));\n  if (GetConsoleAliasExes(buffer.data(), bytes)) {\n    for (auto current = buffer.data();\n         current &lt; buffer.data() + buffer.size();\n         current += wcslen(current) + 1) {\n      std::wcout &lt;&lt; current &lt;&lt; std::endl;\n    }\n  }\n}<\/i>\n<\/pre>\n<p>One annoyance here is that the <code>Get&shy;Console&shy;Aliases&shy;Length<\/code> function returns a byte count rather than a <code>TCHAR<\/code> count, so we have to do conversion between bytes and <code>TCHAR<\/code>s. In case we get an odd number back (which shouldn&#8217;t ever happen, but better safe than sorry), we round up to get the number of <code>wchar_t<\/code>s. <\/p>\n<p>The next annoyance is that the <code>Get&shy;Console&shy;Aliases<\/code> function returns a series of null-terminated strings, but the last string is not double-null-terminated (or more accurately, <a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2009\/10\/08\/9904646.aspx\">terminated with an empty string<\/a>). This means that <i>you don&#8217;t know when you&#8217;re finished<\/i>! After you process one string, the next byte could be the start of the next string, or it could just be uninitialized garbage. If another thread deletes an alias between the calls to <code>Get&shy;Console&shy;Aliases&shy;Length<\/code> and <code>Get&shy;Console&shy;Aliases<\/code>, then we pass a too-large buffer to <code>Get&shy;Console&shy;Aliases<\/code>, and the unused bytes contain uninitialized garbage, and we have no way to know when the valid data ends and the uninitialized garbage begins. <\/p>\n<p>Not knowing when you have reached the end of the valid data is a really bad situation for a program to be in. <\/p>\n<p>We can work around this problem by zeroing out the memory before we call <code>Get&shy;Console&shy;Aliases<\/code>; that way, if the buffer we pass turns out to be too large (because another thread deleted an alias in the meantime), the extra zeros we wrote created a double-null-terminated string buffer. <\/p>\n<p>On the other hand, if that didn&#8217;t happen, then we want to stop when we reached the end of the buffer. <\/p>\n<p>The final problem is that another thread could <i>add<\/i> an alias in between our calls to <code>Get&shy;Console&shy;Aliases&shy;Length<\/code> and <code>Get&shy;Console&shy;Aliases<\/code>, and the call to <code>Get&shy;Console&shy;Aliases<\/code> will fail because the buffer is too small. In that case, we want to loop back and try again with a bigger buffer. <\/p>\n<p>All of the preceding issues with <code>Get&shy;Console&shy;Aliases<\/code> also apply to <code>Get&shy;Console&shy;Alias&shy;Exes<\/code>. <\/p>\n<p>Here&#8217;s the resulting code that tries to solve all of the problems: <\/p>\n<pre>\ntemplate&lt;typename GetLengthBytes, typename GetContents&gt;\nvoid PrintAliasValue(\n  const GetLengthBytes&amp; getLengthBytes,\n  const GetContents&amp; getContents)\n{\n  std::vector&lt;wchar_t&gt; buffer;\n  do {\n    auto bytes = getLengthBytes();\n    auto length = (bytes + sizeof(wchar_t) - 1) \/ sizeof(wchar_t);\n    buffer.resize(length);\n    ZeroMemory(buffer.data(), bytes);\n    SetLastError(ERROR_SUCCESS);\n    if (getContents(buffer.data(), bytes)) {\n      for (auto current = buffer.data();\n           current &lt; buffer.data() + buffer.size() &amp;&amp; *current;\n           current += wcslen(current) + 1) {\n        std::wcout &lt;&lt; current &lt;&lt; std::endl;\n      }\n    }\n  } while (GetLastError() == ERROR_MORE_DATA);\n}\n\nvoid do_showall(int argc, wchar_t **argv)\n{\n  auto program = argv[2];\n  PrintAliasValue(\n    [program]() { return GetConsoleAliasesLength(program); },\n    [program](LPTSTR buffer, DWORD length) {\n        return GetConsoleAliases(buffer, length, program); });\n}\n\nvoid do_showexes(int argc, wchar_t **argv)\n{\n  PrintAliasValue(\n    []() { return GetConsoleAliasExesLength(); },\n    [](LPTSTR buffer, DWORD length) {\n       return GetConsoleAliasExes(buffer, length); });\n}\n<\/pre>\n<p>The underlying algorithm is the same: Get the byte length, allocate a vector of characters, zero-initialize the data in the vector so that we can detect that a short buffer was returned, then ask for the data. If it succeeds, then read out the data, but stop when we hit one of our preallocated zeroes, or when we reach the end of the buffer, whichever comes first. If it fails because the buffer is too small, then loop back and try again. <\/p>\n<p>So there you have it. A quick tour of the console alias functions. Now you can write your own <code>DOSKEY<\/code> replacement. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Kicking the tires.<\/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-92952","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Kicking the tires.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/92952","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=92952"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/92952\/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=92952"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=92952"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=92952"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}