{"id":91701,"date":"2015-08-31T07:00:00","date_gmt":"2015-08-31T21:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20150831-00\/?p=91701\/"},"modified":"2019-03-13T12:19:01","modified_gmt":"2019-03-13T19:19:01","slug":"20150831-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20150831-00\/?p=91701","title":{"rendered":"How do I enumerate remembered connections that are not currently connected?"},"content":{"rendered":"<p><a HREF=\"http:\/\/blogs.msdn.com\/307138\/ProfileUrlRedirect.ashx\">Harry Johnston<\/a> wanted to know <a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2014\/11\/17\/10573408.aspx#10573696\">how to get a list of remembered (but not currently connected) drive mappings<\/a>. <!--more--><\/p>\n<p>The idea here is to make a tweak to the Little Program. Start with <a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2014\/11\/17\/10573408.aspx\">what we had<\/a> and make these changes: <\/p>\n<pre>\nint __cdecl main(int, char **)\n{\n HANDLE hEnum;\n WNetOpenEnum(<font COLOR=\"blue\">RESOURCE_REMEMBERED<\/font>,\n              RESOURCETYPE_DISK,\n              0,\n              NULL,\n              &amp;hEnum);\n\n ...\n}\n<\/pre>\n<p>This changes the program from enumerating connected resources to enumerating remembered resources. <\/p>\n<p>The last step is to skip the remembered resources that are also connected. But this part is not Win32 programming; it&#8217;s just programming, For each remembered resource, check if the <code>lpLocal&shy;Name<\/code> is non-null and matches an <code>lpLocal&shy;Name<\/code> that came out of an enumeration of connected resources. <\/p>\n<p>So let&#8217;s do it. We start with the header files: <\/p>\n<pre>\n#define UNICODE\n#define _UNICODE\n#define STRICT\n#include &lt;windows.h&gt;\n#include &lt;stdio.h&gt; \/\/ horrors! Mixing C and C++ I\/O!\n#include &lt;string&gt;\n#include &lt;set&gt;\n#include &lt;memory&gt;\n#include &lt;winnetwk.h&gt;\n<\/pre>\n<p>Since we are using classes like <code>std::set<\/code> which throw exceptions, we need to wrap our resources inside RAII classes. Here&#8217;s one for network resource enumeration: <\/p>\n<pre>\nclass CNetEnumerator\n{\npublic:\n CNetEnumerator() = default;\n ~CNetEnumerator() { if (m_hEnum) WNetCloseEnum(m_hEnum); }\n operator HANDLE() { return m_hEnum; }\n HANDLE* operator&amp;() { return &amp;m_hEnum; }\nprivate:\n HANDLE m_hEnum = nullptr;\n};\n<\/pre>\n<p>Here is our function to enumerate all network resources. It uses a callback because <a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2014\/11\/17\/10573408.aspx#10573690\">arghhhhhhhhhhh wishes it were so<\/a>. <\/p>\n<pre>\ntemplate&lt;typename Callback&gt;\nvoid for_each_network_resource(\n    DWORD dwScope,\n    DWORD dwType,\n    DWORD dwUsage,\n    LPNETRESOURCE pnrIn,\n    Callback callback)\n{\n CNetEnumerator hEnum;\n WNetOpenEnum(dwScope, dwType, dwUsage, pnrIn, &amp;hEnum);\n\n const DWORD elements = 65536 \/ sizeof(NETRESOURCE);\n static_assert(elements &gt; 1, \"Must have room for data\");\n std::unique_ptr&lt;NETRESOURCE&gt; buffer(new NETRESOURCE[elements]);\n\n DWORD err;\n do {\n  DWORD cEntries = INFINITE;\n  DWORD cb = elements * sizeof(NETRESOURCE);\n  err = WNetEnumResource(hEnum, &amp;cEntries, buffer.get(), &amp;cb);\n  if (err == NO_ERROR || err == ERROR_MORE_DATA) {\n   for (DWORD i = 0; i &lt; cEntries; i++) {\n    callback(&amp;buffer[i]);\n   }\n  }\n } while (err == ERROR_MORE_DATA);\n}\n<\/pre>\n<p>There is a bit of trickery to get the enumeration buffer into a form that C++ likes. We had previously used <code>Local&shy;Alloc<\/code>, which is guaranteed to return memory suitably aligned for <code>NETRESOURCE<\/code>. However, we can&#8217;t do it for <code>new BYTE[]<\/code>, since that returns only byte-aligned data. We solve this problem by explicitly allocating <code>NETRESOURCE<\/code> objects, but choosing a number so that the result is close to our desired buffer size.&sup1; <\/p>\n<p>We need another helper class so we can create a case-insensitive set. <\/p>\n<pre>\nstruct CaseInsensitiveWstring\n{\n bool operator()(const std::wstring&amp; a, const std::wstring&amp; b) const {\n  return CompareStringOrdinal(a.c_str(), a.length(),\n                              b.c_str(), b.length(), TRUE) == CSTR_LESS_THAN;\n }\n};\n<\/pre>\n<p>Okay, now we can start doing actual work: <\/p>\n<pre>\nvoid report(PCWSTR pszLabel, PCWSTR pszValue)\n{\n printf(\"%ls = %ls\\n\", pszLabel, pszValue ? pszValue : L\"(null)\");\n}\n\nint __cdecl wmain(int, wchar_t **)\n{\n std::set&lt;std::wstring, CaseInsensitiveWstring&gt; connected;\n\n \/\/ Collect the local resources which are already connected.\n for_each_network_resource(RESOURCE_CONNECTED,\n  RESOURCETYPE_DISK, 0, nullptr, [&amp;](LPNETRESOURCE pnr) {\n   if (pnr-&gt;lpLocalName != nullptr) {\n    connected.emplace(pnr-&gt;lpLocalName);\n   }\n  });\n\n \/\/ Now look for remembered resources that are not connected.\n for_each_network_resource(RESOURCE_REMEMBERED,\n  RESOURCETYPE_DISK, 0, nullptr, [&amp;](LPNETRESOURCE pnr) {\n   if (pnr-&gt;lpLocalName == nullptr ||\n       connected.find(pnr-&gt;lpLocalName) == connected.end()) {\n    report(L\"localName\", pnr-&gt;lpLocalName);\n    report(L\"remoteName\", pnr-&gt;lpRemoteName);\n    report(L\"provider\", pnr-&gt;lpProvider);\n    printf(\"\\n\");\n   }\n  });\n\n return 0;\n}\n<\/pre>\n<p>Not exciting. Mostly consists of boring typing. But hey, that&#8217;s what programming is like most of the time. <\/p>\n<p>&sup1; If we were being super-weenies about the buffer size, we could have written <\/p>\n<pre>\n union EnumBuffer {\n  BYTE bytes[65536];\n  NETRESOURCE nr;\n };\n\n std::unique_ptr&lt;EnumBuffer&gt; buffer(new EnumBuffer());\n LPNETRESOURCE pnr = &amp;buffer-&gt;nr;\n ...\n  DWORD cb = sizeof(EnumBuffer);\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>WNetEnumResources again.<\/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-91701","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>WNetEnumResources again.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/91701","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=91701"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/91701\/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=91701"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=91701"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=91701"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}