{"id":97385,"date":"2017-11-10T07:00:00","date_gmt":"2017-11-10T22:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/?p=97385"},"modified":"2019-03-13T01:34:40","modified_gmt":"2019-03-13T08:34:40","slug":"20171110-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20171110-00\/?p=97385","title":{"rendered":"Cancelling the <CODE>INamespace&shy;Walk::<\/CODE><CODE>Walk<\/CODE> operation a little faster"},"content":{"rendered":"<p>We saw <a HREF=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20171109-00\/?p=97375\">last time<\/a> that you can stop a <code>INamespace&shy;Walk::<\/code><code>Walk<\/code> operation by returning a COM error code from the <code>Enter&shy;Folder<\/code> or <code>Found&shy;Item<\/code> callback. However, that may not be fast enough. <\/p>\n<p>I noted some time ago that <a HREF=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20050203-00\/?p=36533\">if you&#8217;re going to enumerate the contents of a directory, you&#8217;d best do it all at once<\/a>. And that&#8217;s what <code>INamespace&shy;Walk::<\/code><code>Walk<\/code> does. After it enters a directory, it enumerates the whole thing at one shot, and then (optionally) sorts it, and then calls the <code>Found&shy;Item<\/code> method for each item that was found. <\/p>\n<p>If you happen to enter a large directory, then the &#8220;enumerate the whole thing at one shot&#8221; step can take a while. But there&#8217;s a way to sneak in during the enumeration phase and cancel the operation: Implement the <code>IAction&shy;Progress<\/code> interface on your <code>INamespace&shy;Walk&shy;CB<\/code> object. Note that this works only if you do <i>not<\/i> pass the <code>NSWF_<\/code><code>SHOW_<\/code><code>PROGRESS<\/code> flag. If you pass the <code>NSWF_<\/code><code>SHOW_<\/code><code>PROGRESS<\/code> flag, then the progress dialog&#8217;s Cancel button controls the cancellation. <\/p>\n<p>Assuming you don&#8217;t pass the <code>NSWF_<\/code><code>SHOW_<\/code><code>PROGRESS<\/code> flag, the <code>INamespace&shy;Walk::<\/code><code>Walk<\/code> method will call <code>IAction&shy;Progress::<\/code><code>Begin<\/code> to get the party started, and <code>IAction&shy;Progress::<\/code><code>End<\/code> when it&#8217;s all over. In between, it will call <code>IAction&shy;Progress::<\/code><code>QueryCancel<\/code>. If your <code>IAction&shy;Progress::<\/code><code>QueryCancel<\/code> method returns <code>*pfCancelled = TRUE<\/code>, then the <code>INamespace&shy;Walk::<\/code><code>Walk<\/code> operation will abandon the enumeration, unwind all the entered folders with <code>Leave&shy;Folder<\/code>, and then return <code>HRESULT_<\/code><code>FROM_<\/code><code>WIN32(<\/code><code>ERROR_<\/code><code>CANCELLED)<\/code>. <\/p>\n<p>Let&#8217;s use this technique to cancel the <code>INamespace&shy;Walk::<\/code><code>Walk<\/code> operation a bit more quickly. Make the following changes to the program we had last time: <\/p>\n<pre>\n#define STRICT\n#include &lt;windows.h&gt;\n#include &lt;shlobj.h&gt;\n#include &lt;wrl\/client.h&gt;\n#include &lt;wrl\/implements.h&gt;\n#include &lt;stdio.h&gt; \/\/ Horrors! Mixing stdio and C++!\n\nnamespace wrl = Microsoft::WRL;\n\nclass WalkCallback : public wrl::RuntimeClass&lt;\n  wrl::RuntimeClassFlags&lt;wrl::ClassicCom&gt;,\n  INamespaceWalkCB,\n  <font COLOR=\"blue\">IActionProgress<\/font>&gt; \/\/ New interface!\n{\npublic:\n  \/\/ INamespaceWalkCB\n  IFACEMETHODIMP FoundItem(IShellFolder *,\n   PCUITEMID_CHILD) override\n   { m_itemCount++; return TimeoutStatus(); }\n\n  IFACEMETHODIMP EnterFolder(IShellFolder *,\n   PCUITEMID_CHILD) override\n   { m_folderCount++; return TimeoutStatus(); }\n\n  IFACEMETHODIMP LeaveFolder(IShellFolder *,\n   PCUITEMID_CHILD) override { return S_OK; }\n\n  IFACEMETHODIMP InitializeProgressDialog(PWSTR *ppszTitle,\n    PWSTR *ppszCancel) override\n    { *ppszTitle = nullptr; *ppszCancel = nullptr;\n      return E_NOTIMPL; }\n\n  <font COLOR=\"blue\">\/\/ IActionProgress - new interface!\n  IFACEMETHODIMP Begin(SPACTION, SPBEGINF) override\n  { return S_OK; }\n\n  IFACEMETHODIMP UpdateProgress(ULONGLONG, ULONGLONG) override\n  { return S_OK; }\n\n  IFACEMETHODIMP UpdateText(SPTEXT, LPCWSTR, BOOL) override\n  { return S_OK; }\n\n  IFACEMETHODIMP QueryCancel(BOOL *pfCancelled) override\n  { *pfCancelled = IsTimedOut(); return S_OK; }\n\n  IFACEMETHODIMP ResetCancel() override { return S_OK; }\n  IFACEMETHODIMP End() override { return S_OK; }<\/font>\n\n  int ItemCount() const { return m_itemCount; }\n  int FolderCount() const { return m_folderCount; }\n\nprivate:\n  bool IsTimedOut()\n    { return <a HREF=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20050531-22\/?p=35493\">GetTickCount() - m_startTime<\/a> &gt; 1000; }\n\n  HRESULT TimeoutStatus()\n    { return IsTimedOut() ?\n      HRESULT_FROM_WIN32(ERROR_CANCELLED) : S_OK; }\n\n  DWORD m_startTime = GetTickCount();\n  int m_itemCount = 0;\n  int m_folderCount = 0;\n};\n\nint __cdecl wmain(int argc, PWSTR argv[])\n{\n  <a HREF=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/\">CCoInitialize<\/a> coinit;\n\n  wrl::ComPtr&lt;INamespaceWalk&gt; walk;\n  CoCreateInstance(CLSID_NamespaceWalker, nullptr,\n    CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&amp;walk));\n\n  wrl::ComPtr&lt;IShellItem&gt; root;\n  SHCreateItemFromParsingName(argv[1], nullptr,\n    IID_PPV_ARGS(&amp;root));\n\n  auto callback = wrl::Make&lt;WalkCallback&gt;();\n\n  HRESULT hr = walk-&gt;Walk(root.Get(), NSWF_DEFAULT,\n    100, callback.Get());\n\n  printf(\"Walk completed with result 0x%08x\\n\", hr);\n  printf(\"Found %d items and %d folders\\n\",\n   callback-&gt;ItemCount(), callback-&gt;FolderCount());\n\n  return 0;\n}\n<\/pre>\n<p>All we did was add <code>IAction&shy;Progress<\/code> support to our callback object. When asked if we want to cancel the operation, we report whether the operation has timed out. <\/p>\n<p>Adding this extra support will not be noticeable when enumerating relatively small directories from relatively fast media.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>You can use <CODE>IAction&shy;Progress<\/CODE><\/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-97385","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>You can use <CODE>IAction&shy;Progress<\/CODE><\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/97385","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=97385"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/97385\/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=97385"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=97385"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=97385"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}