gpt4 book ai didi

c++ - 带有虚拟 COM 端口的 USB 串行设备 - 如果将 CreateFile() 与 USB 路径一起使用,ReadFile() 读取零字节

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:33:14 24 4
gpt4 key购买 nike

我有一个销售点应用程序,它使用串行通信端口 (RS-232) 与称重产品的秤进行通信。我现在致力于能够直接支持 USB 设备,而不是使用虚拟串行通信端口,因为它们有令人讨厌的移动倾向。

我们发现,虽然 Windows 7 似乎会自动创建虚拟串行通信端口,但其他版本的 Windows(例如 POS Ready 7)可能不会。我们怀疑这是由于 POS Ready 7 中缺少 Windows 7 的特定 .inf 文件。有人可以确认吗?

我有一个间歇性工作的 USB 示例应用程序。我在与 ReadFile() 的 USB 级通信时遇到问题Windows API 函数。我正在使用 CreateFile()指定 USB 设备路径以获取 I/O 句柄,然后使用 WriteFile()ReadFile()与秤沟通。 ReadFile()在某些情况下不提供数据。

背景资料

我正在使用的特定秤 Brecknell 67xx 台式秤,直接与销售点应用程序一起使用开箱即用的虚拟串行通信端口。我使用 USB 电缆将秤连接到我的 Windows 7 PC,Windows 自动安装驱动程序以创建虚拟串行端口,在我的情况下为 COM4。然后我将应用程序配置为通过 COM4 与秤对话,一切正常。

使用秤的协议(protocol)是向秤发送一个两字节的命令“W\r”(大写字母 W 后跟一个回车符),然后读取一个 16 字节的响应,其中包含当前重量和状态有关比例机制的信息,例如 In Motion。

我正在学习的示例 USB 应用程序将成功提供权重。然后它将停止正常工作与 ReadFile() 的行为返回读取的零字节。一旦它停止工作,它将继续无法提供来自 ReadFile() 的数据。即使我拔下并重新插入 USB 电缆或重新启动我的电脑。

以前版本的学习应用卡在ReadFile()上并且当使用 Visual Studio 完成 Break All 时,会显示一个暂停,然后显示一条指示死锁的消息。但是自从我开始使用 SetCommTimeouts()ReadTotalTimeoutConstant 中有 5000 毫秒的超时值我在 ReadFile() 之前看到了一致的 5 秒停顿读取零字节返回。

奇怪的是,如果我然后使用打开虚拟串行通信端口 COM4 的应用程序,该应用程序工作正常并且秤会报告元素的重量。

然后我可以返回到使用直接 USB 而不是虚拟串行通信端口的示例应用程序,它可以很好地报告权重。

但是,如果我随后拔下连接秤与 PC 的 USB 电缆,这也会关闭秤的电源,然后重新插入 USB 电缆,示例应用程序将不再正常运行,并且我再次看到超时暂停。

然后我尝试使用依赖于使用虚拟串行端口 COM4 的串行通信端口的原始销售点应用程序,并且该应用程序可以很好地称重元素。

当我然后重试我的示例应用程序时,它也会报告项目权重。

我的问题。

如果 USB 设备在插入时创建了一个虚拟串行通信端口,那么是否只需要通过在 CreateFile() 中指定通信端口(在我的情况下为 COM4)来使用虚拟串行端口。称呼?

如何使用 CreateFile() 进行直接 USB 串行通信如果设备导致 Windows 生成虚拟通信端口,则使用 USB 设备路径?

是否有某种方法可以指定任何版本的 Windows 在插入时自动为设备创建虚拟串行通信端口?

示例 USB 应用程序的源代码

我使用 Visual Studio 2005 的示例 USB Windows 控制台应用程序的源代码如下,主要位于底部,其中大部分是用于查找特定 USB 设备然后允许 ReadFile() 的类。和 WriteFile() :

// usb_test_cons.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <windows.h>
#include <setupapi.h>
#include <initguid.h>

#include <stdio.h>

// This is the GUID for the USB device class.
// It is defined in the include file Usbiodef.h of the Microsoft Windows Driver Kit.
// See also https://msdn.microsoft.com/en-us/library/windows/hardware/ff545972(v=vs.85).aspx which
// provides basic documentation on this GUID.
DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED);
// (A5DCBF10-6530-11D2-901F-00C04FB951ED)

// Following are standard defines to be used with all of the
// devices that are use through the UIE interface.
#define UIE_DEVICE_ERROR (-11) /* error when accessing the device */
#define UIE_DEVICE_NOT_PROVIDE (-12) /* device is not provided */
#define UIE_DEVICE_ERROR_RANGE (-13) /* range error */
#define UIE_DEVICE_ERROR_COM (-14) /* communication error */
#define UIE_DEVICE_TIMEOUT (-15) /* communication error */
#define UIE_DEVICE_SPECIFIC (-20) /* device specific errors start here */


#define UIE_SCALE_ETX 0x03 /* ETX character */
#define UIE_SCALE_IN_MOTION 0x01 /* scale in motion */
#define UIE_SCALE_ZERO 0x02 /* zero weight */
#define UIE_SCALE_UNDER 0x01 /* under capacity */
#define UIE_SCALE_OVER 0x02 /* over capacity */

#define UIE_SCALE_ERROR UIE_DEVICE_ERROR /* error */
#define UIE_SCALE_NOT_PROVIDE UIE_DEVICE_NOT_PROVIDE /* not provide */
#define UIE_SCALE_TIMEOUT UIE_DEVICE_TIMEOUT /* time out when reading from scale */
#define UIE_SCALE_MOTION (UIE_DEVICE_SPECIFIC-1) /* motion */
#define UIE_SCALE_UNDER_CAPACITY (UIE_DEVICE_SPECIFIC-2) /* under capacity */
#define UIE_SCALE_OVER_CAPACITY (UIE_DEVICE_SPECIFIC-3) /* over capacity */
#define UIE_SCALE_DATAFORMAT (UIE_DEVICE_SPECIFIC-4) /* Data read from scale incorrect format in UieScaleAnalysis() */
#define UIE_SCALE_DATAUNITS (UIE_DEVICE_SPECIFIC-5) /* Units read from scale incorrect in UieScaleAnalysis() */


static SHORT UieScaleStatus(char *puchBuffer, DWORD sLength)
{
UCHAR uchByte;

switch (sLength) {
case 16:
// The scale message is a weight message with a status section.
// Move the buffer pointer to where the status section should begin.
// A status only message has the same format as the status section of a weight message.
puchBuffer += 10;
case 6:
// The scale message may be a status only message if there is a problem with the scale.
// A status only message is 6 characters with the letter S as the second character.
if (*(puchBuffer + 0) != '\n' ||
*(puchBuffer + 1) != 'S' ||
*(puchBuffer + 4) != '\r' ||
*(puchBuffer + 5) != UIE_SCALE_ETX) {
return (UIE_SCALE_DATAFORMAT); /* exit ... */
}
break;
default:
return (UIE_SCALE_DATAFORMAT); /* exit ... */
break;
}

/* --- check status of low byte --- */
uchByte = *(puchBuffer + 3) - (UCHAR)0x30;

if (uchByte & UIE_SCALE_UNDER) {
return (UIE_SCALE_UNDER_CAPACITY);
} else if (uchByte & UIE_SCALE_OVER) {
return (UIE_SCALE_OVER_CAPACITY);
}

/* --- check status of high byte --- */
uchByte = *(puchBuffer + 2) - (UCHAR)0x30;

if (uchByte & UIE_SCALE_IN_MOTION) {
return (UIE_SCALE_MOTION);
} else if (uchByte & UIE_SCALE_ZERO) {
return (0);
} else {
return (TRUE);
}
}

class UsbSerialDevice
{
public:
UsbSerialDevice();
~UsbSerialDevice();
int CreateEndPoint (wchar_t *wszVendorId);
int CloseEndPoint ();
int ReadStream (void *bString, size_t nBytes);
int WriteStream (void *bString, size_t nBytes);

DWORD m_dwError; // GetLastError() for last action
DWORD m_dwErrorWrite; // GetLastError() for last write
DWORD m_dwErrorRead; // GetLastError() for last read
DWORD m_dwBytesWritten;
DWORD m_dwBytesRead;

private:
HANDLE m_hFile;
DWORD m_dwStatError;
COMMTIMEOUTS m_timeOut;
COMSTAT m_statOut;
};

UsbSerialDevice::UsbSerialDevice() :
m_dwError(0),
m_dwErrorWrite(0),
m_dwErrorRead(0),
m_dwBytesWritten(0),
m_dwBytesRead(0),
m_hFile(NULL)
{
}

UsbSerialDevice::~UsbSerialDevice()
{
CloseHandle (m_hFile);
}

int UsbSerialDevice::WriteStream(void *bString, size_t nBytes)
{
BOOL bWrite = FALSE;

if (m_hFile) {
m_dwError = m_dwErrorWrite = 0;
m_dwBytesWritten = 0;
ClearCommError (m_hFile, &m_dwStatError, &m_statOut);
bWrite = WriteFile (m_hFile, bString, nBytes, &m_dwBytesWritten, NULL);
m_dwError = m_dwErrorWrite = GetLastError();
return 0;
}

return -1;
}

int UsbSerialDevice::ReadStream(void *bString, size_t nBytes)
{
BOOL bRead = FALSE;

if (m_hFile) {
m_dwError = m_dwErrorRead = 0;
m_dwBytesRead = 0;
ClearCommError (m_hFile, &m_dwStatError, &m_statOut);
bRead = ReadFile (m_hFile, bString, nBytes, &m_dwBytesRead, NULL);
m_dwError = m_dwErrorRead = GetLastError();
return 0;
}

return -1;
}

int UsbSerialDevice::CreateEndPoint (wchar_t *wszVendorId)
{
HDEVINFO hDevInfo;


m_dwError = ERROR_INVALID_HANDLE;

// We will try to get device information set for all USB devices that have a
// device interface and are currently present on the system (plugged in).
hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE, NULL, 0, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
if (hDevInfo != INVALID_HANDLE_VALUE)
{
DWORD dwMemberIdx;
BOOL bContinue = TRUE;
SP_DEVICE_INTERFACE_DATA DevIntfData;

// Prepare to enumerate all device interfaces for the device information
// set that we retrieved with SetupDiGetClassDevs(..)
DevIntfData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
dwMemberIdx = 0;

// Next, we will keep calling this SetupDiEnumDeviceInterfaces(..) until this
// function causes GetLastError() to return ERROR_NO_MORE_ITEMS. With each
// call the dwMemberIdx value needs to be incremented to retrieve the next
// device interface information.
for (BOOL bContinue = TRUE; bContinue; ) {
PSP_DEVICE_INTERFACE_DETAIL_DATA DevIntfDetailData;
SP_DEVINFO_DATA DevData;
DWORD dwSize;

dwMemberIdx++;
SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &GUID_DEVINTERFACE_USB_DEVICE, dwMemberIdx, &DevIntfData);

if (GetLastError() == ERROR_NO_MORE_ITEMS) break;

// As a last step we will need to get some more details for each
// of device interface information we are able to retrieve. This
// device interface detail gives us the information we need to identify
// the device (VID/PID), and decide if it's useful to us. It will also
// provide a DEVINFO_DATA structure which we can use to know the serial
// port name for a virtual com port.

DevData.cbSize = sizeof(DevData);

// Get the required buffer size. Call SetupDiGetDeviceInterfaceDetail with
// a NULL DevIntfDetailData pointer, a DevIntfDetailDataSize
// of zero, and a valid RequiredSize variable. In response to such a call,
// this function returns the required buffer size at dwSize.

SetupDiGetDeviceInterfaceDetail(hDevInfo, &DevIntfData, NULL, 0, &dwSize, NULL);

// Allocate memory for the DeviceInterfaceDetail struct. Don't forget to
// deallocate it later!
DevIntfDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize);
DevIntfDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &DevIntfData, DevIntfDetailData, dwSize, &dwSize, &DevData))
{
// Finally we can start checking if we've found a useable device,
// by inspecting the DevIntfDetailData->DevicePath variable.
//
// The DevicePath looks something like this for a Brecknell 67xx Series Serial Scale
// \\?\usb#vid_1a86&pid_7523#6&28eaabda&0&2#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
//
// The VID for a particular vendor will be the same for a particular vendor's equipment.
// The PID is variable for each device of the vendor.
//
// As you can see it contains the VID/PID for the device, so we can check
// for the right VID/PID with string handling routines.

// See https://github.com/Microsoft/Windows-driver-samples/blob/master/usb/usbview/vndrlist.h

if (wcsstr (DevIntfDetailData->DevicePath, wszVendorId)) {
m_dwError = 0;
m_hFile = CreateFile (DevIntfDetailData->DevicePath, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
if (m_hFile == INVALID_HANDLE_VALUE) {
m_dwError = GetLastError();
} else {
GetCommTimeouts (m_hFile, &m_timeOut);
m_timeOut.ReadIntervalTimeout = 0;
m_timeOut.ReadTotalTimeoutMultiplier = 0;
m_timeOut.ReadTotalTimeoutConstant = 5000;
SetCommTimeouts (m_hFile, &m_timeOut);
m_dwError = GetLastError();
}
bContinue = FALSE; // found the vendor so stop processing after freeing the heap.
}
}

HeapFree(GetProcessHeap(), 0, DevIntfDetailData);
}

SetupDiDestroyDeviceInfoList(hDevInfo);
}

return 0;
}


int _tmain(int argc, _TCHAR* argv[])
{
UsbSerialDevice myDev;

myDev.CreateEndPoint (L"vid_1a86&pid_7523");
switch (myDev.m_dwError) {
case 0:
// no error so just ignore.
break;
case ERROR_ACCESS_DENIED:
wprintf (_T(" CreateFile() failed. GetLastError() = %d\n ERROR_ACCESS_DENIED: Access is denied.\n Is it already in use?\n"), myDev.m_dwError);
break;
case ERROR_GEN_FAILURE:
wprintf (_T(" CreateFile() failed. GetLastError() = %d\n ERROR_GEN_FAILURE: A device attached to the system is not functioning.\n Is it an HID?\n"), myDev.m_dwError);
break;
case ERROR_INVALID_HANDLE:
wprintf (_T(" CreateFile() failed. GetLastError() = %d\n ERROR_INVALID_HANDLE: The handle is invalid.\n CreateFile() failed?\n"), myDev.m_dwError);
break;
default:
wprintf (_T(" CreateFile() failed. GetLastError() = %d\n"), myDev.m_dwError);
break;
}

if (myDev.m_dwError == 0) {
char reqWeight[] = "W\r";
char resWeight[256] = {0};

myDev.WriteStream (reqWeight, strlen (reqWeight));
wprintf (_T(" Sent request now get response.\n"));
Sleep (50);
myDev.ReadStream (resWeight, 16);
wprintf (_T(" Got response.\n"));
if (resWeight[0] != '\n' || resWeight[9] != '\r') {
wprintf (_T(" Unexpected format of response.\n"));
}

short sRet = UieScaleStatus (resWeight, myDev.m_dwBytesRead);

resWeight[9] = 0; // terminate the weight string so that we can write it out.
wprintf (_T(" ScaleStatus = %d, Response from device - \"%S\"\n"), sRet, resWeight + 1);
}

return 0;
}

已开发的附加信息

Microsoft MSDN 中的 INF 文件概述 https://msdn.microsoft.com/en-us/windows/hardware/drivers/install/overview-of-inf-files

Stackoverflow Do I need to write my own host side USB driver for a CDC device

Stackoverflow how to get vendor id and product id of a plugged usb device on windows

Is it possible to “transplant” drivers between machines?有一个文档链接 Debugging USB Device Installation on Windows和这个帖子 Remove Windows Device Class in Registry有更多信息。

USB serial driver (Usbser.sys)来自微软。

USB device class drivers included in Windows来自微软。

最佳答案

运行 windows 的 PC(USB 主机)和秤(USB 设备)的通信遵循 USB 协议(protocol)。如果您安装 libusb 对于 Windows,当使用 lsusb -v 时,您可以获得与 PC 从 USB 设备获取的信息类似的信息。 .一个 USB 设备可以实现多个 USB 类。

如果 USB 设备创建了一个虚拟 COM 端口,它肯定会实现 CDC ACM 类(通信设备类抽象控制模型),除此之外它还可以实现其他 USB 类,如大容量存储类,...

直销 与 USB 设备的通信还取决于它实现的设备类及其接口(interface)和端点。如果 USB 设备实现了 CDC ACM(虚拟 COM),则您使用特定的 RS-232 命令(即 https://www.commfront.com/pages/3-easy-steps-to-understand-and-control-your-rs232-devices 或将十六进制“D”发送到万用表以接收测量值),如果它实现了您通常使用的大容量存储类使用批量传输

要更改您使用控制传输的 USB 设备的模式(请参阅 USB 简而言之)

在此链接中是 Win 如何在确定设备的 USB 类后确定加载哪个驱动程序 https://msdn.microsoft.com/en-us/library/windows/hardware/ff538820%28v=vs.85%29.aspx
( https://msdn.microsoft.com/en-us/library/windows/hardware/jj649944%28v=vs.85%29.aspx )

我不知道 Brecknell 如何实现作为虚拟 COM 的 CDC ACM 设备类,但是通常任何支持 USB 的 Win 版本都应该能够加载 CDC ACM 设备类(虚拟 COM)的驱动程序,因此您是正确的,这似乎是 .inf驱动文件问题或驱动程序加载机制(可能是 Brecknell CDC ACM 实现的问题,但我不这么认为)

然后,如果 Win 加载了一个正常工作的驱动程序,那么正常的方式就是你所做的:使用 CreateFile()使用分配给 USB 设备的 COM。

奇怪的是,如果我然后使用打开虚拟串行通信端口 COM4 的应用程序,该应用程序工作正常并且秤会报告元素的重量。 <- 这并不奇怪,奇怪的是某些 Win 版本无法识别 CDC USB 设备。
CDC 设备的标准驱动程序似乎是 USBser.sys ( https://msdn.microsoft.com/de-de/library/windows/hardware/dn707976%28v=vs.85%29.aspx )
如果您搜索“windows 无法识别 CDC 设备”,您会得到结果

如果 USB 设备在插入时创建了一个虚拟串行通信端口,那么是否只需要通过在 CreateFile() 调用中指定通信端口(在我的情况下为 COM4)来使用虚拟串行端口?是的,如果 USB 设备实现了虚拟 COM,则使用此 COM 与此设备进行通信是最简单的方法

另见 http://www.beyondlogic.org/usbnutshell/usb1.shtml USB 简而言之

标准USB:设备描述符(类)->接口(interface)->(配置)->端点

关于c++ - 带有虚拟 COM 端口的 USB 串行设备 - 如果将 CreateFile() 与 USB 路径一起使用,ReadFile() 读取零字节,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40775369/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com