gpt4 book ai didi

c++ - Windows 7/64 中的串行异步 I/O

转载 作者:塔克拉玛干 更新时间:2023-11-03 06:54:04 26 4
gpt4 key购买 nike

我有一个多线程 Windows 程序,它通过“原始”Win API 调用执行串行端口异步 I/O。它在除 Windows 7/64 之外的任何 Windows 版本上都运行良好。

问题是程序可以很好地找到和设置 COM 端口,但它不能发送或接收任何数据。无论我是在 Win XP 还是 7 中编译二进制文件,我都无法在 Win 7/64 上发送/接收。兼容模式、以管理员身份运行等都无济于事。

我已设法将问题缩小到 FileIOCompletionRoutine 回调。每次调用时,dwErrorCode 始终为 0,dwNumberOfBytesTransfered 始终为 0。函数内部的 GetOverlappedResult() 始终返回 TRUE(一切正常)。似乎正确设置了 lp​​NumberOfBytesTransferred。但是 lpOverlapped 参数已损坏,它是一个指向垃圾值的垃圾指针。

我可以通过在调试器中检查正确的 OVERLAPPED 结构分配在哪个地址,或者通过设置一个临时文件来看到它已损坏。指向它的全局变量。

我的问题是:为什么会发生这种情况,为什么只发生在 Windows 7/64 上?我不知道调用约定有什么问题吗?还是以某种方式对重叠结构进行了不同的处理?


发布以下代码的相关部分:

class ThreadedComport : public Comport
{
private:

typedef struct
{
OVERLAPPED overlapped;
ThreadedComport* caller; /* add user data to struct */
} OVERLAPPED_overlap;

OVERLAPPED_overlap _send_overlapped;
OVERLAPPED_overlap _rec_overlapped;

...

static void WINAPI _send_callback (DWORD dwErrorCode,
DWORD dwNumberOfBytesTransfered,
LPOVERLAPPED lpOverlapped);
static void WINAPI _receive_callback (DWORD dwErrorCode,
DWORD dwNumberOfBytesTransfered,
LPOVERLAPPED lpOverlapped);

...
};

打开/关闭是在没有实现多线程或异步 I/O 的基类中完成的:

void Comport::open (void)
{
char port[20];
DCB dcbCommPort;
COMMTIMEOUTS ctmo_new = {0};

if(_is_open)
{
close();
}

sprintf(port, "\\\\.\\COM%d", TEXT(_port_number));

_hcom = CreateFile(port,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
0,
0);

if(_hcom == INVALID_HANDLE_VALUE)
{
// error handling
}

GetCommTimeouts(_hcom, &_ctmo_old);
ctmo_new.ReadTotalTimeoutConstant = 10;
ctmo_new.ReadTotalTimeoutMultiplier = 0;
ctmo_new.WriteTotalTimeoutMultiplier = 0;
ctmo_new.WriteTotalTimeoutConstant = 0;

if(SetCommTimeouts(_hcom, &ctmo_new) == FALSE)
{
// error handling
}

dcbCommPort.DCBlength = sizeof(DCB);
if(GetCommState(_hcom, &(DCB)dcbCommPort) == FALSE)
{
// error handling
}

// setup DCB, this seems to work fine

dcbCommPort.DCBlength = sizeof(DCB);
dcbCommPort.BaudRate = baudrate_int;

if(_parity == PAR_NONE)
{
dcbCommPort.fParity = 0; /* disable parity */
}
else
{
dcbCommPort.fParity = 1; /* enable parity */
}
dcbCommPort.Parity = (uint8)_parity;
dcbCommPort.ByteSize = _databits;
dcbCommPort.StopBits = _stopbits;

SetCommState(_hcom, &(DCB)dcbCommPort);
}


void Comport::close (void)
{
if(_hcom != NULL)
{
SetCommTimeouts(_hcom, &_ctmo_old);
CloseHandle(_hcom);
_hcom = NULL;
}
_is_open = false;
}

整个多线程和事件处理机制比较复杂,相关部分是:

发送

result = WriteFileEx (_hcom,              // handle to output file
(void*)_write_data, // pointer to input buffer
send_buf_size, // number of bytes to write
(LPOVERLAPPED)&_send_overlapped, // pointer to async. i/o data
(LPOVERLAPPED_COMPLETION_ROUTINE )&_send_callback);

接收

  result = ReadFileEx (_hcom,                  // handle to output file
(void*)_read_data, // pointer to input buffer
_MAX_MESSAGE_LENGTH, // number of bytes to read
(OVERLAPPED*)&_rec_overlapped, // pointer to async. i/o data
(LPOVERLAPPED_COMPLETION_ROUTINE )&_receive_callback);

回调函数

void WINAPI ThreadedComport::_send_callback (DWORD dwErrorCode,
DWORD dwNumberOfBytesTransfered,
LPOVERLAPPED lpOverlapped)
{
ThreadedComport* _this = ((OVERLAPPED_overlap*)lpOverlapped)->caller;

if(dwErrorCode == 0) // no errors
{
if(dwNumberOfBytesTransfered > 0)
{
_this->_data_sent = dwNumberOfBytesTransfered;
}
}

SetEvent(lpOverlapped->hEvent);
}


void WINAPI ThreadedComport::_receive_callback (DWORD dwErrorCode,
DWORD dwNumberOfBytesTransfered,
LPOVERLAPPED lpOverlapped)
{
if(dwErrorCode == 0) // no errors
{
if(dwNumberOfBytesTransfered > 0)
{
ThreadedComport* _this = ((OVERLAPPED_overlap*)lpOverlapped)->caller;
_this->_bytes_read = dwNumberOfBytesTransfered;
}
}

SetEvent(lpOverlapped->hEvent);
}

编辑

更新:一天中的大部分时间我都在研究 OVERLAPPED 变量在执行回调之前超出范围的理论。我已经证实这从未发生过,我什至试图将 OVERLAPPED 结构声明为静态,同样的问题仍然存在。如果 OVERLAPPED 结构超出范围,我希望回调指向先前分配该结构的内存位置,但它没有,它指向其他地方,在一个完全不熟悉的内存位置。为什么会这样,我不知道。

也许 Windows 7/64 制作了 OVERLAPPED 结构的内部硬拷贝?我可以看到这将如何导致这种行为,因为我依赖于结构末尾潜入的附加参数(这对我来说似乎是一种 hack,但显然我从官方 MSDN 示例中得到了这种“hack”)。

我也尝试过更改调用约定,但这根本不起作用,如果我更改它,程序就会崩溃。 (标准调用约定会导致它崩溃,无论标准是什么,cdecl?__fastcall 也会导致崩溃。)有效的调用约定是 __stdcall、WINAPI 和 CALLBACK。我认为这些都是 __stdcall 的相同名称,我在某处读到 Win 64 无论如何都会忽略该调用约定。

执行回调似乎是因为 Win 7/64 中的某些“虚假干扰”生成了带有损坏或不相关参数的虚假回调调用。

多线程竞争条件是另一种理论,但在我运行以重现错误的场景中,只有一个线程,我可以确认调用 ReadFileEx 的线程与执行回调的线程相同。

最佳答案

我找到了问题所在,原来它简单得令人讨厌。

在 CreateFile() 中,我没有指定 FILE_FLAG_OVERLAPPED。由于未知原因,这在 32 位 Windows 上不是必需的。但是,如果您在 64 位 Windows 上忘记了它,它显然仍会使用 FileIOCompletionRoutine 生成回调,但它们的参数已损坏。

我没有在任何地方找到这种行为变化的任何文件;也许这只是 Windows 中的内部错误修复,因为旧文档还指定您必须设置 FILE_FLAG_OVERLAPPED。

至于我的具体情况,错误出现是因为我有一个采用同步 I/O 的基类,然后它被使用异步 I/O 的类继承。

关于c++ - Windows 7/64 中的串行异步 I/O,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12583482/

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