class IComEventHandler { public: virtual void onData(const unsigned char* data, size_t length) = 0; }; class CSimpleCom { public: CSimpleCom(const std::string& port, DWORD baud, IComEventHandler* handler = NULL) : mName(port) , mBaud(baud) , mPort(INVALID_HANDLE_VALUE) , mThread(INVALID_HANDLE_VALUE) , mRunning(false) , mHandler(handler) , mHasData(false) { InitializeCriticalSection(&mDataSection); } ~CSimpleCom() { close(); } void write(const unsigned char* data, size_t length) { EnterCriticalSection(&mDataSection); mWriteData.push_back(std::string(reinterpret_cast(data), length)); mHasData = true; LeaveCriticalSection(&mDataSection); } void open() { if (mThread == INVALID_HANDLE_VALUE) { mRunning = false; DWORD tid; mThread = CreateThread(NULL, NULL, StaticThreadStart, this, 0, &tid); while (!mRunning) Sleep(10); } } void close() { if (mRunning) { mRunning = false; WaitForSingleObject(mThread, 2000); CloseHandle(mThread); mThread = INVALID_HANDLE_VALUE; } } protected: void threadproc() { mPort = CreateFileA(mName.c_str(), GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); DCB serialParams = { 0 }; serialParams.DCBlength = sizeof(serialParams); GetCommState(mPort, &serialParams); serialParams.BaudRate = mBaud; serialParams.ByteSize = 8; serialParams.StopBits = 1; serialParams.Parity = 0; SetCommState(mPort, &serialParams); // Set timeouts COMMTIMEOUTS timeout = { 0 }; timeout.ReadIntervalTimeout = MAXDWORD; SetCommTimeouts(mPort, &timeout); mRunning = true; unsigned char buffer[512]; bool fast = false; while (mRunning) { fast = false; DWORD dwRead = 0; if (ReadFile(mPort, buffer, _countof(buffer), &dwRead, NULL)) { if (dwRead > 0) { if (mHandler) mHandler->onData(buffer, dwRead); fast = true; } } if (mHasData) { std::string data; EnterCriticalSection(&mDataSection); if (!mWriteData.empty()) { data = mWriteData.front(); mWriteData.pop_front(); } mHasData = !mWriteData.empty(); LeaveCriticalSection(&mDataSection); DWORD dwWritten = 0; WriteFile(mPort, data.c_str(), data.length(), &dwWritten, NULL); // TODO: handle dwWritten < data.length(); fast = true; } // TODO: WaitForSingleObject... Sleep(fast ? 10 : 100); } CloseHandle(mPort); mPort = INVALID_HANDLE_VALUE; EnterCriticalSection(&mDataSection); mWriteData.clear(); mHasData = false; LeaveCriticalSection(&mDataSection); } static DWORD __stdcall StaticThreadStart(LPVOID lp) { CSimpleCom* com = static_cast(lp); com->threadproc(); return 0; } private: std::string mName; DWORD mBaud; HANDLE mPort; HANDLE mThread; volatile bool mRunning; IComEventHandler* mHandler; CRITICAL_SECTION mDataSection; bool mHasData; std::list mWriteData; }; class xtest : public IComEventHandler { virtual void onData(const unsigned char* data, size_t length) { std::string dataStr(reinterpret_cast(data), length); std::cout << "<<" << dataStr << ">>" << std::flush; } }; void test_proc() { xtest x; CSimpleCom* com = new CSimpleCom("\\\\.\\COM34", 115200, &x); com->open(); Sleep(10000); std::cout << "Writing test" << std::endl; com->write(reinterpret_cast("test\n"), 5); Sleep(10000); std::cout << "Closing" << std::endl; com->close(); std::cout << "Closed" << std::endl; return; }