CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 10 of 10
  1. #1
    Join Date
    Nov 2003
    Location
    Portland, OR
    Posts
    894

    [RESOLVED] How to tell if a user account is set up with a roaming profile?

    Is there a way to know from a Windows service application (running as a local-system) if a specific logged in interactive user account is configured with a roaming profile?

    PS. Oh yes, forgot to say, I need this to be compatible with Windows XP SP3.
    Last edited by dc_2000; November 23rd, 2013 at 12:33 AM.

  2. #2
    2kaud's Avatar
    2kaud is online now Super Moderator Power Poster
    Join Date
    Dec 2012
    Location
    England
    Posts
    7,822

    Re: How to tell if a user account is set up with a roaming profile?

    You can use NetUserGetInfo() (http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx) to obtain information about a specified user or NetUserEnum() (http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx) to obtain information about all users. Level 3 provides details about the path to the user's profile.

    Note that using NetUserGetInfo() with level 3 can only be performed on a server but NetUserEnum() with level 3 can be done on a workstation.
    All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!

    C++23 Compiler: Microsoft VS2022 (17.6.5)

  3. #3
    Join Date
    Nov 2003
    Location
    Portland, OR
    Posts
    894

    Re: How to tell if a user account is set up with a roaming profile?

    Thanks. I need this for a client workstation only. Let me show you what I came up with so far:

    Code:
    LUID* pPSL = NULL;
    ULONG nNumSess;
    NTSTATUS ntStatus = LsaEnumerateLogonSessions(&nNumSess, &pPSL);
    if(ntStatus == STATUS_SUCCESS)
    {
    	CString strUserName, strDomain, strProfile;
    
    	for(ULONG s = 0; s < nNumSess; s++)
    	{
    		PSECURITY_LOGON_SESSION_DATA pSLSD = NULL;
    		ntStatus = LsaGetLogonSessionData(&pPSL[s], &pSLSD);
    		if(ntStatus == STATUS_SUCCESS)
    		{
    			if(pSLSD->LogonType == Interactive ||
    				pSLSD->LogonType == RemoteInteractive)
    			{
    				memcpy(strUserName.GetBufferSetLength(pSLSD->UserName.Length), 
    					pSLSD->UserName.Buffer, pSLSD->UserName.Length * sizeof(TCHAR));
    				strUserName.ReleaseBuffer();
    
    				memcpy(strDomain.GetBufferSetLength(pSLSD->LogonDomain.Length), 
    					pSLSD->LogonDomain.Buffer, pSLSD->LogonDomain.Length * sizeof(TCHAR));
    				strDomain.ReleaseBuffer();
    
    				if(!osIsWinXP_Only)
    				{
    					//Vista and later OS
    					memcpy(strProfile.GetBufferSetLength(pSLSD->ProfilePath.Length), 
    						pSLSD->ProfilePath.Buffer, pSLSD->ProfilePath.Length * sizeof(TCHAR));
    					strProfile.ReleaseBuffer();
    				}
    				else
    				{
    					//Solution for Windows XP
    					NET_API_STATUS nStatus;
    
    					CString strNm = /*strDomain + L"\\" +*/ strUserName;
    
    					USER_INFO_3* pBuf3 = NULL;
    					nStatus = NetUserGetInfo(NULL, 
    						strNm,
    						3,
    						(LPBYTE*)&pBuf3);
    					if(nStatus == NERR_Success)
    					{
    						strProfile = pBuf3->usri3_profile ? pBuf3->usri3_profile : L"";
    					}
    					else
    					{
    						//Error
    						_TRACE(L"<Err=%d, %s>", nStatus, strNm.GetString());
    					}
    
    					if(pBuf3)
    					{
    						NetApiBufferFree(pBuf3);
    						pBuf3 = NULL;
    					}
    				}
    			}
    		}
    
    		//Free mem
    		if(pSLSD)
    		{
    			VERIFY(LsaFreeReturnBuffer(pSLSD) == STATUS_SUCCESS);
    		}
    	}
    }
    
    //Free mem
    if(pPSL)
    {
    	VERIFY(LsaFreeReturnBuffer(pPSL) == STATUS_SUCCESS);
    }
    As you see, the code above has a solution for Vista and later workstation, that works perfectly. The issue is the XP.

    I originally went with your suggestion regarding NetUserEnum(), but it would enumerate only local users. If I was logged in as a DC user, it would still give me users from the local workstation. (Technically I didn't need all DC users, but just the one that I was currently logged in with.) I then tried NetUserGetInfo() as I showed above, and the results are mixed:

    (Again to reiterate, I'm calling this from a service.)

    1. If I call the code above when a local user account is logged in, it correctly returns empty string in the `usri3_profile` for it.

    2. But if I log in as a DC user to the same workstation, it always returns error 2221, or "The user name could not be found."

    3. I then tried to specify each user in the "domain\user" format as such:

    Code:
    CString strNm = strDomain + L"\\" + strUserName;
    but then NetUserGetInfo() always fails, again with the error code 2221.

    So what am I missing here?

  4. #4
    2kaud's Avatar
    2kaud is online now Super Moderator Power Poster
    Join Date
    Dec 2012
    Location
    England
    Posts
    7,822

    Re: How to tell if a user account is set up with a roaming profile?

    If this code is running on a client workstation then as I mentioned you can't use level 3 with NetUserGetInfo as that level is only valid on servers. You have to use NetUserEnum() to get level 3 data. If you specify NULL as the first parameter it only enumerates local users for the local machine. If you want to enumerate DC users then you will need to specify the name of a DC server as parameter 1.
    All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!

    C++23 Compiler: Microsoft VS2022 (17.6.5)

  5. #5
    Join Date
    Nov 2003
    Location
    Portland, OR
    Posts
    894

    Re: How to tell if a user account is set up with a roaming profile?

    I tried to add the following code to my code snippet above:

    Code:
    //Solution for Windows XP
    NET_API_STATUS nStatus;
    
    CString strNm = /*strDomain + L"\\" +*/ strUserName;
    
    DWORD dwPrefMaxLen = MAX_PREFERRED_LENGTH;
    DWORD dwEntriesRead = 0;
    DWORD dwTotalEntries = 0;
    DWORD dwResumeHandle = 0;
    
    USER_INFO_3* pBuf = NULL;
    
    nStatus = NetUserEnum(strDomain,	//='LogonDomain' member from SECURITY_LOGON_SESSION_DATA
    			3,
    			FILTER_NORMAL_ACCOUNT, // global users
    			(LPBYTE*)&pBuf,
    			dwPrefMaxLen,
    			&dwEntriesRead,
    			&dwTotalEntries,
    			&dwResumeHandle);
    But I always get `nStatus` == 53, which I believe is ERROR_BAD_NETPATH.

  6. #6
    Arjay's Avatar
    Arjay is offline Moderator / EX MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    13,490

    Re: How to tell if a user account is set up with a roaming profile?

    What network path format are you trying?

  7. #7
    Join Date
    Nov 2003
    Location
    Portland, OR
    Posts
    894

    Re: How to tell if a user account is set up with a roaming profile?

    @Arjay: I believe it's provided in the old NetBIOS format.

    I think I was able to make it work (through the trial & error process). Here's the code:

    Code:
    LUID* pPSL = NULL;
    ULONG nNumSess;
    NTSTATUS ntStatus = LsaEnumerateLogonSessions(&nNumSess, &pPSL);
    if(ntStatus == STATUS_SUCCESS)
    {
    	CString strUserName, strDomain, strProfile;
    	CString strServerName;
    
    	for(ULONG s = 0; s < nNumSess; s++)
    	{
    		PSECURITY_LOGON_SESSION_DATA pSLSD = NULL;
    		ntStatus = LsaGetLogonSessionData(&pPSL[s], &pSLSD);
    		if(ntStatus == STATUS_SUCCESS)
    		{
    			if(pSLSD->LogonType == Interactive ||
    				pSLSD->LogonType == RemoteInteractive)
    			{
    				memcpy(strUserName.GetBufferSetLength(pSLSD->UserName.Length), 
    					pSLSD->UserName.Buffer, pSLSD->UserName.Length * sizeof(TCHAR));
    				strUserName.ReleaseBuffer();
    
    				memcpy(strDomain.GetBufferSetLength(pSLSD->LogonDomain.Length), 
    					pSLSD->LogonDomain.Buffer, pSLSD->LogonDomain.Length * sizeof(TCHAR));
    				strDomain.ReleaseBuffer();
    
    				if(!osIsWinXP_Only)
    				{
    					//Vista and later OS
    					memcpy(strProfile.GetBufferSetLength(pSLSD->ProfilePath.Length), 
    						pSLSD->ProfilePath.Buffer, pSLSD->ProfilePath.Length * sizeof(TCHAR));
    					strProfile.ReleaseBuffer();
    				}
    				else
    				{
    					//Solution for Windows XP
    					NET_API_STATUS nStatus;
    
    					memcpy(strServerName.GetBufferSetLength(pSLSD->LogonServer.Length), 
    						pSLSD->LogonServer.Buffer, pSLSD->LogonServer.Length * sizeof(TCHAR));
    					strServerName.ReleaseBuffer();
    
    					if(!strServerName.IsEmpty())
    					{
    						USER_INFO_3* pBuf3 = NULL;
    						nStatus = NetUserGetInfo(strServerName, 
    							strUserName,
    							3,
    							(LPBYTE*)&pBuf3);
    						if(nStatus == NERR_Success)
    						{
    							strProfile = pBuf3->usri3_profile ? pBuf3->usri3_profile : L"";
    						}
    						else
    						{
    							//Error
    							_TRACE(L"<Err=%d, %s>", nStatus, strNm.GetString());
    						}
    
    						if(pBuf3)
    						{
    							NetApiBufferFree(pBuf3);
    							pBuf3 = NULL;
    						}
    					}
    					else
    					{
    						//If no server than can't have a roaming profile
    						strProfile = L"";
    					}
    				}
    			}
    		}
    
    		//Free mem
    		if(pSLSD)
    		{
    			VERIFY(LsaFreeReturnBuffer(pSLSD) == STATUS_SUCCESS);
    		}
    	}
    }
    
    //Free mem
    if(pPSL)
    {
    	VERIFY(LsaFreeReturnBuffer(pPSL) == STATUS_SUCCESS);
    }
    As you see I'm using NetUserGetInfo with USER_INFO_3, that seems to work just fine on this test client workstation I'm running it on. Just curious if it's a typo in the documentation? Or if by saying for "level 3":

    This level is valid only on servers.
    they meant

    If the workstation is connected to a server.
    I'm just trying to save on calling NetUserEnum() since I need this info for a single user at a time.

  8. #8
    Arjay's Avatar
    Arjay is offline Moderator / EX MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    13,490

    Re: How to tell if a user account is set up with a roaming profile?

    Glad to hear you got it going.

  9. #9
    2kaud's Avatar
    2kaud is online now Super Moderator Power Poster
    Join Date
    Dec 2012
    Location
    England
    Posts
    7,822

    Re: How to tell if a user account is set up with a roaming profile?

    I believe it's provided in the old NetBIOS format.
    The server name needs to be unicode and I believe it should be proceeded with \\

    I think I was able to make it work (through the trial & error process)
    That's how I've managed to use many of the lsa and net api functions. The documentation IMO is not at all reliable as to how some of these functions actually work.
    All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!

    C++23 Compiler: Microsoft VS2022 (17.6.5)

  10. #10
    Join Date
    Nov 2003
    Location
    Portland, OR
    Posts
    894

    Re: How to tell if a user account is set up with a roaming profile?

    Quote Originally Posted by 2kaud View Post
    The server name needs to be unicode...
    I compile all my code as Unicode by default.

    Thank you, both, for your help.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured