{"id":5433,"date":"2013-01-28T07:00:00","date_gmt":"2013-01-28T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2013\/01\/28\/finding-a-printer-and-then-creating-a-shortcut-to-that-printer\/"},"modified":"2013-01-28T07:00:00","modified_gmt":"2013-01-28T07:00:00","slug":"finding-a-printer-and-then-creating-a-shortcut-to-that-printer","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20130128-00\/?p=5433\/","title":{"rendered":"Finding a printer, and then creating a shortcut to that printer"},"content":{"rendered":"<p><P>\nToday&#8217;s &#8220;Little Program&#8221;\ndoes two things:\nIt looks for a printer in the Printers folder,\nand then once it finds it, it creates a shortcut to that printer.\n<\/P>\n<P>\nAs is common with &#8220;Little Programs&#8221;,\nI don&#8217;t bother with error checking.\nI&#8217;ll leave you to do that.\n<\/P>\n<P>\nSecond part first, since it is handy on its own:\nCreating a shortcut to an arbitrary item in the shell namespace,\nprovided either in the form of an ID list or a shell item.\n(The ID list is the thing that identifies an item in the shell namespace.)\n<\/P>\n<PRE>\nvoid CreateShortcutToIDList(PCWSTR pszName, PCUIDLIST_ABSOLUTE pidl)\n{\n CComPtr&lt;IShellLink&gt; spsl;\n spsl.CoCreateInstance(CLSID_ShellLink);\n spsl-&gt;SetIDList(pidl);\n CComQIPtr&lt;IPersistFile&gt;(spsl)-&gt;Save(pszName, TRUE);\n}<\/p>\n<p>void CreateShortcutToItem(PCWSTR pszName, IShellItem *pitem)\n{\n CComHeapPtr&lt;ITEMIDLIST_ABSOLUTE&gt; spidl;\n CComQIPtr&lt;IPersistIDList&gt;(pitem)-&gt;GetIDList(&amp;spidl);\n CreateShortcutToIDList(pszName, spidl);\n}\n<\/PRE>\n<P>\nNeither of these is particular complicated.\nTo create a shortcut given an ID list:\n<\/P>\n<UL>\n<LI>Create a brand new <CODE>Shell&shy;Link<\/CODE> object.\n<LI>Tell that shell link object to point to our desired ID list.\n<LI>Save the shell link.\n<\/UL>\n<P>\nTo create a shortcut given a shell item:\n<\/P>\n<UL>\n<LI>Ask the <CODE>IShell&shy;Item<\/CODE> for its ID list.\n<LI>Create a shortcut to that ID list.\n<\/UL>\n<P>\nOkay, now the first half:\nFinding the printer.\nThat is a matter of binding to the Printers folder\nand enumerating its contents.\nWhen we find the one whose name matches the target printer,\nwe declare victory.\n<\/P>\n<PRE>\nint __cdecl wmain(int argc, wchar_t **argv)\n{\n <A HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2004\/05\/20\/135841.aspx\">CCoInitialize<\/A> init;\n CComPtr&lt;IShellItem&gt; spPrinters;\n SHGetKnownFolderItem(FOLDERID_PrintersFolder, KF_FLAG_DEFAULT,\n                      nullptr, IID_PPV_ARGS(&amp;spPrinters));\n CComPtr&lt;IEnumShellItems&gt; spEnum;\n spPrinters-&gt;BindToHandler(nullptr, BHID_EnumItems,\n                              IID_PPV_ARGS(&amp;spEnum));\n for (CComPtr&lt;IShellItem&gt; spPrinter;\n      spEnum-&gt;Next(1, &amp;spPrinter, nullptr) == S_OK;\n      spPrinter.Release()) {\n  CComHeapPtr&lt;wchar_t&gt; spszName;\n  spPrinter-&gt;GetDisplayName(SIGDN_NORMALDISPLAY, &amp;spszName);\n  wprintf(L&#8221;Found printer \\&#8221;%ls\\&#8221;\\n&#8221;, spszName);\n  if (lstrcmpiW(spszName, argv[1]) == 0) {\n   wprintf(L&#8221;Creating shortcut as \\&#8221;%ls\\&#8221;\\n&#8221;, argv[2]);\n   CreateShortcutToItem(argv[2], spPrinter);\n  }\n }\n return 0;\n}\n<\/PRE>\n<P>\nThis is a little trickier, but not by much.\n<\/P>\n<UL>\n<LI>Initialize COM.\n<LI>Get the <CODE>IShell&shy;Item<\/CODE> for the Printers folder.\n<LI>Get the enumerator for the Printers folder.\n<LI>For each item in the Printers folder:\n    <UL>\n    <LI>Get its name and print it just for diagnostic purposes.\n    <LI>If the name matches the one we&#8217;re looking for,\n        then create the shortcut.\n    <\/UL>\n<\/UL>\n<P>\nHere are the header files I used:\n<\/P>\n<PRE>\n#define <A HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2013\/01\/24\/10387757.aspx\">STRICT_TYPED_ITEMIDS<\/A> \/\/ enable stricter type checking on ITEMIDs\n#include &lt;windows.h&gt;\n#include &lt;atlbase.h&gt;        \/\/ for CComPtr, CComQIPtr\n#include &lt;atlalloc.h&gt;       \/\/ for CComHeapPtr\n#include &lt;shlobj.h&gt;         \/\/ for shell interfaces\n#include &lt;knownfolders.h&gt;   \/\/ for FOLDERID_PrintersFolder\n#include &lt;stdio.h&gt;          \/\/ for wprintf\n<\/PRE>\n<P>\nFor those of you who prefer to work with CSIDLs,\nthe change is relatively minor.\nReplace the <CODE>SHGet&shy;Known&shy;Folder&shy;Item<\/CODE>\ncall with\n<\/P>\n<PRE>\n <A HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2011\/08\/31\/10203215.aspx\">BindToCsidlItem<\/A>(CSIDL_PRINTERS, &amp;spPrinters);\n<\/PRE>\n<P>\nYou may have noticed that I used\n<A HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2011\/12\/27\/10251210.aspx\">\nthe for-if anti-pattern<\/A>.\nI could&#8217;ve gone for the item directly by using\n<CODE>SHCreate&shy;Item&shy;From&shy;Relative&shy;Name<\/CODE>:\n&lt;!&#8211;\n<CODE>IShell&shy;Folder::Parse&shy;Display&shy;Name<\/CODE>:\n&#8211;&gt;\n<\/P>\n<PRE>\nint __cdecl wmain(int argc, wchar_t **argv)\n{\n CCoInitialize init;\n CComPtr&lt;IShellItem&gt; spPrinters;\n SHGetKnownFolderItem(FOLDERID_PrintersFolder, KF_FLAG_DEFAULT,\n                      nullptr, IID_PPV_ARGS(&amp;spPrinters));\n CComPtr&lt;IShellItem&gt; spPrinter;\n SHCreateItemFromRelativeName(spPrinters, argv[1], nullptr,\n                              IID_PPV_ARGS(&amp;spPrinter));\n CreateShortcutToItem(argv[2], spPrinter);\n return 0;\n}\n<\/PRE>\n<P>\nAs before,\nif you&#8217;re the sort of person who prefers to do things\nold-school, you can parse the name yourself,\nat which point you may as well give up on shell items,\nhike up your pants,\nand do it\n<A HREF=\"http:\/\/www.simpsoncrazy.com\/lists\/grampa-stories\">\nwith an onion on your belt<\/A>:\n<\/P>\n<PRE>\nHRESULT BindToIDList(PCUIDLIST_ABSOLUTE pidl,\n                     REFIID riid, void **ppv)\n{\n *ppv = nullptr;\n CComPtr&lt;IShellFolder&gt; spsfDesktop;\n HRESULT hr = SHGetDesktopFolder(&amp;spsfDesktop);\n if (SUCCEEDED(hr)) {\n  if (pidl-&gt;mkid.cb) {\n   hr = spsfDesktop-&gt;BindToObject(pidl, nullptr, riid, ppv);\n  } else {\n   hr = spsfDesktop-&gt;QueryInterface(riid, ppv);\n  }\n }\n return hr;\n}<\/p>\n<p>int __cdecl wmain(int argc, wchar_t **argv)\n{\n CCoInitialize init;\n CComHeapPtr&lt;ITEMIDLIST_ABSOLUTE&gt; spidlPrinters;\n SHGetSpecialFolderLocation(nullptr,\n                  CSIDL_PRINTERS, &amp;spidlPrinters);\n CComPtr&lt;IShellFolder&gt; spsfPrinters;\n BindToIDList(spidlPrinters, IID_PPV_ARGS(&amp;spsfPrinters));<\/p>\n<p> ULONG cchEaten;\n DWORD dwAttributes = 0;\n CComHeapPtr&lt;ITEMIDLIST_RELATIVE&gt; spidl;\n spsfPrinters-&gt;ParseDisplayName(nullptr, nullptr, argv[1],\n                        &amp;cchEaten, &amp;spidl, &amp;dwAttributes);<\/p>\n<p> CComHeapPtr&lt;ITEMIDLIST_ABSOLUTE&gt; spidlPrinter;\n spidlPrinter.Attach(ILCombine(spidlPrinters, spidl));\n CreateShortcutToIDList(argv[2], spidlPrinter);\n return 0;\n}\n<\/PRE>\n<P>\nThe\n<CODE>Bind&shy;To&shy;ID&shy;List<\/CODE> function is nothing\nspecial;\nwe already saw the guts of it when we wrote\n<A HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2011\/08\/31\/10203215.aspx\">\n<CODE>Bind&shy;To&shy;Csidl&shy;Item<\/CODE><\/A>.\n<\/P>\n<P>\nThe main program proceeds in three steps:\n<\/P>\n<UL>\n<LI>Get the ID list for the Printers folder and bind to it.\n<LI>Parse the printer name, producing a printer ID list.\n<LI>Create a shortcut to the ID list for the printers folder\n    combined with the printer ID list.\n<\/UL><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Today&#8217;s &#8220;Little Program&#8221; does two things: It looks for a printer in the Printers folder, and then once it finds it, it creates a shortcut to that printer. As is common with &#8220;Little Programs&#8221;, I don&#8217;t bother with error checking. I&#8217;ll leave you to do that. Second part first, since it is handy on its [&hellip;]<\/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-5433","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Today&#8217;s &#8220;Little Program&#8221; does two things: It looks for a printer in the Printers folder, and then once it finds it, it creates a shortcut to that printer. As is common with &#8220;Little Programs&#8221;, I don&#8217;t bother with error checking. I&#8217;ll leave you to do that. Second part first, since it is handy on its [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/5433","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=5433"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/5433\/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=5433"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=5433"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=5433"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}