Delphi:TComPort封装在DLL中,通讯时无法接收串口数据的解决办法

来源:互联网 发布:加油软件哪个好 编辑:程序博客网 时间:2024/06/04 01:04
现象:
将Tcomport(版本:ComPort Library ver. 3.0   )封装在DLL中,进行串口通讯时,无法接收数据!
 
解决办法:
在创建组件时,只需要更改一个属性的设置就可以了。如下所示:
          //DLL入口
          
           talencaport:=ttalencaport.Create(nil);
           talencaport.port:='COM1';
           talencaport.SyncMethod:=smWindowSync;
           talencaport.OnRxChar:=talencaport.readbuf;
红色加粗的代码就是关键的一句代码!加入此句代码后,在DLL中发送与接收数据就与在窗体上使用一样。
 
 
分析:
由于工作需要,一直使用DELPHI5,DELPHI7两个版本进行开发。在DELPHI5中,将TCOMPORT封装在DLL中使用,没有任何的异常。后来抱成DELPHI7后,当时以为,同样的代码,应当没有什么问题。于是,编译。成功!哈!原来版本升级这么容易!也没有多想,没有测试,就把这事情放在了脑后。-_-!!
 
时间过了一个月,公司有设备要进行测试,因此,自己很高兴地把新的东西用来测试,结果,所有的通讯指令均是失败(无数据)!头顿时就要炸了。。。。。。
 
还好,是自己测试,有问题还算是可以补救的。仔细看源码,与DELPHI5下的源码是一样的,为什么在DELPHI7下就接收不到数据呢?看来问题还是在TCOMPORT本身上。(:),偶还是粉相信DELPHI实力的!:))
一看DLL入口代码:
talencaport:=ttalencaport.Create(nil);
              talencaport.port:='COM1';
              talencaport.OnRxChar:=talencaport.readbuf;
似乎也看不出什么问题来。
 
心想,是不是在发送完数据后,要延时一点时间呢,于是在发送完数据后,加入一个延时100ms和小过程(这个延时时间跟我们的设备相关),结果发现还是收不到数据。看来不是这个延时的问题。
延时过程代码:
 var Com_waittime:cardinal;
    Com_waittime:=gettickcount;
    While gettickcount- Com_waittime<100 do application.processmessages;
 
没有办法,只能看TCOMPORT源码了。一看源码,TCOMPORT的收数据是由线程完成的。相应线程类是:TcomThread;因此,决定看看线程优先级。哦,是tpNormal.那调高这个优先级会不会有所改善?改成tohigher,tphighest…问题依然!看来与线程优先级无关。
再仔细研究这个线程的代码,
 TComThread = class(TThread)
 private
    FComPort: TCustomComPort;
    FStopEvent: THandle;
    FEvents: TComEvents;
 protected
    procedure DispatchComMsg;
    procedure DoEvents;
    procedure Execute; override;
    procedure SendEvents;
    procedure Stop;
 public
    constructor Create(AComPort: TCustomComPort);
    destructor Destroy; override;
 end;
 
从Execute入口,得知:线程内部是利用了系统事件对象完成对串口数据接收的触发。当收到事件后,把系统对应的事件保存在组合的属性中。
 
 
 
 TComEvent = (evRxChar, evTxEmpty, evRxFlag, evRing, evBreak, evCTS, evDSR,
    evError, evRLSD, evRx80Full);
 TComEvents = set of TComEvent;
 
    // if event occurs, dispatch it
    if (Signaled = WAIT_OBJECT_0 + 1)
      and GetOverlappedResult(FComPort.Handle, Overlapped, BytesTrans, False)
    then
    begin
//
      FEvents := IntToEvents(Mask);
      DispatchComMsg;
    end;
 
从蓝色的代码可知:当发生串口事件时,保存事件类型,然后就分发事件,找对应的事件来执行这个接收过程了。于是再看DispatchComMsg方法:
// dispatch events
procedure TComThread.DispatchComMsg;
begin
 case FComPort.SyncMethod of
    smThreadSync: Synchronize(DoEvents); // call events in main thread
    smWindowSync: SendEvents; // call events in thread that opened the port
    smNone:       DoEvents; // call events inside monitoring thread
 end;
end;
 
doEvents这个同步方法中,无法执行相应的事件。从注释来看,是指在主线程中执行这个操作,由于此时是封装在DLL中,没有主线程存在,因此,这个方法的执行也就是石沉大海了。这就是无法接收数据的原因了。
 
而第二个项就是:调用打开串口的线程对应事件。看来这个有戏。因此,将组件线程的同步方法类型改成smWindowSync
果然,接收数据正常。
 
对于第三个选项:在监视线程中执行事件,本人没有测试,因为时间关系。。。。。哎,有兴趣的DELPHIER可以试试。。。。。。。
原创粉丝点击