{"id":4463,"date":"2013-05-03T07:00:00","date_gmt":"2013-05-03T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2013\/05\/03\/creating-a-simple-pidl-for-the-times-you-care-enough-to-send-the-very-fake\/"},"modified":"2013-05-03T07:00:00","modified_gmt":"2013-05-03T07:00:00","slug":"creating-a-simple-pidl-for-the-times-you-care-enough-to-send-the-very-fake","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20130503-00\/?p=4463","title":{"rendered":"Creating a simple pidl: For the times you care enough to send the very fake"},"content":{"rendered":"<p><P>\nI&#8217;ll assume that we all know what pidls are and how the shell\nnamespace uses them.\nThat&#8217;s the prerequisite for today.\n<\/P>\n<P>\nA <I>simple pidl<\/I> is an item ID list that refers to\na file or directory that may not actually exist.\nIt&#8217;s a way of playing &#8220;what if&#8221;:\n&#8220;If there were a file or directory at this location,\nhere is what I would have created to represent it.&#8221;\nFor the times you\n<A HREF=\"http:\/\/corporate.hallmark.com\/History\/Brand-Legacy\">\ncare enough to send the very fake<\/A>.\n<\/P>\n<P>\nWe&#8217;ve seen these things in action with the\n<A HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2004\/06\/01\/145428.aspx\">\n<CODE>SHGFI_USE&shy;FILE&shy;ATTRIBUTES<\/CODE>\nflag<\/A>,\nwhich tells the\n<CODE>SH&shy;Get&shy;File&shy;Info<\/CODE> function,\n&#8220;Pretend that the file\/directory exists with the attributes\nI specified, and tell me what the icon would be, were that item\nto actually exist.&#8221;\n<\/P>\n<P>\nInternally, the\n<CODE>SH&shy;Get&shy;File&shy;Info<\/CODE> function\ncreates one of these &#8220;simple pidls&#8221;, and then asks the simple pidl\nfor its icon.\n<\/P>\n<P>\nNote that a simple pidl is really a special case of a\npidl created from a\n<CODE>WIN32_FIND_DATA<\/CODE>.\nWhen you parse a display name with a custom bind context,\nand the bind context has a\n<CODE>STR_FILE_SYS_FIND_DATA<\/CODE>\nbind context object,\nthen that object is used to control the information placed\ninto the pidl instead of getting the information from the file system.\n<\/P>\n<P>\nHere&#8217;s a program that creates a simple pidl and then does\na few simple things with it.\n(Note that the\n<A HREF=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/dd940368(v=vs.85).aspx\">\nParsing with Parameters<\/A> sample covers this topic too,\nso if you don&#8217;t like the way I did it, you can look to see\nhow somebody else did it.)\n<\/P>\n<PRE>\n#define STRICT_TYPED_ITEMIDS\n#include &lt;new&gt;\n#include &lt;windows.h&gt;\n#include &lt;ole2.h&gt;\n#include &lt;oleauto.h&gt;\n#include &lt;shlobj.h&gt;\n#include &lt;propkey.h&gt;\n#include &lt;atlbase.h&gt;\n#include &lt;atlalloc.h&gt;<\/p>\n<p>class CFileSysBindData : public IFileSystemBindData\n{\npublic:\n static HRESULT CreateInstance(\n  _In_ const WIN32_FIND_DATAW *pfd,\n  _In_ REFIID riid, _Outptr_ void **ppv);<\/p>\n<p> \/\/ *** IUnknown ***\n IFACEMETHODIMP QueryInterface(\n  _In_ REFIID riid, _Outptr_ void **ppv)\n {\n  *ppv = nullptr;\n  HRESULT hr = E_NOINTERFACE;\n  if (riid == IID_IUnknown ||\n      riid == IID_IFileSystemBindData) {\n   *ppv = static_cast&lt;IFileSystemBindData *&gt;(this);\n   AddRef();\n   hr = S_OK;\n  }\n  return hr;\n }<\/p>\n<p> IFACEMETHODIMP_(ULONG) AddRef()\n {\n  return InterlockedIncrement(&amp;m_cRef);\n }<\/p>\n<p> IFACEMETHODIMP_(ULONG) Release()\n {\n  LONG cRef = InterlockedDecrement(&amp;m_cRef);\n  if (cRef == 0) delete this;\n  return cRef;\n }<\/p>\n<p> \/\/ *** IFileSystemBindData ***\n IFACEMETHODIMP SetFindData(_In_ const WIN32_FIND_DATAW *pfd)\n {\n  m_fd = *pfd;\n  return S_OK;\n }<\/p>\n<p> IFACEMETHODIMP GetFindData(_Out_ WIN32_FIND_DATAW *pfd)\n {\n  *pfd = m_fd;\n  return S_OK;\n }<\/p>\n<p>private:\n CFileSysBindData(_In_ const WIN32_FIND_DATAW *pfd) :\n  m_cRef(1)\n {\n  m_fd = *pfd;\n }\nprivate:\n LONG m_cRef;\n WIN32_FIND_DATAW m_fd;\n};<\/p>\n<p>HRESULT CFileSysBindData::CreateInstance(\n _In_ const WIN32_FIND_DATAW *pfd,\n _In_ REFIID riid, _Outptr_ void **ppv)\n{\n *ppv = nullptr;\n CComPtr&lt;IFileSystemBindData&gt; spfsbd;\n HRESULT hr = E_OUTOFMEMORY;\n spfsbd.Attach(new (std::nothrow) CFileSysBindData(pfd));\n if (spfsbd) {\n  hr = spfsbd-&gt;QueryInterface(riid, ppv);\n }\n return hr;\n}\n<\/PRE>\n<P>\nThe\n<CODE>CFile&shy;Sys&shy;Bind&shy;Data<\/CODE>\nobject is extraordinarily boring.\nIt simply implements\n<CODE>IFile&shy;System&shy;Bind&shy;Data<\/CODE>,\nwhich is a simple interface that just babysits a\n<CODE>WIN32_FIND_DATA<\/CODE> structure.\n(There is also a\n<CODE>IFile&shy;System&shy;Bind&shy;Data2<\/CODE> interface\nwhich babysits a little more information,\nbut for the purpose of this program,\nwe&#8217;re interested only in the\n<CODE>WIN32_FIND_DATA<\/CODE>.)\n<\/P>\n<PRE>\nHRESULT CreateBindCtxWithOpts(\n _In_ BIND_OPTS *pbo, _Outptr_ IBindCtx **ppbc)\n{\n CComPtr&lt;IBindCtx&gt; spbc;\n HRESULT hr = CreateBindCtx(0, &amp;spbc);\n if (SUCCEEDED(hr)) {\n  hr = spbc-&gt;SetBindOptions(pbo);\n }\n *ppbc = SUCCEEDED(hr) ? spbc.Detach() : nullptr;\n return hr;\n}\n<\/PRE>\n<P>\nA bind context is basically a string-indexed associative array\nof COM objects.\nThere is also a\n<CODE>BIND_OPTS<\/CODE> (or <CODE>BIND_OPTS2<\/CODE>)\nstructure in there,\nbut the things most people care about are the object parameters.\nThey provide an extensible method of passing arbitrary parameters\nto a function.\n(Think of it as the COM version of the JavaScript convention of\njamming random junk into an\n<A HREF=\"http:\/\/docs.jquery.com\/Types#Options\">\nOptions<\/A> parameter.)\nYou start with a\n<CODE>IBind&shy;Ctx<\/CODE> parameter,\nand any time you need to add a new flag or parameter,\nyou just stuff it into the\n<CODE>IBind&shy;Ctx<\/CODE>.\nIf you just want to add a new boolean flag,\nyou can even ignore the contents of the object parameter and merely\nbase your behavior on whether the parameter exists at all.\n<\/P>\n<PRE>\nHRESULT AddFileSysBindCtx(\n _In_ IBindCtx *pbc, _In_ const WIN32_FIND_DATAW *pfd)\n{\n CComPtr&lt;IFileSystemBindData&gt; spfsbc;\n HRESULT hr = CFileSysBindData::CreateInstance(\n  pfd, IID_PPV_ARGS(&amp;spfsbc));\n if (SUCCEEDED(hr)) {\n  hr = pbc-&gt;RegisterObjectParam(STR_FILE_SYS_BIND_DATA,\n                                spfsbc);\n }\n return hr;\n}\n<\/PRE>\n<P>\nTo add a file system bind parameter,\nyou just create an object which implements\n<CODE>IFile&shy;System&shy;Bind&shy;Data<\/CODE>\nand register it with the bind context with\nthe string\n<CODE>STR_FILE_SYS_FIND_DATA<\/CODE>.\n<\/P>\n<PRE>\nHRESULT CreateFileSysBindCtx(\n _In_ const WIN32_FIND_DATAW *pfd, _Outptr_ IBindCtx **ppbc)\n{\n CComPtr&lt;IBindCtx&gt; spbc;\n BIND_OPTS bo = { sizeof(bo), 0, STGM_CREATE, 0 };\n HRESULT hr = CreateBindCtxWithOpts(&amp;bo, &amp;spbc);\n if (SUCCEEDED(hr)) {\n  hr = AddFileSysBindCtx(spbc, pfd);\n }\n *ppbc = SUCCEEDED(hr) ? spbc.Detach() : nullptr;\n return hr;\n}\n<\/PRE>\n<P>\nThe\n<CODE>Create&shy;File&shy;Sys&shy;Bind&shy;Ctx<\/CODE>\nfunction simply combines the two steps of creating a bind\ncontext and then adding a file system bind parameter to it.\nIn casual conversation,\na bind context is often named after the parameter inside it.\nIn this case,\nwe have a bind context with a file system bind parameter,\nso we call it a &#8220;file system bind context&#8221;.\n<\/P>\n<PRE>\nHRESULT CreateSimplePidl(\n _In_ const WIN32_FIND_DATAW *pfd,\n _In_ PCWSTR pszPath, _Outptr_ PIDLIST_ABSOLUTE *ppidl)\n{\n *ppidl = nullptr;\n CComPtr&lt;IBindCtx&gt; spbc;\n HRESULT hr = CreateFileSysBindCtx(pfd, &amp;spbc);\n if (SUCCEEDED(hr)) {\n  hr = SHParseDisplayName(pszPath, spbc, ppidl, 0, nullptr);\n }\n return hr;\n}\n<\/PRE>\n<P>\nThis is where everything comes together.\nTo create a simple pidl,\nwe take the\n<CODE>WIN32_FIND_DATAW<\/CODE> containing the metadata\nwe want to use,\nput it inside a file system bind context,\nthen use that bind context to parse the file name.\nThe presence of a file system bind context tells\nthe parser,\n&#8220;Trust me on this, just go with what&#8217;s in the bind context.&#8221;\nIt suppresses all disk access,\nand the final pidl will describe an item that exactly matches\nthe metadata you provided,\nwhether that accurately reflects reality or not.\n(You can also pass the bind context to\n<CODE>SHCreate&shy;Item&shy;From&shy;Parsing&shy;Name<\/CODE>\nif you prefer to get an <CODE>IShell&shy;Item<\/CODE>.)\n<\/P>\n<P>\nOkay, let&#8217;s take this out for a spin.\n<\/P>\n<PRE>\nvoid DoStuffWith(_In_ PCIDLIST_ABSOLUTE pidl)\n{\n \/\/ Print the file name\n wchar_t szBuf[MAX_PATH];\n if (SHGetPathFromIDListW(pidl, szBuf)) {\n  wprintf(L&#8221;Path is \\&#8221;%ls\\&#8221;\\n&#8221;, szBuf);\n }<\/p>\n<p> \/\/ Print the file size\n CComPtr&lt;IShellFolder2&gt; spsf;\n PCUITEMID_CHILD pidlChild;\n if (SUCCEEDED(SHBindToParent(pidl,\n                        IID_PPV_ARGS(&amp;spsf), &amp;pidlChild))) {\n  CComVariant vt;\n  if (SUCCEEDED(spsf-&gt;GetDetailsEx(pidlChild,\n                        &amp;PKEY_Size, &amp;vt))) {\n   if (SUCCEEDED(vt.ChangeType(VT_UI8))) {\n    wprintf(L&#8221;Size is %I64u\\n&#8221;, vt.ullVal);\n   }\n  }\n }\n}<\/p>\n<p>int __cdecl wmain(int argc, PWSTR argv[])\n{\n <A HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2004\/05\/20\/135841.aspx\">CCoInitialize<\/A> init;\n if (SUCCEEDED(init)) {\n  WIN32_FIND_DATAW fd = {};\n  fd.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;\n  fd.nFileSizeLow = 42;\n  CComHeapPtr&lt;ITEMIDLIST_ABSOLUTE&gt; spidlSimple;\n  if (SUCCEEDED(CreateSimplePidl(&amp;fd,\n                L&#8221;Q:\\\\Whatever.txt&#8221;, &amp;spidlSimple))) {\n   DoStuffWith(spidlSimple);\n  }\n }\n return 0;\n}\n<\/PRE>\n<P>\nOur test program asks for a simple pidl to\n<CODE>Q:\\Whatever.txt<\/CODE>,\nand then prints information from it.\nObserve that the creation of the simple pidl succeeds\neven though you probably don&#8217;t have a Q: drive,\nand even if you did,\nthe code never tried to access it.\nAnd when we ask the pidl,\n&#8220;Hey, what&#8217;s the file size?&#8221;\nit retrieves the fake value 42 we passed in the\n<CODE>WIN32_FIND_DATAW<\/CODE> structure.\n<\/P>\n<P>\nSure, that was kind of artificial, but\nso-called simple pidls are handy if you want to talk\nabout an object on slow media (such as a network share)\nwithout actually accessing the target device.\n<\/P>\n<P>\n<B>Exercise<\/B>:\nWhat changes are necessary in order to create a simple\npidl that refers to a file with illegal characters\nin its name?\nHint:\n<CODE>STR_NO_VALIDATE_FILENAME_CHARS<\/CODE>.\n<\/P><\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;ll assume that we all know what pidls are and how the shell namespace uses them. That&#8217;s the prerequisite for today. A simple pidl is an item ID list that refers to a file or directory that may not actually exist. It&#8217;s a way of playing &#8220;what if&#8221;: &#8220;If there were a file or directory [&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-4463","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>I&#8217;ll assume that we all know what pidls are and how the shell namespace uses them. That&#8217;s the prerequisite for today. A simple pidl is an item ID list that refers to a file or directory that may not actually exist. It&#8217;s a way of playing &#8220;what if&#8221;: &#8220;If there were a file or directory [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/4463","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=4463"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/4463\/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=4463"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=4463"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=4463"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}