<form id="hz9zz"></form>
  • <form id="hz9zz"></form>

      <nobr id="hz9zz"></nobr>

      <form id="hz9zz"></form>

    1. 明輝手游網中心:是一個免費提供流行視頻軟件教程、在線學習分享的學習平臺!

      用DELPHI進行 Win32環境下串行通訊的程序設計

      [摘要]張秀德 姜新通 張冬生 摘要 由于在Delphi環境中沒有提供通訊控件,本文介紹了用Delphi4.0實現的Win32環境下基于線程的串行通訊程序設計,能適當降低數據丟失率以及提高系統可靠性,...
      張秀德 姜新通   張冬生  

      摘要 由于在Delphi環境中沒有提供通訊控件,本文介紹了用Delphi4.0實現的Win32環境下基于線程的串行通訊程序設計,能適當降低數據丟失率以及提高系統可靠性,并給出了一個通訊程序實例。

      關鍵詞 串行通訊 多線程 程序設計

        

      在自動化工業控制應用中,經常需要計算機與外圍設備進行數據通訊。而異步串行通訊是一種常用的通訊手段。在單任務操作系統中,不能同時處理兩件以上不同的任務。Win32是基于線程的多任務操作系統,使得應用程序能同時執行多個任務,即在一個進程中可同時運行多個線程。利用Win32的這個特點,在通訊過程中可以適當降低數據丟失率,提高系統可靠性。

      隨著Win95系統的逐步普及,程序員們更愿意在Win95下編程。而Delphi也越來越為廣大程序員所喜愛。然而,令人遺憾的是在Delphi環境中沒有象其它的一些編程語言一樣提供標準通訊控件。因此,利用Delphi進行通訊程序設計時,不但要掌握多線程編程技術,還要了解一些與通訊相關的API函數的使用。

      一 多線程基本概念

      首先介紹進程概念。一個進程通常定義為程序的一個實例。在Win32中,進程占據4GB地址空間。實際上,一個進程可以包含幾個線程,它們可以同時執行進程的地址空間中的代碼。為了運行所有這些線程,操作系統以輪轉方式為每個獨立線程分配一些CPU時間片。這給人一種假象,好像這些線程是在同時運行。創建一個Win32進程時,它的第一個線程稱為主線程,由系統自動生成。然后可由主線程生成其它的線程,這些線程又可生成更多的線程。

      線程描述了進程內的執行,是組成進程的基本單位。每次初始化一個進程時,系統創建一個主線程。通常對于許多應用程序,主線程是應用程序的唯一線程。但是,進程也可以創建額外的線程,目的在于盡可能充分合理的利用CPU時間。線程可以使用CreateThread()函數來創建。

      在有若干線程并行運行的環境里,同步各不同線程活動的能力是非常重要的,這樣可以避免對共享資源的訪問沖突。事件對象是同步線程的最基本形式,它用以向其它線程發信號以表示某一操作已經完成。例如,一個進程可能運行了兩個線程。第一個線程從文件讀數據到內存緩沖區中。每當數據已被讀入,第一個線程就發信號給第二個線程它可以處理數據了。當第二個線程完成了對數據的處理時,它可能需要再次給第一個線程發信號以讓第一個線程能夠從文件中讀入下一塊數據。事件可以使用CreateEvent()函數來創建。線程和事件在任何時候都處于兩種狀態之一:有信號和無信號。當線程被創建和正在運行時,它是無信號的。一旦線程終止,它就變成有信號的。線程可以通過使用SetEvent()和ResetEvent()函數來將事件置成有信號和無信號。

      除了以上介紹的概念和函數,在通訊程序中還要用到等待函數WaitForSingleObject()和重疊I/O操作。等待函數能使線程阻塞自身執行,而重疊I/O操作能使費時的操作在后臺中運行。

      二 通訊程序設計

      在Windows環境下,對于串行通訊的控制是通過中斷機制驅動的,由系統自行處理。Windows禁止應用程序直接和硬件打交道,程序員只能使用Windows提供的標準函數通過通訊驅動程序與硬件接口。首先,用CreateFile()函數打開通訊端口,然后通過SetupComm() 函數給通訊的輸入輸出隊列分配一定大小的內存緩沖區,接著通過BuildCommDCB()函數 和SetCommState()等函數對主要通訊參數進行設置。初始化完成后就可以利用ReadFile()函數和 WriteFile() 函數對通訊端口進行讀寫操作了。程序界面如圖所示。

      本文提供的實例程序使用簡單方便。利用一條串行數據線連接在兩臺計算機Com2之間就可以進行文本文件傳輸。對于Delphi的具體編程方法這里不再贅述。實例中有詳細注釋。

        

      unit comunate;

      interface

      uses

        Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,

        Dialogs, Buttons, StdCtrls, ComCtrls;

      const

        WM_COMMNOTIFY = WM_USER + 1;  // 通訊消息

      type

        TForm1 = class(TForm)

          Button1: TButton;

          Button2: TButton;

          Button3: TButton;

          Button4: TButton;

          OpenDialog1: TOpenDialog;

      Label1: TLabel;

      BitBtn1: TBitBtn;

          RichEdit1: TRichEdit;

          procedure Button1Click(Sender: TObject);

          procedure Button2Click(Sender: TObject);

          procedure Button3Click(Sender: TObject);

          procedure Button4Click(Sender: TObject);

        private

          { Private declarations }

          procedure WMCOMMNOTIFY(var Message :TMessage);message WM_COMMNOTIFY;

        public

          { Public declarations }

        end;

        

      var

        Form1: TForm1;

      implementation

      {$R *.DFM}

      var

        hNewCommFile,Post_Event: THandle;

        Read_os : Toverlapped;

        Receive :Boolean;

        ReceiveData : Dword;

        

      procedure AddToMemo(Str:PChar;Len:Dword);  // 接收的數據送入顯示區

      begin

        str[Len]:=#0;

        Form1.RichEdit1.Text:=Form1.RichEdit1.Text+StrPas(str);

      end;

        

      procedure CommWatch(Ptr:Pointer);stdcall;     // 通訊監視線程

      var

        dwEvtMask,dwTranser : Dword;

        Ok : Boolean;

        Os : Toverlapped;

      begin

        Receive :=True;

        FillChar(Os,SizeOf(Os),0);

        Os.hEvent :=CreateEvent(nil,True,False,nil); // 創建重疊讀事件對象

        if Os.hEvent=null then

          begin

            MessageBox(0,'Os.Event Create Error !','Notice',MB_OK);

            Exit;

          end;

        if (not SetCommMask(hNewCommFile,EV_RXCHAR)) then

          begin

            MessageBox(0,'SetCommMask Error !','Notice',MB_OK);

            Exit;

          end;

        while(Receive) do

          begin

            dwEvtMask:=0;

            // 等待通訊事件發生

            if not WaitCommEvent(hNewCommFile,dwEvtMask,@Os) then

              begin

                if ERROR_IO_PENDING=GetLastError then

                   GetOverLappedResult(hNewCommFile,Os,dwTranser,True)

              end;

            if ((dwEvtMask and EV_RXCHAR)=EV_RXCHAR) then

              begin

                // 等待允許傳遞WM_COMMNOTIFY通訊消息

                WaitForSingleObject(Post_event,INFINITE);

                // 處理WM_COMMNOTIFY消息時不再發送WM_COMMNOTIFY消息

                ResetEvent(Post_Event);

                // 傳遞WM_COMMNOTIFY通訊消息

                Ok:=PostMessage(Form1.Handle,WM_COMMNOTIFY,hNewCommFile,0);

                if (not Ok) then

                  begin

                    MessageBox(0,'PostMessage Error !','Notice',MB_OK);

                    Exit;

                  end;

              end;

          end;

        CloseHandle(Os.hEvent); // 關閉重疊讀事件對象

      end;

        

      procedure TForm1.WMCOMMNOTIFY(var Message :TMessage);  // 消息處理函數

      var

        CommState : ComStat;

        dwNumberOfBytesRead : Dword;

        ErrorFlag : Dword;

        InputBuffer : Array [0..1024] of Char;

      begin

        if not ClearCommError(hNewCommFile,ErrorFlag,@CommState) then

          begin

            MessageBox(0,'ClearCommError !','Notice',MB_OK);

            PurgeComm(hNewCommFile,Purge_Rxabort or Purge_Rxclear);

            Exit;

          end;

        if (CommState.cbInQue>0) then

          begin

            fillchar(InputBuffer,CommState.cbInQue,#0);

            // 接收通訊數據

            if (not ReadFile( hNewCommFile,InputBuffer,CommState.cbInQue,

                              dwNumberOfBytesRead,@Read_os )) then

              begin

                ErrorFlag := GetLastError();

                if (ErrorFlag <> 0) and (ErrorFlag <> ERROR_IO_PENDING) then

                  begin

                    MessageBox(0,'ReadFile Error!','Notice',MB_OK);

                    Receive :=False;

                    CloseHandle(Read_Os.hEvent);

                    CloseHandle(Post_Event);

                    CloseHandle(hNewCommFile);

                    Exit;

                  end

                else

                  begin           

                    WaitForSingleObject(hNewCommFile,INFINITE); // 等待操作完成

                    GetOverlappedResult(hNewCommFile,Read_os,

                                        dwNumberOfBytesRead,False);

                  end;

              end;

            if dwNumberOfBytesRead>0 then

              begin

                Read_Os.Offset :=Read_Os.Offset+dwNumberOfBytesRead;

                ReceiveData := Read_Os.Offset;

                // 處理接收的數據

                AddToMemo(InputBuffer,dwNumberOfBytesRead);

              end;

      end;

        // 允許發送下一個WM_COMMNOTIFY消息

        SetEvent(Post_Event);

      end;

        

      procedure TForm1.Button1Click(Sender: TObject); // 打開文件用于發送

      begin

        if OpenDialog1.Execute then

          begin

            Button3.Enabled :=False;

            Button4.Enabled :=False;

            RichEdit1.Lines.LoadFromFile(OpenDialog1.FileName);

            Form1.Caption := IntToStr(RichEdit1.GetTextLen);

          end;

          Button1.Enabled :=False;

      end;

        

      procedure TForm1.Button2Click(Sender: TObject); // 發送數據

      var

        dcb : TDCB;

        Error :Boolean;

        dwNumberOfBytesWritten,dwNumberOfBytesToWrite,

        ErrorFlag,dwWhereToStartWriting : DWORD;

        pDataToWrite : PChar;

        write_os: Toverlapped;

      begin

        Form1.Caption :='';

        // 打開通訊端口COM2

        hNewCommFile:=CreateFile( 'COM2',GENERIC_WRITE,0,

                                 nil, OPEN_EXISTING,FILE_FLAG_OVERLAPPED,0 );

        if hNewCommFile = INVALID_HANDLE_VALUE then

           MessageBox(0,'Error opening com port!','Notice',MB_OK);

        SetupComm(hNewCommFile,1024,1024); // 設置緩沖區大小及主要通訊參數

        GetCommState( hNewCommFile,dcb);

        dcb.BaudRate :=9600;

        dcb.ByteSize :=8;

        dcb.Parity :=NOPARITY;

        dcb.StopBits := ONESTOPBIT;

        Error := SetCommState( hNewCommFile, dcb );

        if ( not Error) then MessageBox(0,'SetCommState Error!','Notice',MB_OK);

        dwWhereToStartWriting := 0;

        dwNumberOfBytesWritten := 0;

        dwNumberOfBytesToWrite :=RichEdit1.GetTextLen;

        if (dwNumberOfBytesToWrite=0) then

          begin

            ShowMessage('Text Buffer is Empty!');

            Exit;

          end

        else

          begin

            pDataToWrite:=StrAlloc(dwNumberOfBytesToWrite+1);

            try

              RichEdit1.GetTextBuf(pDataToWrite,dwNumberOfBytesToWrite);

              Label1.Font.Color :=clRed;

              FillChar(Write_Os,SizeOf(write_os),0);

              // 為重疊寫創建事件對象

              Write_Os.hEvent := CreateEvent(nil,True,False,nil);

              SetCommMask(hNewCommFile,EV_TXEMPTY);

              Label1.Caption:='正在發送數據...!';

              repeat

                 Label1.Repaint;

                 // 發送通訊數據

                 if not WriteFile( hNewCommFile,pDataToWrite[dwWhereToStartWriting],

                                    dwNumberOfBytesToWrite,dwNumberOfBytesWritten,

                               @write_os ) then

                 begin

                     ErrorFlag :=GetLastError;

                     if ErrorFlag<>0 then

                       begin

                        if ErrorFlag=ERROR_IO_PENDING then

                           begin

                             WaitForSingleObject(Write_Os.hEvent,INFINITE);

                             GetOverlappedResult(hNewCommFile,Write_os,

                                                 dwNumberOfBytesWritten,False);

                           end

                         else

                           begin

                             MessageBox(0,'WriteFile Error!','Notice',MB_OK);

                             Receive :=False;

                             CloseHandle(Read_Os.hEvent);

                             CloseHandle(Post_Event);

                             CloseHandle(hNewCommFile);

                             Exit;

                           end;

                      end;

                   end;

               Dec( dwNumberOfBytesToWrite, dwNumberOfBytesWritten );

                 Inc( dwWhereToStartWriting, dwNumberOfBytesWritten );

              until (dwNumberOfBytesToWrite <= 0);  // Write the whole thing!

              Form1.Caption:=IntToStr(dwWhereToStartWriting);

            finally

              StrDispose(pDataToWrite);

            end;

            CloseHandle(hNewCommFile);

          end;

        Label1.Font.Color :=clBlack;

        Label1.Caption:='發送成功!';

        Button1.Enabled :=True;

        Button3.Enabled :=True;

        Button4.Enabled :=True;

      end;

        

      procedure TForm1.Button3Click(Sender: TObject); // 接收處理

      var

        Ok : Boolean;

        dcb : TDCB;

        com_thread: Thandle;

        ThreadID:DWORD;

      begin

        ReceiveData :=0;

        Button1.Enabled :=False;

        Button2.Enabled :=False;

        RichEdit1.Clear;

        // 打開COM2

        hNewCommFile:=CreateFile( 'COM2',GENERIC_READ,0,

                               nil, OPEN_EXISTING,FILE_FLAG_OVERLAPPED,0 );

        if hNewCommFile = INVALID_HANDLE_VALUE then

           begin

             MessageBox(0,'Error opening com port!','Notice',MB_OK);

             Exit;

           end;

        Ok:=SetCommMask(hNewCommFile,EV_RXCHAR);

        if ( not Ok) then

          begin

            MessageBox(0,'SetCommMask Error!','Notice',MB_OK);

            Exit;

          end;

        SetupComm(hNewCommFile,1024,1024);

        GetCommState( hNewCommFile, dcb );

        dcb.BaudRate :=9600;

        dcb.ByteSize :=8;

        dcb.Parity :=NOPARITY;

        dcb.StopBits := ONESTOPBIT;

        Ok := SetCommState( hNewCommFile, dcb );

        if ( not Ok) then MessageBox(0,'SetCommState Error!','Notice',MB_OK);

        FillChar(Read_Os,SizeOf(Read_Os),0);

        Read_Os.Offset := 0;

        Read_Os.OffsetHigh := 0;

        // Create Event for Overlapped Read

        Read_Os.hEvent :=CreateEvent(nil,true,False,nil);

        if Read_Os.hEvent=null then

          begin

            CloseHandle(hNewCommFile);

            MessageBox(0,'CreateEvent Error!','Notice',MB_OK);

            Exit;

          end;

        // Create Event for PostMessage

        Post_Event:=CreateEvent(nil,True,True,nil);

        if Post_Event=null then

          begin

            CloseHandle(hNewCommFile);

            CloseHandle(Read_Os.hEvent);

            MessageBox(0,'CreateEvent Error!','Notice',MB_OK);

            Exit;

      end;

        // 建立通信監視線程

        Com_Thread:=CreateThread(nil,0,@CommWatch,nil,0,ThreadID);

        if (Com_Thread=0) then

          MessageBox(Handle,'No CraeteThread!',nil,mb_OK);

        EscapeCommFunction(hNewCommFile,SETDTR);

        Label1.Font.Color :=clRed;

        Label1.Caption:='正在接收數據...!';

      end;

        

      procedure TForm1.Button4Click(Sender: TObject); // 停止通訊處理

      begin

        Label1.Font.Color :=clBlack;

        Label1.Caption:='infomation';

        Form1.Caption := IntToStr(ReceiveData);

        Receive :=False;

        CloseHandle(Read_Os.hEvent);

        CloseHandle(Post_Event);

        CloseHandle(hNewCommFile);

        Button1.Enabled :=True;

        Button2.Enabled :=True;

      end;

        

      end.

        

      參考文獻

      1.Windows95 Windows NT3.5高級編程技術 Jeffrey Richter著

      2.基于Windows 95&NT的串行通信編程 李柯 <<微電腦世界>> 1997。5

      3.Windows 95中的串行通信 王齊 <<微電腦世界>> 1997。3



      日韩精品一区二区三区高清