We've got some old serial code which checks whether a serial port is available simply by opening it and then closing it. Now we are adding network support to the app I want to reuse the function by supplying the ip address as a string.
/**
* So far I have tried:
* A passed in portPath normally looks like:
\\?\acpi#pnp0501#1#1#{GUID}
10.2.0.155:2001
//10.2.0.155:2001/
\\.\10.2.0.155:2001\
\\?\10.2.0.155:2001\
* all without success.
*/
bool PortIsAvailable( const CString& portPath )
{
HANDLE hCom = ::CreateFile( portPath,
GENERIC_READ | GENERIC_WRITE,
0, // comm devices must be opened with exclusive-access
NULL, // no security attributes
OPEN_EXISTING, // comm devices must use OPEN_EXISTING
FILE_FLAG_OVERLAPPED, // not overlapped I/O
NULL ); // hTemplate must be NULL for comm devices
if (INVALID_HANDLE_VALUE != hCom )
{
::CloseHandle( hCom );
return true;
}
return false;
}
I know I could use connect followed by shutdown but I want to reuse the function with minimal changes. If I can reuse the function so much the better. If not then I will have to write code that determines whether it is a socket or not.
I was wondering what the correct way of opening a socket via CreateFile is?
You can not create a socket via CreateFile. You should use the windows socket API for this purpose. For creating the SOCKET handle, you use WSASocket. Note that the SOCKET returned by this function can be used as a Windows Handle with some Windows functions, such as ReadFile and WriteFile.
I don't believe you can manipulate sockets with CreateFile(). Sockets are an abstraction imported from BSD (iirc) and implemented in a name-compatible way (originally via winsock.h, and currently winsock2.h). I don't think MS ever added support for sockets to CreateFile().
More rationale: Most (everything?) CreateFile() manipulates returns a native Windows handle. Because sockets are a non-native abstraction, they don't have a native handle in the OS, so it wouldn't make sense for CreateFile() to handle them.
Related
In Windows 7 I have a handle to a custom USB HID device. I'm trying to determine if the handle is no longer able to access the device because the device has been unplugged and re-plugged. When this happens I/O attempts using WriteFile fail. However I am looking for a way to test the handle before doing any I/O to determine if the HID device handle is still valid. And when I say valid I mean handle is connected to device such that I will be able to do I/O to the device. I understand that technically the handle itself may be valid even when it has lost connection to the physical device. The current method in my code is to test by using an attempt to open a new handle to the device and if this succeeds then handle is valid and if it fails handle is not valid. This detects disconnect as expected if the device is unplugged at the time of the test. The problem with this is that if the device has been plugged back in then the test open succeeds but the actual handle used to do I/O (opened before the disconnected/reconnect) is no longer connected to the device. I'm looking for a benign call I can do with the actual I/O handle that will tell me if the device is connected but without doing any actual I/O.
I've tried a few calls so far but none return an indication of the device connection as I require.
DWORD handleInformation;
LARGE_INTEGER size = { 0, 0 };
BOOL isConnected;
isConnected = GetFileSizeEx(m_HidWriteHandle, &size);
isConnected = GetHandleInformation(m_HidWriteHandle, &handleInformation);
GetFileSizeEx always fails and GetLastError() reports (1) Invalid Function
GetHandleInformation always succeeds even when device has been disconnected/reconnected and handle is not able to access device.
The call to use is HidD_GetAttributes(handle, &attributes)
// Try to get the HID attributes. This will fail if device is
// unplugged or has been unplugged since CreateFile() call that
// created m_HidWritehandle
HIDD_ATTRIBUTES Attributes;
Attributes.Size = sizeof(Attributes);
m_HidPresent = HidD_GetAttributes(m_HidWriteHandle, &Attributes) != 0;
This will return false even if device has been plugged back in and thus can be used as an indicator of need to reconnect to HID device.
I reactivated code that I am sure used to work some months ago. It drives me crazy but it does not anymore. I could not find an answer in other questions.
On the server side, I create a pipe using
#define MAX_MESSAGE_LENGTH 1024
SECURITY_ATTRIBUTES sa;
SECURITY_DESCRIPTOR sd;
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&sd, TRUE, static_cast<PACL>(0), FALSE);
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = &sd;
sa.bInheritHandle = FALSE;
auto pipe_name = _T("\\\\.\\pipe\\") + _serviceName;
HANDLE pipe = CreateNamedPipe(
pipe_name.c_str(),
PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
1,
MAX_MESSAGE_LENGTH, MAX_MESSAGE_LENGTH, // buffer lengths (advisory)
0, // default timeout of 50ms when WaitNamedPipe uses NMPWAIT_USE_DEFAULT_WAIT
&sa));
Then a thread waits for incoming clients with ConnectNamedPipe. ConnectNamedPipe blocks until a client connects with
HANDLE pipe = CreateFile(
pipe_name.c_str(), // pipe name
GENERIC_READ | // read and write access
GENERIC_WRITE,
0, // no sharing
NULL, // default security attributes
OPEN_EXISTING, // opens existing pipe
FILE_ATTRIBUTE_NORMAL, // default attributes
NULL); // no template file
ConnectNamedPipe on the server then returns with TRUE and GetLastError == 0. But when it tries to call ReadFile to read incoming data on the pipe, ReadFile immediately returns FALSE and GetLastError==ERROR_BROKEN_PIPE.
On client side, CreateFile has returned GetLastError==231, "All pipe instances are busy". Although it is the only client! A call to WaitNamedPipe(pipe, 2000)returns with error code 121, "The semaphore timeout period has expired".
Increasing the number of allowed clients in CreateNamedPipe does not change anything.
It seems the pipe got completely broken in the moment the client tries to connect. But why? Both client and server run on the same machine with same user and even same session.
Another call to ConnectNamedPipe then failed with GLE=232:"The pipe is being closed".
I also had other SECURITY_ATTRIBUTES for CreateNamedPipe, which shall allow for non-elevated users to connect, but that makes no difference.
Also I tried to use CallNamedPipe on the client with the same result.
PathFileExists is the pipe killer! After hours of trying I finally found what breaks the pipe: a simple call to PathFileExists on the pipe name! This was added recently on the client side to check whether the pipe is already created. I had a look at the code changes but I totally missed that. PathFileExists correctly returns true or false but seems to mess up the pipe (as I told it did not help to allow more than one client to connect). Argh!!!
I am trying to create a demonstration client/server application using named pipes on two Windows 7 machines, but cannot get this to work. The two machines, let's call them server and laptop, are both connected to an ethernet switch which is in turn connected to a cable modem. Both machines can see the internet, and both machines show up in the Windows network map. So I think they are correctly networked.
I am writing in Visual C++ and am basically copying the sample code on MSDN for a named pipe server and client. On my server, I call CreateNamedPipe() using the pipe name:
\\.\pipe\MyServerName
On the client, I call CreateFile with the pipe name of:
\\<machine>\pipe\MyServerName
where "machine" is configurable. If I run the server and client on the same machine, and for machine use either "." or "POWEREDGE" (the name of my server machine according to Windows), the two programs communicate fine.
Now I go to my laptop and run the client software. I use "POWEREDGE" as the machine name. CreateFile fails with GetLastError=1 ("Incorrect Function" apparently).
Any ideas about what could be going wrong here?
In response to requests, here is the actual code:
// CLIENT SIDE
// note: value of gLKServer is "POWEREDGE"
char PipeName[256]="";
wsprintf(PipeName,"\\\\%s\\pipe\\LKServer", gLKServerName);
while (TRUE) {
hPipe = CreateFile(
PipeName, // pipe name
GENERIC_READ | // read and write access
GENERIC_WRITE,
0, // no sharing
NULL, // default security attributes
OPEN_EXISTING, // opens existing pipe
0, // default attributes
NULL); // no template file
if (hPipe != INVALID_HANDLE_VALUE) {
break;
}
// Exit if an error other than ERROR_PIPE_BUSY occurs.
if (err=GetLastError() != ERROR_PIPE_BUSY)
{
printf("CreateFile error %lu\n", err);
}
...
// SERVER SIDE
char PipeName[256]="\\\\.\\pipe\\LKServer";
hPipe = CreateNamedPipe(
PipeName, // pipe name
PIPE_ACCESS_DUPLEX, // read/write access
PIPE_TYPE_MESSAGE | // message type pipe
PIPE_READMODE_MESSAGE | // message-read mode
PIPE_WAIT, // blocking mode
PIPE_UNLIMITED_INSTANCES, // max. instances
BUFSIZE, // output buffer size
BUFSIZE, // input buffer size
0, // client time-out
NULL); // default security attribute
I have a problem regarding communication with a USB device on Windows. I can't use libusb or WinUSB as I have a specific driver for that (Silabs USB to UART, which is a USB-to-serial bridge). This is how I initialize a device file, send&read data and close the handle.
HANDLE hDevFile = CreateFile(L"\\??\\USB#VID_10C4&PID_EA60#0001#{a5dcbf10-6530-11d2-901f-00c04fb951ed}",
GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
PurgeComm(hDevFile, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
DCB dcbInitState;
GetCommState(hDevFile, &dcbInitState);
DCB dcbNewState = dcbInitState;
dcbNewState.BaudRate = 57600;
dcbNewState.Parity = NOPARITY;
dcbNewState.ByteSize = 8;
dcbNewState.StopBits = ONESTOPBIT;
if (SetCommState(hDevFile, &dcbNewState) == 0)
{
printf("Could not set COM state. Error: %i", GetLastError());
return -1;
}
Sleep(60);
BYTE outData[8];
outData[0] = 0x53;
outData[1] = 0x10;
outData[2] = 0x04;
outData[3] = 0x10;
outData[4] = 0x40;
outData[5] = outData[3] ^ outData[4];
outData[6] = 0xAA;
outData[7] = 0x00;
DWORD dwWritten;
if (!WriteData(hDevFile, outData, 8, &dwWritten))
{
printf("Could not write data. Error: %i", GetLastError());
return -1;
}
BYTE inData[8];
DWORD dwRead;
if (!ReadData(hDevFile, inData, 8, &dwRead, 2000))
{
printf("Could not read data. Error: %i", GetLastError());
return -1;
}
SetCommState(hDevFile, &dcbInitState);
Sleep(60);
CloseHandle(hDevFile);
hDevFile = INVALID_HANDLE_VALUE;
(I get the device symbolic name from the registry but I've skipped that part to make my question concise. WriteData() and ReadData() are custom functions that write and read accordingly.)
The problem is that SetCommState() returns a zero-value. GetLastError() returns 122, which is ERROR_INSUFFICIENT_BUFFER.
The problem now is that PurgeComm() generates ERROR_INSUFFICIENT_BUFFER, too. CreateFile() gives ERROR_SUCCESS, so it must be opened properly.
What's wrong? Did I miss something?
Edit: I tried enumerating COM ports and found an interesting thing - there are no COM ports on my computer. Even though the device is connected and enabled, with the driver present and all that stuff. I also tried forcefully putting \\.\COM1, \\.\COM2, and so on as the file name for CreateFile, but with no luck. Everytime got an ERROR_FILE_NOT_FOUND.
Please, help. This is very important to me.
Because this is a CP210x device, it's a virtual COM port, so you should be opening it as such in CreateFile. You had it right when you said that you tried using \.\COMx, you just need to find out which COM port your CP210x device has been assigned and you will not get the ERROR_FILE_NOT_FOUND error. You can find this by looking in device manager:
Take a look a the Serial Communications Guide for the CP210x, this explains how to make these types of calls to your device, there's even a COM port discovery function that will help you find the COMxx name dynamically. It also has accompanying software, AN197SW.zip.
You can use the Win32 Communication Functions just fine with a handle gotten from passing the device interface path to CreateFile. I do this all the time. Ignore the people telling you that you must use COMx.
However, it is important that you use the device interface path corresponding to the (virtual) serial port device (GUID_DEVINTERFACE_COMPORT). Many drivers are implemented as a pair of (USB device, serial port device), where the serial port is a child of the USB device. Opening the USB device (GUID_DEVINTERFACE_USB_DEVICE) will not give you working communication functions, such as PurgeCommState. (And this is exactly what you're trying now, note that the tail end of your device interface path exactly matches the GUID documented on MSDN)
If you don't have anything listed under the Ports section in Device Manager, you either don't have the driver correctly installed, or the device is not connected.
Once you get a port device found, you can use CM_Get_Parent to pair up the GUID_DEVINTERFACE_COMPORT instance with the GUID_DEVINTERFACE_USB_DEVICE, solving your question of "What serial port is attached to USB in this particular way?"
I'm trying to set a timeout to the reading operation of my named pipe.
In order to read from the named pipe, I'm using the ReadFile function.
I read that a timeout can be set for this function with the SetCommTimeouts function but when I try to use it, I get system error 1: "Incorrect function".
Here is my code (this is the client side):
m_pipe = CreateFileA(pipeName, // pipe name
GENERIC_READ | // read and write access
GENERIC_WRITE,
0, // no sharing
NULL, // default security attributes
OPEN_EXISTING, // opens existing pipe
0, // default attributes
NULL); // no template file
if (m_pipe != INVALID_HANDLE_VALUE)
{
DWORD mode = PIPE_READMODE_MESSAGE | PIPE_WAIT;
ok = SetNamedPipeHandleState(m_pipe, &mode, NULL, NULL);
COMMTIMEOUTS cto;
cto.ReadTotalTimeoutConstant = 1000;
BOOL time = SetCommTimeouts(m_pipe, &cto);
}
Am I doing something wrong or the SetCommTimeouts method is not supposed to be used with pipes? Is there any other way to get a reading timeout?
If the purpose of the timeout is to not get stuck forever you may consider a call to PeekNamedPipe(...) in a timed loop. This way you can check whether there is anything to read from time to time.
Alternatively PeekNamedPipe may be used to decide whether a read on the pipe is actually going to get anything before the read is performed. This way a "waiting" read can be avoided.
You cannot use SetCommTimeouts with named pipes. If you want timeouts, you will have to use Async I/O and implement the timeout yourself using CancelIo or CancelIoEx
ReadFile blocks until it read requested amount of bytes or error/abort happen. Overlapped works same, i.e. it completes on same conditions. Tried to implement timeouts using CancelIoEx and figured out that it loses data. Until now see no way to implement timeouts and read only part of requested amount of bytes, or read cached data.