{"id":5003,"date":"2013-03-11T07:00:00","date_gmt":"2013-03-11T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2013\/03\/11\/how-can-i-see-what-files-and-shares-are-being-accessed-remotely-and-the-general-usage-pattern-for-the-netxxx-functions\/"},"modified":"2013-03-11T07:00:00","modified_gmt":"2013-03-11T07:00:00","slug":"how-can-i-see-what-files-and-shares-are-being-accessed-remotely-and-the-general-usage-pattern-for-the-netxxx-functions","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20130311-00\/?p=5003","title":{"rendered":"How can I see what files and shares are being accessed remotely, and the general usage pattern for the NetXxx functions"},"content":{"rendered":"<p><P>\nToday&#8217;s Little Program is a command line version of the\nShared Folders MMC snap-in.\nWhy?\nBecause it illustrates the usage pattern for the <CODE>Net&shy;Xxx<\/CODE>\nfamily of functions.\n(It&#8217;s also a clone of the networking portion of the\n<A HREF=\"http:\/\/technet.microsoft.com\/en-us\/library\/cc732490\">\n<CODE>openfiles<\/CODE><\/A> tool.)\n<\/P>\n<P>\nThe <CODE>Net&shy;Xxx<\/CODE> family of functions generally work like this:\n<\/P>\n<UL>\n<LI>You pass in some parameters that describe what you want.\n    Server name, that sort of thing.\n<LI>You pass a &#8220;level&#8221; parameter that describes\n    what information you want.\n<LI>The function allocates memory to hold the results you requested,\n    and\n    <A HREF=\"http:\/\/msdn.microsoft.com\/library\/aa370676.aspx\">\n    it returns a pointer to that memory<\/A>\n    through a <CODE>bufptr<\/CODE> parameter.\n<LI>If the function returns an array, then\n    <UL>\n    <LI>You can tell the function the maximum number of results you want.\n    <LI>The function tells you how much information it returned.\n    <LI>If the function did not retrieve all the results (because it\n        exceeded your maximum), it tells you how to get the rest of them.\n    <\/UL>\n<LI>When you are finished, you free the memory\n    with <CODE>Net&shy;Api&shy;Buffer&shy;Free<\/CODE>.\n<\/UL>\n<P>\nWe&#8217;ll start with the non-array case,\nsince that is much simpler.\nSuppose you want to get the level 123 information for a Thing.\n<\/P>\n<PRE>\nTHING_INFO_123 *pinfo123;\nif (NetThingGetInfo(pszThing,\n                    123, (LPBYTE*)&amp;pinfo123) == NERR_Success)\n{\n    DoSomethingWith(pinfo123);\n    NetApiBufferFree(pinfo123);\n}\n<\/PRE>\n<P>\nYou call the function, passing the desired information level\nand a pointer to the variable you want to receive the results.\nYou then use the results, and then free them.\nLet&#8217;s try it with a simple function to get information about a user.\n<\/P>\n<PRE>\n#define UNICODE\n#define <A HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2004\/02\/12\/71851.aspx\">_UNICODE<\/A>\n#define STRICT\n#include &lt;windows.h&gt;\n#include &lt;lm.h&gt;\n#include &lt;stdio.h&gt;<\/p>\n<p>void PrintProperty(PCWSTR pszProperty, PCWSTR pszValue)\n{\n wprintf(L&#8221;%ls: %ls\\n&#8221;, pszProperty,\n                        pszValue ? pszValue : L&#8221;&lt;none&gt;&#8221;);\n}<\/p>\n<p>int __cdecl wmain(int argc, wchar_t **argv)\n{\n USER_INFO_10 *pinfo10;\n if (NetUserGetInfo(NULL, L&#8221;Administrator&#8221;, 10,\n                    (LPBYTE*)&amp;pinfo10) == NERR_Success) {\n  PrintProperty(L&#8221;Name&#8221;, pinfo10-&gt;usri10_name);\n  PrintProperty(L&#8221;Comment&#8221;, pinfo10-&gt;usri10_comment);\n  PrintProperty(L&#8221;User comment&#8221;, pinfo10-&gt;usri10_usr_comment);\n  PrintProperty(L&#8221;Full name&#8221;, pinfo10-&gt;usri10_full_name);\n  NetApiBufferFree(pinfo10);\n }\n return 0;\n}\n<\/PRE>\n<P>\nThe trickier case is the functions that return arrays of data.\nIn that case, you need to call the functions in a loop,\nsimilar to <CODE>Find&shy;Next&shy;File<\/CODE>,\nin order to read all the data.\nBut unlike\n<CODE>Find&shy;Next&shy;File<\/CODE>,\nthe functions return chunks of data rather than just one\nentry at a time.\n<\/P>\n<P>\nThe general pattern goes like this:\n<\/P>\n<PRE>\nTHING_INFO_123 *pinfo123;\nNET_API_STATUS status;\nDWORD_PTR resumeHandle = 0;\ndo {\n DWORD actual, estimatedTotal;\n status = NetThingEnum(pszThing, 123,\n                       (LPBYTE*)&amp;pinfo123,\n                       MAX_PREFERRED_LENGTH,\n                       &amp;actual,\n                       &amp;estimatedTotal,\n                       &amp;resumeHandle);\n if (status == NERR_Success ||\n     status == ERROR_MORE_DATA) {\n  for (DWORD i = 0; i &lt; actual; i++) {\n   DoSomethingWith(&amp;pinfo123[i]);\n  }\n  NetApiBufferFree(pinfo123);\n }\n} while (status == ERROR_MORE_DATA);\n<\/PRE>\n<P>\nThe general pattern is to start by calling the\ndata retrieval function.\nIf the function returns with\n<CODE>NERR_Success<\/CODE>,\nthen it means that it was able to get all the information\nyou requested.\nIf the function returns with\n<CODE>ERROR_MORE_DATA<\/CODE>,\nthen it means that it was able to get some of the information\nyou requested.\nIn either of those two cases, it returns the actual number\nof items retrieved in the <CODE>actual<\/CODE> parameter,\nwhich you use to read the values out of the results.\n(It also returns\nan estimate of the total number of items remaining in the\n<CODE>estimated&shy;Total<\/CODE> variable,\nbut very few people use that.)\n<\/P>\n<P>\nIf the return value was\n<CODE>ERROR_MORE_DATA<\/CODE>,\nthen you go back and call the function again to get the next\nbatch of results.\n<\/P>\n<P>\nThe way the functions can tell whether you&#8217;re starting a new\noperation or continuing an old one is via the\n<CODE>resume&shy;Handle<\/CODE> parameter,\nwhich must be a pointer to a <CODE>DWORD_PTR<\/CODE> variable\nwhich the function updates.\nOn the first call, set the <CODE>DWORD_PTR<\/CODE> to zero.\nIf the function returns partial results, then it puts an opaque\nvalue into the <CODE>resume&shy;Handle<\/CODE> so it can remember\nwhere it needs to continue.\n(By comparison,\nthe <CODE>Find&shy;First&shy;File<\/CODE> passes the\nresume handle as its return value.)\n<\/P>\n<P>\nNote that there is no equivalent to\n<CODE>Find&shy;Close<\/CODE> when you are finished with\nthe function.\nIf you don&#8217;t want to retrieve all the results,\nyou just abandon the handle.\n<\/P>\n<PRE>\nint __cdecl wmain(int argc, wchar_t **argv)\n{\n FILE_INFO_3 *pinfo3;\n NET_API_STATUS status;\n DWORD_PTR resumeHandle = 0;\n do {\n  DWORD actual, estimatedTotal;\n  status = NetFileEnum(NULL, NULL, NULL, 3,\n                       (LPBYTE*)&amp;pinfo3,\n                       MAX_PREFERRED_LENGTH,\n                       &amp;actual,\n                       &amp;estimatedTotal,\n                       &amp;resumeHandle);\n  if (status == NERR_Success ||\n      status == ERROR_MORE_DATA) {\n   for (DWORD i = 0; i &lt; actual; i++) {\n    PrintProperty(L&#8221;Path&#8221;, pinfo3[i].fi3_pathname);\n    PrintProperty(L&#8221;User&#8221;, pinfo3[i].fi3_username);\n    if (pinfo3[i].fi3_permissions &amp; PERM_FILE_READ) {\n     PrintProperty(L&#8221;Access&#8221;, L&#8221;READ&#8221;);\n    }\n    if (pinfo3[i].fi3_permissions &amp; PERM_FILE_WRITE) {\n     PrintProperty(L&#8221;Access&#8221;, L&#8221;WRITE&#8221;);\n    }\n    if (pinfo3[i].fi3_permissions &amp; PERM_FILE_CREATE) {\n     PrintProperty(L&#8221;Access&#8221;, L&#8221;CREATE&#8221;);\n    }\n   }\n   NetApiBufferFree(pinfo3);\n  }\n } while (status == ERROR_MORE_DATA);\n return 0;\n}\n<\/PRE>\n<P>\nI&#8217;ve been ignoring the parameter known as\n<CODE>prefmaxlen<\/CODE>\nbecause you pretty much\nalways pass <CODE>MAX_PREFERRED_LENGTH<\/CODE>.\nThe parameter lets you limit how much information is returned\nat a time,\nbut you nearly always want\n<A HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2006\/04\/06\/569873.aspx\">\nas much as possible<\/A>\n(which is why you nearly always\npass <CODE>MAX_PREFERRED_LENGTH<\/CODE>).\nIf, for some reason,\nyou want to retrieve only a little bit at a time,\nyou can pass a smaller value as the\n<CODE>prefmaxlen<\/CODE>.\nNote that\n<CODE>prefmaxlen<\/CODE> is in bytes, not elements,\nand the size in bytes needs to include the auxiliary data\n(like the strings),\nnot just the structures.\nIf you pass a custom\n<CODE>prefmaxlen<\/CODE>,\nthen you also have to be prepared to handle the\n<CODE>NERR_Buf&shy;Too&shy;Small<\/CODE> error code,\nwhich means\n&#8220;The value you passed in\n<CODE>prefmaxlen<\/CODE>\nwasn&#8217;t big enough to hold even <I>one<\/I> item.\nYou&#8217;ll have to try again with a bigger buffer size.&#8221;\nIf you&#8217;re advanced enough to use a custom buffer size,\nthen you&#8217;re advanced enough to figure out how to tweak\nthe algorithm to handle it properly.\n<\/P>\n<P>\nNote that I have no special knowledge of the <CODE>Net&shy;Xxxx<\/CODE>\nfamily of functions.\nI figured this out by reading the documentation.\n<\/P><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Today&#8217;s Little Program is a command line version of the Shared Folders MMC snap-in. Why? Because it illustrates the usage pattern for the Net&shy;Xxx family of functions. (It&#8217;s also a clone of the networking portion of the openfiles tool.) The Net&shy;Xxx family of functions generally work like this: You pass in some parameters that describe [&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-5003","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Today&#8217;s Little Program is a command line version of the Shared Folders MMC snap-in. Why? Because it illustrates the usage pattern for the Net&shy;Xxx family of functions. (It&#8217;s also a clone of the networking portion of the openfiles tool.) The Net&shy;Xxx family of functions generally work like this: You pass in some parameters that describe [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/5003","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=5003"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/5003\/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=5003"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=5003"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=5003"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}