CSerial串口类
来源:互联网 发布:无法连接到emule网络 编辑:程序博客网 时间:2024/06/09 07:52
本来想把自己写的CSerial上传,但是看到网上有个类似的,功能都一样的,设计原理基本一致,所以就下载后转了一下。
#include "serialport.h"#ifndef _WINERROR_#include "winerror.h"#endif///////////////////////////////// defines /////////////////////////////////////#ifdef _DEBUG#define new DEBUG_NEW#undef THIS_FILEstatic char THIS_FILE[] = __FILE__;#endif//////////////////////////////// Implementation /////////////////////////////////Class which handles CancelIo function which must be constructed at run time//since it is not imeplemented on NT 3.51 or Windows 95. To avoid the loader//bringing up a message such as "Failed to load due to missing export...", the//function is constructed using GetProcAddress. The CSerialPort::CancelIo //function then checks to see if the function pointer is NULL and if it is it //throws an exception using the error code ERROR_CALL_NOT_IMPLEMENTED which//is what 95 would have done if it had implemented a stub for it in the first//place !!class _SERIAL_PORT_DATA{public://Constructors /Destructors _SERIAL_PORT_DATA(); ~_SERIAL_PORT_DATA(); HINSTANCE m_hKernel32; typedef BOOL (WINAPI CANCELIO)(HANDLE); typedef CANCELIO* LPCANCELIO; LPCANCELIO m_lpfnCancelIo;};_SERIAL_PORT_DATA::_SERIAL_PORT_DATA(){ m_hKernel32 = LoadLibrary(_T("KERNEL32.DLL")); VERIFY(m_hKernel32 != NULL); m_lpfnCancelIo = (LPCANCELIO) GetProcAddress(m_hKernel32, "CancelIo");}_SERIAL_PORT_DATA::~_SERIAL_PORT_DATA(){ FreeLibrary(m_hKernel32); m_hKernel32 = NULL;}//The local variable which handle the function pointers_SERIAL_PORT_DATA _SerialPortData;////////// Exception handling codevoid AfxThrowSerialException(DWORD dwError /* = 0 */){ if (dwError == 0) dwError = ::GetLastError(); CSerialException* pException = new CSerialException(dwError); TRACE(_T("Warning: throwing CSerialException for error %d\n"), dwError); THROW(pException);}BOOL CSerialException::GetErrorMessage(LPTSTR pstrError, UINT nMaxError, PUINT pnHelpContext){ ASSERT(pstrError != NULL && AfxIsValidString(pstrError, nMaxError)); if (pnHelpContext != NULL) *pnHelpContext = 0; LPTSTR lpBuffer; BOOL bRet = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, m_dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT), (LPTSTR) &lpBuffer, 0, NULL); if (bRet == FALSE) *pstrError = '\0'; else { lstrcpyn(pstrError, lpBuffer, nMaxError); bRet = TRUE; LocalFree(lpBuffer); } return bRet;}CString CSerialException::GetErrorMessage(){ CString rVal; LPTSTR pstrError = rVal.GetBuffer(4096); GetErrorMessage(pstrError, 4096, NULL); rVal.ReleaseBuffer(); return rVal;}CSerialException::CSerialException(DWORD dwError){ m_dwError = dwError;}CSerialException::~CSerialException(){}IMPLEMENT_DYNAMIC(CSerialException, CException)#ifdef _DEBUGvoid CSerialException::Dump(CDumpContext& dc) const{ CObject::Dump(dc); dc << "m_dwError = " << m_dwError;}#endif////////// The actual serial port codeCSerialPort::CSerialPort(){ m_hComm = INVALID_HANDLE_VALUE; m_bOverlapped = FALSE; m_hEvent = NULL;}CSerialPort::~CSerialPort(){ Close();}IMPLEMENT_DYNAMIC(CSerialPort, CObject)#ifdef _DEBUGvoid CSerialPort::Dump(CDumpContext& dc) const{ CObject::Dump(dc); dc << _T("m_hComm = ") << m_hComm << _T("\n"); dc << _T("m_bOverlapped = ") << m_bOverlapped;}#endifvoid CSerialPort::Open(int nPort, DWORD dwBaud, Parity parity, BYTE DataBits, StopBits stopbits, FlowControl fc, BOOL bOverlapped){ //Validate our parameters ASSERT(nPort>0 && nPort<=255); Close(); //In case we are already open //Call CreateFile to open up the comms port CString sPort; sPort.Format(_T("\\\\.\\COM%d"), nPort); DWORD dwCreateProperty; if(bOverlapped) { dwCreateProperty=FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED; } else { dwCreateProperty=FILE_ATTRIBUTE_NORMAL; } // bOverlapped ? FILE_FLAG_OVERLAPPED : 0 m_hComm = CreateFile(sPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,dwCreateProperty, NULL); if (m_hComm == INVALID_HANDLE_VALUE) { TRACE(_T("Failed to open up the comms port\n")); AfxThrowSerialException(); } this->m_CurPortNum = nPort; //Create the event we need for later synchronisation use if(bOverlapped) { m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (m_hEvent == NULL) { Close(); TRACE(_T("Failed in call to CreateEvent in Open\n")); AfxThrowSerialException(); } } m_bOverlapped = bOverlapped; //Get the current state prior to changing it DCB dcb; dcb.DCBlength = sizeof(DCB); GetState(dcb); //Setup the baud rate dcb.BaudRate = dwBaud; //Setup the Parity switch (parity) { case EvenParity: dcb.Parity = EVENPARITY; break; case MarkParity: dcb.Parity = MARKPARITY; break; case NoParity: dcb.Parity = NOPARITY; break; case OddParity: dcb.Parity = ODDPARITY; break; case SpaceParity: dcb.Parity = SPACEPARITY; break; default: ASSERT(FALSE); break; } //Setup the data bits dcb.ByteSize = DataBits; //Setup the stop bits switch (stopbits) { case OneStopBit: dcb.StopBits = ONESTOPBIT; break; case OnePointFiveStopBits: dcb.StopBits = ONE5STOPBITS; break; case TwoStopBits: dcb.StopBits = TWOSTOPBITS; break; default: ASSERT(FALSE); break; } //Setup the flow control dcb.fDsrSensitivity = FALSE; switch (fc) { case NoFlowControl: { dcb.fOutxCtsFlow = FALSE; dcb.fOutxDsrFlow = FALSE; dcb.fOutX = FALSE; dcb.fInX = FALSE; break; } case CtsRtsFlowControl: { dcb.fOutxCtsFlow = TRUE; dcb.fOutxDsrFlow = FALSE; dcb.fRtsControl = RTS_CONTROL_HANDSHAKE; dcb.fOutX = FALSE; dcb.fInX = FALSE; break; } case CtsDtrFlowControl: { dcb.fOutxCtsFlow = TRUE; dcb.fOutxDsrFlow = FALSE; dcb.fDtrControl = DTR_CONTROL_HANDSHAKE; dcb.fOutX = FALSE; dcb.fInX = FALSE; break; } case DsrRtsFlowControl: { dcb.fOutxCtsFlow = FALSE; dcb.fOutxDsrFlow = TRUE; dcb.fRtsControl = RTS_CONTROL_HANDSHAKE; dcb.fOutX = FALSE; dcb.fInX = FALSE; break; } case DsrDtrFlowControl: { dcb.fOutxCtsFlow = FALSE; dcb.fOutxDsrFlow = TRUE; dcb.fDtrControl = DTR_CONTROL_HANDSHAKE; dcb.fOutX = FALSE; dcb.fInX = FALSE; break; } case XonXoffFlowControl: { dcb.fOutxCtsFlow = FALSE; dcb.fOutxDsrFlow = FALSE; dcb.fOutX = TRUE; dcb.fInX = TRUE; dcb.XonChar = 0x11; dcb.XoffChar = 0x13; dcb.XoffLim = 100; dcb.XonLim = 100; break; } default: { ASSERT(FALSE); break; } } //Now that we have all the settings in place, make the changes SetState(dcb);}void CSerialPort::Close(){ if (IsOpen()) { //Close down the comms port BOOL bSuccess = CloseHandle(m_hComm); m_hComm = INVALID_HANDLE_VALUE; if (!bSuccess) TRACE(_T("Failed to close up the comms port, GetLastError:%d\n"), GetLastError()); m_bOverlapped = FALSE; //Free up the event object we are using if(m_hEvent) { CloseHandle(m_hEvent); m_hEvent = NULL; } }}void CSerialPort::Attach(HANDLE hComm, BOOL bOverlapped){ Close(); m_hComm = hComm; m_bOverlapped = bOverlapped; //Create the event we need for later synchronisation use m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (m_hEvent == NULL) { Close(); TRACE(_T("Failed in call to CreateEvent in Attach\n")); AfxThrowSerialException(); }}HANDLE CSerialPort::Detach(){ HANDLE hrVal = m_hComm; m_hComm = INVALID_HANDLE_VALUE; CloseHandle(m_hEvent); m_hEvent = NULL; return hrVal;}DWORD CSerialPort::Read(void* lpBuf, DWORD dwCount){ ASSERT(IsOpen()); ASSERT(!m_bOverlapped); DWORD Errors; COMSTAT comstat; BOOL ok; ok = ::ClearCommError(this->m_hComm,&Errors,&comstat); if(!ok) { return 0; } if(comstat.cbInQue==0) { return 0; } DWORD dwBytesRead = 0; if (!ReadFile(m_hComm, lpBuf, comstat.cbInQue, &dwBytesRead, NULL)) { TRACE(_T("Failed in call to ReadFile\n")); AfxThrowSerialException(); } return dwBytesRead;}BOOL CSerialPort::Read(void* lpBuf, DWORD dwCount, OVERLAPPED& overlapped, DWORD* pBytesRead){ ASSERT(IsOpen()); ASSERT(m_bOverlapped); DWORD dwBytesRead = 0; DWORD Errors; COMSTAT comstat; /* BOOL ok; ok = ::ClearCommError(this->m_hComm,&Errors,&comstat); if(!ok) { return false; } if(comstat.cbInQue==0) { return false; } */ BOOL bSuccess = ReadFile(m_hComm, lpBuf, dwCount, &dwBytesRead, &overlapped); if (!bSuccess) { if (GetLastError() != ERROR_IO_PENDING) { TRACE(_T("Failed in call to ReadFile\n")); AfxThrowSerialException(); } } else { if (pBytesRead) *pBytesRead = dwBytesRead; } return bSuccess;}DWORD CSerialPort::Write(const void* lpBuf, DWORD dwCount){ ASSERT(IsOpen()); ASSERT(!m_bOverlapped); DWORD dwBytesWritten = 0; if (!WriteFile(m_hComm, lpBuf, dwCount, &dwBytesWritten, NULL)) { TRACE(_T("Failed in call to WriteFile\n")); AfxThrowSerialException(); } return dwBytesWritten;}BOOL CSerialPort::Write(const void* lpBuf, DWORD dwCount, OVERLAPPED& overlapped, DWORD* pBytesWritten){ ASSERT(IsOpen()); ASSERT(m_bOverlapped); DWORD dwBytesWritten = 0; BOOL bSuccess = WriteFile(m_hComm, lpBuf, dwCount, &dwBytesWritten, &overlapped); if (!bSuccess) { if (GetLastError() != ERROR_IO_PENDING) { TRACE(_T("Failed in call to WriteFile\n")); AfxThrowSerialException(); } } else { if (pBytesWritten) *pBytesWritten = dwBytesWritten; } return bSuccess;}bool CSerialPort::GetOverlappedResult(OVERLAPPED& overlapped, DWORD& dwBytesTransferred, BOOL bWait){ ASSERT(IsOpen()); ASSERT(m_bOverlapped); BOOL bSuccess = ::GetOverlappedResult(m_hComm, &overlapped, &dwBytesTransferred, bWait); if (!bSuccess) { if (GetLastError() != ERROR_IO_PENDING) { TRACE(_T("Failed in call to GetOverlappedResult\n")); AfxThrowSerialException(); } } return bSuccess;}void CSerialPort::_OnCompletion(DWORD dwErrorCode, DWORD dwCount, LPOVERLAPPED lpOverlapped){ //Validate our parameters ASSERT(lpOverlapped); //Convert back to the C++ world CSerialPort* pSerialPort = (CSerialPort*) lpOverlapped->hEvent; ASSERT(pSerialPort); ASSERT(pSerialPort->IsKindOf(RUNTIME_CLASS(CSerialPort))); //Call the C++ function pSerialPort->OnCompletion(dwErrorCode, dwCount, lpOverlapped);}void CSerialPort::OnCompletion(DWORD /*dwErrorCode*/, DWORD /*dwCount*/, LPOVERLAPPED lpOverlapped){ //Just free up the memory which was previously allocated for the OVERLAPPED structure delete lpOverlapped; //Your derived classes can do something useful in OnCompletion, but don't forget to //call CSerialPort::OnCompletion to ensure the memory is freed up}void CSerialPort::CancelIo(){ ASSERT(IsOpen()); if (_SerialPortData.m_lpfnCancelIo == NULL) { TRACE(_T("CancelIo function is not supported on this OS. You need to be running at least NT 4 or Win 98 to use this function\n")); AfxThrowSerialException(ERROR_CALL_NOT_IMPLEMENTED); } if (!::_SerialPortData.m_lpfnCancelIo(m_hComm)) { TRACE(_T("Failed in call to CancelIO\n")); AfxThrowSerialException(); }}DWORD CSerialPort::BytesWaiting(){ ASSERT(IsOpen()); //Check to see how many characters are unread COMSTAT stat; GetStatus(stat); return stat.cbInQue;}BOOL CSerialPort::DataWaiting(DWORD dwTimeout){ ASSERT(IsOpen()); ASSERT(m_hEvent); //Setup to wait for incoming data DWORD dwOldMask; GetMask(dwOldMask); SetMask(EV_RXCHAR); //Setup the overlapped structure OVERLAPPED o; o.hEvent = m_hEvent; //Assume the worst; BOOL bSuccess = FALSE; DWORD dwEvent; bSuccess = WaitEvent(dwEvent, o); if (!bSuccess) { if (WaitForSingleObject(o.hEvent, dwTimeout) == WAIT_OBJECT_0) { DWORD dwBytesTransferred; GetOverlappedResult(o, dwBytesTransferred, FALSE); bSuccess = TRUE; } } //Reset the event mask SetMask(dwOldMask); return bSuccess;}void CSerialPort::WriteEx(const void* lpBuf, DWORD dwCount){ ASSERT(IsOpen()); OVERLAPPED* pOverlapped = new OVERLAPPED; ZeroMemory(pOverlapped, sizeof(OVERLAPPED)); pOverlapped->hEvent = (HANDLE) this; if (!WriteFileEx(m_hComm, lpBuf, dwCount, pOverlapped, _OnCompletion)) { delete pOverlapped; TRACE(_T("Failed in call to WriteFileEx\n")); AfxThrowSerialException(); }}void CSerialPort::ReadEx(void* lpBuf, DWORD dwCount){ ASSERT(IsOpen()); OVERLAPPED* pOverlapped = new OVERLAPPED; ZeroMemory(pOverlapped, sizeof(OVERLAPPED)); pOverlapped->hEvent = (HANDLE) this; if (!ReadFileEx(m_hComm, lpBuf, dwCount, pOverlapped, _OnCompletion)) { delete pOverlapped; TRACE(_T("Failed in call to ReadFileEx\n")); AfxThrowSerialException(); }}void CSerialPort::TransmitChar(char cChar){ ASSERT(IsOpen()); if (!TransmitCommChar(m_hComm, cChar)) { TRACE(_T("Failed in call to TransmitCommChar\n")); AfxThrowSerialException(); }}void CSerialPort::GetConfig(COMMCONFIG& config){ ASSERT(IsOpen()); DWORD dwSize = sizeof(COMMCONFIG); if (!GetCommConfig(m_hComm, &config, &dwSize)) { TRACE(_T("Failed in call to GetCommConfig\n")); AfxThrowSerialException(); }}void CSerialPort::SetConfig(COMMCONFIG& config){ ASSERT(IsOpen()); DWORD dwSize = sizeof(COMMCONFIG); if (!SetCommConfig(m_hComm, &config, dwSize)) { TRACE(_T("Failed in call to SetCommConfig\n")); AfxThrowSerialException(); }}void CSerialPort::SetBreak(){ ASSERT(IsOpen()); if (!SetCommBreak(m_hComm)) { TRACE(_T("Failed in call to SetCommBreak\n")); AfxThrowSerialException(); }}void CSerialPort::ClearBreak(){ ASSERT(IsOpen()); if (!ClearCommBreak(m_hComm)) { TRACE(_T("Failed in call to SetCommBreak\n")); AfxThrowSerialException(); }}void CSerialPort::ClearError(DWORD& dwErrors){ ASSERT(IsOpen()); if (!ClearCommError(m_hComm, &dwErrors, NULL)) { TRACE(_T("Failed in call to ClearCommError\n")); AfxThrowSerialException(); }}void CSerialPort::GetDefaultConfig(int nPort, COMMCONFIG& config){ //Validate our parameters ASSERT(nPort>0 && nPort<=255); //Create the device name as a string CString sPort; sPort.Format(_T("COM%d"), nPort); DWORD dwSize = sizeof(COMMCONFIG); if (!GetDefaultCommConfig(sPort, &config, &dwSize)) { TRACE(_T("Failed in call to GetDefaultCommConfig\n")); AfxThrowSerialException(); }}void CSerialPort::SetDefaultConfig(int nPort, COMMCONFIG& config){ //Validate our parameters ASSERT(nPort>0 && nPort<=255); //Create the device name as a string CString sPort; sPort.Format(_T("COM%d"), nPort); DWORD dwSize = sizeof(COMMCONFIG); if (!SetDefaultCommConfig(sPort, &config, dwSize)) { TRACE(_T("Failed in call to GetDefaultCommConfig\n")); AfxThrowSerialException(); }}void CSerialPort::GetStatus(COMSTAT& stat){ ASSERT(IsOpen()); DWORD dwErrors; if (!ClearCommError(m_hComm, &dwErrors, &stat)) { TRACE(_T("Failed in call to ClearCommError\n")); AfxThrowSerialException(); }}void CSerialPort::GetState(DCB& dcb){ ASSERT(IsOpen()); if (!GetCommState(m_hComm, &dcb)) { TRACE(_T("Failed in call to GetCommState\n")); AfxThrowSerialException(); }}void CSerialPort::SetState(DCB& dcb){ ASSERT(IsOpen()); if (!SetCommState(m_hComm, &dcb)) { TRACE(_T("Failed in call to SetCommState\n")); AfxThrowSerialException(); }}void CSerialPort::Escape(DWORD dwFunc){ ASSERT(IsOpen()); if (!EscapeCommFunction(m_hComm, dwFunc)) { TRACE(_T("Failed in call to EscapeCommFunction\n")); AfxThrowSerialException(); }}void CSerialPort::ClearDTR(){ Escape(CLRDTR);}void CSerialPort::ClearRTS(){ Escape(CLRRTS);}void CSerialPort::SetDTR(){ Escape(SETDTR);}void CSerialPort::SetRTS(){ Escape(SETRTS);}void CSerialPort::SetXOFF(){ Escape(SETXOFF);}void CSerialPort::SetXON(){ Escape(SETXON);}void CSerialPort::GetProperties(COMMPROP& properties){ ASSERT(IsOpen()); if (!GetCommProperties(m_hComm, &properties)) { TRACE(_T("Failed in call to GetCommProperties\n")); AfxThrowSerialException(); }}void CSerialPort::GetModemStatus(DWORD& dwModemStatus){ ASSERT(IsOpen()); if (!GetCommModemStatus(m_hComm, &dwModemStatus)) { TRACE(_T("Failed in call to GetCommModemStatus\n")); AfxThrowSerialException(); }}void CSerialPort::SetMask(DWORD dwMask){ ASSERT(IsOpen()); if (!SetCommMask(m_hComm, dwMask)) { TRACE(_T("Failed in call to SetCommMask\n")); AfxThrowSerialException(); }}void CSerialPort::GetMask(DWORD& dwMask){ ASSERT(IsOpen()); if (!GetCommMask(m_hComm, &dwMask)) { TRACE(_T("Failed in call to GetCommMask\n")); AfxThrowSerialException(); }}void CSerialPort::Flush(){ ASSERT(IsOpen()); if (!FlushFileBuffers(m_hComm)) { TRACE(_T("Failed in call to FlushFileBuffers\n")); AfxThrowSerialException(); }}void CSerialPort::Purge(DWORD dwFlags){ ASSERT(IsOpen()); if (!PurgeComm(m_hComm, dwFlags)) { TRACE(_T("Failed in call to PurgeComm\n")); AfxThrowSerialException(); }}void CSerialPort::TerminateOutstandingWrites(){ Purge(PURGE_TXABORT);}void CSerialPort::TerminateOutstandingReads(){ Purge(PURGE_RXABORT);}void CSerialPort::ClearWriteBuffer(){ Purge(PURGE_TXCLEAR);}void CSerialPort::ClearReadBuffer(){ Purge(PURGE_RXCLEAR);}void CSerialPort::Setup(DWORD dwInQueue, DWORD dwOutQueue){ ASSERT(IsOpen()); if (!SetupComm(m_hComm, dwInQueue, dwOutQueue)) { TRACE(_T("Failed in call to SetupComm\n")); AfxThrowSerialException(); }}void CSerialPort::SetTimeouts(COMMTIMEOUTS& timeouts){ ASSERT(IsOpen()); if (!SetCommTimeouts(m_hComm, &timeouts)) { TRACE(_T("Failed in call to SetCommTimeouts\n")); AfxThrowSerialException(); }}void CSerialPort::GetTimeouts(COMMTIMEOUTS& timeouts){ ASSERT(IsOpen()); if (!GetCommTimeouts(m_hComm, &timeouts)) { TRACE(_T("Failed in call to GetCommTimeouts\n")); AfxThrowSerialException(); }}void CSerialPort::Set0Timeout(){ COMMTIMEOUTS Timeouts; ZeroMemory(&Timeouts, sizeof(COMMTIMEOUTS)); Timeouts.ReadIntervalTimeout = MAXDWORD; SetTimeouts(Timeouts);}void CSerialPort::Set0WriteTimeout(){ COMMTIMEOUTS Timeouts; GetTimeouts(Timeouts); Timeouts.WriteTotalTimeoutMultiplier = 0; Timeouts.WriteTotalTimeoutConstant = 0; SetTimeouts(Timeouts);}void CSerialPort::Set0ReadTimeout(){ COMMTIMEOUTS Timeouts; GetTimeouts(Timeouts); Timeouts.ReadIntervalTimeout = MAXDWORD; Timeouts.ReadTotalTimeoutMultiplier = 0; Timeouts.ReadTotalTimeoutConstant = 0; SetTimeouts(Timeouts);}bool CSerialPort::WaitEvent(DWORD& dwMask){ ASSERT(IsOpen()); ASSERT(!m_bOverlapped); if (!WaitCommEvent(m_hComm, &dwMask, NULL)) { TRACE(_T("Failed in call to WaitCommEvent\n")); //AfxThrowSerialException(); return false; } else return true;}BOOL CSerialPort::WaitEvent(DWORD& dwMask, OVERLAPPED& overlapped){ ASSERT(IsOpen()); ASSERT(m_bOverlapped); ASSERT(overlapped.hEvent); BOOL bSuccess = WaitCommEvent(m_hComm, &dwMask, &overlapped); if (!bSuccess) { if (GetLastError() != ERROR_IO_PENDING) { TRACE(_T("Failed in call to WaitCommEvent\n")); AfxThrowSerialException(); } } return bSuccess;}
0 0
- CSerial 串口通信类
- CSerial串口类
- CSerial串口通信类的说明
- 串行通信类CSerial简介
- 串口类
- 串口类
- 串口类
- 上位机简单串口类,VC串口类, MFC串口
- 串口类 10以上串口号 \\\\.\\COM10
- vc串口枚举类
- 同步串口类
- 串口发送数据类
- 串口发送数据类
- SerialPort类连接串口
- 串口操作类
- 同步串口类
- 串口操作类
- 串口通讯类
- HDU1000
- 职场经典定律 http://my.weibo.com/u
- Markdown语法学习
- 【字符串拷贝函数】memcpy strcpy strncpy lstrcpy lstrncpy wstrcpy的区别
- 试卷导出,poi操作
- CSerial串口类
- Java7
- Struts的取值与标签
- JAVA学习心得--JDBC使用
- IPv4头部结构详解
- geoserver下载安装
- java之final的各种用法
- day10-jsp&ELのPPT学习1
- Unable to locate Spring NamespaceHandler for XML schema namespace异常解决