Related
I'm writing an application for handling communication with the device by COM port.
For it I use ReadFile() from <windows.h>, as I had some problems with synchronous use, I tried overlapped implementation from http://www.egmont.com.pl/addi-data/instrukcje/standard_driver.pdf
And from what I understand for constant reading (to not lose any data sent by device), I need to call that reading func in a separate thread in infinite while() - I created some class for handling that and it's working perfectly, but after some random amount of time it's causing bluescreen DPC WATCHDOG VIOLATION unfortunately, I can't tell any specific event that may cause this :(
#edit1
I should add that's if I use some other program for COM communication, like Termite or TerraTerm it's working with no problems.
PortHandling.cpp (basically copy of that's overlapped implementation) :
#define OVERLAPPED_READ_TIMEOUT 1
PortHandling::PortHandling()
{
this->portHandle = INVALID_HANDLE_VALUE;
this->done = false;
this->portSelected = "";
this->baudRate = 0;
}
void PortHandling::ClearPort()
{
if (this->done)
{
this->done = false;
PurgeComm(this->portHandle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
CloseHandle(this->portHandle);
std::cout << "[E] Disconnected from port " << this->portSelected << std::endl;
}
}
bool PortHandling::SetPortProp(std::string portName, int baudRate, int maxReadSize, int maxWriteSize)
{
this->portSelected = portName;
this->portHandle = CreateFileA(("\\\\.\\" + this->portSelected).c_str(), GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
if (this->portHandle == INVALID_HANDLE_VALUE)
{
std::cout << "[E] Connecting to port failed - Port occupied" << std::endl;
this->portSelected = "";
return false;
}
SetupComm(this->portHandle, maxReadSize, maxWriteSize); // The size of input buffer and output buffer
COMMTIMEOUTS timeOuts;
timeOuts.ReadIntervalTimeout = 3; //Set read timeout
timeOuts.ReadTotalTimeoutMultiplier = 1;
timeOuts.ReadTotalTimeoutConstant = 3;
timeOuts.WriteTotalTimeoutMultiplier = 0; // Set write timeout
timeOuts.WriteTotalTimeoutConstant = 0;
SetCommTimeouts(this->portHandle, &timeOuts); // Set timeout
DCB dcb;
GetCommState(this->portHandle, &dcb);
dcb.BaudRate = baudRate; /* Baudrate at which running */
dcb.fBinary = 1; /* Binary Mode (skip EOF check) */
dcb.fParity = 0; /* Enable parity checking */
dcb.fOutxCtsFlow = 0; /* CTS handshaking on output */
dcb.fOutxDsrFlow = 0; /* DSR handshaking on output */
dcb.fDtrControl = DTR_CONTROL_ENABLE; /* DTR Flow control */
dcb.fDsrSensitivity = 0; /* DSR Sensitivity */
dcb.fTXContinueOnXoff = 1; /* Continue TX when Xoff sent */
dcb.fOutX = 0; /* Enable output X-ON/X-OFF */
dcb.fInX = 0; /* Enable input X-ON/X-OFF */
dcb.fErrorChar = 1; /* Enable Err Replacement */
dcb.fNull = 1; /* Enable Null stripping */
dcb.fRtsControl = RTS_CONTROL_DISABLE; /* Rts Flow control */
dcb.ByteSize = 8; /* Number of bits/byte, 4-8 */
dcb.Parity = NOPARITY; /* 0-4 = None,Odd,Even,Mark,Space */
dcb.StopBits = ONESTOPBIT; /* 0,1,2 = 1, 1.5, 2 */
dcb.XonChar; /* Tx and Rx X-ON character */
dcb.XoffChar; /* Tx and Rx X-OFF character */
dcb.ErrorChar; /* Error replacement char */
dcb.EofChar; /* End of Input character */
dcb.EvtChar; /* Received Event character */
SetCommState(this->portHandle, &dcb);
PurgeComm(this->portHandle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
this->done = true;
std::cout << "[I] Connecting to port " << this->portSelected << " succeeded" << std::endl;
return true;
}
//#brief
//param size - Maximal read size, may be shorter if read is waiting for longer than set timeout (10ms)
//param buffer - Buffer into witch read data is saved
//param onRead - Function that is called on succesfull read
DWORD PortHandling::ReadFromPort(int size, char *buffer, std::function<void()> onRead)
{
DWORD dwRead = 0;
DWORD dwRes = 0;
BOOL fWaitingOnRead = FALSE;
OVERLAPPED osReader = {0};
// Create the overlapped event. Must be closed before exiting to avoid a handle leak.
osReader.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (osReader.hEvent == NULL)
{
std::cout << "[E] Error in communication, overlapped construction failed" << std::endl;
return dwRead;
}
if (!fWaitingOnRead)
{
// Issue read operation.
if (!ReadFile(this->portHandle, buffer, size, &dwRead, &osReader))
{
if (GetLastError() != ERROR_IO_PENDING) // read not delayed?
{
std::cout << "[E] Error in communication (read:1)" << std::endl;
} else
{
fWaitingOnRead = TRUE;
}
} else
{
// read completed immediately
}
}
if (fWaitingOnRead)
{
dwRes = WaitForSingleObject(osReader.hEvent, OVERLAPPED_READ_TIMEOUT);
switch (dwRes)
{
// Read completed.
case WAIT_OBJECT_0:
if (!GetOverlappedResult(this->portHandle, &osReader, &dwRead, FALSE))
{
std::cout << "[E] Error in communication (read:2)" << std::endl;
} else
{
// Read completed successfully.
}
// Reset flag so that another opertion can be issued.
fWaitingOnRead = FALSE;
break;
case WAIT_TIMEOUT:
// Operation isn't complete yet. fWaitingOnRead flag isn't
// changed since I'll loop back around, and I don't want
// to issue another read until the first one finishes.
// This is a good time to do some background work.
break;
default:
// Error in the WaitForSingleObject; abort.
// This indicates a problem with the OVERLAPPED
// structure's event handle
std::cout << "[E] Error in communication, overlapped construction failed" << std::endl;
return dwRead;
}
}
if (strlen(buffer) > 0)
{
onRead();
}
return dwRead;
}
//#brief
//param size - Size of written message
//param buffer - Buffer from witch data is send
//param onWrite - Function that is called on succesfull write
bool PortHandling::WriteToPort(int size, const char *buffer, std::function<void()> onWrite)
{
OVERLAPPED osWrite = {0};
DWORD dwWritten = 0;
DWORD dwRes = 0;
BOOL fRes = FALSE;
// Create this writes OVERLAPPED structure hEvent.
osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (osWrite.hEvent == NULL)
{
std::cout << "[E] Error in communication, overlapped construction failed" << std::endl;
return FALSE;
}
// Issue write.
if (!WriteFile(this->portHandle, buffer, size, &dwWritten, &osWrite))
{
if (GetLastError() != ERROR_IO_PENDING)
{
// WriteFile failed, but it isn't delayed. Report error and abort.
std::cout << "[E] Error in communication (write:1)" << std::endl;
fRes = FALSE;
} else
{
// Write is pending.
dwRes = WaitForSingleObject(osWrite.hEvent, INFINITE);
switch (dwRes)
{
// OVERLAPPED structure's event has been signaled.
case WAIT_OBJECT_0:
if (!GetOverlappedResult(this->portHandle, &osWrite, &dwWritten, FALSE))
{
std::cout << "[E] Error in communication (write:2)" << std::endl;
fRes = FALSE;
} else
{
// Write operation completed successfully.
fRes = TRUE;
onWrite();
}
break;
default:
// An error has occurred in WaitForSingleObject.
// This usually indicates a problem with the
// OVERLAPPED structure's event handle.
std::cout << "[E] Error in communication (write:3)" << std::endl;
fRes = FALSE;
break;
}
}
} else
{
// WriteFile completed immediately.
fRes = TRUE;
onWrite();
}
CloseHandle(osWrite.hEvent);
return fRes;
}
My class for handling that,
PortOperator.cpp
bool PortOperator::Construct(std::string portName, int baudRate, int maxReadSize, int maxWriteSize)
{
this->portName = portName;
this->baudRate = baudRate;
this->maxReadSize = maxReadSize;
this->maxWriteSize = maxWriteSize;
this->cRead = new char[maxReadSize];
memset(cRead, 0, this->maxReadSize);
this->cWrite = new char[maxWriteSize];
memset(cWrite, 0, this->maxWriteSize);
if (this->PortHandler.SetPortProp(portName, baudRate, maxReadSize, maxWriteSize))
{
// if connection succesfull, start Reading in the background
this->Reading = std::thread(&PortOperator::ReadingThread, this);
} else
{
static ftor_DeltaTime reconnect_timeout = ftor_DeltaTime();
int n = 0;
while (true)
{
// try reconnecting to port each second, max 5 tries
if (reconnect_timeout(TimeUnits::SECONDS) >= 1)
{
n++;
if (this->PortHandler.SetPortProp(portName, baudRate, maxReadSize, maxWriteSize))
{
// if connection succesfull, start Reading in the background
this->Reading = std::thread(&PortOperator::ReadingThread, this);
break;
}
if (n > 5)
{
return false;
}
reconnect_timeout.Reset();
}
}
}
return true;
}
void PortOperator::ReadingThread()
{
this->notReciving = ftor_DeltaTime();
char *buffer = new char[this->maxReadSize];
while (true)
{
memset(buffer, 0, this->maxReadSize);
//make sure the thread is stopped when port disconnect
if (!this->PortHandler.done)
{
break;
}
this->PortHandler.ReadFromPort(this->maxReadSize, buffer,
[&]() {
//block executed only when there's some data read (not empty)
if (ConsolePreview)
{
std::cout << buffer;
}
mtx_cRead.lock();
strcpy(this->cRead, buffer);
mtx_cRead.unlock();
readFlag = 1;
cv.notify_all();
notReciving.Reset();
});
}
}
std::string PortOperator::Write(std::string command)
{
std::mutex mtx_cv;
std::unique_lock<std::mutex> write_lck(mtx_cv);
std::string response = "";
this->PortHandler.WriteToPort(command.length(), command.c_str(), []() {});
if (cv.wait_for(write_lck, std::chrono::milliseconds(45)) == std::cv_status::no_timeout)
{
response = cRead;
}
return response;
}
bool PortOperator::Reconnect()
{
this->Disconnect();
return this->Construct(this->portName, this->baudRate, this->maxReadSize, this->maxWriteSize);
}
bool PortOperator::Disconnect()
{
//first clear port connection to stop thread, and then join
this->PortHandler.ClearPort();
if (Reading.joinable())
{
Reading.join();
}
return true;
}
long long int PortOperator::SilenceTime(TimeUnits t)
{
return this->notReciving(t);
}
std::vector<std::string> PortOperator::GetPorts()
{
std::vector<std::string> portsCOM;
//try connecting with each port to check which are avaiable
for (int i = 0; i <= 256; i++)
{
std::string portName = "COM" + std::to_string(i);
if (this->CheckPort(portName))
{
portsCOM.push_back(portName.c_str());
}
}
//if there's no avaiable port add "-" to vector
if (portsCOM.size() == 0)
{
portsCOM.push_back(" - ");
}
return portsCOM;
}
bool PortOperator::CheckPort(std::string portName)
{
//try connecting with given port
HANDLE serialHandle;
serialHandle = CreateFileA(("\\\\.\\" + portName).c_str(), GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (serialHandle == INVALID_HANDLE_VALUE)
{
return false;
}
CloseHandle(serialHandle);
return true;
}
I'm not the best programmer, if something hurt your eyes please tell me...
Thanks for your time :D
I am working on a project which need to operate two serial port as the same time on windows, but whatever I try I can only open one serial port. Unless repeat open and close, I can operate the two ports but it is too slow. I need to write and read first port and formalize it by protocol and sent it to another port, but I can not initial two port. I was using open and close two ports repeatly to do that before but it was toooooooo slow. Anybody know how to open two port in the same time; Thx!!!
here are my code here:
#include <iostream>
#include <iomanip>
#include "udp.h"
#include "Serial.h"
#include "Formalized.h"
using namespace std;
bool sthread = false;
unsigned char status[6] = {};
HANDLE hMutex = NULL;
DWORD WINAPI ULTRAread(LPVOID lpParamter)
{
Cserial ultra;
unsigned char input0[7] = { 0x00,0x01,0x00,0x01,0x01,0xE5,0xAC };
unsigned char input1[7] = { 0x00,0x02,0x00,0x01,0x01,0xE5,0xE8 };
unsigned char input2[7] = { 0x00,0x03,0x00,0x01,0x01,0xE4,0x14 };
unsigned char input3[7] = { 0x00,0x04,0x00,0x01,0x01,0xE5,0x60 };
unsigned char input4[7] = { 0x00,0x05,0x00,0x01,0x01,0xE4,0x9C };
unsigned char input5[7] = { 0x00,0x06,0x00,0x01,0x01,0xE4,0xD8 };
unsigned char* input[6] = { input0,input1,input2,input3,input4,input5 };
unsigned char pool[8] = {};
if (!ultra.Initcom("COM10")) //ultrasonic COM interface
{
cout << "Init ultrasonic failure \n";
getchar();
sthread = false;
}
else
{
sthread = true;
for (;;)
{
WaitForSingleObject(hMutex, INFINITE);
for (int i = 0; i < 6; i++)
{
if (ultra.Write(input[i], 7))
{
ultra.Read(pool, 8);
}
switch (pool[5])
{
case 0x01:
status[i] = 0x01;
break;
case 0x00:
status[i] = 0x00;
break;
}
}
Sleep(5000);
ReleaseMutex(hMutex);
}
}
}
int main()
{
HANDLE uThread = CreateThread(NULL, 0, ULTRAread, NULL, 0, NULL);
hMutex = CreateMutex(NULL, FALSE, "ultrasonics status");
Cserial zigbee;
cUDP UWB;
char buff[300];
char data[256];
if (!UWB.Initial(6685)) //latitude and longitude port
{
cout << "UWB Port connecting failure \n";
getchar();
}
else
{
cout << "UWB Com connecting success \n";
}
if (!zigbee.Initcom("COM7")) //zigbee access port
{
cout << "Zigbee Initial Failure! \n";
getchar();
}
else
{
cout << "Zigbee Initial Success! \n";
}
for (;;)
{
WaitForSingleObject(hMutex, INFINITE);
switch (sthread)
{
case TRUE: //ultrasonics trustful
WaitForSingleObject(hMutex, INFINITE);
cout << "1";
ReleaseMutex(hMutex);
break;
case FALSE:
cout << "2";
break;
}
Sleep(5000);
}
CloseHandle(uThread);
getchar();
return 0;
}
#include"Serial.h"
Cserial::Cserial()
{
hcom = INVALID_HANDLE_VALUE;
}
Cserial::~Cserial()
{
}
bool Cserial::Initcom(LPCSTR Port)
{
hcom = CreateFile(Port, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL, 0);
if (hcom == INVALID_HANDLE_VALUE)
{
fprintf(stderr, "fail serial1\n");
return false;
}
SetupComm(hcom, 1024, 1024);
DCB dcb;
GetCommState(hcom, &dcb);
dcb.BaudRate = 9600;
dcb.ByteSize = 8;
dcb.Parity = 0;
dcb.StopBits = 1;
SetCommState(hcom, &dcb);
COMMTIMEOUTS CommTimeouts;
GetCommTimeouts(hcom, &CommTimeouts);
CommTimeouts.ReadIntervalTimeout = MAXDWORD;
CommTimeouts.ReadTotalTimeoutMultiplier = 10;
CommTimeouts.ReadTotalTimeoutConstant = 100;
CommTimeouts.WriteTotalTimeoutMultiplier = 1;
CommTimeouts.WriteTotalTimeoutConstant = 10;
if (!SetCommTimeouts(hcom, &CommTimeouts))
{
fprintf(stderr, "fail serial2\n");
return FALSE;
}
return true;
}
void Cserial::Uninitcom()
{
CloseHandle(hcom);
hcom = INVALID_HANDLE_VALUE;
}
bool Cserial::Clearcom()
{
PurgeComm(hcom, PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR | PURGE_TXABORT);
return TRUE;
}
bool Cserial::Write(unsigned char* buf, int len)
{
if (hcom == INVALID_HANDLE_VALUE)
{
return FALSE;
}
DWORD dwWrittenLen = 0;
if (!WriteFile(hcom, buf, len, &dwWrittenLen, NULL))
{
return FALSE;
}
else
{
return TRUE;
}
}
bool Cserial::Read(unsigned char* buf, int len)
{
DWORD dwReadLen = 0;
if (hcom == INVALID_HANDLE_VALUE)
{
return FALSE;
}
if (!ReadFile(hcom, buf, len, &dwReadLen, NULL))
{
return FALSE;
}
else
{
return TRUE;
}
}
My program normally detects what Serial Ports are available from the OS at start up. It is a simple method of polling if a port can be accessed by name.
project defines for serial port
std::string COMPortNumber[MAXPORTS] {"\\\\.\\COM1", "\\\\.\\COM2", "\\\\.\\COM3", "\\\\.\\COM4", "\\\\.\\COM5",
"\\\\.\\COM6", "\\\\.\\COM7", "\\\\.\\COM8", "\\\\.\\COM9", "\\\\.\\COM10",
"\\\\.\\COM11", "\\\\.\\COM12", "\\\\.\\COM13", "\\\\.\\COM14", "\\\\.\\COM15",
"\\\\.\\COM16", "\\\\.\\COM17", "\\\\.\\COM18", "\\\\.\\COM19", "\\\\.\\COM20"};
std::string COMPortName[MAXPORTS] = {"com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8", "com9", "com10",
"com11", "com12", "com13", "com14", "com15", "com16", "com17", "com18", "com19", "com20"};
polling function:
void updateSerialList(){
ComboBox_ResetContent(SerialPortDropDown); //clears all content from drop down box
//int iresult = ComboBox_AddString(SerialPortDropDown, "Update Port List\0");
for(int n=0; n<MAXPORTS; n++)
{
COMPortAvailable[n] = serial.getComPortList( COMPortNumber[n] );
if(COMPortAvailable[n] == true)
{
char* tempBuf = new char[COMPortName[n].length() + 1];
for(unsigned int t=0; t<COMPortName[n].length(); t++)
{
tempBuf[t] = COMPortName[n][t];
}
tempBuf[COMPortName[n].length()] = '\0';
int iResult = ComboBox_AddString(SerialPortDropDown, tempBuf);
{
if(iResult == CB_ERR){std::cout << "error adding string" << std::endl;}
else if(iResult == CB_ERRSPACE){std::cout << "error no room" << std::endl;}
}
delete[] tempBuf;
}
}
//place baud rates in select box
for(int n=NUMBERBAUDRATES-1; n>-1; n--)
{
char* tempBuf = new char[BaudRateName[n].length() + 1];
for(unsigned int t=0; t<BaudRateName[n].length(); t++)
{
tempBuf[t] = BaudRateName[n][t];
}
tempBuf[BaudRateName[n].length()] = '\0';
int iResult = ComboBox_AddString(BaudRateDropDown, tempBuf);
{
if(iResult == CB_ERR){std::cout << "error adding string" << std::endl;}
else if(iResult == CB_ERRSPACE){std::cout << "error no room" << std::endl;}
}
delete[] tempBuf;
}
This compiles a list in a dropdown box for the user to select. It uses a function in a class for a serial instance. This is the function call inside the class.
bool getComPortList(std::string portName)
{
bool test;
HANDLE testSerial;
testSerial = CreateFile( (portName.c_str()) , GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, NULL, NULL);
if(testSerial == INVALID_HANDLE_VALUE)
{
test = false;
}
else
{
test = true;
cout << "port number " << portName << " is available" << endl;
}
CloseHandle(testSerial);
return test;
}
This method has worked fine until I tried running the program on Windows 10. It previously was tested and used on Vista, Win7, Win 8.1 however even if the Windows10 device manager says comm ports are available on the system, my program cannot get a list of them.
What is different about Win10 serial port access?
your main logic error, that you assume - if CreateFile for some name return INVALID_HANDLE_VALUE - this mean that this name not exist. but this is of course false, because CreateFile can fail by different reasons. you need call GetLastError after fail. only if it return ERROR_FILE_NOT_FOUND the name really not exist (ERROR_PATH_NOT_FOUND can not be for "\\\\.\\COMX" because path here always exist and correct). for com devices very common error was STATUS_ACCESS_DENIED - because it have DO_EXCLUSIVE flag. with this flag only one file on device can be open at a time.
however for enumerate com devices - you need enumerate interfaces for GUID_DEVINTERFACE_COMPORT via CM_Get_Device_Interface_ListW
enumInterfaces(const_cast<PGUID>(&GUID_DEVINTERFACE_COMPORT));
static volatile UCHAR guz;
void enumInterfaces(PGUID InterfaceClassGuid)
{
CONFIGRET status;
ULONG len = 0, cb = 0, rcb;
PVOID stack = alloca(guz);
PWSTR buf = 0;
do
{
if (status = CM_Get_Device_Interface_List_SizeW(&len, InterfaceClassGuid, 0, CM_GET_DEVICE_INTERFACE_LIST_PRESENT))
{
break;
}
if (cb < (rcb = len * sizeof(WCHAR)))
{
len = (cb = RtlPointerToOffset(buf = (PWSTR)alloca(rcb - cb), stack)) / sizeof(WCHAR);
}
status = CM_Get_Device_Interface_ListW(InterfaceClassGuid, 0, buf, len, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
if (status == CR_SUCCESS)
{
while (*buf)
{
DbgPrint("use this name in CreateFile = %S\n", buf);
PrintFriendlyNameByInterface(buf);
buf += 1 + wcslen(buf);
}
}
} while (status == CR_BUFFER_SMALL);
}
CONFIGRET PrintFriendlyNameByInterface(PCWSTR pszDeviceInterface)
{
ULONG cb = 0, rcb = 64;
PVOID stack = alloca(guz);
DEVPROPTYPE PropertyType;
CONFIGRET status;
union {
PVOID pv;
PWSTR DeviceID;
PBYTE pb;
};
do
{
if (cb < rcb)
{
rcb = cb = RtlPointerToOffset(pv = alloca(rcb - cb), stack);
}
status = CM_Get_Device_Interface_PropertyW(pszDeviceInterface, &DEVPKEY_Device_InstanceId, &PropertyType, pb, &rcb, 0);
if (status == CR_SUCCESS)
{
if (PropertyType == DEVPROP_TYPE_STRING)
{
DbgPrint("DeviceID = %S\n", DeviceID);
status = PrintFriendlyNameByDeviceID(DeviceID);
}
else
{
status = CR_WRONG_TYPE;
}
break;
}
} while (status == CR_BUFFER_SMALL);
return status;
}
CONFIGRET PrintFriendlyNameByDeviceID(PWSTR DeviceID)
{
DEVINST dnDevInst;
CONFIGRET status = CM_Locate_DevNodeW(&dnDevInst, DeviceID, CM_LOCATE_DEVNODE_NORMAL);
if (status == CR_SUCCESS)
{
ULONG cb = 0, rcb = 256;
PVOID stack = alloca(guz);
DEVPROPTYPE PropertyType;
union {
PVOID pv;
PWSTR sz;
PBYTE pb;
};
do
{
if (cb < rcb)
{
rcb = cb = RtlPointerToOffset(pv = alloca(rcb - cb), stack);
}
status = CM_Get_DevNode_PropertyW(dnDevInst, &DEVPKEY_NAME, &PropertyType, pb, &rcb, 0);
if (status == CR_SUCCESS)
{
if (PropertyType == DEVPROP_TYPE_STRING)
{
DbgPrint("show this name for user = %S\n", sz);
}
else
{
status = CR_WRONG_TYPE;
}
}
} while (status == CR_BUFFER_SMALL);
}
return status;
}
and demo output:
use this name in CreateFile = \\?\ACPI#PNP0501#0#{86e0d1e0-8089-11d0-9ce4-08003e301f73}
DeviceID = ACPI\PNP0501\0
show this name for user = Communications Port (COM1)
in my system \\?\ACPI#PNP0501#0#{86e0d1e0-8089-11d0-9ce4-08003e301f73} is symbolic link to PDO device \Device\00000034 (created by aspi.sys) and it have not DO_EXCLUSIVE flag. despite this on second call of CreateFile i got access denied error. to this device FDO - \Device\Serial0 (\\?\COM1 symbolic link to it) attached. it already have DO_EXCLUSIVE flag. anyway SerialCreateOpen (IRP_MJ_CREATE procedure serial.sys) denied access create more than one file - at very begin in increment some counter in device extension, and if it != 1 - return STATUS_ACCESS_DENIED
so even if we try open PDO (\\?\ACPI#PNP0501#0#{86e0d1e0-8089-11d0-9ce4-08003e301f73}) which not exclusive device (setting the exclusive flag for the FDO has no effect here) - the create request begin execute on stack top from \Device\Serial0 and serial.sys enforce exclusivity themselves within their SerialCreateOpen routine.
I have written a class to handle named pipe connections, and if I create an instance, close it, and then try to create another instance the call to CreateFile() returns INVALID_HANDLE_VALUE, and GetLastError() returns ERROR_PIPE_BUSY. What's going on here? What can I do to insure the call to Connect() succeeds?
PipeAsync A, B;
A.Connect("\\\\.\\pipe\\test",5000);
A.Close();
cout << GetLastError(); // some random value
B.Connect("\\\\.\\pipe\\test",5000);
cout << GetLastError(); // 231 (ERROR_PIPE_BUSY)
B.Close();
Here are my implementations of Connect() and Close()
BOOL PipeAsync::Connect(LPCSTR pszPipeName, DWORD dwTimeout)
{
this->pszPipeName = pszPipeName;
this->fExisting = TRUE;
DWORD dwMode = this->fMessageMode ? PIPE_READMODE_MESSAGE : PIPE_READMODE_BYTE;
hPipe = CreateFile(
this->pszPipeName,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
if( INVALID_HANDLE_VALUE == hPipe )
return FALSE; /* set break point here ; breaks here on second call to Connect() */
if( GetLastError() == ERROR_PIPE_BUSY )
if(!WaitNamedPipe( this->pszPipeName, dwTimeout ))
return FALSE; /* set break point here */
if( !SetNamedPipeHandleState( hPipe, &dwMode, NULL, NULL ) )
return FALSE; /* set break point here */
return TRUE;
}
VOID PipeAsync::Close()
{
if( fExisting )
DisconnectNamedPipe( hPipe );
CloseHandle( hPipe );
}
EDIT: I forgot to tell you how I concluded this... I set break points indicated in the comments. When run, it stops on the first break point.
EDIT: This is my updated code
if( INVALID_HANDLE_VALUE == hPipe )
if( GetLastError() == ERROR_PIPE_BUSY )
{
if(!WaitNamedPipe( this->pszPipeName, dwTimeout ))
return FALSE; /* break-point: breaks here on second call */
}
else
return FALSE; /* break-point /*
Now, WaitNamedPipe() is returning false on the second call to Connect() and GetLastError() is returning 2, or ERROR_FILE_NOT_FOUND ?
From Named Pipe Client:
If the pipe exists but all of its instances are busy, CreateFile
returns INVALID_HANDLE_VALUE and the GetLastError function returns
ERROR_PIPE_BUSY. When this happens, the named pipe client uses the
WaitNamedPipe function to wait for an instance of the named pipe to
become available.
The link has example code on coping with ERROR_PIPE_BUSY.
EDIT:
Small compilable example that demonstrates accepting and connecting on a named pipe:
const char* const PIPE_NAME = "\\\\.\\pipe\\test";
const int MAX_CONNECTIONS = 10;
void client_main()
{
DWORD last_error;
unsigned int elapsed_seconds = 0;
const unsigned int timeout_seconds = 5;
HANDLE handle = CreateFile(PIPE_NAME,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
while (INVALID_HANDLE_VALUE == handle &&
elapsed_seconds < timeout_seconds)
{
last_error = GetLastError();
if (last_error != ERROR_PIPE_BUSY)
{
break;
}
Sleep(1 * 1000);
elapsed_seconds++;
handle = CreateFile(PIPE_NAME,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
}
if (INVALID_HANDLE_VALUE == handle)
{
std::cerr << "Failed to connect to pipe " << PIPE_NAME <<
": last_error=" << last_error << "\n";
}
else
{
std::cout << "Connected to pipe " << PIPE_NAME << "\n";
CloseHandle(handle);
}
}
HANDLE _get_server_handle()
{
// Error handling omitted for security descriptor creation.
SECURITY_DESCRIPTOR sd;
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&sd, TRUE, static_cast<PACL>(0), FALSE);
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = &sd;
sa.bInheritHandle = FALSE;
// Create a bi-directional message pipe.
HANDLE handle = CreateNamedPipe(PIPE_NAME,
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE |
PIPE_READMODE_MESSAGE |
PIPE_NOWAIT,
PIPE_UNLIMITED_INSTANCES,
4096,
4096,
0,
&sa);
if (INVALID_HANDLE_VALUE == handle)
{
std::cerr << "Failed to create named pipe handle: last_error=" <<
GetLastError() << "\n";
}
return handle;
}
void server_main()
{
HANDLE handle = _get_server_handle();
if (INVALID_HANDLE_VALUE != handle)
{
int count = 0;
while (count < MAX_CONNECTIONS)
{
BOOL result = ConnectNamedPipe(handle, 0);
const DWORD last_error = GetLastError();
if (ERROR_NO_DATA == last_error)
{
count++;
std::cout << "A client connected and disconnected: count=" <<
count << "\n";
CloseHandle(handle);
handle = _get_server_handle();
}
else if (ERROR_PIPE_CONNECTED == last_error)
{
count++;
std::cout << "A client connected before call to " <<
"ConnectNamedPipe(): count=" << count << "\n";
CloseHandle(handle);
handle = _get_server_handle();
}
else if (ERROR_PIPE_LISTENING != last_error)
{
std::cerr << "Failed to wait for connection: last_error=" <<
GetLastError() << "\n";
CloseHandle(handle);
break;
}
Sleep(100);
}
}
}
int main(int a_argc, char** a_argv)
{
if (2 == a_argc)
{
if (std::string("client") == *(a_argv + 1))
{
for (int i = 0; i < MAX_CONNECTIONS; i++)
{
client_main();
}
}
else if (std::string("server") == *(a_argv + 1))
{
server_main();
}
}
return 0;
}
Execute server-side first:
pipetest.exe server
Then execute client-side:
pipetest.exe client
I could not tell what the problem was from the posted code. Hopefully this small example will assist you in finding the issue.
When I connect my embedded device to my system, I am running my program which will write to the port my embedded is connect and it prints the reply to console.
When I connect my device and run this program it is not giving any output.
But when I connect my device and use PUTTY to send some commands first and then run my program it is working.
Maybe there is a problem in the way I am starting communication?
My source code is:
#include "stdafx.h"
#include <iostream>
//#include <windows.h>
#include <afx.h>
int main()
{
using namespace std;
int i=0;
// cout << "Hello world!" << endl;
HANDLE hSerial;
hSerial = CreateFile("COM5",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_WRITE | FILE_SHARE_READ,
0,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
if(hSerial==INVALID_HANDLE_VALUE)
{
if(GetLastError()==ERROR_FILE_NOT_FOUND)
{
// TRACE("serial port does not exist for reading\n");
//serial port does not exist. Inform user.
}
// TRACE("some other error,serial port does not exist for reading\n");
//some other error occurred. Inform user.
}
DCB dcbSerialParams = {0};
dcbSerialParams.DCBlength=sizeof(dcbSerialParams);
if (!GetCommState(hSerial, &dcbSerialParams))
{
// TRACE("error getting state for reading\n");
//error getting state
}
dcbSerialParams.BaudRate=9600;
dcbSerialParams.ByteSize=8;
dcbSerialParams.StopBits=ONESTOPBIT;
dcbSerialParams.Parity=NOPARITY;
if(!SetCommState(hSerial, &dcbSerialParams))
{
//TRACE("error setting state for reading\n");
//error setting serial port state
}
COMMTIMEOUTS timeouts={0};
timeouts.ReadIntervalTimeout=50;
timeouts.ReadTotalTimeoutConstant=50;
timeouts.ReadTotalTimeoutMultiplier=10;
timeouts.WriteTotalTimeoutConstant=50;
timeouts.WriteTotalTimeoutMultiplier=10;
if(!SetCommTimeouts(hSerial, &timeouts))
{
// TRACE("some error occured for reading\n");
//error occureed. Inform user
}
int n=100,n1=100;
char szBuff[100];
DWORD dwBytesRead = 0;
char szBuff1[100];
DWORD dwByteswrote = 0;
memset(szBuff1,0,100);
memcpy(szBuff1,"LIST\r",5);
if(!WriteFile(hSerial, szBuff1,5, &dwByteswrote, NULL))
{
cout << "error writing" ;
}
cout << szBuff1 << endl;
cout << dwByteswrote << endl;
while(1)
{
if(!ReadFile(hSerial, szBuff, n1, &dwBytesRead, NULL))
{
cout << "error reading";
break;
}
else
{
cout << dwBytesRead << endl;
szBuff[dwBytesRead]='\0';
if(dwBytesRead>0)
{
cout << (szBuff);
break;
}
}
}
cin >> i;
}
Try this... you will probably need to do the code for exceptions (ex: if the response is bigger than 2024)
bool SendModemATCommand(const string &strCommand, int iModemPort, string &strRetValue)
{
bool bRetValue = false;
strRetValue = "";
char cBuffer[2024];
HANDLE hCom = NULL;
char cComPort[64];
sprintf_s(cComPort,"\\\\.\\COM%d", iModemPort);
hCom = CreateFile( cComPort,
GENERIC_READ | GENERIC_WRITE,
0, // must be opened with exclusive-access
NULL, // no security attributes
OPEN_EXISTING, // must use OPEN_EXISTING
0, // not overlapped I/O
NULL // hTemplate must be NULL for comm devices
);
if (hCom != INVALID_HANDLE_VALUE)
{
COMMTIMEOUTS comTimeOuts;
comTimeOuts.ReadIntervalTimeout = MAXDWORD;
comTimeOuts.ReadTotalTimeoutMultiplier = MAXDWORD;
comTimeOuts.ReadTotalTimeoutConstant = 0;//MAXDWORD;
comTimeOuts.WriteTotalTimeoutMultiplier = 0;
comTimeOuts.WriteTotalTimeoutConstant = 0;
if(SetCommTimeouts(hCom, &comTimeOuts))
{
DCB dcb;
dcb.DCBlength = sizeof(DCB);
if(GetCommState(hCom, &dcb))
{
DWORD dwBytesWritten = 0;
DWORD dwBytesRead = 0;
DWORD dwBytesTotal = 0;
if( WriteFile(hCom, strCommand.c_str(), (int)strCommand.size(), &dwBytesWritten, NULL) )
{
if(dwBytesWritten == strCommand.size())
{
dwBytesRead = 0;
DWORD tickStart = GetTickCount();
bool bTimeOut = false;
while(true)
{
while(ReadFile(hCom, cBuffer + dwBytesTotal, 1, &dwBytesRead, NULL))
{
if(dwBytesRead == 0 && dwBytesTotal != dwBytesWritten)
break;
dwBytesTotal += dwBytesRead;
}
if ( dwBytesTotal == 0 )
{
// timeout
if ( GetTickCount() - tickStart > 10000) // 10 Seconds
{
bTimeOut = true;
break;
}
}
else
break;
}
cBuffer[dwBytesTotal] = '\0';
strRetValue = cBuffer;
if(bTimeOut)
strRetValue = "Timed out:" + strCommand;
else
bRetValue = true;
}
}
}
}
CloseHandle(hCom);
}
return bRetValue;
}
Most likely the problem is with your initialization.
I recall having this type of trouble before and Com Timeouts structure was particularly troublesome.
I suggest you get a null modem cable from COM5 to another port on the machine (if you have one), or to another computer. Then use a terminal program to open up the other port and see you can see the "List" command coming through when you run the program. If not then it's very likely to be related to the way you are initializing & opening the com port.
This link may prove useful. Just strip out the Afx stuff and look particularly at the initialization.
http://www.codeproject.com/KB/system/chaiyasit_t.aspx
One other suggestion, you only send List once. If the device is not already plugged in and ready, nothing will happen. Maybe it should keep sending the list command until it gets a
Response.
Also, do you need "List\r\n" or just "List\r"? What is the other ends expecting?