c# named pipe two way communcation - c#

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.

Related

NamedPipeClientStream in Message Mode never sets IsMessageComplete to true

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( );
}

Serial port communication issue from c# to c application

I am writing a c#application which communicates with a device c application.
I am trying to write data from the c# application to c application over the serial port and vice versa.
I am trying to write some sample data using the following code.
C#
using PORT = System.IO.Port.SerialPort;
PORT p = new PORT("COM4", 9600, System.IO.Ports.Parity.None, 8, System.IO.Ports.StopBits.One);
string data="HELLO";
//String to byte[] conversion
byte[] byt_buff = new byte[data.Length];
byt_buff = Encoding.ASCII.GetBytes(data);
p.Open();
p.DiscardOutBuffer();
p.Write(byt_buff, 0, byt_buff.Length);
p.Close();
The c application running on the device has the following code.
C
#include<stdio.h>
#include <poll.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include<stdint.h>
#include<string.h>
#define USB_DEV_NODE "usb.dat"
#define MAX_SIZE 20
int main()
{
int fd;
int ret_poll;
int ret_read;
struct pollfd pollfd;
char buf[MAX_SIZE];
int i = 0;
pollfd.events = POLLIN;
fd = open(USB_DEV_NODE, O_RDWR | O_NOCTTY);
pollfd.fd = fd;
while (1)
{
ret_poll = poll( &pollfd, 1, -1);
if( ret_poll == -1 )
{
printf("Gpio_poll poll failed\r\n");
close(fd);
}else{
if (pollfd.revents & POLLIN )
{
ret_read = read(fd, buf, MAX_SIZE);
buf[ret_read]=0;
if(ret_read > 0)
{
for(i=0;i<ret_read ; i++)
{
printf("BUF[%d]=%c\n",i,buf[i]);
}
}
}
}
}
return 0;
}
But I am failing to receive the data written as byte[],No data is recieved. Instead if I write data as a string using p.WriteLine("HELLO"); it will be recieved. Please help.

How to reproduce quser.exe using API calls?

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.

USB to Serial (to and fro) communication using C# and C language

This is the 1st time am posting my query. I am in need of help. Any help is appreciated.
As i agree that i have given my prob as long story. But am sorry i am not getting how to make it short and my intention is give complete information regarding my prob.
Problem :
I have to communicate between two laptops using USB-to-Serial adapter on windows platform. I have written 2 programs one for sending and another for receiving. Programs were written in both C and C# programming languages.
Using C language :
I am able to successfully communicate using C-Programs mentioned below. But the problem is speed. It takes around 1 hour(60min) for just to pass 150MB. Anyone plz help me in improving the performance of this programs or you may suggest me other approaches which is robust and give high performance. I also mention some comments along with programs for self understanding.
Sender File on laptop with serial port :
#include <stdio.h>
#include <bios.h>
#include <conio.h>
#define COM1 0
#define DATA_READY 0x100
#define TRUE 1
#define FALSE 0
#define SETTINGS ( 0xE0 | 0x03 | 0x00 | 0x00)
int main(void)
{
int in, out, status, DONE = FALSE,i=0;
char c;
FILE *fp,*fp1;
unsigned long count = 0,shiftcount = 0;
clrscr();
fp = fopen("C:/TC/pic.jpg","rb"); //opened for reading actual content
fp1 = fopen("C:/TC/pic.jpg","rb"); //opened for reading the size of file
fseek(fp1,0L,2);
count = ftell(fp1) + 1; // file size
bioscom(0, SETTINGS, COM1); // initializing the port
printf("No. of Characters = %lu\n",count);
// since bioscom function can send or receive only 8bits at a time, am sending file size in
4 rounds so that we can send at max of 4GB file.
bioscom(1,count,COM1); // sneding 1st lower 8bits
bioscom(1,count>>8,COM1); // sending 2nd set of lower 8bits
bioscom(1,count>>16,COM1); // sending 3rd set of lower 8bits
bioscom(1,count>>24,COM1); // sending upper 8 bits
cprintf("... BIOSCOM [ESC] to exit ...\n");
while (!DONE)
{
status = bioscom(3, 0, COM1);// get the status of port
//printf("%d",status);
if (status & DATA_READY) //checks if data is ready
{
out = bioscom(2, 0, COM1); // receives the ack
if(!feof(fp))
{
c = fgetc(fp); //read character by character from file
bioscom(1,c,COM1);//send character to receiver
putch(c);//display
}
}
//to interrupt
if (kbhit())
{
if ((in = getch()) == '\x1B')
DONE = TRUE;
}
}
fclose(fp);
return 0;
}
Receiving file on laptop with USB port :
#include <stdio.h>
#include <bios.h>
#include <conio.h>
#define COM4 3
#define DATA_READY 0x100
#define TRUE 1
#define FALSE 0
#define SETTINGS ( 0xE0 | 0x03 | 0x00 | 0x00)
int main(void)
{
int in, out, status;
char c;
FILE *fp;
unsigned long shiftcount1=0,shiftcount2=0,shiftcount3=0,shiftcount4=0;
unsigned long count = 0, DONE = 1;
clrscr();
fp = fopen("C:/TC/pic1.jpg","wb");// file opened for writing
bioscom(0, SETTINGS, COM4);//initialize tyhe port
cprintf("... BIOSCOM [ESC] to exit ...\n");
//receives all the 32 bits of file size sent from sender
shiftcount1 = bioscom(2,0,COM4);
shiftcount2 = bioscom(2,0,COM4);
shiftcount3 = bioscom(2,0,COM4);
shiftcount4 = bioscom(2,0,COM4);
//send an ack
bioscom(1,'x',COM4);
count = shiftcount1 | (shiftcount2<<8) | (shiftcount3<<16) | (shiftcount4<<24);
printf("shift4 = %lu\tshift3 = %lu\tshift2 = %lu\tshift1 = %lu\n",shiftcount4,shiftcount3,shiftcount2,shiftcount1);
printf("File Size = %lu\n",count);
//loop till the size of the file
while (DONE < count)
{
status = bioscom(3, 0, COM4);// check the status
// printf("%d",status);
if (status & DATA_READY)//check for data ready at the port
{
out = bioscom(2, 0, COM4);//receive the data
DONE++;
fputc(out,fp);
putch(out);
bioscom(1,'x',COM4);//send an ack
}
if (kbhit())
{
if ((in = getch()) == '\x1B')
break;
}
}
fclose(fp);
return 0;
}
Sender file on laptop with USB port:
#include <stdio.h>
#include <bios.h>
#include <conio.h>
#include<dos.h>
#include<stdlib.h>
#include<time.h>
#define RTS 0x02
#define COM1 0
#define COM4 3
#define CURRCOM COM4
#define DATA_READY 0x100
#define TRUE 1
#define FALSE 0
#define SETTINGS ( 0xE0 | 0x03 | 0x00 | 0x00)
int main(void)
{
int in, out, status, DONE = FALSE,nextfile = 1;
char c;
FILE *fp,*fp1;
unsigned long count = 0,shiftcount = 0;
clock_t start,end;
start = clock();
clrscr();
fp = fopen("C:/TC/pic.jpg","rb");
fp1 = fopen("C:/TC/pic.jpg","rb");
fseek(fp1,0L,2);
count = ftell(fp1) + 1;
bioscom(0, SETTINGS, CURRCOM);
/* while(!feof(fp1))
{
c = fgetc(fp1);
count++;
} */
printf("No. of Cheracters = %lu\n",count);
bioscom(1,count,CURRCOM);
bioscom(1,count>>8,CURRCOM);
bioscom(1,count>>16,CURRCOM);
bioscom(1,count>>24,CURRCOM);
cprintf("\n... BIOSCOM [ESC] to exit ...\n");
while (!DONE)
{
status = bioscom(3, 0, CURRCOM);
if (status & DATA_READY)
{
out = bioscom(2,0,CURRCOM);
if(!feof(fp))
{
c = fgetc(fp);
bioscom(1,c,CURRCOM);
putch(c);
}
}
if (kbhit())
{
if ((in = getch()) == '\x1B')
DONE = TRUE;
}
}
fclose(fp);
end = clock();
printf("\nTotal time = %d\n",(end - start)/CLK_TCK);
return 0;
}
Receiver file on laptop with serial port :
#include <stdio.h>
#include <bios.h>
#include <conio.h>
#include<time.h>
#define COM1 0
#define DATA_READY 0x100
#define TRUE 1
#define FALSE 0
#define SETTINGS ( 0xE0 | 0x03 | 0x00 | 0x00)
int main(void)
{
int in, out, status;
char c;
FILE *fp;
int y = 0,esc;
unsigned long count=0,shiftcount1 = 0,shiftcount2 = 0,shiftcount3 = 0,shiftcount4 = 0, DONE = 1;
clock_t start,end;
start = clock();
clrscr();
fp = fopen("C:/TC/pic1.jpg","wb");
bioscom(0, SETTINGS, COM1);
cprintf("... BIOSCOM [ESC] to exit ...\n");
shiftcount1 = bioscom(2,0,COM1);
shiftcount2 = bioscom(2,0,COM1);
shiftcount3 = bioscom(2,0,COM1);
shiftcount4 = bioscom(2,0,COM1);
bioscom(1,'x',COM1);
count = shiftcount1 | (shiftcount2<<8) | (shiftcount3<<16) | (shiftcount4<<24);
printf("shift4 = %lu\tshift3 = %lu\tshift2 = %lu\t shift1 = %lu\n",shiftcount4,shiftcount3,shiftcount2,shiftcount1);
printf("file size = %lu\n",count);
while (DONE < count)
{
status = bioscom(3, 0, COM1);
//printf("%d",status);
if (status & DATA_READY)
{
out = bioscom(2, 0, COM1);
DONE++;
fputc(out,fp);
putch(out);
bioscom(1,'x',COM1);
}
if (kbhit())
{
if ((in = getch()) == '\x1B')
break;
}
}
fclose(fp);
end = clock();
printf("\nTotal time = %f\n",(end - start)/CLK_TCK);
return 0;
}
The above 4 programs behaves as, sender send a character and receives an ack for every character. I have followed this approach, bcoz other approaches were not working fine (in the sense the complete data is not sent, the amount of data sent is not judgeable, bcoz it will different every tym). when i used this approach it worked fine.
Using C# language :
Below two programs are written in C# using visual studio. I have used SerilaPort class , its properties and methods for communication. Using this, am able to communicate text and xml files on both the sides successfully.Also image files with .jpg extention, can be transferred from USB to serial end withot any loss of data(successful transmission), but if i transfer from serial to usb end, am able receive image with some data loss, even with the data loss am able to see the image.
Sender file on laptop with serial port :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;
using System.IO;
using System.Threading;
namespace Communication
{
class Program
{
static void Main(string[] args)
{
string name;
string message;
StringComparer stringComparer = StringComparer.OrdinalIgnoreCase;
//Thread readThread = new Thread(Read);
FileStream fs = new FileStream("C:/text.xml", FileMode.Open);
//StreamReader sr = new StreamReader(fs);
BinaryReader br = new BinaryReader(fs);
// Create a new SerialPort object with default settings.
SerialPort _serialPort = new SerialPort();
// Allow the user to set the appropriate properties.
_serialPort.PortName = "COM1";
_serialPort.BaudRate = 115200;
_serialPort.Parity = Parity.None;
_serialPort.DataBits = 8;
_serialPort.StopBits = StopBits.One;
_serialPort.Handshake = Handshake.None;
// Set the read/write timeouts
_serialPort.ReadTimeout = 500;
_serialPort.WriteTimeout = 500;
_serialPort.Open();
bool _continue = true;
//readThread.Start();
int len = (int)fs.Length;
char[] data = new char[len+1];
br.Read(data, 0, len);
for (int i = 0; i < len+1; i++)
{
_serialPort.Write(data, i, 1);
//Console.Write(data,i,1);
}
br.Close();
fs.Close();
_serialPort.Close();
}
}
}
Receiver file on laptop with USB port :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;
using System.IO;
using System.Threading;
using System.Collections;
namespace Communication
{
class Program
{
static void Main(string[] args)
{
SerialComm comm = new SerialComm();
comm.Init();
comm.ReadSerial();
comm.WriteToFile();
comm.ResClose();
Console.ReadKey();
}
}
class SerialComm
{
FileStream fs = null;
BinaryWriter file = null;
ArrayList al = null;
public Boolean Init()
{
if (fs == null)
{
fs = new FileStream("C:/text1.txt", FileMode.OpenOrCreate);
}
if (file == null)
{
file = new BinaryWriter(fs);
}
if (al == null)
{
al = new ArrayList();
}
return true;
}
public void ResClose()
{
file.Close();
fs.Close();
}
public Boolean ReadSerial()
{
SerialPort port;
StreamWriter sw;
ConsoleKeyInfo ck;
port = new SerialPort();
port.PortName = "COM4";
port.BaudRate = 115200;
port.DataBits = 8;
port.Parity = Parity.None;
port.StopBits = StopBits.One;
port.Handshake = Handshake.None;
port.Open();
port.BaseStream.Flush();
port.DiscardInBuffer();
int c = 1;
while (c != 0)
{
c = port.ReadByte();
al.Add((byte)c);
}
return true;
}
public void WriteToFile()
{
int i = 0;
byte[] message = al.ToArray(typeof(byte)) as byte[];
file.Write(message, 0, message.Length - 1);
}
}
}
Please help me.
Thanks in advance.
Transmission speed:
Serial ports are simple, not fast. I am assuming that you are using 230kbps which is already more than many hardware can handle. Compression is only thing that might help, but compressing already compressed data (like .mp3) won't help much.
Data loss:
Serial ports are simple, not robust. Data loss is common, and only thing you can do about it is to have protocol to detect errors on incoming frames, and have ability to retry send if there is an error.
Conclusion:
Use TCP/IP instead.
Way too long a question. I've found only one actual question, and that's the performance bit.
Just use Ethernet or WiFi. "Serial port" (you probably mean RS-232) speeds are low. 0.1 Mbit/second is considered fast by RS-232 standards. You clock 1200 Mbit/3600 seconds, which is 0.3 Mbit/second. That is ultra-fast. I'm in fact surprised that you achieve that, your C# program is explicitly setting the speed to 0.1 Mbit/second.

passing c++ char* to c# via shared-memory

Sorry for probably simple question but I'm newbie in shared memory and trying to learn by example how to do things.
On c++ side I receive such pair: const unsigned char * value, size_t length
On c# side I need to have regular c# string. Using shared memory what is the best way to do that?
It's not that easy to using the string.
If it's me, I'll try these ways:
1.simply get a copy of the string. System.Text.Encoding.Default.GetString may convert from a byte array to a string.
You may try in a unsafe code block (for that you could use pointer type) to do:
(1) create a byte array, size is your "length"
byte[] buf = new byte[length];
(2) copy your data to the array
for(int i = 0; i < length; ++i) buf[i] = value[i];
(3) get the string
string what_you_want = System.Text.Encoding.Default.GetString(buf);
2.write a class, having a property "string what_you_want", and each time you access it, the above process will perform.
before all, you should first using P/Invoke feature to get the value of that pair.
edit: this is an example.
C++ code:
struct Pair {
int length;
unsigned char value[1024];
};
#include <windows.h>
#include <stdio.h>
int main()
{
const char* s = "hahaha";
HANDLE handle = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(Pair), L"MySharedMemory");
struct Pair* p = (struct Pair*) MapViewOfFile(handle, FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, sizeof(Pair));
if (p != 0) {
p->length = lstrlenA(s);
lstrcpyA((char*)p->value, s);
puts("plz start c# program");
getchar();
} else
puts("create shared memory error");
if (handle != NULL)
CloseHandle(handle);
return 0;
}
and C# code:
using System;
using System.IO.MemoryMappedFiles;
class Program
{
static void Main(string[] args)
{
MemoryMappedFile mmf = MemoryMappedFile.OpenExisting("MySharedMemory");
MemoryMappedViewStream mmfvs = mmf.CreateViewStream();
byte[] blen = new byte[4];
mmfvs.Read(blen, 0, 4);
int len = blen[0] + blen[1] * 256 + blen[2] * 65536 + blen[3] * 16777216;
byte[] strbuf = new byte[len];
mmfvs.Read(strbuf, 0, len);
string s = System.Text.Encoding.Default.GetString(strbuf);
Console.WriteLine(s);
}
}
just for example.
you may also add error-check part.

Categories

Resources