Selena
Selena

Reputation: 41

Get current logged in user name in C++ doesn't show anything when run as service

This is my code. I compiled and run it in release mode. It prints the output. I have added library in linker as wtsapi32.lib.

 #include "stdafx.h"
        #include <windows.h>
        #include <conio.h>
        #include <iostream>
        #include <tchar.h>
        #include <wtsapi32.h>
        #include <atlstr.h>
        #include <string>

        #define INFO_BUFFER_SIZE 32767

        int _tmain(int argc, _TCHAR* argv[])
        {
            //bool pidToSessionId = ProcessIdToSessionId(GetCurrentProcessId(),_Out_ DWORD *pSessionId);
            DWORD*pSessionId = (DWORD*)malloc(sizeof(DWORD));
            bool pidToSessionId = ProcessIdToSessionId(GetCurrentProcessId(),pSessionId);
            std::cout << *pSessionId << std::endl;

            LPWSTR*infoBuf = (LPWSTR*)malloc(sizeof(LPWSTR));
            DWORD  bufCharCount = INFO_BUFFER_SIZE;

            WTSQuerySessionInformation(NULL, *pSessionId,/**_WTS_INFO_CLASS.*/WTSUserName, infoBuf, &bufCharCount);
            std::wcout << *infoBuf << std::endl;

            std::string st = CW2A(*infoBuf);
            std::cout << st << std::endl;

            _getch();
            return 0;
        }

I need to run it as service. Then I install it with nssm https://nssm.cc/ and set a file to print the output. nssm lets you to print the output in file. But it just prints pSessionId only. What's wrong with my code? Can anybody tell the reason?

Upvotes: 0

Views: 1489

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 596267

First, your use of malloc() to allocate pointer variables is wasteful and unnecessary, not to mention you are leaking the memory you are allocating. Just declare the pointers on the stack instead, it is much easier and safer.

Second, in Vista and later, all services run in Session 0, and users login to Session 1 and higher. This is known as Session 0 Isolation:

Services have always run in session 0. Before Windows Vista, the first user to log on was also assigned to session 0. Now, session 0 is reserved exclusively for services and other applications not associated with an interactive user session. (The first user to log on is connected to session 1, the second user to log on is connected to session 2, and so on.) Session 0 does not support processes that interact with the user.

This change means that a service cannot post or send a message to an application and an application cannot send or post a message to a service. In addition, services cannot display a user interface item such as a dialog box directly. A service can use the WTSSendMessage function to display a dialog box in another session.

Impact of Session 0 Isolation on Services and Drivers in Windows

Session 0 isolation: Where backward compatibility loses to security

To find logged in users from inside a service, you have to enumerate the available sessions using WTSEnumerateSessions(), calling WTSQuerySessionInformation() on each one (you can also do this in Win2000 and XP, which allow users to login to Session 0). And don't forget to free all of the memory buffers that the WTS functions return to you.

And third, you are not doing any error handling at all.

Try something more like this:

int _tmain(int argc, _TCHAR* argv[])
{
    PWTS_SESSION_INFO pSessions = NULL;
    DWORD dwCount = 0;
    DWORD dwError;

    if (!WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessions, &dwCount))
    {
        dwError = GetLastError();
        std::cout << "Error enumerating sessions: " << dwError << std::endl;
    }
    else if (dwCount == 0)
    {
        std::cout << "No sessions available" << std::endl;
    }
    else
    {
        DWORD dwNumActive = 0;
        for (DWORD i = 0; i < dwCount; ++i)
        {
            if (pSessions[i].State == WTSActive) // has a logged in user
            {
                ++dwNumActive;

                std::cout << "Session: " << pSessions[i].SessionId << ", ";

                LPWSTR pUserName = NULL;
                DWORD dwBufSize = 0;

                if (!WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, pSessions[i].SessionId, WTSUserName, &pUserName, &dwBufSize))
                {
                    dwError = GetLastError();
                    std::cout << "Error getting username: " << dwError;
                }
                else
                {
                    //std::wcout << pUserName << std::endl;
                    std::string st = CW2A(pUserName);
                    std::cout << "User: " << st;
                    WTSFreeMemory(pUserName);
                }

                std::cout << std::endl;
            }
        }

        if (!dwNumActive)
            std::cout << "No users are logged in" << std::endl;
    }

    WTSFreeMemory(pSessions);

    _getch();
    return 0;
}

With that said, in a non-service process, if you need the logged in username that is associated with the session that your app is running in, and you are not running your code as a different user than the one that is logged in (UAC elevation, impersonation, etc), then you can just use GetUserName() instead:

int _tmain(int argc, _TCHAR* argv[])
{
    WCHAR szUserName[UNLEN+1];
    DWORD dwCount = UNLEN+1;

    if (!GetUserNameW(szUserName, &dwCount))
    {
        DWORD dwError = GetLastError();
        std::cout << "Error getting username: " << dwError << std::endl;
    }
    else
    {
        //std::wcout << szUserName << std::endl;
        std::string st = CW2A(szUserName);
        std::cout << "User: " << st << std::endl;
    }

    _getch();
    return 0;
}

Otherwise, you are back to WTSQuerySessionInformation() again:

int _tmain(int argc, _TCHAR* argv[])
{
    DWORD dwSessionId;
    DWORD dwError;

    if (!ProcessIdToSessionId(GetCurrentProcessId(), &dwSessionId))
    {
        dwError = GetLastError();
        std::cout << "Error getting Session ID: " << dwError << std::endl;
    }
    else
    {
        std::cout << "Session: " << dwSessionId << ", ";

        LPWSTR pUserName = NULL;
        DWORD dwBufSize = 0;

        if (!WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, dwSessionId, WTSUserName, &pUserName, &dwBufSize))
        {
            dwError = GetLastError();
            std::cout << "Error getting username: " << dwError;
        }
        else
        {
            //std::wcout << pUserName << std::endl;
            std::string st = CW2A(pUserName);
            std::cout << "User: " << st;
            WTSFreeMemory(pUserName);
        }

        std::cout << std::endl;
    }

    _getch();
    return 0;
}

Upvotes: 3

Related Questions