非重叠串行端口挂在 CloseHandle 处

2024-03-22

我编写了一个自己开发的串行端口类,为了简单起见,我使用了阻塞/同步/不重叠。我浏览了所有 MSDN 文档,这对我来说很困难。

我在从端口打开、传输或接收字节方面没有任何问题。所有操作都是同步并且不存在线程复杂性。

function TSerialPort.Open: Boolean;
var
  h: THandle;
  port_timeouts: TCommTimeouts;
  dcb: TDCB;
begin
  Result := False;

  if Assigned(FHandleStream) then
  begin
    // already open
    Exit(True);
  end;

  h := CreateFile(PChar('\\?\' + FComPort),
                  GENERIC_WRITE or GENERIC_READ, 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

  // RaiseLastOSError();
  if h <> INVALID_HANDLE_VALUE then
  begin
    {
      REMARKS at https://learn.microsoft.com/en-us/windows/desktop/api/winbase/ns-winbase-_commtimeouts
      If an application sets ReadIntervalTimeout and ReadTotalTimeoutMultiplier to MAXDWORD and
      sets ReadTotalTimeoutConstant to a value greater than zero and less than MAXDWORD, one
      of the following occurs when the ReadFile function is called:

      * If there are any bytes in the input buffer, ReadFile returns immediately with the bytes in the buffer.
      * If there are no bytes in the input buffer, ReadFile waits until a byte arrives and then returns immediately.
      * If no bytes arrive within the time specified by ReadTotalTimeoutConstant, ReadFile times out.
    }

    FillChar(port_timeouts, Sizeof(port_timeouts), 0);
    port_timeouts.ReadIntervalTimeout := MAXDWORD;
    port_timeouts.ReadTotalTimeoutMultiplier := MAXDWORD;
    port_timeouts.ReadTotalTimeoutConstant := 50; // in ms
    port_timeouts.WriteTotalTimeoutConstant := 2000; // in ms

    if SetCommTimeOuts(h, port_timeouts) then
    begin
      FillChar(dcb, Sizeof(dcb), 0);
      dcb.DCBlength := sizeof(dcb);

      if GetCommState(h, dcb) then
      begin
        dcb.BaudRate := FBaudRate;                            //  baud rate
        dcb.ByteSize := StrToIntDef(FFrameType.Chars[0], 8);  //  data size
        dcb.StopBits := ONESTOPBIT;                           //  1 stop bit
        dcb.Parity   := NOPARITY;
        case FFrameType.ToUpper.Chars[1] of
          'E': dcb.Parity   := EVENPARITY;
          'O': dcb.Parity   := ODDPARITY;
        end;

        dcb.Flags := dcb_Binary or dcb_Parity or dcb_ErrorChar or
                     (DTR_CONTROL_ENABLE shl 4) or (RTS_CONTROL_ENABLE shl 12);

        dcb.ErrorChar := '?'; // parity error will be replaced with this char

        if SetCommState(h, dcb) then
        begin
          FHandleStream := THandleStream.Create(h);
          Result := True;
        end;
      end;
    end;

    if not Result then
    begin
      CloseHandle(h);
    end;
  end;
end;

function TSerialPort.Transmit(const s: TBytes): Boolean;
var
  len: NativeInt;

begin
  Result := False;
  len := Length(s);

  if Assigned(FHandleStream) and (len > 0) then
  begin
    // total timeout to transmit is 2sec!!
    Result := (FHandleStream.Write(s, Length(s)) = len);
  end;
end;

function TSerialPort.Receive(var r: Byte): Boolean;
begin
  Result := False;

  if Assigned(FHandleStream) then
  begin
    // read timeout is 50ms
    Result := (FHandleStream.Read(r, 1) = 1);
  end;
end;

我的问题从关闭端口开始。 在我进行所有通信之后,当我尝试关闭串行端口时,我的应用程序完全挂在 CloseHandle() API 处。这发生了randomly。这对我来说毫无意义,因为我使用同步模式,不能有任何挂起的操作。当我请求关闭时,它必须简单地关闭句柄。

我在google和stack-overflow上搜索了这个问题。有很多人遇到过类似的问题,但大多数都与.NET串口驱动程序及其异步模式操作有关,而我没有。

而且有些人忘记正确设置超时,他们在 ReadFile 和 WriteFile API 中遇到阻塞问题,这是完全正常的。但这又不是我的问题,我已经按照 MSDN 注释中的指示设置了 CommTimeouts。

function TSerialPort.Close: Boolean;
var
  h: THandle;

begin
  Result := True;

  if Assigned(FHandleStream) then
  begin
    h := FHandleStream.Handle;
    FreeAndNil(FHandleStream);

    if h <> INVALID_HANDLE_VALUE then
    begin
      //PurgeComm(h, PURGE_TXABORT or PURGE_RXABORT or PURGE_TXCLEAR or PURGE_RXCLEAR); // didn't help
      //ClearCommError(h, PDWORD(nil)^, nil);  // didn't help
      //CancelIO(h);  // didn't help
      Result := CloseHandle(h); <------------ hangs here
    end;
  end;
end;

微软论坛上的一些人建议在不同的线程中调用CloseHandle()。我也尝试过。但那次它在尝试释放我创建的 AnonymousThread 时挂起。即使我将 FreeOnTerminate:=true 保留为默认值,它也会挂起,并且我会收到 Delphi 的内存泄漏报告。

当它挂起时,另一个令人烦恼的问题是,我必须完全关闭 Delphi IDE 并重新打开。否则我无法再次编译代码,因为仍然使用 exe。

function TSerialPort.Close: Boolean;
var
  h: THandle;
  t: TThread;
  Event: TEvent;

begin
  Result := True;

  if Assigned(FHandleStream) then
  begin
    h := FHandleStream.Handle;
    FreeAndNil(FHandleStream);

    if h <> INVALID_HANDLE_VALUE then
    begin
      PurgeComm(h, PURGE_TXABORT or PURGE_RXABORT or PURGE_TXCLEAR or PURGE_RXCLEAR);
      Event := TEvent.Create(nil, False, False, 'COM PORT CLOSE');
      t := TThread.CreateAnonymousThread(
        procedure()
        begin
          CloseHandle(h);
          If Assigned(Event) then Event.SetEvent();
        end);

      t.FreeOnTerminate := False;
      t.Start;
      Event.WaitFor(1000);
      FreeAndNil(t);  // <---------- that time it hangs here, why??!!
      FreeAndNil(Event);
    end;
  end;
end;

在我的笔记本中,我使用 FTDI 的 USB 转串口转换器。有人说是因为FTDI驱动的原因。但我使用的是由 Microsoft Windows Hardware Compatibility Publisher 签名的所有 Microsoft 驱动程序。我的系统中没有第三方驱动程序。但是当我断开 USB 适配器时,CloseHandle API 会自行解冻。有些人报告说,即使是主板上内置的本机串行端口也存在同样的问题。

到目前为止我还无法解决这个问题。任何帮助或解决方法都受到高度赞赏。

Thanks.


此问题与 FTDI USB 串行转换器驱动程序有关。它不能正确处理硬件流控制,有时会挂起 CloseHandle 调用。

要解决此问题,请手动实施硬件流控制。在 C++ 中(不确定在 Delphi 中如何完成)设置这些 DCB 结构字段以允许手动控制 RTS 线:

// Assuming these variables are defined in the header
HANDLE m_hComm; // Comm port handle.
DCB m_dcb;      // DCB comm port settings.

// Put these settings in the DCB structure.
m_dcb.fRtsControl = RTS_CONTROL_ENABLE;
m_dcb.fOutxCtsFlow = TRUE;

Then use

EscapeCommFunction(m_hComm, CLRRTS); // Call this before calling WriteFile.

And

EscapeCommFunction(m_hComm, SETRTS); // Call this after Write is complete.

在你的情况下,因为它是同步的 - 你可以用这两个调用包装对 WriteFile 的每个调用。如果使用异步(如我的情况),请在从 WriteFile 调用中的重叠结构中获取完成事件后使用 SETRTS 调用该异步。

在我们实施此操作之前,由于我们使用 12 个串行端口,因此一直冻结,解锁端口的唯一方法是重新启动计算机。 现在就像手动控制的魅力一样,从那以后就没有冻结过。

需要记住的一件事是,某些 USB 串行设备(甚至不同版本的 FTDI)可能会反转 RTS 线!因此,如果上述方法不起作用,请尝试使用 SETRTS 将线路设置为低电平,并使用 CLRRTS 将其设置为高电平。

编辑:如果您可以访问 Windows XP 计算机,请使用 portmon 工具查看 RTS 线路发生了什么,这样您就可以知道它是否已反转,或者是否正在获取命令。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

非重叠串行端口挂在 CloseHandle 处 的相关文章

  • Delphi 2010 控制闪烁

    我一直在从 XP 操作系统升级或迁移我们的软件 以便能够在 Windows 7 下编译和运行 我们的软件开始出现我们在 Windows XP 下没有注意到的问题 目前 我正在处理 TForm 上闪烁的用户定义控件 它似乎时不时地闪烁 但并非
  • 如何解决内存分段并强制FastMM释放内存给OS?

    注意 32 位应用程序不计划迁移到 64 位 我正在使用一个非常消耗内存的应用程序 并且几乎优化了与内存分配 取消分配相关的所有相关路径 应用程序本身没有内存泄漏 没有句柄泄漏 没有任何其他类型的泄漏 据我所知并经过测试 我无法触及的第 3
  • 是否有适用于 >= Delphi 2007 的 Delphi 混淆器

    我曾经使用 Pythia 来混淆我的 D6 程序 但 Pythia 似乎不再适用于我的 D2007 这是 Pythia 的链接 自 2007 年初以来没有更新 http www the interweb com serendipity in
  • 为什么我不能在接收数组参数的函数中使用 SetLength?

    我正在尝试使用以下函数来设置动态数组 即 var 参数 的长度 当我尝试编译代码时只有一个错误 dcc64 错误 lolcode dpr 138 E2008 不兼容类型 function execute var command array
  • Delphi错误数据集未处于插入或编辑模式

    客观的 单击 TRxDBCombo 上的按钮调用搜索框 从搜索框中选择记录时 结果将设置为 TComboEditBox 的字段值 并发布在 TRxMemoryData 数据集中 错误 第二次调用此函数时出现数据集未处于插入或编辑模式 TDB
  • 如何更改 Delphi 2010 IDE 中编辑器选项卡的字体大小?

    有谁知道如何更改 Delphi 2010 IDE 中编辑器选项卡的字体大小 我的 1080p 22 显示器的字体太小 无法阅读 而且会导致眼睛疼痛 一些笔记 它不尊重系统的 DPI 设置 因此更改系统设置没有帮助 而且 我现在已经使用 14
  • 如何更改 Chromium 组件的默认背景颜色?

    I use TChromium http code google com p delphichromiumembedded 我分配AWebPageAsString这是一个带有灰色背景颜色的静态 HTML 页面 FBrowser TChrom
  • 递归遍历树视图中的节点?

    我有一个树视图 其中已经填充了另一个过程中的文件 文件夹 我想按照从上到下的确切顺序逐项迭代树视图中的项目 但是 与普通列表不同 我不能仅使用简单的for对此的声明 我必须进入每个节点等 我该怎么做呢 我希望有一种方法可以在不运行递归过程的
  • Delphi 7,加载PNG到TImage

    只是想加载 PNG 尝试使用适用于其他格式的 OleGraphic 来使用我的 LoadPic 但在 PNG 上失败 目标是将图像复制到隐藏位图 然后将其屏蔽并复制到可见的工作图像画布 如果 CopyRect 不这样做 请随意提出其他建议
  • 如何从具有管理员权限的应用程序接收键盘输入到非管理员应用程序?

    我编写了一个应用程序 该应用程序具有覆盖类型的窗口 可以通过热键显示和隐藏该窗口 而另一个应用程序具有焦点 所述另一个应用程序是一个以管理员权限运行的 DirectX 游戏 我已经尝试了 3 种可能的解决方案 以便在其他应用程序中按下我的热
  • VCL.位图到 FMX.位图

    我在网上找到了这段代码 但 FMX Bitmap 没有扫描线 是否可以以某种方式将 VCL TBitmap 复制或绘制到 FMX Bitmap IFDEF MSWINDOWS type TBitmap FMX Types TBitmap T
  • 如何确保 FormClose 程序运行,无论程序如何退出?

    在 Delphi 7 中 我有一个 TMainForm FormClose 过程 旨在在程序退出时写出一些状态 这在手动关闭程序时效果很好 但是 我发现如果程序被 Windows 强制 退出 例如在 Windows 更新后需要重新启动 则不
  • Delphi线程死锁

    我有时会在销毁某些线程时遇到死锁问题 我尝试过调试该问题 但在 IDE 中调试时似乎从未存在死锁 可能是因为 IDE 中的事件速度较低 问题 当应用程序启动时 主线程会创建多个线程 线程始终处于活动状态并与主线程同步 完全没有问题 当应用程
  • Arduino 上的串行消息到整数

    我希望我的 Arduino 通过串行通信接收一个整数 你能帮我解决这个问题吗 它应该是这样的形式 int value strtoint Serial read 有多种方法可以读取整数Serial 很大程度上取决于数据发送时的编码方式 Ser
  • Firemonkey 编辑/组合自动完成/打字时自动建议

    实施方式是什么Autocomplete or Autosuggest适用于 Windows Android 平台以及 MacOS 和 iOS 的 Delphi Firemonkey Example 当用户在 Google 搜索框中输入文本时
  • 如何遍历任意给定集合中的枚举?

    我有很多枚举类型 它们与相应的集合相结合 例如 type TMyEnum meOne meTwo meThree TMyEnums set of TMyEnum 我正在尝试提出一组可以运行的函数any枚举集 而不是为每个枚举编写单独的函数
  • 如何在Delphi中下载一个非常简单的HTTPS页面?

    我尝试了在这里看到的代码 但它不适用于 HTTPS 我需要将此页面作为字符串下载 并在其上添加一些换行符 以便将信息按顺序放入 TMemo 中 怎么做 我尝试使用 Indy 但由于 SSL 问题而失败 我尝试了此页面的解决方案 如何将网页下
  • C# .Net Serial DataReceived 事件响应对于高速数据来说太慢

    我已经设置了一个 SerialDataReceivedEventHandler 并在 VS2008 Express 中使用基于表单的程序 我的串口设置如下 115200 8N1 Dtr 和 Rts 已启用 接收字节阈值 1 我有一个通过蓝牙
  • 不断断点?如何去除它们?

    我下载了一个用Delphi 2009制作的项目 这也是我使用的 但是有一个断点我无法删除 如果我尝试删除它 它会在程序执行后再次执行 我在其他调试器中遇到了这样的事情 称为硬件断点 但这并不重要 如何删除断点 EDIT Article ht
  • 当responseText包含有效的Xml时,IXMLHttpRequest.responseXml为空,没有解析错误

    我正在从中获取一些 XML政府网站 http www bankofcanada ca stats assets rates rss noon en all xml http www bankofcanada ca stats assets

随机推荐

  • PHP 图片大小小于 1mb

    目前我正在使用以下内容来计算文件大小是否小于 1MB 但是由于以下代码来自 9lession 示例站点 它说要检查 1mb 的大小 但如果我乘以 1024 2 这就是他们在这里所做的不等于 1mb 而是 2048kb 说它上传的大小不是以
  • 如何在 macOS 上的 SwiftUI 中检测键盘事件?

    如何在 macOS 上的 SwiftUI 视图中检测键盘事件 我希望能够使用击键来控制特定屏幕上的项目 但不清楚如何检测键盘事件 这通常是通过覆盖keyDown event NSEvent in NSView 与 Xcode 12 捆绑在一
  • 将正则表达式匹配到第一个空格

    我必须填充商店的图像 并提供以下格式的图像文件夹 例如BRL0368 Side jpg 5510 Dura Guard Carpet jpg 现在我想做的就是把所有这些都砍掉 这样我就可以尝试在 Excel 中匹配零件编号 例如 BRL03
  • Func 的性能和继承

    我一直无法理解使用的性能特征Func lt gt 在我的代码中 当使用继承和泛型时 我发现自己一直在使用继承和泛型的组合 让我从一个最小的测试用例开始 这样我们都知道我们在说什么 然后我将发布结果 然后我将解释我的期望和原因 最小测试用例
  • AWS python lambda函数:没有名为请求的模块

    我对 AWS 还很陌生 遇到了一些问题 这是我的代码 from future import print function from urllib2 import Request urlopen URLError import request
  • 多索引 pandas 更新

    有两个数据帧 df1 和 df2 import pandas as pd import numpy as np import datetime A list range 3 9 B datetime date 2019 1 1 dateti
  • 单元测试复杂交互的正确方法

    我必须开始使用 QualityTools UnitTestFramework 为我们开发的 Web 服务层编写一些单元测试 而我的方法从一开始似乎就不正确 单元测试似乎应该能够以任何顺序运行 而不依赖于其他测试 我最初的想法是拥有类似于以下
  • 错误:由于表单未连接,表单提交被取消

    关于潜在重复的免责声明 我也遇到过类似的问题 我想我现在已经阅读了几乎所有这些答案 但没有一个为我解决了问题 我有一个应用程序 前端是 React 后端是 Flask 服务器 我有一个重大问题 该代码似乎检查了前端和后端 但是当我单击表单的
  • R - 如何分割文本和标点符号但有例外?

    在 R 中分析 Facebook 评论进行情感分析 表情符号是在 符号之间的文本编码 例子 Jesus te ama
  • 使用 jQuery 的 GridView 向上和向下导航

    我正在尝试使用 jQuery 实现 GridView 向上和向下键盘导航功能 我已经为此编写了代码 但有一个错误 它只能工作一次 重现错误的步骤 将我的示例代码复制到您的 WebForm aspx 和 WebForm aspx cs 后 分
  • 在 C# 中向 DateTime 添加时间

    我有一个日历和一个包含一天中某个时间的文本框 我想创建一个由两者组合而成的日期时间 我知道我可以通过查看小时和分钟然后将它们添加到日历 DateTime 中来做到这一点 但这看起来相当混乱 有没有更好的办法 您可以使用日期时间 添加 htt
  • jquery中如何定义变量

    我想知道如何在 jQuery 中声明变量 我当前使用的代码是 name anirudha alert name 该代码工作正常 但如果我将其写为 name document myForm txtname value alert name 那
  • HttpListener 的使用

    我有以下 HTTP 侦听器方法 深受 MSDN 使用 HttpListener 类的示例的启发 我对编程相当陌生 我不知道从这里到哪里从我的 Main 初始化它 有什么建议么 public static void HttpListener
  • 使用 typegoose 将项目添加到 Ref 数组

    我延长了 2 个课程Typegoose Item and Player In the Player我的班级有一个数组Ref
  • 为 VS2022 构建扩展时出现有关 ProductArchitecture 的错误 VSSDK1311

    我正在构建 VSIX 项目并看到以下错误消息 VSSDK1311 vsixmanifest 必须包含 PackageManifest Installation InstallTarget ProductArchitecture 的值 我需要
  • 传递参数以包含在 Liquid 模板中

    在我的 Jekyll 支持的网站上 我有一个包含函数 可以执行类似功能的操作 但我不知道如何正确传递它的参数 当我使用 include 像这样传递参数 include function liquid foo baz quux 它只是传递文字
  • 从网络驱动器启动时,Structuremap 不加载注册表

    我是 Structuremap 的热心 新 用户 但我在加载注册表时遇到问题 当我从本地驱动器启动应用程序时 应用程序中的所有注册表都用于解析类型 我通过 ObjectFactory WhatDoIHave 验证了这一点 但是 当我从共享启
  • 如何隐藏 VsCode 滚动条?

    VsCode 中似乎曾经有一个隐藏滚动条的设置 editor scrollbar vertical hidden 但是 这似乎已被弃用 现在我收到错误Unknown configuration setting VsCode 中有隐藏滚动条的
  • LibGit2Sharp CheckoutPaths()

    我做了一次提交 49916 现在我想将提交的一个文件检出到工作目录中 该文件名为 NEW txt 如果我输入 Git 签出 49916 NEW txt 进入 Git Bash 后 它会创建 NEW txt 文件 其内容位于我的工作目录中 但
  • 非重叠串行端口挂在 CloseHandle 处

    我编写了一个自己开发的串行端口类 为了简单起见 我使用了阻塞 同步 不重叠 我浏览了所有 MSDN 文档 这对我来说很困难 我在从端口打开 传输或接收字节方面没有任何问题 所有操作都是同步并且不存在线程复杂性 function TSeria