Reputation: 41
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
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