Как воспроизвести quser.exe с помощью вызовов API?

Quser.exe позволяет клиенту видеть сеансы пользователя на удаленном RDP-сервере. Например,

 C:\>quser /server:MyRDPserver

USERNAME              SESSIONNAME        ID  STATE   IDLE TIME  LOGON TIME
userA                                     3  Disc      1+20:03  08/07/2014 12:36
userB                                     4  Disc      1+22:28  08/07/2014 10:38

Я хотел бы встроить эту функциональность в программу C ++ или C #. Да, я мог бы просто создать quser.exe и проанализировать вывод, но есть ли класс Win32 API или .Net Framework, который может дать мне ту же информацию? В частности:

  • Имя пользователя
  • Состояние соединения
  • Время входа

Я обнаружил, что использование WMI (Win32_LoggedOnUser) для нахождения той же информации ненадежно, так как в нем часто перечисляются устаревшие соединения. Я также попробовал PsLoggedOn подход перечисления подразделов HKEY_USERS и поиск ключа Volatile Environment, но это также страдает от той же проблемы.

1

Решение

Вы можете вызвать Win32 API для создания процесса и передать в качестве параметров «quser / server : MyRDPserver», обычно я делаю так:

PROCESS_INFORMATION process_info;
STARTUPINFOA startup_info;
string cmdline2;
char error_msg[1024];

memset(&process_info, 0, sizeof(process_info));
memset(&startup_info, 0, sizeof(startup_info));
startup_info.cb = sizeof(startup_info);

argc = argarray.size();
for(int i = 0; i < argc; i++) {
cmdline2 += argarray.at(i);
if(i != (argc - 1)) cmdline2 += " ";
}

string command = suCmdLineRemoveQuotations(argarray.at(0));
retval = CreateProcessA(command.c_str(), (LPSTR)cmdline2.c_str(), NULL, NULL, TRUE,
0, NULL, NULL, &startup_info, &process_info);

if (!retval) {
windows_error_string(error_msg, sizeof(error_msg));
error = error_msg;
return false;
}

WaitForSingleObject(process_info.hProcess, msecs);
if(GetExitCodeProcess(process_info.hProcess, &status)) {
// status maybe is STILL_ACTIVE, in that case, the process is killed
if(status == STILL_ACTIVE) {
TerminateProcess(process_info.hProcess, 1);
}
ecode = status;
}

return true;

когда процесс запускается, вы можете перенаправить вывод. Если вы используете Qt, проблема становится простой, вы можете использовать QProcess для реализации.

1

Другие решения

Я собираюсь ответить на свой вопрос.

Прежде всего, вам необходимо убедиться, что права доступа установлены правильно на целевой машине. Это влечет за собой установку HKLM \ SYSTEM \ CurrentControlSet \ Control \ Terminal Server \ AllowRemoteRPC в 1. Сценарий powershell, чтобы сделать это:

# Get the service account credential
$cred = Get-Credential my_admin_account
$Target_Computers = @("Computer_1","Computer_2")

# open the remote registry
[long]$HIVE_HKLM = 2147483650
foreach($c in $Target_Computers)
{
$StdRegProv = Get-WmiObject -List -Namespace root\default -ComputerName $c -Credential $cred | where { $_.Name -eq "StdRegProv" }
$StdRegProv.SetDWORDValue($HIVE_HKLM, "SYSTEM\CurrentControlSet\Control\Terminal Server", "AllowRemoteRPC", 1)
}

Как сказал Xearinox, для C ++ вы можете использовать функции WTSxxx в Win32 API. Предполагая, что ваши компьютеры не XP, вот код C ++:

#include <string>
#include <iostream>
#include <iomanip>
#include <windows.h>
#include <WtsApi32.h>

using namespace std;

const unsigned num_connection_states = 10;
const wchar_t* connection_state_list[num_connection_states] = {
L"Active",
L"Connected",
L"ConnectQuery",
L"Shadow",
L"Disc",
L"Idle",
L"Listen",
L"Reset",
L"Down",
L"Init" };

int print_error(DWORD err)
{
// format the message
LPTSTR* ppBuffer = nullptr;
DWORD retval = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER, nullptr, err, 0, reinterpret_cast<LPTSTR>(ppBuffer), 0, nullptr);

// print out
wcerr << "Error: *ppBuffer" << endl;
return 1;
}

wstring format_time(const LARGE_INTEGER& time)
{
// convert to a local Win32 file time
FILETIME ft = { time.LowPart, time.HighPart };
FileTimeToLocalFileTime( &ft, &ft );

// convert to a system time
SYSTEMTIME st;
FileTimeToSystemTime( &ft, &st );

wchar_t local_date[255], local_time[255];
GetDateFormat( LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, local_date, sizeof(local_date)/sizeof(wchar_t) );
GetTimeFormat( LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, local_time, sizeof(local_time)/sizeof(wchar_t) );

wstring result = local_date;
result.append(L" ");
result.append(local_time);

return result;
}

const _int64 SECOND = 10000000;
const _int64 MINUTE = 60*SECOND;
const _int64 HOUR = 60*MINUTE;
const _int64 DAY = 24*HOUR;

wstring format_timespan(const LARGE_INTEGER& timespan)
{
// convert to a local Win32 file time
FILETIME ft = { timespan.LowPart, timespan.HighPart };
FileTimeToLocalFileTime( &ft, &ft );

// convert to a system time
SYSTEMTIME st;
FileTimeToSystemTime( &ft, &st );

wchar_t local_time[255];
int daydiff = floor(
GetTimeFormat( LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, local_time, sizeof(local_time)/sizeof(wchar_t) );

wstring result = local_date;
result.append(L" ");
result.append(local_time);

return result;
}

int wmain(int argc, wchar_t* argv[])
{
// check args
if(argc > 2)
{
wcout << "Usage: " << argv[0] << " [server_name]\n";
return 1;
}

// server name
bool current_server = true;
wstring server_name = L".";
if(argc == 2)
{
server_name = argv[1];
current_server = false;
}

// open the server
HANDLE hServer;
if(current_server)
hServer = WTS_CURRENT_SERVER_HANDLE;
else
hServer = WTSOpenServer(const_cast<LPWSTR>(server_name.c_str()));

// enumerate through the sessions
DWORD Count = 0;
WTS_SESSION_INFO* pSessionInfo = nullptr;
BOOL success = WTSEnumerateSessions(hServer, 0, 1, &pSessionInfo, &Count);
if(success == 0)
return false;

// write the headers
wcout << " " << left << setw(24) << "USERNAME";
wcout << setw(19) << "SESSIONNAME";
wcout << "ID  ";
wcout << setw(9) << "STATE";
wcout << "IDLE TIME  LOGON TIME";

// loop through each session
for(unsigned long s=0; s<Count; s++)
{
LPTSTR pBuffer = nullptr;
DWORD BytesReturned = 0;
wcout << "\n " << left;

// try getting all info at once
WTSINFO* info = nullptr;
success = WTSQuerySessionInformation(hServer, pSessionInfo[s].SessionId, WTSSessionInfo, reinterpret_cast<LPTSTR*>(&info), &BytesReturned);
bool have_wtsinfo = true;
if(!success)
{
// see why failed
DWORD err = GetLastError();
if(err == ERROR_NOT_SUPPORTED)
have_wtsinfo = false;
else
return print_error(err);
}

// print user name
wstring user_name;
if(have_wtsinfo)
user_name = info->UserName;
else
{
success = WTSQuerySessionInformation(hServer, pSessionInfo[s].SessionId, WTSUserName, &pBuffer, &BytesReturned);
if(!success)
continue;
user_name = pBuffer;
WTSFreeMemory(pBuffer);
}
wcout << setw(24) << user_name;

// print session name
wstring session_name;
if(have_wtsinfo)
session_name = info->WinStationName;
else
{
success = WTSQuerySessionInformation(hServer, pSessionInfo[s].SessionId, WTSWinStationName, &pBuffer, &BytesReturned);
if(!success)
continue;
session_name = pBuffer;
WTSFreeMemory(pBuffer);
}
wcout << setw(19) << session_name;

// print session ID
wcout << right << setw(2) << pSessionInfo[s].SessionId;

// print connection state
WTS_CONNECTSTATE_CLASS connect_state;
if(have_wtsinfo)
connect_state = info->State;
else
{
success = WTSQuerySessionInformation(hServer, pSessionInfo[s].SessionId, WTSConnectState, &pBuffer, &BytesReturned);
if(!success)
continue;
connect_state = *reinterpret_cast<WTS_CONNECTSTATE_CLASS*>(pBuffer);
WTSFreeMemory(pBuffer);
}
if(connect_state>=num_connection_states)
continue;
wcout << "  " << left << setw(8) << connection_state_list[connect_state];

// get idle time
LARGE_INTEGER idle = info->CurrentTime;
idle.QuadPart -= info->LogonTime.QuadPart;

// print logon time - not supported
if(info->LogonTime.QuadPart!=0)
{
wcout << format_time(info->LogonTime);
}

// clean up
WTSFreeMemory(info);
}

// clean up
WTSFreeMemory(pSessionInfo);
if(!current_server)
WTSCloseServer(hServer);
}

Для C # самый простой способ — использовать Библиотека Кассии, который в основном является оберткой C # вокруг тех же функций API.

1

По вопросам рекламы [email protected]