I've got a problem i've been struggling with for a few days now and couldn't find any explanation with debugging or searching the internet. So as a last resort i'm asking here.
I have a simple named pipe server written in c++ and a NamedPipeStreamClient in c# running on Windows. Before trying to make a protocol that passes the message size in bytes in front of the message i wanted to try and use the message mode to seperate individual messages.
The relevant code fragments are:
Creation of the C++ NamedPipe
hOutputPipe = CreateNamedPipeA(
lOutputPipeName, // pipe name
PIPE_ACCESS_OUTBOUND, // only write access
PIPE_TYPE_MESSAGE | // message type pipe
PIPE_READMODE_MESSAGE | // message-read mode
PIPE_WAIT, // blocking mode
PIPE_UNLIMITED_INSTANCES, // max. instances
1024, // output buffer size
1024, // input buffer size
0, // client time-out
NULL); // default security attribute
if (hOutputPipe == INVALID_HANDLE_VALUE)
{
std::cout << "CreateNamedPipe failed, GLE=" << GetLastError() << std::endl;
return -1;
}
// Wait for the client to connect; if it succeeds,
// the function returns a nonzero value. If the function
// returns zero, GetLastError returns ERROR_PIPE_CONNECTED.
BOOL fConnected = ConnectNamedPipe(hOutputPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
if (!fConnected)
{
// The client could not connect, so close the pipe.
CloseHandle(hOutputPipe);
return -1;
}
Creation of C# NamedPipeClientStream:
fromagent_pipe = new NamedPipeClientStream(".", pipeName + "_output", PipeDirection.In);
// timeout after 2 seconds to prevent blocking
fromagent_pipe.Connect(2000);
fromagent_pipe.ReadMode = PipeTransmissionMode.Message;
C++ Funciton called in a while(true) loop:
bool writeOutputMessage(HANDLE hPipe, const CubesExample::OutputMessage& outputMessage)
{
size_t messageBytes = outputMessage.ByteSizeLong();
char buffer[messageBytes];
DWORD bytesWritten = 0;
outputMessage.SerializeToArray(&buffer, messageBytes);
std::cout << std::string("Writing ") + std::to_string(messageBytes) + " bytes" << std::endl;
BOOL fSuccess = WriteFile(
hPipe, // handle to pipe
buffer, // buffer to write from
messageBytes, // number of bytes to write
&bytesWritten, // number of bytes written
NULL); // not overlapped I/O
if (!fSuccess || bytesWritten != messageBytes)
{
std::cout << "InstanceThread WriteFile failed, GLE=" << GetLastError() << std::endl;
return false;
}
return true;
}
C# method for reading a full message from the pipe and returning the byte[]:
public byte[] readOutputMessage()
{
int offset = 0;
int readBytes = 0;
do{
readBytes = fromagent_pipe.Read(inputBuffer, offset, 1);
offset++;
Debug.Log("Reading from output pipe! isComplete: " + fromagent_pipe.IsMessageComplete + " readBytes: " + readBytes);
}while(!fromagent_pipe.IsMessageComplete && readBytes > 0);
Debug.Log("Read " + offset + " bytes from agent pipe");
byte[] buffer = new byte[offset];
Array.Copy(inputBuffer, buffer, offset);
return buffer;
}
The C# method above is run in a Task<byte[]> so the main thread isn't blocked when waiting for PipeStream.Read(). The inputBuffer is a field in the class where this code is located and has a size of 4096 so i don't have to allocate it before every read.
The problem is now, that fromagent_pipe.IsMessageComplete is never set to true, no matter how many bytes it reads. The message size i'm sending is 7 bytes for reference so i would expect the do while loop to iterate 7 times and after the 7nth byte is read IsMessageComplete should be set to true, right? I'm new to Named Pipes and IPC so i might be missing sth obvious but it's drivig me crazy bc i set every possible flag from the docs and use the IsMessageComplete flag just like other people on the internet but mine just never seems to switch to true.
Another piece of info is that c++ server runs much faster than the c# loop so the pipe consumes data slower than it gets it. I will eventually discard all messages delivered between single Reads but for now i can't even get one message read.
PS. Before someone points it out, yes the inputBuffer does overflow after it reads 4096 bytes. I want to get the IsMessageComplete problem out of the way before dealing with reseting it.
Thanks to anyone reading in advance and ... pls send help
I was able to get message mode to work by setting the pipe direction as duplex.
This accepted answer explains why I had to do that.
This is the quick example I whipped up.
I've provided both an asynchronous and synchronous example on the C# side. I did not provide an asynchronous example using OVERLAPPED on the C++ side.
Server
int main( )
{
std::cout << "Press any key to begin\n";
std::cin.ignore( );
const auto print_error{ [ ]( DWORD code )
{
const std::error_code ec{ static_cast<int>( code ),
std::system_category( ) };
std::cerr << ec.message( ) << '\n';
} };
const auto pipe{ CreateNamedPipeA(
R"(\\.\pipe\testpipe)",
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
1024,
1024,
0,
nullptr ) };
if ( pipe == INVALID_HANDLE_VALUE )
{
print_error( GetLastError( ) );
return EXIT_FAILURE;
}
if ( !ConnectNamedPipe( pipe, nullptr ) )
{
if ( const auto ec{ GetLastError( ) }; ec != ERROR_PIPE_CONNECTED )
{
print_error( ec );
CloseHandle( pipe );
return EXIT_FAILURE;
}
}
std::string_view message{ "Hello friend!" };
const auto length{ static_cast<DWORD>( message.size( ) ) };
std::cout << "Sending message\n";
DWORD sent{ 0 };
const auto result{ WriteFile(
pipe, message.data( ), length , &sent, nullptr ) };
if ( !result || sent != length )
{
print_error( GetLastError( ) );
CloseHandle( pipe );
return EXIT_FAILURE;
}
std::cout << "Message sent, press enter to exit\n";
std::cin.ignore( );
std::cout << "Closing handle\n";
CloseHandle( pipe );
}
Synchronous Client
static void Main( string[ ] args )
{
Console.WriteLine( "Press any key to begin" );
Console.ReadKey( );
using var client = new NamedPipeClientStream( ".", "testpipe", PipeDirection.InOut );
Console.WriteLine( "Connecting to server" );
client.Connect( 5000 );
client.ReadMode = PipeTransmissionMode.Message;
Console.WriteLine( "Connected" );
var buffer = new byte[ 1024 ];
var nBytes = 0;
do
{
nBytes += client.Read( buffer.AsSpan( )[ nBytes.. ] );
} while ( !client.IsMessageComplete );
var message = Encoding.ASCII.GetString( buffer.AsSpan( )[ ..nBytes ] );
Console.WriteLine( $"Received message: {message}" );
Console.WriteLine( "Press any key to exit" );
Console.ReadKey( );
}
Asynchronous Client
static async Task Main( string[ ] args )
{
Console.WriteLine( "Press any key to begin" );
Console.ReadKey( );
using var client = new NamedPipeClientStream( ".", "testpipe", PipeDirection.InOut );
Console.WriteLine( "Connecting to server" );
using ( var cts = new CancellationTokenSource( TimeSpan.FromSeconds( 5 ) ) )
{
await client.ConnectAsync( cts.Token ).ConfigureAwait( false );
client.ReadMode = PipeTransmissionMode.Message;
}
Console.WriteLine( "Connected" );
var buffer = new byte[ 1024 ];
var nBytes = 0;
do
{
nBytes += await client.ReadAsync(
buffer.AsMemory( )[ nBytes.. ] ).ConfigureAwait( false );
} while ( !client.IsMessageComplete );
var message = Encoding.ASCII.GetString( buffer.AsSpan( )[ ..nBytes ] );
Console.WriteLine( $"Received message: {message}" );
Console.WriteLine( "Press any key to exit" );
Console.ReadKey( );
}
Related
Hi I wanted to proxy multicast video to unicast like udpxy does: http://www.udpxy.com, but in C#
since I could not find any suitable RTP library that I could use (they were eather too complex or I could't understand how to use them), I decided to port over one that udpxy uses rtp.c: https://github.com/pcherenkov/udpxy/blob/master/chipmunk/rtp.c
everything went fine (almost, as I didn't want to use pointers), until I wanted to translate RTP_Process to C#
RTP_Process in C
RTP_process( void** pbuf, size_t* len, int verify, FILE* log )
{
int rtp_padding = -1;
size_t front_skip = 0, back_skip = 0, pad_len = 0;
char* buf = NULL;
size_t pkt_len = 0;
assert( pbuf && len && log );
buf = *pbuf;
pkt_len = *len;
if( verify && !RTP_verify( buf, pkt_len, log ) )
return -1;
if( 0 != RTP_hdrlen( buf, pkt_len, &front_skip, log ) )
return -1;
rtp_padding = buf[0] & 0x20;
if( rtp_padding ) {
pad_len = buf[ pkt_len - 1 ];
}
back_skip += pad_len;
if( verify && (pkt_len < (front_skip + back_skip)) ) {
(void) tmfprintf( log, "RTP_process: invalid header "
"(skip [%lu] exceeds packet length [%lu])\n",
(u_long)(front_skip + back_skip), (u_long)pkt_len );
return -1;
}
/* adjust buffer/length to skip heading and padding */
/*
TRACE( (void)tmfprintf( log, "In: RTP buf=[%p] of [%lu] bytes, "
"fskip=[%ld], bskip=[%lu]\n",
(void*)buf, (u_long)pkt_len,
(u_long)front_skip, (u_long)back_skip ) );
*/
buf += front_skip;
pkt_len -= (front_skip + back_skip);
/*
TRACE( (void)tmfprintf( log, "Out RTP buf=[%p] of [%lu] bytes\n",
(void*)buf, (u_long)pkt_len ) );
*/
*pbuf = buf;
*len = pkt_len;
return 0;
}
RTP_Process in C#
public byte[] RTP_process(int verify)
{
/* process RTP package to retrieve the payload: set
* pbuf to the start of the payload area; set len to
* be equal payload's length
*
* #param pbuf address of pointer to beginning of RTP packet
* #param len pointer to RTP packet's length
* #param verify verify that it is an RTP packet if != 0
* #param log log file
*
* #return 0 if there was no error, -1 otherwise;
* set pbuf to point to beginning of payload and len
* be payload size in bytes
*/
int rtp_padding = -1;
int front_skip = 0, back_skip = 0, pad_len = 0;
int pkt_len = 0;
//assert(pbuf && len && log);
//buf = *pbuf;
pbuf = buf;
//pkt_len = *len;
len = pkt_len;
/*
if (verify != 1 && RTP_verify() != 1)
RTPOK = - 1;
if (0 != RTP_hdrlen(buf, pkt_len, front_skip)) //?????
RTPOK = - 1;
*/
rtp_padding = buf[0] & 0x20;
if (rtp_padding != -1) //???????
{
pad_len = buf[pkt_len - 1];
}
back_skip += pad_len;
if ((verify != -1) && (pkt_len < (front_skip + back_skip))) //???????
{
Console.WriteLine("RTP_process: invalid header (skip {0} exceeds packet length {1})\n", (long)(front_skip + back_skip), (long)pkt_len);
RTPOK = - 1;
}
/* adjust buffer/length to skip heading and padding */
/*
TRACE( (void)tmfprintf( log, "In: RTP buf=[%p] of [%lu] bytes, "
"fskip=[%ld], bskip=[%lu]\n",
(void*)buf, (u_long)pkt_len,
(u_long)front_skip, (u_long)back_skip ) );
*/
//buf += front_skip;
//pkt_len -= (front_skip + back_skip);
/*
TRACE( (void)tmfprintf( log, "Out RTP buf=[%p] of [%lu] bytes\n",
(void*)buf, (u_long)pkt_len ) );
*/
pbuf = buf;
len = pkt_len;
RTPOK = 0;
return pbuf;
}
here the problems started
1. buf += front_skip; complained that operator += cannot be applied to operands of type byte[] and int
then why did it work in RTP_Process in C and what is a C# equivalent of that
2. in
if (rtp_padding != -1) //???????
{
pad_len = buf[pkt_len - 1]; //There is an exeption trown: System.IndexOutOfRangeException: Index was outside the bounds of the array.
its clear that I interpreted and translated something the wrong way, but the onlything I would like to do is to get MPEG-TS frame out of RTP stream to then forward it to a TCP socket, so if anyone can suggest a better way of doing that I would love to hear it
Thanks for Anwsering and Best Regards
}
First, I suggest to read carefully RFC-3550, it has all information about RTP-packet structure (mostly you need Section #5: RTP Fixed Header and extensions).
Then you have to implement RTP_hdrlen to calculate RTP header size, it must return front_skip value as RTP header size including extensions. So, you don't have to use buf += front_skip;, RTP payload starts from byte buf[front_skip].
You have wrong packet length parameter here: int pkt_len = 0;, that's why the exception is thrown here pad_len = buf[pkt_len - 1];.
I have a TCPListener in C# and the client in Unix C. I use TcpClient.Client.SendFile for transmitting a file to client socket in Unix and it works fine for plain txt file. It fails to produce the full file on the unix end, when I send JPEG files. Any Idea?
C# code part
============
static void Main(string[] args)
{
TcpListener serverSocket = new TcpListener(10001);
TcpClient clientSocket = default(TcpClient);
serverSocket.Start();
Console.WriteLine(" >> Server Started");
clientSocket = serverSocket.AcceptTcpClient();
Console.WriteLine(" >> Accept connection from client");
while ((true))
{
try
{
NetworkStream networkStream = clientSocket.GetStream();
byte[] bytesFrom = new byte[10025];
networkStream.Read(bytesFrom, 0, (int)clientSocket.ReceiveBufferSize);
string dataFromClient = System.Text.Encoding.ASCII.GetString(bytesFrom).TrimEnd('\0');
Console.WriteLine(" >> Data from client - " + dataFromClient +" Length "+ dataFromClient.Length);
string a1 = "C:\\MRTD\\PICTURE\\abc.jpg";
clientSocket.Client.SendFile(a1);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
Console.ReadLine();
}
}
}
}
}
Unix C code to read the file
============================
char *tcp_recv( int id_type )
{
register int nbytes;
static char readbuf[MAXLINE];
static char tempbuf[980000];
int ngot = 0, bulk_data=0, p_read_val;
char * x, * y, * z, *j, *k, *x1, *bar;
char bulk_buf[180000], tempstr1[180000] , fstr[40], termtor[40];
int fd_ready;
fd_set read_fds; /* Read file descriptors */
int fd_port = gfd_sock;
struct timeval timeout;
timeout.tv_sec = 3600;
timeout.tv_usec = 0;
while (TRUE)
{
FD_ZERO( &read_fds );
FD_SET( fd_port, &read_fds );
strcpy(fstr,"FAIL");
if((fd_ready = select(fd_port+1,&read_fds,NULL,NULL,&timeout))< 0)
{
if( errno == EINTR ) { continue; }
logmsg("ERROR select() returned errno %d", errno );
logmsg("Select error waiting for packet." );
return fstr;
}
else if( fd_ready == 0 )
{
logmsg("Timeout waiting for packet.");
strcpy(fstr,"TIMEOUT");
return fstr;
}
memset( readbuf, 0x00, sizeof( readbuf ));
memset( br_kde, 0x00, sizeof( br_kde ));
if((nbytes = read( fd_port, readbuf, sizeof(readbuf)-1 )) < 0 )
{
logmsg( "tcp_recv: nbytes %d, errno %d, %s", nbytes, errno, strerror( errno ) );
if (errno == EINTR || errno == EAGAIN )
{
errno = 0;
continue; /* assume SIGCLD */
}
else
{
/*
* connection failer.
*/
logcon( "Desko connection failed. Pier link is DOWN!" );
logmsg( "Desko connection failed. Pier link is DOWN!" );
close_files();
exit (1);
break;
} /* end else if */
}
else
{
logmsg("tcp_recv: readbuf is %s, bytes %d", readbuf, nbytes);
strcat(tempbuf, readbuf);
logmsg("tempbuf is %s", tempbuf );<== full file listing is missing in case of JPEG
}
return tempbuf;
}
}
I have two programs written in c++ and c#.I want to establish a two way communication using named-pipe between them. The C# client program can be connected to the named-pipe created by c++ server program.But nothing received in both ends.
Here is the c++ part (Server):
#include <iostream>
#include <windows.h>
#include <stdlib.h>
#define UNICODE
using namespace std;
HANDLE hnamedPipe = INVALID_HANDLE_VALUE;
BOOL Finished =false;
HANDLE hThread = NULL;
unsigned long __stdcall CS_RcvThr(void * pParam) ;
int main(int argc, char **argv)
{
hnamedPipe = CreateNamedPipe(
"\\\\.\\pipe\\vikeyP",
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE|
PIPE_READMODE_MESSAGE|
PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
1024,
1024,
NMPWAIT_USE_DEFAULT_WAIT,
NULL);
if(hnamedPipe == INVALID_HANDLE_VALUE)
{
cout << "Failed" << endl;
}
while (true)
{
cout<< "Waiting for client"<< endl;
if(!ConnectNamedPipe(hnamedPipe,NULL))
{
if(ERROR_PIPE_CONNECTED != GetLastError())
{
cout << "FAIL"<< endl;
}
}
else
{
cout<<"Connected!"<<endl;
hThread = CreateThread( NULL, 0, &CS_RcvThr, NULL, 0, NULL);
if(hThread) cout<<"read thread created"<<endl; else cout<<"cant crat rd thed\n";
break;
}
}
while(1)
{
cout<<"lst loop"<<endl;
//Send over the message
char chResponse[] = "hello\n";
DWORD cbResponse,cbWritten;
cbResponse = sizeof(chResponse);
if (!WriteFile(
hnamedPipe,
chResponse,
cbResponse,
&cbWritten,
NULL))
{
wprintf(L"failiure w/err 0x%08lx\n",GetLastError);
}
cout<<"Sent bytes :)" << endl;
Sleep(10);
}
}
unsigned long __stdcall CS_RcvThr(void * pParam) {
BOOL fSuccess;
char chBuf[100];
DWORD dwBytesToWrite = (DWORD)strlen(chBuf);
DWORD cbRead;
int i;
while (1)
{
fSuccess =ReadFile( hnamedPipe,chBuf,dwBytesToWrite,&cbRead, NULL);
if (fSuccess)
{
printf("C++ App: Received %d Bytes : ",cbRead);
for(i=0;i<cbRead;i++)
printf("%c",chBuf[i]);
printf("\n");
}
if (! fSuccess && GetLastError() != ERROR_MORE_DATA)
{
printf("Can't Read\n");
if(Finished)
break;
}
}
}
Here is the C# part (Client):
private Thread vikeyClientThread;
public void ThreadStartClient()
{
Console.WriteLine("Thread client started ID ={0} name = {1} " ,
Thread.CurrentThread.ManagedThreadId,Thread.CurrentThread.Name);
using (NamedPipeClientStream pipeStream = new NamedPipeClientStream(".", "vikeyP"))
{
// The connect function will indefinately wait for the pipe to become available
// If that is not acceptable specify a maximum waiting time (in ms)
Console.WriteLine("Connecting to ViKEY server...");
pipeStream.Connect();
Console.WriteLine("Connected :)");
//Write from client to server
StreamWriter sw = new StreamWriter(pipeStream);
while (true)
{
//Read server reply
StreamReader sr = new StreamReader(pipeStream);
string temp = "";
sw.WriteLine(System.DateTime.Now);
byte[] c = new byte[200];
temp = sr.ReadLine();
pipeStream.Read(c, 0, c.Length);
Console.WriteLine("RX =:{0}", Encoding.UTF8.GetString(c, 0, c.Length));
Thread.Sleep(500);
}
}
Console.WriteLine("Vikey pipe Closed");
Console.WriteLine("Thread with ID ={0} name = {1} is closed.",
Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.Name);
}
You've set the server side up as a message type vs byte type. If you want to read an arbitrary number of bytes, you'll need to use a byte type named pipe.
You're wrapping the stream in 2 objects one StreamReader and one StreamWriter. Don't do that, just use a Stream. You're trying to read by line, don't do that either. Instead send the number of bytes to read followed by the bytes. On the client side you'll read the byte count then create a buffer big enough then read. If it's text data you then would use an encoder (probably ASCII) to translate it back into a C# string.
Your while(true) should instead detect when the server has closed the pipe.
You should probably look into using an asynchronous named pipe.
Quser.exe allows a client to see user sessions on a remote RDP server. For example,
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
I would like to build this functionality into a C++ or C# program. Yes, I could just spawn quser.exe and parse the output, but is there an Win32 API or .Net framework class that can give me the same information? Specifically:
User Name
Connection State
Logon time
I've found that using WMI (Win32_LoggedOnUser) to find the same information is unreliable, as it often lists stale connections. I've also tried the psloggedon approach of enumerating subkeys of HKEY_USERS and looking for the Volatile Environment key, but this also suffers from the same problem.
I'm going to answer my own question.
First of all, you need to make sure that permissions are set correctly on the target machine. This entails setting HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server\AllowRemoteRPC to 1. A powershell script to do this is:
# 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)
}
As Xearinox said, for C++ you can use the WTSxxx functions in the Win32 API. Assuming your computers are not XP, here is some C++ code:
#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);
}
For C#, the easiest way is to use the Cassia library, which is basically a C# wrapper around the same API functions.
You can call a Win32 API to create a process and pass the "quser /server:MyRDPserver" as parameters,I usually do like this:
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;
when the process startup, you can redirect the output.If you use Qt,the problem become simple,you can use QProcess to implement.
I have a proxy program which resend network traffic. It listens specific port, and addresses server with another port. It works, but I need to add some encryption between 2 instances of my proxy. When encryption is not enabled two sequential proxy work good, but with encryption don't work. Here is code:
public class CommunicationContext
{
/// <summary>Define buffer max size. Influence on memory usage.</summary>
public const int MaxBuffer = 16 * 1024;
/// <summary>Unique counter...</summary>
private static int _uniqueCounter = 0;
public readonly Socket SocketIn;
public readonly Socket SocketOut;
public readonly PortMapping Mapping;
public byte[] BufferIn;
public byte[] BufferOut;
public bool IsShutdown;
public readonly object SyncObject = new object();
public readonly int UniqueId;
public CommunicationContext(Socket socketIn, Socket socketOut, PortMapping map){
SocketIn = socketIn;
SocketOut = socketOut;
Mapping = map;
UniqueId = Interlocked.Increment( ref _uniqueCounter );
}
public void InitializeBuffers(){
this.BufferIn = new byte[MaxBuffer];
this.BufferOut = new byte[MaxBuffer];
}
}
private static void ReadInputSocket( IAsyncResult ar ){
CommunicationContext context = (CommunicationContext)ar.AsyncState;
try{
int length = context.SocketIn.EndReceive( ar );
if( length <= 0 )
throw new NoDataSocketException();
lock( context.SyncObject ){
Switches.GeneralLog.Verbose( "==> Client data size: " + length );
SocketFlags flags = ( context.SocketIn.Available == 0 ) ? SocketFlags.None : SocketFlags.Partial;
if(!CryptoTools.CryptEnabled){
//without encrypion works fine
}
else if(CryptoTools.CryptInner){
context.BufferIn = CryptoTools.Crypt(context.BufferIn);
}else{
context.BufferIn = CryptoTools.Decrypt(context.BufferIn);
}
context.SocketOut.Send(context.BufferIn, 0, length, flags);
}
Thread.Sleep( 0 );
context.SocketIn.BeginReceive(context.BufferIn, 0, MaxBuffer, SocketFlags.None, ReadInputSocket, context);
}
catch( Exception ex ){
Switches.GeneralLog.Verbose( ex );
Switches.GeneralLog.Info( ex.Message );
ShutdownCommunication( context );
}
}
private static void ReadOutputSocket(IAsyncResult ar ){
CommunicationContext context = (CommunicationContext)ar.AsyncState;
try{
int length = context.SocketOut.EndReceive( ar);
if( length <= 0 )
throw new NoDataSocketException();
lock( context.SyncObject )
{
Switches.GeneralLog.Verbose( "<== Server data size: " + length );
SocketFlags flags = ( context.SocketOut.Available == 0 ) ? SocketFlags.None : SocketFlags.Partial;
if (!CryptoTools.CryptEnabled){
//without encrypion works fine
}
else if (CryptoTools.CryptInner){
context.BufferOut = CryptoTools.Decrypt(context.BufferOut);
}
else{
context.BufferOut = CryptoTools.Crypt(context.BufferOut);
}
context.SocketIn.Send(context.BufferOut, 0, length, flags);
}
context.SocketOut.BeginReceive(context.BufferOut, 0, MaxBuffer, SocketFlags.None, ReadOutputSocket, context);
}
catch( Exception ex )
{
Switches.GeneralLog.Verbose( ex );
Switches.GeneralLog.Info( ex.Message );
ShutdownCommunication( context );
}
}
Edit from comments:
What not works: Data keep getting corrupted.
I get no exceptions. Just malformed data. I used different methods for Crypt/Decrypt. I made them even both equal - simple XOR. Encryption algorithm has not significance, for example, XOR was used.
My configuration is like this Client <--> Proxy1 <--enc--> Proxy2 <---> Server. Between two proxies must be the encrypted stream.