免费预览已结束,剩余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 传送及接收缓冲区的大小在开启完 serial port 之后, 可以藉由呼叫 SetupComm() 来进行配置传送时的缓冲区及接收时的缓冲区. 如果没有呼叫 SetupComm() 的话, Win95 会配置内定的缓冲区.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 自行配置空间. 有趣的问题是, 系统在这个结构之后会需要额外的空间. 但是配置者也就是 programmer 却不知道系统会需要多少. 很简单的做法是配置一大块绝对会够的空间. 另一个聪明的做法是执行两次 GetCommProperties() , 第一次只配置 sizeof(COMMPROP) 这么大的空间, 因为还没有开始执行一些动作, 所以系统并不会尝试着在后面填东西, 所以不会出问题. 接着执行第一次的 GetCommProperties(), 得到结果, 取出结构中的 wPacketLength, 这个 member 代表实际上需要的大小, 然后依据这个大小重新配置一个新的. 这样的话 , 就不会有浪费任何空间的问题了.至于上述 COMMPROP 结构的成员所代表的意思, on-line help 中应该写的都满清楚的 .4.设定及取得通讯状态 你可以利用 COMMPROP 来取得一些状态, 但是当你想改变目前的设定时你需要两个 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 的状态, 也可以用 SetCommState() 来设定 Serial Port 的状态.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 的机制因为传输时并不会维持一个绝对稳定的速率. 因为传输品质的关系, 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 就发生了.第二种 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)小于这个值. 通常在错误发生或是 timeout 发生时这个 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() 是用来暂停目前的传输作业, 它会使 buffer 中的资料都不再被送出, 这个时候, program 可以去做些杂七杂八的事, 之后, 再利用 ClearCommBreak() 回复传输作业.TransmitCommChar() 是用来立即性的赶在所有 buffer 数据被送出去之前, 传输一个字符的数据出去, 即使 buffer 中还有资料. 换句话说, 这个字符有最高的优先权被送出去.9.事件驱动式的 I/O在 Win32 里头, 对于通讯设备的 I/O 可以用像是事件驱动式的方法来达成. 主要是利用一个叫 WaitCommEvent() 的 API. 呼叫这个 API 之后, 会一直 block 到设定的事件发生之后才会返回. 我们先来看看如何设定事件, 再回过头来看 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 到你所设定的通讯事件发生为止. 所以当 WaitCommEvent() 返回时, 你可以由 lpEvtMask 取得究竟是那一事件发生, 再来决定要如何处理.举例来说, 可以用 SetCommMask() 设定事件为 EV_RXCHAR, 那么在呼叫 WaitCommEvent() 时, 它会等到有字符可供读取时才会返回, 那么在它返回之后, 可以检查一下 lpEvtMask 中是否 set 了 EV_RXCHAR, 如果是的话就可以用 ReadFile() 去读取. 这样的话, 可以避免掉某些情形之下, 需要做 polling 所引起效率不彰的问题.10.错误的处理前面提过的 ClearnCommError() 可以用来取得目前发生错误的原因.(请参见 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位串口通信有很大的区别。在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的逻辑位直接影响到数据的读写及收发数据的缓冲区控制。 软件流控:串口通信中采用特殊字符XON和XOFF作为控制串口数据的收发。与此相关的DCB成员是:fOut、fInX、XoffChar、XonChar、 XoffLim和XonLim。具体含义参见MSDN帮助。 串口读写操作:串口读写有两种方式:同步方式(NonOverlapped)和异步方式(Overlapped)。同步方式是指必须完成了读写操作,函数才返回,这可能造成程序死掉,因为如果在读写时发生了错误,永远不返回就会出错,可能线程将永远等待在那儿。而异步方式则灵活得多,一旦读写不成功,就将读写挂起,函数直接返回,可以通过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()函数等待读操作完成或者超时发生,再调用 GetOverlappedResult()得到想要的信息。 写操作:与读操作相似,故不详述,调用的API函数是: WriteFile函数。 串口状态: (1)通信事件:用SetCommMask()函数设置想要得到的通信事件的掩码,再调用WaitCommEvent()函数检测通信事件的发生。可设置的通信事件标志(即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()函数一起读取串口接收数据时,可能会出现错误,造成少读字节数,具体原因查看MSDN帮助。可以采用循环读的办法,另外一个比较好的解决办法是调用ClearCommError()函数,确定在一次读操作中在缓冲区中等待被读的字节数。 (2)错误处理
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025国家中节能铁汉生态环境股份有限公司干部岗位招聘2人笔试历年参考题库附带答案详解
- 房地产交易流程规范化操作手册
- 养老机构服务质量管理办法(试行)
- 2026重庆丰都县民政局公益岗招聘1人笔试备考题库及答案详解
- 2026重庆垫江县包家镇人民政府选拔本土人才1人笔试参考试题及答案详解
- 2026年陕西师范大学基建处招聘笔试参考题库及答案详解
- 旅游服务业市场调研分析及未来趋势与商业布局研究报告
- 西华师范大学2026年6月公开招聘非事业编制工作人员笔试参考试题及答案详解
- 2026年白银市平川区中小学编制教师招聘笔试备考试题及答案详解
- 2026年西安市临潼区人民法院就业见习招募考试备考题库及答案详解
- 村卫生所医疗规章制度
- 2026年及未来5年中国环孢素滴眼液行业市场全景监测及投资战略咨询报告
- 婚礼督导培训课件
- 儿童肺脓肿诊疗指南(2025年版)
- 建筑边坡工程鉴定与加固技术规范
- 2026年广发证券港股通开通测试题及实战解析
- 2026年书记员考试题库100道(历年真题)
- 人工智能深度学习入门
- 盘扣打包工人合同协议
- 2025云南临沧高新技术产业开发区管理委员会公益性岗位招聘4人考试笔试备考试题及答案解析
- 水工建构筑物维护检修工岗前操作技能考核试卷含答案
评论
0/150
提交评论