Win32串口編程學習手冊.doc_第1頁
Win32串口編程學習手冊.doc_第2頁
Win32串口編程學習手冊.doc_第3頁
Win32串口編程學習手冊.doc_第4頁
Win32串口編程學習手冊.doc_第5頁
免費預覽已結束,剩余9頁可下載查看

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

以下的東西是我在閱讀 Communications Programming for Windows 95 時做的一點筆記, 所以在 topic 上大致上都和這書上的第三章一樣. 一些 structure 和 API 的宣告都是 copy from VC 5.0 的 on-line help. 這些東西主要談的是如何在 Win32 的平臺下對 serial port 的通訊. 可能可以帶來幫助的是那些了解 serial port 的通訊, 但是不清楚在 Win32 平臺究竟有那些 API 可用的 programer (就像我? :p) 我只是做做整理罷了, 更清楚的內容可能要再翻翻 on-line help 或是書本. 如果你要嘗試在 Win32 下做像是對 modem 做控制(例如撥號, 通訊)的程序, 但是對于 serial port 并不了解, 那你應該再去找一本有講到這些東西的書看看.1.開啟一個 Serial Port利用一般開啟檔案的 CreatFile() 即可開啟 serial port deviceHANDLE CreateFile(LPCTSTR lpFileName, / pointer to name of the fileDWORD dwDesiredAccess, / access (read-write) modeDWORD dwShareMode, / share modeLPSECURITY_ATTRIBUTES lpSecurityAttributes, / pointer to security attributesDWORD dwCreationDistribution, / how to createDWORD dwFlagsAndAttributes, / file attributesHANDLE hTemplateFile / handle to file with attributes to copy);用 CreateFile() API.lpFileName 為 COM1 或是 COM2dwDersiredAccess 一般為 GENERIC_READ|GENERIC_WRITEdwShareMode 必須為 0, 即不能共享, 但同一個 process 中的不同 thread 在一開啟之后就可以共享.lpSecurityAttributes 一般為 NULLdwCreateionDistributon 在這里必須為 OPEN_EXISTINGdwFlagsAndAttributes 定義了開啟的屬性, 若是設成 FILE_FLAG_OVERLAPPED 則可使用異步的 I/O.hTemplateFile 必須為 NULL傳回檔案 handle設定 Serial Port 傳送及接收緩沖區(qū)的大小在開啟完 serial port 之后, 可以藉由呼叫 SetupComm() 來進行配置傳送時的緩沖區(qū)及接收時的緩沖區(qū). 如果沒有呼叫 SetupComm() 的話, Win95 會配置內定的緩沖區(qū).BOOL SetupComm(HANDLE hFile, / handle of communications deviceDWORD dwInQueue, / size of input bufferDWORD dwOutQueue / size of output buffer);2.關閉 Serial Port file利用一般的 CloseHandle() 即可.BOOL CloseHandle(HANDLE hObject / handle to object to close)3.取得 Seial Port 的信息在 Win32 里頭, 將一些通訊時會用到的信息用 COMMPROP 這個結構來表示. (當然不僅僅是 Serial Port) 可以用 GetCommProperties() 來取得:BOOL GetCommProperties(HANDLE hFile, / handle of communications deviceLPCOMMPROP lpCommProp / address of communications properties structure);COMMPROP 長的像這個樣子:typedef struct _COMMPROP / cmmpWORD wPacketLength; / packet size, in bytesWORD wPacketVersion; / packet versionDWORD dwServiceMask; / services implementedDWORD dwReserved1; / reservedDWORD dwMaxTxQueue; / max Tx bufsize, in bytesDWORD dwMaxRxQueue; / max Rx bufsize, in bytesDWORD dwMaxBaud; / max baud rate, in bpsDWORD dwProvSubType; / specific provider typeDWORD dwProvCapabilities; / capabilities supportedDWORD dwSettableParams; / changable parametersDWORD dwSettableBaud; / allowable baud ratesWORD wSettableData; / allowable byte sizesWORD wSettableStopParity; / stop bits/parity allowedDWORD dwCurrentTxQueue; / Tx buffer size, in bytesDWORD dwCurrentRxQueue; / Rx buffer size, in bytesDWORD dwProvSpec1; / provider-specific dataDWORD dwProvSpec2; / provider-specific dataWCHAR wcProvChar1; / provider-specific data COMMPROP;在這里, lpCommProp 需要 programmer 自行配置空間. 有趣的問題是, 系統(tǒng)在這個結構之后會需要額外的空間. 但是配置者也就是 programmer 卻不知道系統(tǒng)會需要多少. 很簡單的做法是配置一大塊絕對會夠的空間. 另一個聰明的做法是執(zhí)行兩次 GetCommProperties() , 第一次只配置 sizeof(COMMPROP) 這么大的空間, 因為還沒有開始執(zhí)行一些動作, 所以系統(tǒng)并不會嘗試著在后面填東西, 所以不會出問題. 接著執(zhí)行第一次的 GetCommProperties(), 得到結果, 取出結構中的 wPacketLength, 這個 member 代表實際上需要的大小, 然后依據這個大小重新配置一個新的. 這樣的話 , 就不會有浪費任何空間的問題了.至于上述 COMMPROP 結構的成員所代表的意思, on-line help 中應該寫的都滿清楚的 .4.設定及取得通訊狀態(tài) 你可以利用 COMMPROP 來取得一些狀態(tài), 但是當你想改變目前的設定時你需要兩個 API 來完成:BOOL GetCommState(HANDLE hFile, / handle of communications deviceLPDCB lpDCB / address of device-control block structure);BOOL SetCommState(HANDLE hFile, / handle of communications deviceLPDCB lpDCB / address of device-control block structure);你可以用 GetCommState() 來取得目前 Serial Port 的狀態(tài), 也可以用 SetCommState() 來設定 Serial Port 的狀態(tài).DCB 的結構就請自行翻閱 help 啰.另外, programmer 最??刂频膸讉€設定就是 baud rate, parity method, data bits, 還有 stop bit. BuildCommDCB() 提供了對于這幾個常見設定的控制.BOOL BuildCommDCB(LPCTSTR lpDef, / pointer to device-control stringLPDCB lpDCB / pointer to device-control block);lpDef 長的像這樣: baud=2400 parity=N data=8 stop=15.通訊設定對話盒Win32 API 中提供了一個開啟通訊設定對話盒的 API: CommConfigDialog(), 當呼叫這個 API 時, 會蹦現一個可供設定 Baud Rate, Data Bits, Parity . 等信息的對話盒, programmer 可以利用它來讓使用者設定一些信息, 并且取得結果.BOOL CommConfigDialog(LPTSTR lpszName, / pointer to device name stringHWND hWnd, / handle to windowLPCOMMCONFIG lpCC / pointer to comm. configuration structure);其中 lpCC 被用來存放設定值的結果.typedef struct _COMM_CONFIG DWORD dwSize;WORD wVersion;WORD wReserved;DCB dcb;DWORD dwProviderSubType;DWORD dwProviderOffset;DWORD dwProviderSize;WCHAR wcProviderData1; COMMCONFIG, *LPCOMMCONFIG;在我們呼叫 CommConfigDialog() 之前, dwSize 要設為 sizeof(COMMCONFIG), wVersion 的值在這邊似乎不重要(我不清楚, VC5 的 on-line help 說可以設為 1, 我手中的 book 的范例是設為 0x100), 呼叫完 CommConfigDialog() 之后, 成員 dcb 中的 BaudRate, ByteSize, StopBits, Parity 就是使用者的設定.6.Timeout 的機制因為傳輸時并不會維持一個絕對穩(wěn)定的速率. 因為傳輸品質的關系, programer 會需要 timeout 的機制來協助他們做一些控制. 在 Win32 通訊 Timeout 的機制中, timeout 的性質共分為兩類, 先來看看 COMMTIMEOUTS 這個結構:typedef struct _COMMTIMEOUTS / ctmoDWORD ReadIntervalTimeout;DWORD ReadTotalTimeoutMultiplier;DWORD ReadTotalTimeoutConstant;DWORD WriteTotalTimeoutMultiplier;DWORD WriteTotalTimeoutConstant; COMMTIMEOUTS,*LPCOMMTIMEOUTS;programmer 可以利用 GetCommTimeouts() 和 SetCommTimeouts() 來讀取或是設定目前的 timeout 值.BOOL GetCommTimeouts(HANDLE hFile, / handle of communications deviceLPCOMMTIMEOUTS lpCommTimeouts / address of comm. time-outs structure);BOOL SetCommTimeouts(HANDLE hFile, / handle of communications deviceLPCOMMTIMEOUTS lpCommTimeouts / address of communications time-out structure);第一種 timeout 的機制稱為 interval timeout, 從字面上的意義很容易可以理解這種 timeout 的機制是讀取字符之間的間隔時間的 timeout, 只有讀取字符時才能夠使用interval timeout. 也就是在這個結構中的 ReadIntervalTimeout, 單位為 ms, 當讀取完一個字符后, 超過了 ReadIntervalTimeout 的值, 卻還沒有讀到下一個字符時, timeout 就發(fā)生了.第二種 timeout 的機制稱為 total timeout, 顧名思義即是傳輸的總時間的 timeout . 在這種 timeout 的機制下, Win32 提供了一個具有彈性的方式來設定 total timeout. 以讀取的 total timeout 為例, 利用 ReadTotalTimeoutMultiplier 和 ReadTotalTimeoutConstant 構成了一個線性的上限值. 什么意思呢? 實際上的 total timeout 應該是這樣的一個式子:ReadTotalTimeout = ReadTotalTimeOutMultiplier*BytesToRead+ReadTotalTimeoutConstantWriteTotalTimeout 用同樣的公式來計算. 這樣的話, 不僅可以用一個固定的值來做為 timeout 值, 也可以用條線來做為 timeout 的值, 而隨著要讀取或是要寫的 bytes 數而變動.如果不想使用 timeout, 就把 COMMTIMEOUTS 里頭的資料成員都填為 0.如果你將 ReadIntervalTimeout 設為 MAXDWORD, 且將 ReadTotalTimeOutMultiplier 和 ReadTotalTimeoutConstant 都設為 0 的話, 那么讀取時, 如果 receive queue 里頭并沒有資料, 讀取的動作將會馬上返回, 而不會停滯在讀取的動作.這里有一個和 BuildCommDCB() 很像的 API 叫 BuildCommDCBAndTimeouts():BOOL BuildCommDCBAndTimeouts(LPCTSTR lpDef, / pointer to the device-control stringLPDCB lpDCB, / pointer to the device-control blockLPCOMMTIMEOUTS lpCommTimeouts / pointer to comm. time-out structure);lpDef 一樣是控制字符串, 可以給像 BuildCommDCB() 中的 lpDef 那樣格式的字符串, 但是多了 TO=XXX 這個設定. 如果 TO=ON, 這個 API 會依據 lpCommTimeouts 里頭的值來設定讀和寫的 timeout 值. 如果 TO=OFF, 則會設定這個 device 沒有 timeout. 如果是 ON 和 OFF 之外的其它值, 則 lpCommTimeouts 的設定將會被忽略.對了, 在設定完 timeout 值之后, 記得要檢查 COMMPROP 里的 dwProvCapabilities 中的 PCF_INTTIMEOUTS 和 PCF_TOTALTIMEOUTS 兩個 flags 是否有被 set, 以確認 interval timeout 和 total timeout 是否有支持.7.讀取資料 從 serial port 里頭讀取資料就跟讀取一般的檔案一樣, 使用 ReadFile() 來達成.BOOL ReadFile(HANDLE hFile, / handle of file to readLPVOID lpBuffer, / address of buffer that receives dataDWORD nNumberOfBytesToRead, / number of bytes to readLPDWORD lpNumberOfBytesRead, / address of number of bytes readLPOVERLAPPED lpOverlapped / address of structure for data);要注意的是, nNumberOfBytesToRead 設定的是一次最多的讀取量, 很有可能所讀取的值(檢查 lpNumberOfBytesRead)小于這個值. 通常在錯誤發(fā)生或是 timeout 發(fā)生時這個 API 就會返回.PurgeComm() 這個 API 可以用來終止目前正在進行的讀或寫的動作, 也可以 flush 掉 I/O buffer 內等待讀或寫的資料.BOOL PurgeComm(HANDLE hFile, / handle of communications resourceDWORD dwFlags / action to perform);其中 dwFlags 共有四種 flags:PURGE_TXABORT: 終止目前正在進行的(背景)寫入動作PURGE_RXABORT: 終正目前正在進行的(背景)讀取動作PURGE_TXCLEAR: flush 寫入的 bufferPURGE_TXCLEAR: flush 讀取的 buffer而使用 FlushFileBuffers() 可以確保所有的資料都被送出, 這個 API 才會返回.另外一個有趣的 API 是 ClearCommError(), 從字面上的意思看來, 它是用來清除錯誤情況用的, 但是實際上它還可以拿來取得目前通訊設備的一些信息.BOOL ClearCommError(HANDLE hFile, / handle to communications deviceLPDWORD lpErrors, / pointer to variable to receive error codesLPCOMSTAT lpStat / pointer to buffer for communications status);呼叫這個 API 之后, 關于通訊設備的一些信息會被儲存在 lpStat 中, COMSTAT 的結構如下:typedef struct _COMSTAT / cstDWORD fCtsHold : 1; / Tx waiting for CTS signalDWORD fDsrHold : 1; / Tx waiting for DSR signalDWORD fRlsdHold : 1; / Tx waiting for RLSD signalDWORD fXoffHold : 1; / Tx waiting, XOFF char recdDWORD fXoffSent : 1; / Tx waiting, XOFF char sentDWORD fEof : 1; / EOF character sentDWORD fTxim : 1; / character waiting for TxDWORD fReserved : 25; / reservedDWORD cbInQue; / bytes in input bufferDWORD cbOutQue; / bytes in output buffer COMSTAT, *LPCOMSTAT藉由 fCtsHold, fDsrHold, fRlsdHold, fXoffHold, fXoffSent 可以知道目前因為什么因素而使通訊阻礙住了.( 跟 handshaking 和 flow control 有關) cbInque 和 cbOutQue 則可以顯示出還有多少 bytes 在讀取或是寫入 queue 中.8.寫入資料和讀取資料一樣, programmer 可以使用 WriteFile() 來將資料寫入 serial port.BOOL WriteFile(HANDLE hFile, / handle to file to write toLPCVOID lpBuffer, / pointer to data to write to fileDWORD nNumberOfBytesToWrite, / number of bytes to writeLPDWORD lpNumberOfBytesWritten, / pointer to number of bytes writtenLPOVERLAPPED lpOverlapped / pointer to structure needed for overlapped I/O);關于通訊設備的寫入有三個很有趣的 API, 它們分別是 SetCommBreak(), ClearCommBreak, 和 TransmitCommChar().BOOL SetCommBreak(HANDLE hFile / handle of communications device);BOOL ClearCommBreak(HANDLE hFile / handle to communications device);BOOL TransmitCommChar(HANDLE hFile, / handle of communications devicechar cChar / character to transmit);SetCommBreak() 是用來暫停目前的傳輸作業(yè), 它會使 buffer 中的資料都不再被送出, 這個時候, program 可以去做些雜七雜八的事, 之后, 再利用 ClearCommBreak() 回復傳輸作業(yè).TransmitCommChar() 是用來立即性的趕在所有 buffer 數據被送出去之前, 傳輸一個字符的數據出去, 即使 buffer 中還有資料. 換句話說, 這個字符有最高的優(yōu)先權被送出去.9.事件驅動式的 I/O在 Win32 里頭, 對于通訊設備的 I/O 可以用像是事件驅動式的方法來達成. 主要是利用一個叫 WaitCommEvent() 的 API. 呼叫這個 API 之后, 會一直 block 到設定的事件發(fā)生之后才會返回. 我們先來看看如何設定事件, 再回過頭來看 WaitCommEvent() .programer 可以用 GetCommMask() 和 SetCommMask() 來取得或是設定目前設定的通訊事件.BOOL GetCommMask(HANDLE hFile, / handle of communications deviceLPDWORD lpEvtMask / address of variable to get event mask);BOOL SetCommMask(HANDLE hFile, / handle of communications deviceDWORD dwEvtMask / mask that identifies enabled events);可以設定的事件有 EV_BREAK, EV_CTS, EV_DSR, EV_ERR, EV_RING, EV_RLSD, EV_RXCHAR, EV_RXFLAG, EV_TXEMPTY.(其意義請自行參考 help), 當然, 你可以把它們 or 起來成為組合的事件.在設定完想要處理的事件之后, 可以使用 WaitCommEvent()BOOL WaitCommEvent(HANDLE hFile, / handle of communications deviceLPDWORD lpEvtMask, / address of variable for event that occurredLPOVERLAPPED lpOverlapped, / address of overlapped structure);WaitCommEvent() 會一直 block 到你所設定的通訊事件發(fā)生為止. 所以當 WaitCommEvent() 返回時, 你可以由 lpEvtMask 取得究竟是那一事件發(fā)生, 再來決定要如何處理.舉例來說, 可以用 SetCommMask() 設定事件為 EV_RXCHAR, 那么在呼叫 WaitCommEvent() 時, 它會等到有字符可供讀取時才會返回, 那么在它返回之后, 可以檢查一下 lpEvtMask 中是否 set 了 EV_RXCHAR, 如果是的話就可以用 ReadFile() 去讀取. 這樣的話, 可以避免掉某些情形之下, 需要做 polling 所引起效率不彰的問題.10.錯誤的處理前面提過的 ClearnCommError() 可以用來取得目前發(fā)生錯誤的原因.(請參見 help)11.硬件的控制命令Win32 中提供了 EscapeCommFunction() 允許 programer 對幾個硬件訊號做控制.BOOL EscapeCommFunction(HANDLE hFile, / handle to communications deviceDWORD dwFunc / extended function to perform);其中 dwFunc 可以是:CLRDTR : 讓 DTR OFFCLRRTS : 讓 RTS OFFSETDTR : 讓 DTR ONSETRTS : 讓 RTS ONSETXOFF : 仿真 接收到 XOFF 字符SETXON : 仿真 接收到 XON 字符SETBREAK : 和 SetCommBreak() 的意思相同CLRBREAK : 和 ClearCommBreak() 的意思相同Win32串口編程金貝貝 一、基本知識 Win32下串口通信與16位串口通信有很大的區(qū)別。在Win32下,可以使用兩種編程方式實現串口通信,其一是調用的Windows的API函數,其二是使用ActiveX控件。使用API 調用,可以清楚地掌握串口通信的機制,熟悉各種配置和自由靈活采用不同的流控進行串口通信。下面介紹串口操作的基本知識。 打開串口:使用CreateFile()函數,可以打開串口。有兩種方法可以打開串口,一種是同步方式(NonOverlapped),另外一種異步方式(Overlapped)。使用Overlapped打開時,適當的方法是: HANDLE hComm; hComm = CreateFile( gszPort, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); if (hComm = INVALID_HANDLE_VALUE) / error opening port; abort配置串口: 1.DCB配置 DCB(Device Control Block)結構定義了串口通信設備的控制設置。許多重要設置都是在DCB結構中設置的,有三種方式可以初始化DCB。 (1)通過GetCommState()函數得DCB的初始值,其使用方式為: DCB dcb = 0;if (!GetCommState(hComm, dcb)/ Error getting current DCB settingselse/ DCB is ready for use.(2)用BuildCommDCB()函數初始化DCB結構,該函數填充 DCB的波特率、奇偶校驗類型、數據位、停止位。對于流控成員函數設置了缺省值。其用法是: DCB dcb;FillMemory(dcb, sizeof(dcb), 0);dcb.DCBlength = sizeof(dcb);if (!BuildCommDCB(“9600,n,8,1, dcb) / Couldnt build the DCB. Usually a problem/ with the communications specification string.return FALSE;else/ DCB is ready for use.(3)用SetCommState()函數手動設置DCB初值。用法如下: DCB dcb;FillMemory(dcb, sizeof(dcb), 0);if (!GetCommState(hComm, dcb) / get current DCB/ Error in GetCommStatereturn FALSE;/ Update DCB rate.dcb.BaudRate = CBR_9600 ;/ Set new state.if (!SetCommState(hComm, dcb)/ Error in SetCommState. Possibly a problem with the communications / port handle or a problem with the DCB structure itself.手動設置DCB值時,DCB的結構的各成員的含義,可以參看MSDN幫助。 2.流控設置 硬件流控:串口通信中的硬件流控有兩種,DTE/DSR方式和RTS/CTS方式,這與DCB結構的初始化有關系,DCB結構中的OutxCtsFlow、 fOutxDsrFlow、fDsrSensitivity、fRtsControl、fDtrControl幾個成員的初始值很關鍵,不同的值代表不同流控,也可以自己設置流控,但建議采用標準流行的流控方式。采用硬件流控時,DTE、DSR、RTS、CTS的邏輯位直接影響到數據的讀寫及收發(fā)數據的緩沖區(qū)控制。 軟件流控:串口通信中采用特殊字符XON和XOFF作為控制串口數據的收發(fā)。與此相關的DCB成員是:fOut、fInX、XoffChar、XonChar、 XoffLim和XonLim。具體含義參見MSDN幫助。 串口讀寫操作:串口讀寫有兩種方式:同步方式(NonOverlapped)和異步方式(Overlapped)。同步方式是指必須完成了讀寫操作,函數才返回,這可能造成程序死掉,因為如果在讀寫時發(fā)生了錯誤,永遠不返回就會出錯,可能線程將永遠等待在那兒。而異步方式則靈活得多,一旦讀寫不成功,就將讀寫掛起,函數直接返回,可以通過GetLastError函數得知讀寫未成功的原因,所以常常采用異步方式操作。 讀操作:ReadFile()函數用于完成讀操作。異步方式的讀操作為: DWORD dwRead; BOOL fWaitingOnRead = FALSE; OVERLAPPED osReader = 0; / Create the overlapped event. Must be closed before exiting / to avoid a handle leak. osReader.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL); if (osReader.hEvent = NULL) / Error creating overlapped event; abort. if (!fWaitingOnRead) / Issue read operation. if (!ReadFile(hComm, lpBuf, READ_BUF_SIZE, dwRead, osReader) if (GetLastError() != ERROR_IO_PENDING) / read not delayed? / Error in communications; report it. else fWaitingOnRead = TRUE; else / read completed immediately HandleASuccessfulRead(lpBuf, dwRead); 如果讀操作被掛起,可以調用WaitForSingleObject()函數或WaitForMuntilpleObjects()函數等待讀操作完成或者超時發(fā)生,再調用 GetOverlappedResult()得到想要的信息。 寫操作:與讀操作相似,故不詳述,調用的API函數是: WriteFile函數。 串口狀態(tài): (1)通信事件:用SetCommMask()函數設置想要得到的通信事件的掩碼,再調用WaitCommEvent()函數檢測通信事件的發(fā)生??稍O置的通信事件標志(即SetCommMask()函數所設置的掩碼)可以有EV_BREAK、EV_CTS、EV_DSR、 EV_ERR、EV_RING、EV_RLSD、EV_RXCHAR、EV_RXFLAG、EV_TXEMPTY。 注意:1對于EV_RING標志的設置,WIN95是不會返回EV_RING事件的,因為WIN95不檢測該事件。2設置EV_RXCHAR,可以檢測到字符到達,但是在綁定此事件和ReadFile()函數一起讀取串口接收數據時,可能會出現錯誤,造成少讀字節(jié)數,具體原因查看MSDN幫助??梢圆捎醚h(huán)讀的辦法,另外一個比較好的解決辦法是調用ClearCommError()函數,確定在一次讀操作中在緩沖區(qū)中等待被讀的字節(jié)數。 (2)錯誤處理

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論