I have the following C++ code and when I run the program sometimes it works and sometimes it does not!
I think the problem occurs when I try to open the serial port while there is data left to be read.
Sometimes after running this program, it makes Windows XP restart unexpectedly! It does not Blue Screen, it justs restarts.
I am using Visual Studio 2010 to compile it.
main()
{
while(0) { // BIG FAT WARNING: MIGHT SUDDEN REBOOT YOUR MACHINE IF ENABLED
read_from_serial(_data);
}
}
bool read_from_serial(octed_string &_data)
{
HANDLE hSerial;
hSerial = CreateFile(TEXT("COM2"),
GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
if (hSerial == INVALID_HANDLE_VALUE)
{
if (GetLastError() == ERROR_FILE_NOT_FOUND)
{
cout << "1:";
return false;
//serialportdoesnotexist.Informuser.
}
cout << "2:";
return false;
//someothererroroccurred.Informuser.
}
DCB dcbSerialParams = {0};
dcbSerialParams.DCBlength=sizeof(dcbSerialParams);
if(!GetCommState(hSerial,&dcbSerialParams))
{
cout<<"3:";
return false;
//errorgettingstate
}
dcbSerialParams.BaudRate=CBR_9600;
dcbSerialParams.ByteSize=7;
dcbSerialParams.StopBits=ONESTOPBIT;
dcbSerialParams.Parity=EVENPARITY;
if (!SetCommState(hSerial,&dcbSerialParams))
{
cout<<"4:";
return false;
//errorsettingserialportstate
}
COMMTIMEOUTS timeouts={0};
timeouts.ReadIntervalTimeout=50;
timeouts.ReadTotalTimeoutConstant=10;
timeouts.ReadTotalTimeoutMultiplier=10;
timeouts.WriteTotalTimeoutConstant=50;
timeouts.WriteTotalTimeoutMultiplier=10;
if (!SetCommTimeouts(hSerial,&timeouts))
{
cout<<"5:";
return false;
//erroroccureed.Informuser
}
const int n=1;
DWORD dwBytesRead=0;
char_t tmp_receive[255]={0};
char_t buff[255];
int len=255;
if (!ReadFile(hSerial,tmp_receive,len,&dwBytesRead,NULL))
{
cout<<"6:";
CloseHandle(hSerial);
return false;
}
CloseHandle(hSerial);
tmp_receive[dwBytesRead+1]=END_OF_STRING;
string tmp_buff_str=tmp_receive;
_data.append(tmp_buff_str);
return true;
}
Cause
I have a suspicion that your program is crashing on this line
tmp_receive[dwBytesRead+1]=END_OF_STRING;
You defined the tmp_receive array with 255 elements, which makes the possible indexes 0 to 254. You then initialized len to 255. If there are 255 bytes available to read on the call to ReadFile(...), then dwBytesRead will be equal to 255 and the line I mentioned above will effectively be as follows, and would mean you're attempting to write to memory outside of the scope of the tmp_receive array.
tmp_receive[256] = END_OF_STRING;
As for the rebooting, I don't know for sure, but maybe your program is causing a system crash when it attempts to write to invalid memory and you have Windows XP configured to reboot instead of displaying a BSOD.
Solutions
In order to keep your program from crashing I see that you have 2 options. I can't say which one is better since I don't know what the format of the data you're expecting to receive is, so you will have to analyize the outcomes of each option and decide for yourself.
Option #1
Use an element count of 257 when defining the tmp_receive array.
Option #2
Subtract 2 from len when making the call to ReadFile(...)
if (!ReadFile(hSerial,tmp_receive,len-2,&dwBytesRead,NULL))
Additional Information
Have a look at the MSDN documentation on ReadFile(...) for more information on the behaviour of the ReadFile(...) Windows API.
If you would like to learn more about how strings are stored in memory, I would suggest having a look at the Character Sequences article on www.cplusplus.com.
Related
Our program has a piece of code that calculates the list of computers on our local network. It uses the Windows Networking API (WNetOpenEnum/WNetEnumResource) to unwind the network. For many years, the resulting list was identical to the one that can be seen in Windows Explorer under the "Network" entry. However, recently we have noticed that the same code returns an empty list. During debugging I found that WNetOpenEnum returns error 1231 (ERROR_NETWORK_UNREACHABLE) when it is called for the "Microsoft Windows Network" under the root node.
I have to mention, though I'm pretty sure it has nothing to do with the matter, that the network unwinding is done multithreaded, to avoid possible delays in the main GUI thread. Each time a node of type RESOURCEUSAGE_CONTAINER is encountered, a new worker thread is launched. The thread function calls the following procedure:
DWORD WINAPI EnumNetwork(NETRESOURCE_M* lpNR)
{
const int BUF_SIZE = 16384; // 16K is a good size.
HANDLE hEnum;
DWORD Result;
// Call the WNetOpenEnum function to begin the enumeration.
Result = ::WNetOpenEnum(RESOURCE_GLOBALNET, // all network
RESOURCETYPE_ANY, // all resource types
0, // enumerate all
(LPNETRESOURCE)lpNR,// parent resource
&hEnum); // enumeration handle
if (Result != NO_ERROR) // -> for "Microsoft Windows Network" Result = 1231
return Result;
std::vector<std::wstring> SrvList;
// Allocate buffer for enumeration.
LPNETRESOURCE lpEnumNR = (LPNETRESOURCE)new char[BUF_SIZE];
if (lpEnumNR == 0)
Result = ERROR_OUTOFMEMORY;
else
{
while (1)
{
::ZeroMemory(lpEnumNR, BUF_SIZE); // Initialize the buffer.
DWORD NumEntries = -1; // Enumerate all entries.
DWORD BufSize = BUF_SIZE;
// Call WNetEnumResource to continue the enumeration.
Result = ::WNetEnumResource(hEnum, // enumeration handle
&NumEntries,// number of entries to enumerate
lpEnumNR, // array of resources to return
&BufSize); // buffer size
if (Result == NO_ERROR)
{
// If the call succeeds, loop through the array.
for (unsigned i = 0; i < NumEntries; ++i)
{
if (lpEnumNR[i].dwDisplayType == RESOURCEDISPLAYTYPE_SERVER)
{
// Collect servers.
LPCWSTR SrvName = lpEnumNR[i].lpRemoteName;
if (PathHelpers::IsFullPath(SrvName))
SrvList.push_back(SrvName);
}
else if ((lpEnumNR[i].dwUsage & RESOURCEUSAGE_CONTAINER) &&
lpEnumNR[i].lpRemoteName != 0)
{
TCHAR PathBuf[1024] = {0};
if (lpNR && lpNR->Path)
{
_tcscpy(PathBuf, lpNR->Path);
::PathAddBackslash(PathBuf);
}
_tcscat(PathBuf, lpEnumNR[i].lpRemoteName);
if (RegisterServer(PathBuf))
{
// Start new thread for recursive enumeration.
NETRESOURCE_M* lpChildNR = DeepCopyNR(&lpEnumNR[i], PathBuf);
ExploreNetwork(lpChildNR); // -> this starts a worker thread
}
else
{
GetLogger().LogMessage(
_T("Cycles found while unwinding network: %s"), PathBuf);
}
}
}
}
else
{
if (Result == ERROR_NO_MORE_ITEMS)
Result = NO_ERROR;
break;
}
} // end while
delete [] (char*)lpEnumNR;
} // end if
::WNetCloseEnum(hEnum);
if (!SrvList.empty())
NotifyServerAdded(SrvList);
return Result;
}
where NETRESOURCE_M is the structure
struct NETRESOURCE_M
{
NETRESOURCE NR;
LPTSTR Path;
};
Trying to figure out what could have caused such a sudden change in behavior, I found in Google that a few years ago Microsoft disabled the SMB1 protocol, which could affect Network Discovery. However, I can't believe they could have damaged their own API without saying a word in the documentation.
EDIT: At the same time, Windows Explorer has a bunch of computers under its "Network" node. In the network settings, the network type is "Domain", and the network discovery is ON. Services "Function Discovery Provider Host" and "Function Discovery Resources Publication" are running. Windows OS build is 19042.685.
Edit 2: The Sysinternals' "ShareEnum" tool also fails with the error: "No domains or workgroups where found on your network". Because of this, and also because some time ago our company moved all of its computers to a different network, I got the feeling that the problem is in the network configuration. Such as though the network is declared as "Domain", the computers were not enrolled to this domain. I do not understand much in that, but something like this.
I need the simplest most reliable IPC method from one C++ app running on the RPi to another app.
All I'm trying to do is send a string message of 40 characters from one app to another
The first app is running as a service on boot, the other app is started at a later time and is frequently exited and restarted for debugging
The frequent debugging for the second app is whats causing problems with the IPCs I've tried so far
I've tried about 3 different methods and here is where they failed:
File FIFO, the problem is one program hangs while the other program is writing to the file
Shared memory: cannot initialize on one thread and read from another thread. Also frequent exiting while debugging causing GDB crashes with the following GDB command is taking too long to complete -stack-list-frames --thread 1
UDP socket with localhost - same issue as above, plus improper exits block the socket, forcing me to reboot device
Non blocking pipe - not getting any messages on the receiving process
What else can I try? I dont want to get the DBus library, seems too complex for this application.
Any simple server and client code or a link to it would be helpful
Here is my non-blockign pipe code, that doesnt work for me,
I assume its because I dont have a reference to the pipe from one app to the other
Code sourced from here: https://www.geeksforgeeks.org/non-blocking-io-with-pipes-in-c/
char* msg1 = "hello";
char* msg2 = "bye !!";
int p[2], i;
bool InitClient()
{
// error checking for pipe
if(pipe(p) < 0)
exit(1);
// error checking for fcntl
if(fcntl(p[0], F_SETFL, O_NONBLOCK) < 0)
exit(2);
//Read
int nread;
char buf[MSGSIZE];
// write link
close(p[1]);
while (1) {
// read call if return -1 then pipe is
// empty because of fcntl
nread = read(p[0], buf, MSGSIZE);
switch (nread) {
case -1:
// case -1 means pipe is empty and errono
// set EAGAIN
if(errno == EAGAIN) {
printf("(pipe empty)\n");
sleep(1);
break;
}
default:
// text read
// by default return no. of bytes
// which read call read at that time
printf("MSG = % s\n", buf);
}
}
return true;
}
bool InitServer()
{
// error checking for pipe
if(pipe(p) < 0)
exit(1);
// error checking for fcntl
if(fcntl(p[0], F_SETFL, O_NONBLOCK) < 0)
exit(2);
//Write
// read link
close(p[0]);
// write 3 times "hello" in 3 second interval
for(i = 0 ; i < 3000000000 ; i++) {
write(p[0], msg1, MSGSIZE);
sleep(3);
}
// write "bye" one times
write(p[0], msg2, MSGSIZE);
return true;
}
Please consider ZeroMQ
https://zeromq.org/
It is lightweight and has wrapper for all major programming languages.
I recently created an application that I developed on my x86 Linux machine. Basically it's just two threads that communicate over a pipe() with each other. Thread 0 listens on the read end and Thread 1 writes into that pipe. That program worked perfectly fine.
But when I copied the sources over to a RaspberryPi and built it, there were some runtime issues (but compiled with no errors). It seems that thread0 never gets something out of the pipe, it just blocks.
Since pipes are made for Interprocess communication, i thought it would also be thread safe (since there also are two different file descriptors for read and write end).
BUT: Stepping through the program in the Qt Creator debugger on the RPi, everything seemed to work fine! I know the debugger initializing certain variables different can lead to such conditions, but I couldn't find any usages of uninitialized variables etc. in my Code.
thread 1:
void *midiThread(void *fds)
{
midiDevice = ((int*)fds)[0]; // device file for midi input
midiBuffer = ((int*)fds)[1]; // write end of the pipe
unsigned char rawBuffer[MIDI_MSG_LENGTH];
while (read(midiDevice, rawBuffer, MIDI_MSG_LENGTH)
>= MIDI_MSG_LENGTH)
{
struct midievent_t currentEvent;
unsigned char *rawBuffer = (unsigned char *)buffer;
currentEvent.channel = rawBuffer[0] & 0x0f;
// ....
write(midiBuffer, ¤tEvent, sizeof(struct midievent_t));
}
close(midiBuffer);
return NULL;
}
main thread:
void MidiInput::createMidiThread()
{
if (pipe(_midiBufferPipe) < 0)
{
// error
}
int fds[2];
fds[0] = _midiFileDescriptor;
fds[1] = _midiBufferPipe[1];
pthread_create(&_midiThreadId, NULL,
midiThread, fds);
}
bool MidiInput::read(midievent_t *event)
{
if (!_initialized)
{
return false;
}
if (read(_midiBufferPipe[0], event, sizeof(struct midievent_t))
< sizeof(struct midievent_t))
{
// some error
return _initialized = false;
}
return true;
}
Currently I try to write a serial port communication in VC++ to transfer data from PC and robot via XBee transmitter. But after I wrote some commands to poll data from robot, I didn't receive anything from the robot (the output of filesize is 0 in the code.). Because my MATLAB interface works, so the problem should happen in the code not the hardware or communication. Would you please give me help?
01/03/2014 Updated: I have updated my codes. It still can not receive any data from my robot (the output of read is 0). When I use "cout<<&read" in the while loop, I obtain "0041F01C1". I also don't know how to define the size of buffer, because I don't know the size of data I will receive. In the codes, I just give it a random size like 103. Please help me.
// This is the main DLL file.
#include "StdAfx.h"
#include <iostream>
#define WIN32_LEAN_AND_MEAN //for GetCommState command
#include "Windows.h"
#include <WinBase.h>
using namespace std;
int main(){
char init[]="";
HANDLE serialHandle;
// Open serial port
serialHandle = CreateFile("\\\\.\\COM8", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
// Do some basic settings
DCB serialParams;
DWORD read, written;
serialParams.DCBlength = sizeof(serialParams);
if((GetCommState(serialHandle, &serialParams)==0))
{
printf("Get configuration port has a problem.");
return FALSE;
}
GetCommState(serialHandle, &serialParams);
serialParams.BaudRate = CBR_57600;
serialParams.ByteSize = 8;
serialParams.StopBits = ONESTOPBIT;
serialParams.Parity = NOPARITY;
//set flow control="hardware"
serialParams.fOutX=false;
serialParams.fInX=false;
serialParams.fOutxCtsFlow=true;
serialParams.fOutxDsrFlow=true;
serialParams.fDsrSensitivity=true;
serialParams.fRtsControl=RTS_CONTROL_HANDSHAKE;
serialParams.fDtrControl=DTR_CONTROL_HANDSHAKE;
if (!SetCommState(serialHandle, &serialParams))
{
printf("Set configuration port has a problem.");
return FALSE;
}
GetCommState(serialHandle, &serialParams);
// Set timeouts
COMMTIMEOUTS timeout = { 0 };
timeout.ReadIntervalTimeout = 30;
timeout.ReadTotalTimeoutConstant = 30;
timeout.ReadTotalTimeoutMultiplier = 30;
timeout.WriteTotalTimeoutConstant = 30;
timeout.WriteTotalTimeoutMultiplier = 30;
SetCommTimeouts(serialHandle, &timeout);
if (!SetCommTimeouts(serialHandle, &timeout))
{
printf("Set configuration port has a problem.");
return FALSE;
}
//write packet to poll data from robot
WriteFile(serialHandle,">*>p4",strlen(">*>p4"),&written,NULL);
//check whether the data can be received
char buffer[103];
do {
ReadFile (serialHandle,buffer,sizeof(buffer),&read,NULL);
cout << read;
} while (read!=0);
//buffer[read]="\0";
CloseHandle(serialHandle);
return 0;
}
GetFileSize is documented not to be valid when used with a serial port handle. Use the ReadFile function to receive serial port data.
You should use strlen instead of sizeof here:
WriteFile(serialHandle,init,strlen(init),&written,NULL)
You would be even better off creating a function like this:
function write_to_robot (const char * msg)
{
DWORD written;
BOOL ok = WriteFile(serialHandle, msg, strlen(msg), &written, NULL)
&& (written == strlen(msg));
if (!ok) printf ("Could not send message '%s' to robot\n", msg);
}
But that's only the appetizer. The main trouble is, as MDN says:
You cannot use the GetFileSize function with a handle of a nonseeking device such as a pipe or a communications device.
If you want to read from the port, you can simply use ReadFile until it returns zero bytes.
If you already know the max size of your robot's response, try reading that many characters.
Continue reading until the read reports an actual number of bytes read inferior to the size of the buffer. For instance:
#define MAX_ROBOT_ANSWER_LENGTH 1000 /* bytes */
const char * read_robot_response ()
{
static char buffer[MAX_ROBOT_ANSWER_LENGTH];
DWORD read;
if (!ReadFile (serialHandle, buffer, sizeof(buffer), &read, NULL))
{
printf ("something wrong with the com port handle");
exit (-1);
}
if (read == sizeof(buffer))
{
// the robot response is bigger than it should
printf ("this robot is overly talkative. Flushing input\n");
// read the rest of the input so that the next answer will not be
// polluted by leftovers of the previous one.
do {
ReadFile (serialHandle, buffer, sizeof(buffer), &read, NULL);
} while (read != 0);
// report error
return "error: robot response exceeds maximal length";
}
else
{
// add a terminator to string in case Mr Robot forgot to provide one
buffer[read] = '\0';
printf ("Mr Robot said '%s'\n", buffer);
return buffer;
}
}
This simplistic function returns a static variable, which will be overwritten each time you call read_robot_response.
Of course the proper way of doing things would be to use blocking I/Os instead of waiting one second and praying for the robot to answer in time, but that would require a lot more effort.
If you feel adventurous, you can use overlapped I/O, as this lenghty MDN article thoroughly explores.
EDIT: after looking at your code
// this reads at most 103 bytes of the answer, and does not display them
if (!ReadFile(serialHandle,buffer,sizeof(buffer),&read,NULL))
{
printf("Reading data to port has a problem.");
return FALSE;
}
// this could display the length of the remaining of the answer,
// provided it is more than 103 bytes long
do {
ReadFile (serialHandle,buffer,sizeof(buffer),&read,NULL);
cout << read;
}
while (read!=0);
You are displaying nothing but the length of the response beyond the first 103 characters received.
This should do the trick:
#define BUFFER_LEN 1000
DWORD read;
char buffer [BUFFER_LEN];
do {
if (!ReadFile(
serialHandle, // handle
buffer, // where to put your characters
sizeof(buffer) // max nr of chars to read
-1, // leave space for terminator character
&read, // get the number of bytes actually read
NULL)) // Yet another blody stupid Microsoft parameter
{
// die if something went wrong
printf("Reading data to port has a problem.");
return FALSE;
}
// add a terminator after last character read,
// so as to have a null terminated C string to display
buffer[read] = '\0';
// display what you actually read
cout << buffer;
}
while (read!=0);
I advised you to wrap the actual calls to serial port accesses inside simpler functions for a reason.
As I said before, Microsoft interfaces are a disaster. They are verbose, cumbersome and only moderately consistent. Using them directly leads to awkward and obfuscated code.
Here, for instance, you seem to have gotten confused between read and buffer
read holds the number of bytes actually read from the serial port
buffer holds the actual data.
buffer is what you will want to display to see what the robot answered you
Also, you should have a documentation for your robot stating which kind of answers you are supposed to expect. It would help to know how they are formatted, for instance whether they are null-terminated strings or not. That could dispense to add the string terminator.
In the following code:
// If GetPrinter didn't fill in the DEVMODE, try to get it by calling
// DocumentProperties...
if (pi2->pDevMode == NULL)
{
dwNeeded = DocumentProperties(NULL, hPrinter,
printerName,
NULL, NULL, 0);
if (dwNeeded <= 0)
{
GlobalFree(pi2);
ClosePrinter(hPrinter);
return FALSE;
}
pDevMode = (DEVMODE *)GlobalAlloc(GPTR, dwNeeded);
if (pDevMode == NULL)
{
GlobalFree(pi2);
ClosePrinter(hPrinter);
return FALSE;
}
lFlag = DocumentProperties(NULL, hPrinter,
printerName,
pDevMode, NULL,
DM_OUT_BUFFER);
if (lFlag != IDOK || pDevMode == NULL)
{
GlobalFree(pDevMode);
GlobalFree(pi2);
ClosePrinter(hPrinter);
return FALSE;
}
pi2->pDevMode = pDevMode;
}
On the line
lFlag = DocumentProperties(NULL, hPrinter,
printerName,
pDevMode, NULL,
DM_OUT_BUFFER);
When I run Visual Studio 2012 "Code analysis" feature it throws warning:
C6386 Write overrun Buffer overrun while writing to 'pDevMode': the writable size is 'dwNeeded' bytes, but '220' bytes might be written. Invalid write to 'pDevMode', (outside its writable range)
The code functions fine, but wondering how to fix this warning from occurring (preferably without disabling warning)
The help page for this error does not seem to apply (or I can't figure how it does) http://msdn.microsoft.com/query/dev11.query?appId=Dev11IDEF1&l=EN-US&k=k(C6386)&rd=true
The SAL annotation for DocumentProperties cannot express that DEVMODE is a structure than might be larger than its declared size. The function doesn't also take an argument that states the passed size of the DEVMODE. Nor does the structure have a single field that states the size. So an annotation like __out_bcount_opt or __out_bcount_part could not be used.
It is an issue with all winapi functions that take a DEVMODE. It is a structure that dates from the stone age, long before SAL was ever on the horizon. If Microsoft could do it all over again then they would do this differently. Too late now.
Nothing you can do about it beyond knowing that you got it right and the tool got it wrong. It is just a warning.