Solved

Getting the current logged in Windows user name when running as an NT service

Posted on 2003-11-04
24
110,882 Views
Last Modified: 2013-12-03
I have a service (written in Visual Basic), running under Windows NT/2K/XP/2003.  I need to get the name of the currently logged in user, but most Windows APIs I have found return the username of the *calling thread*, which when run as a system, is always "SYSTEM".

I have tried these APIs so far:

GetUserName
GetUserNameEx
WNetGetUser
NetWkstaUserGetInfo

Unfortunately, all of the above return the username "SYSTEM".  Any suggestions?

This code needs to run on Windows NT, 2000, XP and 2003.
0
Comment
Question by:nicjansma
24 Comments
 
LVL 3

Expert Comment

by:Madz
ID: 9685947
Hi,
   If I am not mistaken you can get the name of the logged in user from the USERNAME environment variable. You can get the logged in domain using the USERDOMAIN environment variable. You can use the GetEnvironmentVariable API to retrieve the said environment variables.

Hope this helps,
Madz
0
 
LVL 86

Accepted Solution

by:
jkr earned 125 total points
ID: 9686961
Use

//--------------------------------------------------------------------
//
// DisplayLocalLogons
//
// Scans the HKEY_USERS key of the specified computer to see who
// has their profile loaded. Returns true if someone is logged on.
//
//--------------------------------------------------------------------
BOOLEAN DisplayLocalLogons( LPWSTR ServerName, LPWSTR UserName  )
{
    BOOLEAN          first = TRUE;
   TCHAR          errorMessage[1024];
   TCHAR          userName[MAX_NAME_STRING], domainName[MAX_NAME_STRING];
   TCHAR          subKeyName[MAX_PATH];
   DWORD          subKeyNameSize, index;
   DWORD          userNameSize, domainNameSize;
   FILETIME     lastWriteTime;
   HKEY          usersKey;
   PSID          sid;
   SID_NAME_USE sidType;
   SID_IDENTIFIER_AUTHORITY authority;
    BYTE          subAuthorityCount;
   DWORD          authorityVal, revision;
   DWORD          subAuthorityVal[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
   
   //
   // Use RegConnectRegistry so that we work with remote computers
   //
    if( ServerName ) {
         
         wprintf(L"Connecting to Registry of %s...", ServerName );
         fflush( stdout );

         if( RegConnectRegistry( ServerName, HKEY_USERS, &usersKey ) != ERROR_SUCCESS) {
       
              wprintf(L"\r                                                      \r");
              wprintf( L"Error opening HKEY_USERS for %s\n", ServerName );
              return FALSE;
         }
         wprintf(L"\r                                                      \r");

    } else {

         if( RegOpenKey( HKEY_USERS, NULL, &usersKey ) != ERROR_SUCCESS ) {

              wprintf( errorMessage, L"Error opening HKEY_USERS" );
              PrintWin32Error( errorMessage, GetLastError() );
              return FALSE;
         }
    }
 
    //
   // Enumerate keys under HKEY_USERS
   //
   index = 0;
   subKeyNameSize = sizeof( subKeyName );
   while( RegEnumKeyEx( usersKey, index, subKeyName, &subKeyNameSize,
                        NULL, NULL, NULL, &lastWriteTime ) == ERROR_SUCCESS ) {

       //
       // Ignore the default subkey and win2K user class subkeys
       //
       if( wcsicmp( subKeyName, L".default" ) &&
              !wcsstr( subKeyName, L"Classes")) {

              //
              // Convert the textual SID into a binary SID
              //
           subAuthorityCount= swscanf( subKeyName, L"S-%d-%x-%lu-%lu-%lu-%lu-%lu-%lu-%lu-%lu",
                                       &revision, &authorityVal,
                                       &subAuthorityVal[0],
                                       &subAuthorityVal[1],
                                       &subAuthorityVal[2],
                                       &subAuthorityVal[3],
                                       &subAuthorityVal[4],
                                       &subAuthorityVal[5],
                                       &subAuthorityVal[6],
                                       &subAuthorityVal[7] );

           if( subAuthorityCount >= 3 ) {

               subAuthorityCount -= 2;
               
               //
               // Note: we can only deal with authority values
               // of 4 bytes in length
               //
               authority.Value[5] = *(PBYTE) &authorityVal;
               authority.Value[4] = *((PBYTE) &authorityVal+1);
               authority.Value[3] = *((PBYTE) &authorityVal+2);
               authority.Value[2] = *((PBYTE) &authorityVal+3);
               authority.Value[1] = 0;
               authority.Value[0] = 0;

                   //
               // Initialize variables for subsequent operations
               //
               sid = NULL;
               userNameSize   = MAX_NAME_STRING;
               domainNameSize = MAX_NAME_STRING;

               if( AllocateAndInitializeSid( &authority,
                                              subAuthorityCount,
                                              subAuthorityVal[0],
                                              subAuthorityVal[1],
                                              subAuthorityVal[2],
                                              subAuthorityVal[3],
                                              subAuthorityVal[4],
                                              subAuthorityVal[5],
                                              subAuthorityVal[6],
                                              subAuthorityVal[7],
                                              &sid )) {

                        //
                        // We can finally lookup the account name
                        //
                        if( LookupAccountSid( ServerName,
                                                   sid,
                                                    userName,
                                                   &userNameSize,
                                                   domainName,
                                                   &domainNameSize,
                                                   &sidType )) {

                             //
                             // We've successfully looked up the user name
                             //
                           if( first && !UserName ) {
                               
                                   wprintf(L"Users logged on locally:\n");
                                  first = FALSE;
                           }
                           if( !UserName || !wcsicmp( UserName, userName )) {
                             
                                first = FALSE;
                                if( UserName ) wprintf(RESETLINE L"%s\\%s logged onto %s locally.\n",
                                                                 domainName, UserName, ServerName );
                                else                 wprintf( L"     %s\\%s\n", domainName, userName );
                           }                              
                         }
               }              
                if( sid ) FreeSid( sid );
           }
       }
       subKeyNameSize = sizeof( subKeyName );
       index++;
   }
    RegCloseKey( usersKey );

    if( first && !UserName ) wprintf(L"No one is logged on locally.\n");
    return !first;
}


Code taken from http://www.sysinternals.com/files/PsLoggedonSrc.zip
0
 

Author Comment

by:nicjansma
ID: 9690051
Madz - Unfortunately, I tried this and it returns USERNAME="" when running as a service (it did work properly when not running as a service, so I know the code is OK).

jkr - I do not believe this would work with Fast User Switching in XP, correct?  As several users would have their hives loaded?

My software works with FUS, so it needs to know the login name of the current session, not just all users who are logged in.

Any more ideas?
0
 
LVL 86

Expert Comment

by:jkr
ID: 9690173
>>I do not believe this would work with Fast User Switching in XP, correct?

It should (using 'RegEnumKeyEx()' for the hives) - that's why I posted it in the 1st place.
0
 

Expert Comment

by:granslant
ID: 9781542
>>>>I do not believe this would work with Fast User Switching in XP, correct?
>>
>>It should (using 'RegEnumKeyEx()' for the hives) - that's why I posted it in the 1st place.

Unfortunalety no, it returns all logged users, not only the user of the current session.
0
 
LVL 86

Expert Comment

by:jkr
ID: 9781566
>>it returns all logged users, not only the user of the current session

Yes, but that was the purpose.
0
 

Expert Comment

by:granslant
ID: 9795932
Actually, and if I remember well :), here one solution to get only the current local session user (and not all logged users):
. get the desktop window
. get its process ID
. use OpenProcessToken and GetTokenInformation to retreive the SID of the desktop window's owner.
0
 

Expert Comment

by:bharat_mane
ID: 10059601
Hello Experts,
    I have one doubt
                      By looking at the function I feel the data is coming from registry. I mean username and domin name are coming from registry.
                      I exactlly don't from which key but I think subkey of
                                      HKEY_USERS\S-1-5-21-1244467668-1331586020-26564730-13072
What if I change these values from regisry manualkly then ,
what this function will return......
And if anybody knows from exactlly which kay the value of userid and domain name is comming then it would help me a lot.

Thanks
Bharat
0
 

Expert Comment

by:bharat_mane
ID: 10059608
Hello Experts,
    I have one doubt
                      By looking at the function I feel the data is coming from registry. I mean username and domin name are coming from registry.
                      I exactlly don't from which key but I think subkey of
                                      HKEY_USERS\S-1-5-21-1244467668-1331586020-26564730-13072
What if I change these values from regisry manually then ,
what this function will return......

And if anybody knows from exactly which key, value of userid and domain name is coming, then it would help me a lot.

Thanks
Bharat
0
 

Expert Comment

by:scoubidou944
ID: 10104505
Here is a shorter example but it returns SYSTEM for a serivce added to Windows Station Winsta0\default.
Having another idea ?

HDESK                  hDesk;
BOOL                  bResult;
HWINSTA                  hwinsta;
HANDLE                  hToken;
TOKEN_USER            oUser[16];
DWORD                  u32Needed;
SID_NAME_USE      sidType;
TCHAR                  userName[256], domainName[256];
DWORD                  userNameSize, domainNameSize;

// INIT
bResult            = OpenProcessToken      (GetCurrentProcess(), TOKEN_QUERY, &hToken);
hwinsta            = OpenWindowStation      ("winsta0", FALSE, WINSTA_ACCESSCLIPBOARD | WINSTA_ACCESSGLOBALATOMS | WINSTA_READATTRIBUTES | WINSTA_READSCREEN | WINSTA_ENUMERATE | WINSTA_ENUMDESKTOPS);
hDesk            = OpenDesktop            ("default", 0, FALSE, READ_CONTROL | WRITE_DAC | DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS);

memset (&oUser[0], 0, sizeof (oUser));
bResult = GetTokenInformation (hToken, TokenUser, &oUser[0], sizeof (oUser), &u32Needed);

userNameSize      = sizeof (userName) - 1;
domainNameSize      = sizeof (domainName) - 1;
bResult = LookupAccountSid (NULL, oUser[0].User.Sid, userName, &userNameSize, domainName, &domainNameSize, &sidType);
      
// HERE IS THE RESULT

// CLOSE
bResult = CloseDesktop (hDesk);
bResult = CloseWindowStation (hwinsta);
bResult = CloseHandle (hToken);
0
 

Expert Comment

by:kirannapit
ID: 10216977
hello
       how to get user logged on to  of a remote machine.
0
 

Expert Comment

by:scoubidou944
ID: 10217197
Looks like something not very white what you want to do...........

create a NULL connection to remote computer using NetBIOS "feature" to retrieve usefull informations.
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 3

Expert Comment

by:deemehtani
ID: 10499197
HI
If you put your VB program in the startup for all users instead of running it as a service, you would get the actual username instead of System
0
 

Expert Comment

by:Magadass
ID: 10669074
The reason scoubidou944's solution did not work is because you are retrieving the current process handle by using OpenProcessToken     (GetCurrentProcess(), TOKEN_QUERY, &hToken); you are recieving a security token for the currently running instance.  You will need to retrieve the process handle of the currently logged on user, which I am attempting to find out how to also.  But if anyone know this would resolve the problem by chaning this one line to point to the current users process handle rather than GetCurrentProcess()

0
 

Expert Comment

by:Magadass
ID: 10679579
HANDLE hToken;
TOKEN_USER          oUser[16];
DWORD               u32Needed;
TCHAR               sUserName[256], domainName[256];
DWORD               userNameSize, domainNameSize;
SID_NAME_USE            sidType;

ZeroMemory(oUser,sizeof(oUser));

OpenProcessToken(GetExplorerProcessHandle(), TOKEN_QUERY, &hToken);
            GetTokenInformation(hToken,TokenUser,&oUser[0], sizeof(oUser), &u32Needed);
            userNameSize     = sizeof (sUserName) - 1;
            domainNameSize     = sizeof (domainName) - 1;
            
            LookupAccountSid (NULL, oUser[0].User.Sid, sUserName, &userNameSize, domainName, &domainNameSize, &sidType);


HANDLE GetExplorerProcessHandle()  //Needed to impersonate the logged in user...
{
      HANDLE hSnapshot;
      PROCESSENTRY32 pe32;
      ZeroMemory(&pe32,sizeof(pe32));
      HANDLE temp = NULL;

      try
      {
            hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,NULL);

            pe32.dwSize = sizeof(PROCESSENTRY32);

            if(Process32First(hSnapshot,&pe32))
            {
                  do
                  {
                        if(!strcmp(pe32.szExeFile,"explorer.exe"))
                        {
                              temp = OpenProcess (PROCESS_ALL_ACCESS,FALSE, pe32.th32ProcessID);

                              break;
                        }

                  }while(Process32Next(hSnapshot,&pe32));
            }
      }
      catch(char *str)
      {
            HandleError(str);
      }
      return temp;
}
0
 

Expert Comment

by:Magadass
ID: 10679593
That works, I have it working in my application.

0
 

Expert Comment

by:DevDan
ID: 10785163
I had the exact same problem. One of the componenets in my app runs on the PC and reports back to the server who is logged into the PC. It is a standard VB6 EXE and runs hidden. I upgraded this app to an NTService and now have the problem of only seeing "SYSTEM" as the logged in user. I've read your note, amongst others on the net, and decided a little creative thinking was necessary. To get around this problem I'm doing the following:

     1.) When the user logs in, a standard EXE fires and grabs the login name via "GetUserName".
     2.) Put the username in a file, or registry entry
     3.) My NTService will read the file, or registry, and then subsequently kill the file or clear the reg
     4.) The NTService will loop when loading until file exists or reg value > "".

Hope this round about trick helps. It's working fine for me.

Regards,

Dan
dmcgrail@coorsysinc.com

0
 

Expert Comment

by:shivakarsh_sochrist
ID: 12520849
Using API's like Getusername and stuff like that may not help. It returns SYSTEM when running as a service. The best thing is to share IPC and turn the NetBIOS on. So try doing this in the command prompt. nbtstat -a <ip address> . The address <03> returns the username. I've done this in my ISAPI App and runs well. When there's no user logged, it returns a NULL value.Generally IPC is shared by default in NT based systems. But if that is disabled, this idea may not work.  Now our task is to run DOS commands and get the Output back in VB. Visit http://www.planetsourcecode.com/vb/scripts/ShowCode.asp?txtCodeId=43370&lngWId=1

Hope this works!
0
 

Expert Comment

by:legionx
ID: 13040967
You can also simply get the environment variable %USERNAME%, I have used it in this situation before.

e.g. in VBScript

Dim oShell
Dim UserName

Set oShell       = Wscript.CreateObject("Wscript.Shell")
UserName = oShell.ExpandEnvironmentStrings("%USERNAME%")

WScript.Echo UserName
0
 
LVL 86

Expert Comment

by:jkr
ID: 13044621
>> You can also simply get the environment variable %USERNAME%

OK, so that'll be 'System'. Always.
0
 
LVL 1

Expert Comment

by:DarthChucks
ID: 13306744
How about using WTSGetActiveConsoleSessionId?  Then use WTSQuerySessionInformation which returns a WTSInfoClass which in turn contains WTSDomainName and WTSUserName.

Check out
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/termserv/TermServ/wtsgetactiveconsolesessionid.asp
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/termserv/TermServ/wtsquerysessioninformation.asp

This should work under fast user switching.  Sorry, no code.  I'm not a VB Programmer.

Rob
0
 

Expert Comment

by:legionx
ID: 13320942
>> OK, so that'll be 'System'. Always.

Not necessarily. I have tested in the situation where a standard user logs in and applications are delivered through Novell ZEN. The application install is set to run as unsecure system user but this does not alter the %USERNAME% variable.
0
 
LVL 1

Expert Comment

by:raidevnet
ID: 21480059
I've tried using WTSGetActiveConsoleSessionId on a Windows XP with FUS disabled.
It always returns 0.

-Rai
0
 

Expert Comment

by:scoubidou944
ID: 21491939
Can U publish some code.
Are you running it as Windows Service ?
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

This article describes how to programmatically preset the "Pages per Sheet" option that's available with most printer drivers.   This setting lets you do "n-Up" printing, where two, four, or more pages are printed on each sheet of paper. If your …
A theme is a collection of property settings that allow you to define the look of pages and controls, and then apply the look consistently across pages in an application. Themes can be made up of a set of elements: skins, style sheets, images, and o…
It is a freely distributed piece of software for such tasks as photo retouching, image composition and image authoring. It works on many operating systems, in many languages.
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…

707 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now