WIN32 API串列埠通訊程式設計
WIN32 API串列埠通訊例項教程
第一節實現串列埠通訊的函式及串列埠程式設計簡介
API函式不僅提供了開啟和讀寫通訊埠的操作方法,還提供了名目繁多的函式以支援對序列通訊的各種操作。常用函式及作用下:
函式名 作用
CreateFile 開啟串列埠
GetCommState 檢測串列埠設定
SetCommState 設定串列埠
BuilderCommDCB 用字串中的值來填充裝置控制塊
GetCommTimeouts 檢測通訊超時設定
SetCommTimeouts 設定通訊超時引數
SetCommMask 設定被監控事件
WaitCommEvent 等待被監控事件發生
WaitForMultipleObjects 等待多個被監測物件的結果
WriteFile 傳送資料
ReadFile 接收資料
GetOverlappedResult 返回最後重疊(非同步)操作結果
PurgeComm 清空串列埠緩衝區,退出所有相關操作
ClearCommError 更新串列埠狀態結構體,並清除所有串列埠硬體錯誤
CloseHandle 關閉序列口
用Windows API 編寫串列埠程式本身是有巨大優點的,因為控制能力會更強,效率也會更
高。
API編寫串列埠,過程一般是這樣的:
1、 建立串列埠控制代碼,用CreateFile;
2、 對串列埠的引數進行設定,其中比較重要的是波特率(BaudRate),資料寬度(BytesBits),奇偶校驗(Parity),停止位(StopBits),當然,重要的還有埠號(Port);
3、 然後對串列埠進行相應的讀寫操作,這時候用到ReadFile和WriteFile函式;
4、 讀寫結束後,要關閉串列埠控制代碼,用CloseFile。
下面依次講述各個步驟的過程。
第二節建立串列埠控制代碼開啟串列埠
從字面上去理解,大家也可以發現CreateFile實際上表明Windows是把串列埠當作一個檔案來處理的,所以它也有檔案那樣的緩衝區、控制代碼、讀寫錯誤等,不同的是,這個檔名字只有固定的幾個(一般為四個),而且始終存在(EXSITING),而且在呼叫CreateFile的時候請注意它的引數。CreateFile函式原型如下:
HANDLE CreateFile(LPCTSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode, //0
LPSECURITY_ATTRIBUTESlpSecurityAttributes, //NULL
DWORD dwCreationDisposition, //OPEN_EXISTING
DWORDdwFlagsAndAttributes,
HANDLE hTemplateFile ); //NULL
lpFileName:指向一個以NULL結束的字串,該串指定了要建立、開啟或截斷的檔案、管道、通訊源、磁碟裝置或控制檯的名字。當用CreateFile開啟串列埠時,這個引數可用“COM1”指定串列埠1,用“COM2”指定串列埠2,依此類推。
dwDesireAccess: 指定對檔案訪問的型別,該引數可以為GENERIC_READ(指定對該檔案的讀訪問權)或GENERIC_WRITE(指定該檔案的寫訪問權)兩個值之一或同時為為這兩個值。用ENERIC_READ|GENERIC_WRITE則指定可對串列埠進行讀寫;
dwShareMode:指定此檔案可以怎樣被共享。因為序列口不支援任何共享模式,所以dwShareMode必須設為0;
lpSecurityAttributes定義安全屬性,一般不用,可設為NULL。Win 9x下該引數被忽略;
dwCreationDistribution定義檔案建立方式, 對串列埠必須設為OPEN_EXISTING,表示開啟已經存在的檔案;
dwFlagsAndAttributes為該檔案指定定義檔案屬性和標誌,這個程式中設為FILE_FLAG_OVERLAPPED,表示非同步通訊方式;
hTemplateFile 指向一個模板檔案的控制代碼,串列埠無模板可言,設為NULL。在 Windows 9x下該引數必須為NULL。
串列埠被成功開啟時,返回其控制代碼,否則返回INVALID_HANDLE_value(0XFFFFFFFF)。
上面說到了非同步,那什麼是非同步呢?非同步是相對同步這個概念而言的。非同步,就是說,
在進行串列埠讀寫操作時,不用等到I/O操作完成後函式才返回,也就是說,非同步可以更快得
響應使用者操作;同步,相反,響應的I/O操作必須完成後函式才返回,否則阻塞執行緒。對於
一些很簡單的通訊程式來說,可以選擇同步,這樣可以省去很多錯誤檢查,但是對於複雜一點的應用程式,非同步是最佳選擇。
例項1:
/****************** example1.cpp *****************************************/
/* lishaoan 2009-06-29 *****************************************************/
/* [email protected] *****************************************************/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
bool openport(char *portname)//開啟串列埠
{
HANDLE hComm;
hComm = CreateFile(portname, //串列埠號
GENERIC_READ | GENERIC_WRITE, //允許讀寫
0, //通訊裝置必須以獨佔方式開啟
0, //無安全屬性
OPEN_EXISTING, //通訊裝置已存在
FILE_FLAG_OVERLAPPED,//非同步I/O
0); //通訊裝置不能用模板開啟
if (hComm == INVALID_HANDLE_VALUE)
{
CloseHandle(hComm);
return FALSE;
}
else
return true;
}
void main()
{
bool open;
open=openport("com2");
if(open)
printf("open comport success");
system("pause") ;
}
/************************** programend***************************************/
例項2:
/****************** example2.cpp ******************************************/
/* lishaoan 2009-06-29 *****************************************************/
/* [email protected] ******************************************************/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
bool openport(char *portname)//開啟串列埠
{
HANDLE hComm;
hComm = CreateFile(portname, //串列埠號
GENERIC_READ | GENERIC_WRITE, //允許讀寫
0, //通訊裝置必須以獨佔方式開啟
0, //無安全屬性
OPEN_EXISTING, //通訊裝置已存在
0, //同步I/O
0); //通訊裝置不能用模板開啟
if (hComm == INVALID_HANDLE_VALUE)
{
CloseHandle(hComm);
return FALSE;
}
else
return true;
}
void main()
{
bool open;
open=openport("com2");
if(open)
printf("open comport success");
system("pause") ;
}
/************************** programend***************************************/
第三節 設定串列埠
在開啟通訊裝置控制代碼後,常常需要對序列口進行一些初始化工作。這需要通過一個DCB結構來進行。DCB結構包含了諸如波特率、每個字元的資料位數、奇偶校驗和停止位數等資訊。在查詢或配置串列埠的屬性時,都要用DCB結構來作為緩衝區。
第一次開啟串列埠時,串列埠設定為系統預設值,函式GetCommState和SetCommState可用於檢索和設定埠設定的DCB(裝置控制塊)結構,該結構中BaudRate、ByteSize、StopBits和Parity欄位含有串列埠波特率、資料位數、停止位和奇偶校驗控制等資訊。
程式中用DCB進行串列埠設定時,應先呼叫API函式GetCommState,來獲得串列埠的設定資訊:
GetCommState()
用途:取得串列埠當前狀態
原型:BOOL GetCommState(HANDLE hFile, LPDCB lpDCB);
引數說明:
-hFile:串列埠控制代碼
-lpDCB:裝置控制塊(DeviceControl Block)結構地址。此結構中含有和裝置相關的引數。此處是與串列埠相關的引數。由於引數非常多,當需要設定串列埠引數時,通常是先取得串列埠的引數結構,修改部分引數後再將引數結構寫入。
然後在需要設定的地方對dcb進行設定。串列埠有很多的屬性,上面也已經介紹了一些最重要的引數。這裡介紹資料結構 DCB:
typedef struct _DCB { //dcb
DWORD DCBlength; //DCB結構體大小
DWORD BaudRate; //波特率
DWORD fBinary: 1; //是否是二進位制,一般設定為TRUE
DWORD fParity: 1; //是否進行奇偶校驗
DWORD fOutxCtsFlow:1; //CTS線上的硬體握手
DWORD fOutxDsrFlow:1; //DSR線上的硬體握手
DWORD fDtrControl:2; //DTR控制
DWORD fDsrSensitivity:1; // DSR sensitivity
DWORD fTXContinueOnXoff:1;// XOFF continues Tx
DWORD fOutX: 1; //是否使用XON/XOFF協議
DWORD fInX: 1; //是否使用XON/XOFF協議
DWORD fErrorChar: 1; //傳送錯誤協議
DWORD fNull: 1; // enable null stripping
DWORD fRtsControl:2; // RTS flow control
DWORD fAbortOnError:1; // abort reads/writes on error
DWORD fDummy2:17; // reserved
WORD wReserved; // not currently used
WORD XonLim; //設定在XON字元傳送之前inbuf中允許的最少位元組數
WORD XoffLim; //在傳送XOFF字元之前outbuf中允許的最多位元組數
BYTE ByteSize; //資料寬度,一般為8,有時候為7
BYTE Parity; //奇偶校驗
BYTE StopBits; //停止位數
char XonChar; //設定表示XON字元的字元,一般是採用0x11這個數值
char XoffChar; //設定表示XOFF字元的字元,一般是採用0x13這個數值
char ErrorChar; // error replacement character
char EofChar; // end of input character
char EvtChar; // received event character
WORDwReserved1; // reserved; do not use
} DCB;
我們真正在串列埠程式設計中用到的資料成員沒有幾個,在此僅介紹少數的幾個常用的引數:
DWORD BaudRate:串列埠波特率
DWORD fParity:為1的話啟用奇偶校驗檢查
DWORD Parity:校驗方式,值0~4分別對應無校驗、奇校驗、偶校驗、校驗置位、校驗清零
DWORD ByteSize:一個位元組的資料位個數,範圍是5~8
DWORD StopBits:停止位個數,0~2分別對應1位、1.5位、2位停止位
然後再末尾呼叫SetCommState就可以了,還是比較方便的。這樣可不必構造一個完整的DCB結構。
SetCommState()
用途:設定串列埠狀態,包括常用的更改串列埠號、波特率、奇偶校驗方式、資料位數等
原型:BOOL SetCommState(HANDLE hFile, LPDCB lpDCB);
引數說明:
-hFile:串列埠控制代碼
-lpDCB:裝置控制塊(Device Control Block)結構地址。要更改的串列埠引數包含在此結構中。
然後呼叫SetCommMask,用來指定程式接收特定的串列埠事件,呼叫SetupComm函式,設定串列埠緩衝區大小:
SetCommMask()說明:
用途:設定串列埠通訊事件。
原型:BOOL SetCommMask(HANDLE hFile,
DWORD dwEvtMask
);
引數說明:
-hFile:串列埠控制代碼
-dwEvtMask:準備監視的串列埠事件掩碼
該引數有如下資訊掩碼位值:
EV_BREAK:收到BREAK訊號
EV_CTS:CTS(clear to send)線路發生變化
EV_DSR:DST(Data Set Ready)線路發生變化
EV_ERR:線路狀態錯誤,包括了CE_FRAME\CE_OVERRUN\CE_RXPARITY3鍾錯誤。
EV_RING:檢測到振鈴訊號。
EV_RLSD:CD(Carrier Detect)線路訊號發生變化。
EV_RXCHAR:輸入緩衝區中已收到資料。
EV_RXFLAG:使用SetCommState()函式設定的DCB結構中的等待字元已被傳入輸入緩衝區中。
EV_TXEMPTY:輸出緩衝區中的資料已被完全送出。
還有,串列埠因為是I/O操作,可能會產生錯誤,這時候需要用SetCommTimeouts()設定超時限制,以避免阻塞現象。設定超時設定需要一個結構體COMMTIMEOUTS。
SetCommTimeouts()
BOOL SetCommTimeouts( hCommDev, lpctmo );
Lpctmo指向包含新的超時引數的COMMTIMEOUTS結構。
COMMTIMEOUTS結構定義如下:
typedef struct _ COMMTIMEOUTS{
DWORD ReadIntervalTimeout;
DWORD ReadTotalTimeoutMultiplier;
DWORD ReadTotalTimeoutconstant;
DWORD WriteTotalTimeoutMultiplier;
DWORD WriteTotalTimeoutconstant;
}COMMTIMEOUTS, LPCOMMTIMEOUTS;
ReadIntervalTimeout: 以毫秒為單位指定通訊線上兩個字元到達之間的最大時間。在
ReadFile操作其間,收到第一個字元時開始計算時間。若任意兩個字元到達之間的間隔超過
這個最大值,ReadFile操作完成,返回緩衝資料。0值表示不用間隔限時。若該成員為
MAXDWORD,且ReadTotalTimeoutconstant和ReadTotalTimeoutMultiplier成員為零,則指
出讀操作要立即返回已接收到的字元,即使未收到字元,讀操作也要返回。
ReadTotalTimeoutMultiplier:以毫秒為單位指定一個乘數,該乘數用來計算讀操作的總限時時間。每個讀操作的總限時時間等於讀操作所需的位元組數與該值的乘積。
ReadTotalTimeoutConstant:以毫秒為單位指定一個常數,用於計算讀操作的總限時時間。每個操作的總限時時間等於ReadTotalTimeoutMultiplier成員乘以讀操作所需位元組數再加上該值的和。ReadTotalTimeoutMultiplier和ReadTotalTimeoutConstant成員的值為0表示讀操作不使用限時時間。
WriteTotalTimeoutMultiplier和WriteTotalTimeoutconstant的意義和作用分別與
ReadTotalTimeoutMultiplier和ReadTotalTimeoutConstant相似,不再重複。
舉例:
COMMTIMEOUTS timeouts;
timeouts.ReadIntervalTimeout=MAXDWORD;
timeouts.ReadTotalTimeoutConstant=0;
timeouts.ReadTotalTimeoutMultiplier=0;
timeouts.WriteTotalTimeoutConstant=50;
timeouts.WriteTotalTimeoutMultiplier=2000;
SetCommTimeouts(m_hCom, &timeouts);
這裡將ReadIntervalTimeout設定為最大位元組數,.ReadTotalTimeoutConstant和
ReadTotalTimeoutMultiplier都設定為0,表示不設定讀操作超時,也就是說讀操作瞬間完
成,不進行等待。
呼叫PurgeComm函式可以終止正在進行的讀寫操作,該函式還會清除輸入或輸出緩衝區中的內容。
PurgeComm()說明:
功能:終止目前正在進行的讀或寫的動作
函式原型:BOOL PurgeComm(
HANDLE hFile, // handle of communications resource
DWORD dwFlags // action to perform
);
引數說明:
HANDLE hFile,//串列埠名稱字串
dwFlags 共有四種 flags:
PURGE_TXABORT: 終止目前正在進行的(背景)寫入動作
PURGE_RXABORT: 終正目前正在進行的(背景)讀取動作
PURGE_TXCLEAR: flush 寫入的 buffer
PURGE_TXCLEAR: flush 讀取的 buffer
例項3:
/****************** example3.cpp *****************************************/
/* lishaoan 2009-06-29 *****************************************************/
/* [email protected] *****************************************************/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
bool openport(char *portname)//開啟串列埠
{
HANDLE hComm;
hComm = CreateFile(portname, //串列埠號
GENERIC_READ | GENERIC_WRITE, //允許讀寫
0, //通訊裝置必須以獨佔方式開啟
0, //無安全屬性
OPEN_EXISTING,//通訊裝置已存在
0, //同步I/O
0); //通訊裝置不能用模板開啟
if (hComm == INVALID_HANDLE_VALUE)
{
CloseHandle(hComm);
return FALSE;
}
else
return true;
}
boolsetupdcb(int rate_arg)//設定DCB,先獲取DCB配置,再設定,最後看是否設定//好
{
DCB dcb;
int rate= rate_arg;
memset(&dcb,0,sizeof(dcb));//在一段記憶體塊中填充某個給定的值,是對較大的結構//體或陣列進行清零操作的一種最快方法
if(!GetCommState(hComm,&dcb))//獲取當前DCB配置
return FALSE;
// set DCB to configure the serial port
dcb.DCBlength =sizeof(dcb);
/* ---------- Serial Port Config ------- */
dcb.BaudRate = rate;
dcb.Parity = NOPARITY;
dcb.fParity = 0;
dcb.StopBits = ONESTOPBIT;
dcb.ByteSize = 8;
dcb.fOutxCtsFlow = 0;
dcb.fOutxDsrFlow = 0;
dcb.fDtrControl = DTR_CONTROL_DISABLE;
dcb.fDsrSensitivity =0;
dcb.fRtsControl = RTS_CONTROL_DISABLE;
dcb.fOutX = 0;
dcb.fInX = 0;
/* ----------------- misc parameters ----- */
dcb.fErrorChar = 0;
dcb.fBinary = 1;
dcb.fNull = 0;
dcb.fAbortOnError = 0;
dcb.wReserved = 0;
dcb.XonLim = 2;
dcb.XoffLim = 4;
dcb.XonChar = 0x13;
dcb.XoffChar = 0x19;
dcb.EvtChar = 0;
// set DCB
if(!SetCommState(hComm,&dcb))
return false;
else
return true;
}
boolsetuptimeout(DWORD ReadInterval,DWORD ReadTotalMultiplier,DWORD ReadTotalconstant,DWORDWriteTotalMultiplier,DWORD WriteTotalconstant)
{
COMMTIMEOUTS timeouts;
timeouts.ReadIntervalTimeout=ReadInterval;
timeouts.ReadTotalTimeoutConstant=ReadTotalconstant;
timeouts.ReadTotalTimeoutMultiplier=ReadTotalMultiplier;
timeouts.WriteTotalTimeoutConstant=WriteTotalconstant;
timeouts.WriteTotalTimeoutMultiplier=WriteTotalMultiplier;
if(!SetCommTimeouts(hComm, &timeouts))
return false;
else
return true;
}
void main()
{
bool open;
open=openport("com2");
if(open)
printf("open comport success");
if(setupdcb(9600))
printf("setupDCB success\n");
if(setuptimeout(0,0,0,0,0))
printf("setuptimeout success\n");
SetCommMask(hComm, EV_RXCHAR); //當有字元在inbuf中時產生這個事件
//清除串列埠的所有操作
PurgeComm(hComm,PURGE_RXCLEAR|PURGE_TXCLEAR|PURGE_RXABORT|PURGE_TXABORT);
system("pause") ;
}
/************************** programend***************************************/
第四節 讀寫串列埠資料及關閉串列埠
Win32API函式ReadFile和WriteFile支援對序列口的讀寫操作。在呼叫ReadFile和WriteFile之前,執行緒應該呼叫ClearCommError函式清除錯誤標誌。
該函式負責報告指定的錯誤和裝置的當前狀態。
ClearCommError()
用途:清除串列埠錯誤或者讀取串列埠現在的狀態
原型:BOOL ClearCommError(HANDLE hFile,
LPDWORD lpErrors,
LPCOMATAT lpStat
);
引數說明:
-hFile:串列埠控制代碼
-lpErrors:返回錯誤數值,錯誤常數如下:
1-CE_BREAK:檢測到中斷訊號。意思是說檢測到某個位元組資料缺少合法的停止位。
2-CE_FRAME:硬體檢測到幀錯誤。
3-CE_IOE:通訊裝置發生輸入/輸出錯誤。
4-CE_MODE:設定模式錯誤,或是hFile值錯誤。
5-CE_OVERRUN:溢位錯誤,緩衝區容量不足,資料將丟失。
6-CE_RXOVER:溢位錯誤。
7-CE_RXPARITY:硬體檢查到校驗位錯誤。
8-CE_TXFULL:傳送緩衝區已滿。
-lpStat:指向通訊埠狀態的結構變數,原型如下:
typedef struct _COMSTAT{
...
...
DWORD cbInQue; //輸入緩衝區中的位元組數
DWORD cbOutQue;//輸出緩衝區中的位元組數
}COMSTAT,*LPCOMSTAT;
該結構中對我們很重要的只有上面兩個引數,其他的我們可以不用管。
假如當前串列埠中有5個位元組資料的話,那麼執行完ClearCommError()函式後,ComStat結構中的ComStat.cbInQue將被填充為5,此值在ReadFile函式中可被直接利用。
例如:
COMSTAT ComStat;
DWORD dwError=0;
ClearCommError(hComm,&dwError,&ComStat);
上式執行完後,ComStat.cbInQue就是串列埠中當前含有的資料位元組個數,我們利用此
數值就可以用ReadFile()函式去讀串列埠中的資料了。
函式ReadFile和WriteFile的行為還受是否使用非同步I/O(Overlapped)及通訊超時設定的影響。序列口讀寫的同步、非同步方式是在開啟埠的同時給dwGlagsAndAttributes引數傳入適當的值而設定的。
WriteFile()
用途:向串列埠寫資料
原型:BOOL WriteFile(HANDLE hFile,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped);
引數說明:
-hFile:串列埠控制代碼
-lpBuffer:待寫入資料的首地址
-nNumberOfBytesToWrite:待寫入資料的位元組數長度
-lpNumberOfBytesWritten:函式返回的實際寫入串列埠的資料個數的地址,利用此變數可判斷實際寫入的位元組數和準備寫入的位元組數是否相同。
-lpOverlapped:重疊I/O結構的指標 //不懂
ReadFile()
用途:讀串列埠資料
原型:BOOL ReadFile(HANDLE hFile,
LPVOID lpBuffer,
DWORD nNumberOfBytesToRead,
lpNumberOfBytesRead,
lpOverlapped);
引數說明:
-hFile:串列埠控制代碼
-lpBuffer:儲存被讀出資料的首地址
-nNumberOfBytesToRead:準備讀出的位元組個數
-NumberOfBytesRead:實際讀出的位元組個數
-lpOverlapped:非同步I/O結構
在同步方式下,呼叫ReadFile或WriteFile後,當實際讀寫操作完成或發生超時時才返回呼叫程式。而非同步方式函式在啟動接收或傳送過程後立即返回,程式繼續向下執行,程式在呼叫ReadFile和WriteFile時必須提供一個Overlapped資料結構指標,該結構中包含一個手動事件同步物件,其後的程式必須藉助於該事件同步物件,完成資料的接收和傳送過程。
通訊埠的超時設定對讀寫的處理方式也會產生影響,如果呼叫讀寫函式時發生埠超時,
則讀寫函式立即返回並返回已傳輸的資料位元組數。
ReadFile函式只要在序列口輸入緩衝區中讀入指定數量的字元,就算完成操作。
而WriteFile函式不但要把指定數量的字元拷入到輸出緩衝中,而且要等這些字元從序列口送出去後才算完成操作。
如果不再使用某一埠,須將該埠關閉,以便其他程式可以使用該埠。如果不顯式關閉某埠,當程式退出時開啟的埠也將被自動關閉。但為了安全起見,最好是顯式的關閉它。
關閉串列埠的語句為CloseHandle()。
CloseHandle()
用途:關閉串列埠
原型:BOOL CloseHandle(HANDLE hObjedt)
說明:
-hObjedt:串列埠控制代碼
操作說明:成功關閉串列埠時返回true,否則返回false
當ReadFile和WriteFile返回FALSE時,不一定就是操作失敗,執行緒應該呼叫GetLastError函式分析返回的結果。例如,在重疊操作時如果操作還未完成函式就返回,那麼函式就返回FALSE,而且GetLastError函式返回ERROR_IO_PENDING。如果GetLastError函式返回ERROR_IO_PENDING,則說明重疊操作還未完成,執行緒可以等待操作完成。
有兩種等待辦法:一種辦法是用象WaitForSingleObject這樣的等待函式來等待OVERLAPPED結構的hEvent成員,可以規定等待的時間,在等待函式返回後,呼叫GetOverlappedResult。
另一種辦法是呼叫GetOverlappedResult函式等待,如果指定該函式的bWait引數為TRUE,那麼該函式將等待OVERLAPPED結構的hEvent 事件。GetOverlappedResult可以返回一個OVERLAPPED結構來報告包括實際傳輸位元組在內的重疊操作結果。
如果規定了讀/寫操作的超時,那麼當超過規定時間後,hEvent成員會變成有訊號的。因此,在超時發生後,WaitForSingleObject和GetOverlappedResult都會結束等待。WaitForSingleObject的dwMilliseconds引數會規定一個等待超時,該函式實際等待的時間是兩個超時的最小值。注意GetOverlappedResult不能設定等待的時限,因此如果hEvent成員無訊號,則該函式將一直等待下去
GetOverlappedResult函式呼叫方法如下:
BOOL GetOverlappedResult(
HANDLE hFile, //用CreateFile獲得的檔案控制代碼
LPOVERLAPPED lpOverlapped, //指向一個在啟動重疊操作時指定的OVERLAPPED結構(即
//讀寫函式中指定的OverLapped結構)
LPDWORDlpNumberOfBytesTransferred,//實際傳輸的位元組數
BOOL bWait, //是否等待懸掛的重疊操作完成,若為TRUE,則此函式直到操作完成後才//返回。
);
OVERLAPPED結構定義如下:
typedef struct _OVERLAPPED {
DWORD Internal;
DWORD InternalHigh;
DWORD Offset;
DWORD OffsetHigh;
HANDLE hEvent;
} OVERLAPPED;
如果採用非同步方式,則在呼叫ReadFile或WriteFile函式時必需指定一個Overlapped結構,呼叫後程序可繼續執行其它操作,在合適的地方再呼叫函式GetOverlappedResult判斷非同步重疊操作是否完成(判斷OVERLAPPED結構中的hEvent是否被置位)。
WaitCommEvent()
用途:用來判斷用SetCommMask()函式設定的串列埠通訊事件是否已發生。
原型:BOOL WaitCommEvent(HANDLE hFile,
LPDWORD lpEvtMask,
LPOVERLAPPED lpOverlapped s
);
引數說明:
-hFile:串列埠控制代碼
-lpEvtMask:函式執行完後如果檢測到串列埠通訊事件的話就將其寫入該引數中。
-lpOverlapped:非同步結構,用來儲存非同步操作結果。
當由SetCommMask函式所指定的事件產生時這個函式將返回TRUE。
注:在用api函式撰寫串列埠通訊函式時大體上有兩種方法,一種是查尋法,另外一種是事件通知法。
這兩種方法的區別在於收串列埠資料時,前一種方法是主動的週期性的查詢串列埠中當前有沒有資料;後一種方法是事先設定好需要監視的串列埠通訊事件,然後依靠單獨開設的輔助執行緒進行監視該事件是否已發生,如果沒有發生的話該執行緒就一直不停的等待直到該事件發生後,將該串列埠事件以訊息的方式通知主窗體,然後主窗體收到該訊息後依據不同的事件性質進行處理。比如說當主窗體收到監視執行緒發來的RX_CHAR(串列埠中有資料)的訊息後,就可以用ReadFile() 函式去讀串列埠。
例項4:
/****************** example4.cpp ******************************************/
/* lishaoan 2009-07-10 *****************************************************/
/* [email protected] ******************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
HANDLE hComm;
OVERLAPPED m_ov;
COMSTAT comstat;
DWORD m_dwCommEvents;
bool openport(char *portname)//開啟一個串列埠
{
hComm = CreateFile(portname,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED, //如果在呼叫CreateFile建立控制代碼時指//定了FILE_FLAG_OVERLAPPED標誌,那麼//呼叫ReadFile和WriteFile對該控制代碼進//行的操作就應該是重疊的;如果未指定//重疊標誌,則讀寫操作應該是同步的
//在同步執行時,函式直到操作完成後才返回。這意味著同步執行時執行緒會被阻塞,從
//而導致效率下降。在重疊執行時,即使操作還未完成,這兩個函式也會立即返回,費
//時的I/O操作在後臺進行
0);
if (hComm == INVALID_HANDLE_VALUE)
return FALSE;
else
return true;
}
bool setupdcb(int rate_arg)
{
DCB dcb;
int rate= rate_arg;
memset(&dcb,0,sizeof(dcb)); //在一段記憶體塊中填充某個給定的值,是對較大的結構//體或陣列進行清零操作的一種最快方法
if(!GetCommState(hComm,&dcb))//獲取當前DCB配置
{
return FALSE;
}
/*-------------------------------------------------------------------- */
// set DCB to configure theserial port
dcb.DCBlength = sizeof(dcb);
/* ---------- Serial PortConfig ------- */
dcb.BaudRate = rate;
dcb.Parity = NOPARITY;
dcb.fParity = 0;
dcb.StopBits = ONESTOPBIT;
dcb.ByteSize = 8;
dcb.fOutxCtsFlow = 0;
dcb.fOutxDsrFlow = 0;
dcb.fDtrControl = DTR_CONTROL_DISABLE;
dcb.fDsrSensitivity =0;
dcb.fRtsControl = RTS_CONTROL_DISABLE;
dcb.fOutX = 0;
dcb.fInX = 0;
/* -----------------misc parameters ----- */
dcb.fErrorChar = 0;
dcb.fBinary = 1;
dcb.fNull = 0;
dcb.fAbortOnError = 0;
dcb.wReserved = 0;
dcb.XonLim = 2;
dcb.XoffLim = 4;
dcb.XonChar = 0x13;
dcb.XoffChar =0x19;
dcb.EvtChar = 0;
/*-------------------------------------------------------------------- */
// set DCB
if(!SetCommState(hComm,&dcb))
{
return false;
}
else
return true;
}
bool setuptimeout(DWORDReadInterval,DWORD ReadTotalMultiplier,DWORD ReadTotalconstant,DWORDWriteTotalMultiplier,DWORD WriteTotalconstant)
//在用readfile和writefile讀寫序列口時,需要考慮超時問題, 讀寫串列埠的超時有兩//種:間隔超時和總超時,寫操作只支援總超時,而讀操作兩種超時均支援, 如果所有//寫超時引數均為0,那麼就不使用寫超時。
{
COMMTIMEOUTS timeouts;
timeouts.ReadIntervalTimeout=ReadInterval; //讀間隔超時
timeouts.ReadTotalTimeoutConstant=ReadTotalconstant; //讀時間係數
timeouts.ReadTotalTimeoutMultiplier=ReadTotalMultiplier; //讀時間常量
timeouts.WriteTotalTimeoutConstant=WriteTotalconstant;// 寫時間係數
timeouts.WriteTotalTimeoutMultiplier=WriteTotalMultiplier; //寫時間常//量, 總超時的計算公式是:總超時=時間係數×要求讀/寫的字元數+時間常量
if(!SetCommTimeouts(hComm, &timeouts))
{
return false;
}
else
return true;
}
ReceiveChar( )
{
BOOL bRead = TRUE;
BOOL bResult = TRUE;
DWORD dwError = 0;
DWORD BytesRead = 0;
char RXBuff;
for (;;)
{
bResult= ClearCommError(hComm, &dwError, &comstat);
// 在使用ReadFile 函式進行讀操作前,應先使用ClearCommError函式清除錯誤
if (comstat.cbInQue ==0)// COMSTAT結構返回串列埠狀態資訊
//本文只用到了cbInQue成員變數,該成員變數的值代表輸入緩衝區的位元組數
continue;
if (bRead)
{
bResult = ReadFile(hComm, // Handle to COMM port串列埠的控制代碼
&RXBuff, //RX Buffer Pointer
// 讀入的資料儲存的地址,即讀入的資料將存
//儲在以該指標的值為首地址的一片記憶體區
1, //Read one byte要讀入的資料的位元組數,
&BytesRead, // Stores number of bytes read,指向一個DWORD
//數值,該數值返回讀操作實際讀入的位元組數
&m_ov); //pointer to the m_ov structure
// 重疊操作時,該引數指向一個OVERLAPPED結構,同步操作時,該引數為NULL
printf("%c",RXBuff);
if (!bResult)//當ReadFile和WriteFile返回FALSE時,不一定就是操作失//敗,執行緒應該呼叫GetLastError函式分析返回的結果
{
switch (dwError =GetLastError())
{
case ERROR_IO_PENDING:
{
bRead = FALSE;
break;
}
default:
{
break;
}
}
}
else
{
bRead = TRUE;
}
} //close if (bRead)
if (!bRead)
{
bRead = TRUE;
bResult =GetOverlappedResult(hComm, // Handle toCOMM port
&m_ov, // Overlappedstructure
&BytesRead, // Stores number of bytes read
TRUE); // Wait flag
}
}
}
WriteChar(BYTE* m_szWriteBuffer,DWORD m_nToSend)
{
BOOL bWrite = TRUE;
BOOL bResult = TRUE;
DWORD BytesSent = 0;
HANDLE m_hWriteEvent;
ResetEvent(m_hWriteEvent);
if (bWrite)
{
m_ov.Offset = 0;
m_ov.OffsetHigh = 0;
// Clear buffer
bResult = WriteFile(hComm, // Handle toCOMM Port, 串列埠的控制代碼
m_szWriteBuffer,// Pointer to message buffer in calling finction
// 即以該指標的值為首地址的nNumberOfBytesToWrite
// 個位元組的資料將要寫入串列埠的傳送資料緩衝區
m_nToSend, // Length of message to send, 要寫入的資料的位元組數
&BytesSent, //Where to store the number of bytes sent
// 指向指向一個DWORD數值,該數值返回實際寫入的位元組數
&m_ov ); // Overlapped structure
// 重疊操作時,該引數指向一個OVERLAPPED結構,
// 同步操作時,該引數為NULL
if(!bResult) // 當ReadFile和WriteFile返回FALSE時,不一定就是操作失
//敗,執行緒應該呼叫GetLastError函式分析返回的結果
{
DWORD dwError = GetLastError();
switch (dwError)
{
case ERROR_IO_PENDING: //GetLastError函式返回
//ERROR_IO_PENDING。這說明重疊操作還未完成
{
// continue to GetOverlappedResults()
BytesSent = 0;
bWrite = FALSE;
break;
}
default:
{
// all other error codes
break;
}
}
}
} // end if(bWrite)
if (!bWrite)
{
bWrite = TRUE;
bResult = GetOverlappedResult(hComm, //Handle to COMM port
&m_ov, // Overlapped structure
&BytesSent, // Stores number of bytessent
TRUE); //Wait flag
// deal with the error code
if (!bResult)
{
printf("GetOverlappedResults() in WriteFile()");
}
} // end if (!bWrite)
// Verify that the data size send equals what we tried to send
if (BytesSent != m_nToSend)
{
printf("WARNING: WriteFile() error.. Bytes Sent: %d; MessageLength: %d\n", BytesSent, strlen((char*)m_szWriteBuffer));
}
return true;
}
void main()
{
if(openport("com2"))
printf("open comport success\n");
if(setupdcb(9600))
printf("setupDCB success\n");
if(setuptimeout(0,0,0,0,0)) //如果所有寫超時引數均為0,那麼就不使用寫超時
printf("setuptimeout success\n");
PurgeComm(hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT |PURGE_TXABORT); // 在讀寫串列埠之前,還要用PurgeComm()函式清空緩衝區
//PURGE_TXABORT 中斷所有寫操作並立即返回,即使寫操作還沒有完成。
//PURGE_RXABORT 中斷所有讀操作並立即返回,即使讀操作還沒有完成。
//PURGE_TXCLEAR 清除輸出緩衝區
//PURGE_RXCLEAR 清除輸入緩衝區
WriteChar("please send data now",20);
printf("received data:\n");
ReceiveChar( );
system("pause");
}
/************************** program end***************************************/
第五節 多執行緒串列埠通訊及其它
程序和執行緒都是作業系統的概念。程序是應用程式的執行例項,每個程序是由私有的虛擬地址空間、程式碼、資料和其它各種系統資源組成,程序在執行過程中建立的資源隨著程序的終止而被銷燬,所使用的系統資源在程序終止時被釋放或關閉。
執行緒是程序內部的一個執行單元。系統建立好程序後,實際上就啟動執行了該程序的主執行執行緒,主執行執行緒以函式地址形式,比如說main或WinMain函式,將程式的啟動點提供給Windows系統。主執行執行緒終止了,程序也就隨之終止。
每一個程序至少有一個主執行執行緒,它無需由使用者去主動建立,是由系統自動建立的。使用者根據需要在應用程式中建立其它執行緒,多個執行緒併發地運行於同一個程序中。一個程序中的所有執行緒都在該程序的虛擬地址空間中,共同使用這些虛擬地址空間、全域性變數和系統資源,所以執行緒間的通訊非常方便,多執行緒技術的應用也較為廣泛。
多執行緒可以實現並行處理,避免了某項任務長時間佔用CPU時間。要說明的一點是,目前大多數的計算機都是單處理器(CPU)的,為了執行所有這些執行緒,作業系統為每個獨立執行緒安排一些CPU時間,作業系統以輪換方式向執行緒提供時間片,這就給人一種假象,好象這些執行緒都在同時執行。由此可見,如果兩個非常活躍的執行緒為了搶奪對CPU的控制權,線上程切換時會消耗很多的CPU資源,反而會降低系統的效能。這一點在多執行緒程式設計時應該注意。
Win32 SDK函式支援進行多執行緒的程式設計,並提供了作業系統原理中的各種同步、互斥和臨界區等操作。
Win32 提供了一系列的API函式來完成執行緒的建立、掛起、恢復、終結以及通訊等工作。下面將選取其中的一些重要函式進行說明。
由於建立執行緒所使用的函式CreateThread()是windows API函式,所以,必須包含標頭檔案windows.h 。CreateThread()函式有一個HANDLE 型別的返回值,用來標識建立的執行緒,因此,應該定義一個HANDLE型別的變數用於儲存這個控制代碼(不是必須)。執行緒建立完成之後,如果不需要使用這個控制代碼變數,應當將其關閉,以釋放系統資源。關閉控制代碼的方法是呼叫CloseHandle()函式。
HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId);
該函式在其呼叫程序的程序空間裡建立一個新的執行緒,並返回已建執行緒的控制代碼,其中各引數說明如下:
lpThreadAttributes:指向一個 SECURITY_ATTRIBUTES 結構的指標,該結構決定了執行緒的安全屬性,一般置為 NULL;
dwStackSize:指定了執行緒的堆疊深度,一般都設定為0;
lpStartAddress:表示新執行緒開始執行時程式碼所在函式的地址,即執行緒的起始地址。一般情況為(LPTHREAD_START_ROUTINE)ThreadFunc,ThreadFunc 是執行緒函式名;
lpParameter:指定了執行緒執行時傳送給執行緒的32位引數,即執行緒函式的引數;
dwCreationFlags:控制執行緒建立的附加標誌,可以取兩種值。如果該引數為0,執行緒在被建立後就會立即開始執行;如果該引數為CREATE_SUSPENDED,則系統產生執行緒後,該執行緒處於掛起狀態,並不馬上執行,直至函式ResumeThread被呼叫;
lpThreadId:該引數返回所建立執行緒的ID;
如果建立成功則返回執行緒的控制代碼,否則返回NULL。
DWORD WINAPI ThreadFunc( LPVOIDlpParam )為執行緒函式,lpParam為執行緒函式的引數。
DWORD SuspendThread(HANDLE hThread);該函式用於掛起指定的執行緒,如果函式執行成功,則執行緒的執行被終止。
DWORD ResumeThread(HANDLE hThread);該函式用於結束執行緒的掛起狀態,執行執行緒。
VOID ExitThread(DWORD dwExitCode);該函式用於執行緒終結自身的執行,主要線上程的執行函式中被呼叫。其中引數dwExitCode用來設定執行緒的退出碼。
BOOL TerminateThread(HANDLE hThread,DWORD dwExitCode);一般情況下,執行緒執行結束之後,執行緒函式正常返回,但是應用程式可以呼叫TerminateThread強行終止某一執行緒的執行。各引數含義如下:
hThread:將被終結的執行緒的控制代碼;
dwExitCode:用於指定執行緒的退出碼。
使用TerminateThread()終止某個執行緒的執行是不安全的,可能會引起系統不穩定;雖然該函式立即終止執行緒的執行,但並不釋放執行緒所佔用的資源。因此,一般不建議使用該函式。
BOOL PostThreadMessage(DWORD idThread,
UINT Msg,
WPARAM wParam,
LPARAM lParam);
該函式將一條訊息放入到指定執行緒的訊息佇列中,並且不等到訊息被該執行緒處理時便返回。
idThread:將接收訊息的執行緒的ID;
Msg:指定用來發送的訊息;
wParam:同訊息有關的字引數;
lParam:同訊息有關的長引數;
呼叫該函式時,如果即將接收訊息的執行緒沒有建立訊息迴圈,則該函式執行失敗。
DWORD WaitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds);
hHandle為要監視的物件(一般為同步物件,也可以是執行緒)的控制代碼;
dwMilliseconds為hHandle物件所設定的超時值,單位為毫秒;
當在某一執行緒中呼叫該函式時,執行緒暫時掛起,系統監視hHandle所指向的物件的狀態。如果在掛起的dwMilliseconds毫秒內,執行緒所等待的物件變為有訊號狀態,則該函式立即返回;如果超時時間已經到達dwMilliseconds毫秒,但hHandle所指向的物件還沒有變成有訊號狀態,函式照樣返回。引數dwMilliseconds有兩個具有特殊意義的值:0和INFINITE。若為0,則該函式立即返回;若為INFINITE,則執行緒一直被掛起,直到hHandle所指向的物件變為有訊號狀態時為止。
DWORDWaitForMultipleObject(DWORD dwCount , CONST HANDLE* phObject, BOOL fWaitAll,DWORD dwMillisecinds);
dwCount引數 用於指明想要讓函式檢視的核心物件的數量。這個值必須在1與MAXIMUM_WAIT_OBJECTS(在Windows標頭檔案中定義為64之間.
phObjects引數 是指向核心物件控制代碼的陣列的指標。可以以兩種不同的方式來使用WaitForMultipleObjects函式。一種方式是讓執行緒進入等待狀態,直到指定核心物件中的任何一個變為已通知狀態。另一種方式是讓執行緒進入等待狀態,直到所有指定的核心物件都變為已通知狀態。
fWaitAll引數 告訴該函式,你想要讓它使用何種方式。如果為該引數傳遞TRUE,那麼在所有物件變為已通知狀態之前,該函式將不允許呼叫執行緒執行。
dwMil liseconds引數 該引數的作用與它在WaitForSingleObject中的作用完全相同。如果在等待的時候規定的時間到了,那麼該函式無論如何都會返回。同樣,通常為該引數傳遞INFINITE,但是在編寫程式碼時應該小心,以避免出現死鎖情況。
WaitForMultipleObjects函式的返回值告訴呼叫執行緒,為什麼它會被重新排程。可能的返回值是WAIT_FAILED和WAIT_TIMEOUT,這兩個值的作用是很清楚的。如果為f WaitAll引數傳遞TRUE,同時所有物件均變為已通知狀態,那麼返回值是WAIT_OBJECT_0。如果為fWaitAll傳遞FALSE,那麼一旦任何一個物件變為已通知狀態,該函式便返回。在這種情況下,你可能想要知道哪個物件變為已通知狀態。返回值是WAIT_OBJECT_0與(WAIT_OBJECT_0 + dwCount-1)之間的一個值。換句話說,如果返回值不是WAIT_TIMEOUT,也不是WAIT_FAILED,那麼應該從返回值中減去WAIT_OBJECT_0。產生的數字是作為第二個引數傳遞給WaitForMultipleObjects的控制代碼陣列中的索引。該索引說明哪個物件變為已通知狀態。
通常,在寫WINDOWS程式的時候我們會用 GetLastError()來獲得錯誤代號,進而想要知道具體出錯原因(文字描述),我們可以用 FormatMessage 函式來得到。
DWORDFormatMessage(
DWORD dwFlags,
LPCVOID lpSource,
DWORD dwMessageId,
DWORD dwLanguageId,
LPTSTR lpBuffer,
DWORD nSize,
va_list* Arguments
);
dwFlags: FORMAT_MESSAGE_ALLOCATE_BUFFER //
此函式會分配記憶體以包含描述字串。
FORMAT_MESSAGE_FROM_SYSTEM, // 在系統的id對映表中尋找描述字串
FORMAT_MESSAGE_FROM_HMODULE // 在其他資源模組中尋找描述字串
FORMAT_MESSAGE_FROM_STRING // 訊息ID是個字串,不是個DWORD
通常為:
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM。
lpSource:指定了FORMAT_MESSAGE_FROM_HMODULE的話,此引數表示模組的HANDLE;指定了FORMAT_MESSAGE_FROM_STRING的話,此引數表示id字串通常為:NULL。
dwMessageId:訊息ID;如果指定FORMAT_MESSAGE_FROM_STRING,將被忽略。
dwLanguageId:訊息描述所用的語言通常為:0表示自動選擇。
lpBuffer:如果未指定FORMAT_MESSAGE_ALLOCATE_BUFFER,則為自己提供的緩衝區否則為系統LocalAlloc分配,需要被使用者LocalFree。
nSize:如果未指定FORMAT_MESSAGE_ALLOCATE_BUFFER,則為自己提供的緩衝區大小否則為系統LocalAlloc分配之最小緩衝區大小。
Arguments:通常不使用。
如果非同步操作不能立即完成的話,函式返回FALSE,並且呼叫GetLastError()函式分析錯誤原因後返回ERROR_IO_PENDING,指示非同步操作正在後臺進行.這種情況下,在函式返回之前系統設定OVERLAPPED結構中的事件為無訊號狀態,該函式等待用SetCommMask()函式設定的串列埠事件發生,共有9種事件可被監視:EV_BREAK,EV_CTS,EV_DSR,EV_ERR,EV_RING,EV_RLSD,EV_RXCHAR,EV_RXFLAG,EV_TXEMPTY;當其中一個事件發生或錯誤發生時,函式將 OVERLAPPED結構中的事件置為有訊號狀態,並將事件掩碼填充到dwMask引數中。
操作舉例1:OVERLAPPED os;
DWORDdwMask,dwTrans,dwError=0,err;
memset(&os,0,sizeof(OVERLAPPED));
os.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
if(!WaitCommEvent(hComm,&dwMask,&os)){
GetOverlappedResult(hComm,&os,&dwTrans,true);
switch(dwMask){
case EV_RXCHAR:
PostMessage(Parent,WM_COMM_RXCHAR,0,0);
break;
caseEV_TXEMPTY:
PostMessage(Parent,WM_COMM_TXEMPTY,0,0);
break;
case EV_ERR:
switch(dwError){
caseCE_FRAME:
err=0;
break;
caseCE_OVERRUN:
err=1;
break;
caseCE_RXPARITY:
err=2;
break;
default:break;
}
PostMessage(Parent,WM_COMM_ERR,(WPARAM)0,(LPARAM)err);
break;
caseEV_BREAK:
PostMessage(Parent,WM_COMM_BREAK,0,0);
break;
case ...://其他用SetCommMask()函式設定的被監視的串列埠通訊事件。
... ...
break;
default:break;
}
}
操作舉例2:
COMSTAT comStat;
DWORD dwErrors;
BOOL fOOP, fOVERRUN, fPTO, fRXOVER, fRXPARITY,fTXFULL;
BOOL fBREAK, fDNS, fFRAME, fIOE, fMODE;
// Get and clear current errors on the port.
if (!ClearCommError(hComm, &dwErrors,&comStat))
// Report error in ClearCommError.
return false;
// Get error flags.
fDNS = dwErrors & CE_DNS;
fIOE = dwErrors & CE_IOE;
fOOP = dwErrors & CE_OOP;
fPTO = dwErrors & CE_PTO;
fMODE = dwErrors & CE_MODE;
fBREAK = dwErrors & CE_BREAK;
fFRAME = dwErrors & CE_FRAME;
fRXOVER = dwErrors & CE_RXOVER;
fTXFULL = dwErrors & CE_TXFULL;
fOVERRUN = dwErrors & CE_OVERRUN;
fRXPARITY = dwErrors & CE_RXPARITY;
// COMSTAT structure contains information regarding
// communications status.
if (comStat.fCtsHold)
// Tx waiting for CTS signal
if (comStat.fDsrHold)
// Tx waiting for DSR signal
if (comStat.fRlsdHold)
// Tx waiting for RLSD signal
if (comStat.fXoffHold)
// Tx waiting, XOFF char rec'd
if (comStat.fXoffSent)
// Tx waiting, XOFF char sent
if (comStat.fEof)
// EOF character received
if (comStat.fTxim)
// Character waiting for Tx; char queued withTransmitCommChar
if (comStat.cbInQue)
// comStat.cbInQue bytes have been received, but notread
if (comStat.cbOutQue)
// comStat.cbOutQue bytes are awaiting transfer
return true;
memset函式詳細說明:
void *memset(void *s,int c,size_t n)總的作用:將已開闢記憶體空間 s 的首 n 個位元組的值設為值 c。常用於記憶體空間初始化。
CreateEvent函式詳細說明:
函式功能描述:建立或開啟一個命名的或無名的事件物件
函式原型:
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes, // 安全屬性
BOOL bManualReset, // 復位方式
BOOL bInitialState, // 初始狀態
LPCTSTR lpName // 物件名稱
);
引數: