Level Extreme platform
Subscription
Corporate profile
Products & Services
Support
Legal
Français
NetUserGetGroups and NetUserGetLocalGroups erratic
Message
General information
Forum:
Visual FoxPro
Category:
Windows API functions
Miscellaneous
Thread ID:
00602643
Message ID:
00603886
Views:
28
This message has been marked as the solution to the initial question of the thread.
>I have created a call to NetUserGetGroups and NetUserGetLocalGroups for use in Windows NT/2K/XP. (Thanks to Anatoliy Mogylevets' page for the utility functions.)
>
>It works as follows:
>
>  =Groups("\\myserver","user")  && display global groups
>
>or
>
>  =Groups("\\myserver","user",.T.)  && display local groups
>
>
>The problem is that it works fine the first time it is called. After that it keeps returning an error even though I am calling it with the exact same parameters. Typically the error is 2221 (user not found) or 1722 (???).
>
>Any ideas what might be going wrong? The code is listed below. Thanks.
>
>Neil
>
>
>*** GROUPS.PRG ***
>PARAMETERS cServer, cUser, local
>	
>	DECLARE INTEGER NetApiBufferFree IN netapi32 INTEGER Buffer
>	
>	DECLARE INTEGER NetUserGetLocalGroups IN netapi32 ;
>		string @ servername, ;
>		string @ username, ;
>		integer lvl, ;
>		integer flags, ;
>		integer @ buffer, ;
>		integer maxlen, ;
>		integer @ entriesread, ;
>		integer @ total
>	
>	DECLARE INTEGER NetUserGetGroups IN netapi32 ;
>		string @ servername, ;
>		string @ username, ;
>		integer lvl, ;
>		integer @ buffer, ;
>		integer maxlen, ;
>		integer @ entriesread, ;
>		integer @ total
>
>	DECLARE RtlMoveMemory IN kernel32 ;
>		STRING @ Destination, INTEGER Source, INTEGER nLength
>
>	#DEFINE GROUPINFO_0_SIZE         4
>	#DEFINE MAX_PREFERRED_LENGTH    -1
>	#DEFINE StrBufferLength   250
StrBufferLength may need to be significantly larger, since the buffer will hold a Unicode string for a group, which might be as long as 256 characters (a Unicode character is two bytes long) plus a trailing double null. I'd try 514 instead.
>	
>	STORE 0 TO lnBuffer, lnEntriesRead, lnTotalEntries
>	
>	cServer = STRCONV(cServer, 5)
>	cUser = STRCONV(cUser,5)
These really should be STRCONV(STRCONV(VFP string,1),5) since it's likely that VFP is using the ANSI single-byte representation, and I'd explicitly add CHR(0)+CHR(0) to the end of each string to ensure that the string is null-terminated properly - VFP may not append the needed trailing null characters. VFP's internal strings are not LPSTR datatypes; the NTI contains the actual length of the array of characters assigned to the string variable, which permits us to embed arbitrary binary content to string variables, binary character fields and binary memo fields.

You're about to encounter a big OUCH! Under Win9x/ME, the app must allocate and deallocate buffers for the operation itself, and NetAPIBufferFree() results in 'entrypoint not supported.' You need a truly static block in this case, which must be pointed to by lnBuffer. ClsHeap manages static block allocations by implementing a private heap and allocating/deallocating blocks on this heap (I have definite reasons for using a private heap rather than global memory related to garbage collection and future issues which might require using TLS), returning pointers for the blocks as VFP integer values, and offers a number of UDFs useful for dereferencing pointers, converting common data types, and writing to specific addresses in the process address space of the VFP app - including memory belonging to an app that uses a VFP in-process COM server (.DLL) locally. You can reach other process address spaces with a little code to request a process handle with the right access tokens.
	
>	IF local
>		lnResult = NetUserGetLocalGroups(cServer , ;
>				cUser, ;
>				0,;
>				0, ;
>				@lnBuffer, ;
>				MAX_PREFERRED_LENGTH, ;
>				@lnEntriesRead , ;
>				@lnTotalEntries)
>	ELSE
>		lnResult = NetUserGetGroups(cServer , ;
>				cUser, ;
>				0,;
>				@lnBuffer, ;
>				MAX_PREFERRED_LENGTH, ;
>				@lnEntriesRead , ;
>				@lnTotalEntries)
>	ENDIF
>	
>	IF lnResult != 0
>		? "Error code:", lnResult
>		RETURN
>	ENDIF
>	
>	lcBuffer = Repli (Chr(0),StrBufferLength)
>	= RtlMoveMemory (@lcBuffer, lnBuffer, StrBufferLength)
>	
>	* scanning returned entries
>	FOR lnEntry = 1 TO lnEntriesRead
>	    * copying the UserInfo structure to a VFP string
>	    lcGroupInfo = SUBSTR (lcBuffer,;
>	        (lnEntry-1)*GROUPINFO_0_SIZE+1, GROUPINFO_0_SIZE)
>	
>	    lpwstrGroupname     = buf2dword (SUBSTR(lcGroupInfo,  1,4))
>	
>	    * copying data from pointers to VFP strings
>	    lcGroupname     = getStrFromMem (lpwstrGroupname)
>	
>	    ? "Member of group:", lcGroupName
>	ENDFOR
>	
>	* this buffer is allocated by the system and must be freed
>	= NetApiBufferFree (lnBuffer)
>	
>RETURN
>
>FUNCTION  getStrFromMem (lnMemBlock)
>	* converting allocated in memory Unicode string to a VFP string
>	    LOCAL lcBuffer
>	    lcBuffer = SPACE(StrBufferLength)
>	    = RtlMoveMemory (@lcBuffer, lnMemBlock, StrBufferLength)
You can use lstrcpyW() instead of RtlMoveMemory() to handle copying Unicode strings, making life a lot easier. You can template lstrcpyW() from the lstrcpy() code in the GetMemString() UDF in ClsHeap.
>	    lcBuffer = SUBSTR (lcBuffer, 1, AT(Chr(0)+Chr(0),lcBuffer)-1)
>RETURN  STRTRAN(lcBuffer, Chr(0),"")
Rather than this, use STRCONV(STRCONV(lcBuufer,6),2). Better et, implement and use WideCharToMultiByte() from the Platform SDK to do the whole thing for you, an API call offered by KERNEL32.DLL; if you use a Win9x OS, you may need to add UnicoWS.dll - the Microsoft Layer for Unicode DLL, and may need to make additional code changes; there's a whole section on Unicode issues with Win9x/ME in the MSDN docs. WideCharToMultiByte would look like:
DEFINE INTEGER WideCharToMultiByte IN KERNEL32 ;
  INTEGER CodePage, ;
  INTEGER dwFlags, ;
  INTEGER lpWideCharStr, ;
  INTEGER cchWideChar, ;
  STRING @ lpMultiByteStr, ;
  INTEGER cbMultiByte, ;
  INTEGER lpDefaultChar, ;
  INTEGER @ lpUsedDefaultChar
lpWideCharStr should be the pointer to the Group Name in lnMemBlock, ccWideChar should be 0, indicating it is null-terminated, lpMultiByteString should be a pre-inited VFP string variable, cbMultiByte it's length. I use codepage 437 in general; YMMV. All other values should be 0, so:
cMyBuff = SPACE(257)
nResult=WideCharToMultiByte(437,0,lnMemBlock,0,@cMyBuff,257,0,0)
>
>FUNCTION buf2dword (lcBuffer)
>	RETURN Asc(SUBSTR(lcBuffer, 1,1)) + ;
>	       Asc(SUBSTR(lcBuffer, 2,1)) * 256 +;
>	       Asc(SUBSTR(lcBuffer, 3,1)) * 65536 +;
>	       Asc(SUBSTR(lcBuffer, 4,1)) * 16777216
>
Rather than this, I'd use the conversion DWORDToNum() from my ClsHeap class because of both speed and casting issues with a pointer address which uses the high-order bit; you could recast by wrapping the return value with BITOR(your return value,0)
>*** end GROUPS.PRG ***
>
EMail: EdR@edrauh.com
"See, the sun is going down..."
"No, the horizon is moving up!"
- Firesign Theater


NT and Win2K FAQ .. cWashington WSH/ADSI/WMI site
MS WSH site ........... WSH FAQ Site
Wrox Press .............. Win32 Scripting Journal
eSolutions Services, LLC

The Surgeon General has determined that prolonged exposure to the Windows Script Host may be addictive to laboratory mice and codemonkeys
Previous
Next
Reply
Map
View

Click here to load this message in the networking platform