Member 12278335 Ответов: 1

Чтение последовательного порта Win API "C"


Я написал приложение Win API. Для чтения данных последовательного порта. Это было прекрасно работать в консольном приложении без каких-либо проблем. Когда я пробую его в приложении win32 gui, его дозатор работает нормально, некоторое время он читает несколько символов, только некоторое время его читает полный буфер...
Почему это происходит...

Эти данные я отправляю из Arduino, используя "последовательный.код println("NM0");"
мой код написан для подобного ниже fromat:-
Для чтения: 4(это байты для чтения) -> NM0(это данные)

мой выход показывает как показано ниже

Читать: 4 -> NM0

Читать: 1 ->


Читать: 4 -> NM1

Читать: 1 ->


Читать: 4 -> NM1

Читать: 1 ->


Читать: 2 -> Нм

Читать: 3 -> 0


Читать: 4 -> NM0

Читать: 1 ->


Читать: 1 -> N

Читать: 4 -> M0


Читать: 1 -> N

Читать: 4 -> M1


Читать: 1 -> N

Читать: 4 -> M1


Читать: 4 -> NM0

Читать: 1 ->


Читать: 4 -> NM1

Читать: 1 ->

Что я уже пробовал:

//----------------------Чтение файла порта..----------------------------

#pragma once

#include "stdafx.h"
#include <istream>
#include<ios>
#include <Windows.h>
#include <string>
#include <vector>
#include "myMonitor.h"
#include <sstream>

HANDLE hComm; // handel for the serial port
bool quit = false;
BOOL ConfigureSerialPort()
{
	// Setup Com Time Outs
	COMMTIMEOUTS TimeOuts = { 0 };
	TimeOuts.ReadIntervalTimeout = 1000; // Set Read Timeouts
	TimeOuts.ReadTotalTimeoutMultiplier = 500;
	TimeOuts.ReadTotalTimeoutConstant = 5000;
	TimeOuts.ReadIntervalTimeout = 1000;
	TimeOuts.WriteTotalTimeoutConstant = 2000; // Set Write Time Outs
	TimeOuts.WriteTotalTimeoutMultiplier = 500;

	if (!SetCommTimeouts(hComm, &TimeOuts))// Set Time Outs
	{
		myMonitor::sendTxt("\nERROR: Set Com time out faild.");
	};
	DCB dcb;

	if (!GetCommState(hComm, &dcb))
	{
		myMonitor::sendTxt("\nERROR: Get Comm state faild");
	}
	dcb.BaudRate = 9600; // Baud Rates
	dcb.ByteSize = 8;	// 8 bit per byte
	dcb.Parity = NOPARITY; // No Parity Bits
	dcb.StopBits = TWOSTOPBITS; //Two Stop Bits

	if (!SetCommState(hComm, &dcb))
	{
		myMonitor::sendTxt("\nERROR: Set Com State faild.");
		return true;
	}
	else
	{
		return false;
	}

	if (!PurgeComm(hComm, PURGE_TXCLEAR | PURGE_RXCLEAR))
	{
		myMonitor::sendTxt("\nERROR:  Comm Purge Faild.");
	}



}

BOOL OpenSerialPort(int portNo, int baud)
{
	hComm = CreateFile("\\\\.\\COM9", // COM Port
		GENERIC_READ | GENERIC_WRITE, // Configure for read and write
		0,
		NULL,
		OPEN_EXISTING, // Open existing without creating new 
		FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, //Synchrnous Mode.
		NULL);
	if (hComm == (HANDLE)-1)
	{
		myMonitor::sendTxt("\nPort opend");
		return 0;
	}
	else
	{
		ConfigureSerialPort();
		return 1;
	}

}

BOOL  ReadSerialPort()
{
	char lpBuffer[256];
	DWORD dwBytesRead = 256; // read bytes
	COMSTAT ComStat;
	DWORD dwErrorFlag;
	OVERLAPPED m_osRead;
	stringstream ss;

	stringstream st;
	memset(&m_osRead, 0, sizeof(OVERLAPPED));
	m_osRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	ClearCommError(hComm, &dwErrorFlag, &ComStat);
	dwBytesRead = min(dwBytesRead, (DWORD)ComStat.cbInQue);

	if (!dwBytesRead)return FALSE;

	BOOL bReadStatus;

	bReadStatus = ReadFile(hComm, lpBuffer, dwBytesRead, &dwBytesRead, &m_osRead);

	ss <<"To read: "<< (size_t)ComStat.cbInQue<<" -> ";

	for (size_t i = 0; i < (size_t)ComStat.cbInQue; i++)
	{
		if (i < sizeof(lpBuffer)) 
		{
			//printf("/n%c", lpBuffer[i]);
			ss << lpBuffer[i];
		}
	}
	
	myMonitor::sendTxt(ss.str());

	if (!bReadStatus) // if the Read File returns False
	{
		if (GetLastError() == ERROR_IO_PENDING)
		{
			WaitForSingleObject(m_osRead.hEvent, 2000);
			PurgeComm(hComm, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
			return dwBytesRead;
		}
		return 0;
	}
	PurgeComm(hComm, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);

	memset(lpBuffer, 0, sizeof(lpBuffer));
	return dwBytesRead;
}


void readPort()
{
	while (!quit)
	{
		ReadSerialPort();
	}
}


///--------------------------------------------------------------------------------------
//----------------------Последовательный графический интерфейс приложения ------------------------------------------
// SerialHUB.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "SerialHUB.h"
#include <Windows.h>
#include "Init.h"
#include "myMonitor.h"
#include "Port.h"

#define MAX_LOADSTRING 100
#define IDC_BUTTION_OPEN_COMM 20003
#define IDC_BUTTION_CLOSE_COMM 20004
#define IDC_BUTTION_REFRESH 20005
#define IDC_CMBOX_PORT 20006
#define IDC_TXTBOX 20007
#define IDC_LBL_STATUS 20008

#define WNDOW_WIDTH 1000
#define WNDOW_HEIGHT 600
#define BTN_GAP (WNDOW_WIDTH-500)


HWND buttion_ConnectFSX;
HWND buttion_DisConnectFSX;
HWND buttion_OpenComm;
HWND buttion_CloseComm;
HWND buttion_Refresh;
HWND cbBox_Com;
HWND txtbox_Monitor;
HWND staticLable;

//Serial port Handle

HANDLE RxThread;
DWORD ThreadID;

// Global Variables:
HINSTANCE hInst;                                // current instance
WCHAR szTitle[MAX_LOADSTRING];                  // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING];            // the main window class name

// Forward declarations of functions included in this code module:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: Place code here.

    // Initialize global strings
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_SERIALHUB, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // Perform application initialization:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_SERIALHUB));

    MSG msg;

    // Main message loop:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}



//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_SERIALHUB));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_SERIALHUB);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

//
//   FUNCTION: InitInstance(HINSTANCE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // Store instance handle in our global variable
   //hWnd = CreateWindowW(szWindowClass, (LPCWSTR)"CockpitX-MCP", WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
	   //CW_USEDEFAULT, CW_USEDEFAULT, 435, 350, nullptr, nullptr, hInstance, nullptr);


   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, CW_USEDEFAULT, WNDOW_WIDTH, WNDOW_HEIGHT, nullptr, nullptr, hInstance, nullptr);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);
   myMonitor monitor(&txtbox_Monitor);
   return TRUE;
}

//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_COMMAND  - process the application menu
//  WM_PAINT    - Paint the main window
//  WM_DESTROY  - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
	case WM_CREATE:
	{
		//Buttion
		buttion_OpenComm = CreateWindow("BUTTON", "Open COM", WS_CHILD | WS_VISIBLE | WS_BORDER | BS_PUSHBUTTON, BTN_GAP+283, 139, 121, 23, hWnd, (HMENU)IDC_BUTTION_OPEN_COMM, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL);
		buttion_CloseComm = CreateWindow("BUTTON", "Close COM", WS_CHILD | WS_VISIBLE | WS_BORDER | BS_PUSHBUTTON, BTN_GAP+283, 168, 121, 23, hWnd, (HMENU)IDC_BUTTION_CLOSE_COMM, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL);
		buttion_Refresh = CreateWindow("BUTTON", "Refresh", WS_CHILD | WS_VISIBLE | WS_BORDER | BS_PUSHBUTTON, BTN_GAP+283, 233, 121, 23, hWnd, (HMENU)IDC_BUTTION_REFRESH, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL);
		
		//Combo Box;
		cbBox_Com = CreateWindow("COMBOBOX", NULL, WS_CHILD | ES_READONLY | WS_VISIBLE | WS_BORDER | WS_TABSTOP | CBS_DROPDOWN, BTN_GAP+284, 80, 125, 225, hWnd, (HMENU)IDC_CMBOX_PORT, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL);
		initComboBox(&cbBox_Com);

		//Text Box;
		txtbox_Monitor = CreateWindow("EDIT", "Welcome\n", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_HSCROLL | WS_VSCROLL | ES_MULTILINE | WS_BORDER | ES_READONLY | ES_AUTOVSCROLL | ES_AUTOHSCROLL, 12, 12, BTN_GAP + 265, WNDOW_HEIGHT-100, hWnd, NULL, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL);

		// Set the buttion endbled condtion 
		EnableWindow(GetDlgItem(hWnd, IDC_BUTTION_OPEN_COMM), TRUE);
		EnableWindow(GetDlgItem(hWnd, IDC_BUTTION_CLOSE_COMM), FALSE);
		EnableWindow(GetDlgItem(hWnd, IDC_CMBOX_PORT), TRUE);
	}
	break;
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // Parse the menu selections:
            switch (wmId)
            {
			case IDC_BUTTION_OPEN_COMM :
			{
				//Greeting to Command execution
				myMonitor::sendTxt("\n Init Open");

				//Get the selected port from the combobox
				unsigned int index = 0;
				char strText[255] = { 0 };
				//Get Selected index form the combobox.
				index = SendMessage(cbBox_Com, CB_GETCURSEL, 0, 0);
				SendMessage(cbBox_Com, CB_GETLBTEXT, index, (LPARAM)strText);
				string str = strText;
				myMonitor::sendTxt("\n Selected Port is " + str);

				//ConfigureSerialPort();
				OpenSerialPort(9, 9600);

				RxThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)readPort, NULL, NULL, &ThreadID);


			}break;
			case IDC_BUTTION_CLOSE_COMM:
			{
				//Closing of the port.
				quit = true;
				WaitForSingleObject(RxThread, INFINITE);
				
				CloseHandle(RxThread);


			}break;

            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: Add any drawing code that uses hdc here...
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}

Gerry Schmitz

Разве вы никогда не "пишете" в последовательный порт? Откуда вы знаете, что посылается?

Member 12278335

Спасибо за повтор. Я мог бы успешно писать в последовательный порт. но попытка получить данные была проблемой.
данные принимаются . я контролирую через последовательный монитор(Eltima).

Rick York

У меня нет реального решения, но две вещи кажутся мне странными. Во-первых, ваши выходные сообщения говорят, что есть x байтов для чтения, но вы никогда не показываете столько байтов, которые были прочитаны. Что это значит на самом деле? Может быть, вам стоит сделать их колдовскую свалку или что-то в этом роде.

Во-вторых, я никогда не использовал перекрывающийся ввод-вывод. Я всегда устанавливаю порт в режим блокировки с коротким таймаутом, например 500 мс, чтобы периодически видеть, должен ли поток завершиться. В тот единственный раз, когда мы использовали перекрывающийся ввод-вывод, у нас был процесс, ожидающий нескольких событий, одним из которых был последовательный порт. Насколько я помню, было четыре возможных события : порт связи, внутреннее сообщение, клавиатура и завершение. В любом случае, вы можете подумать об этом.

KarstenK

Пожалуйста, добавьте его в качестве решения, чтобы закрыть вопрос&A.

Rick York

ОК.

[no name]

Рик Йорк определил проблему. Вы создаете перекрывающийся файл для чтения, но не обрабатываете события. Тот факт, что у вас что-то должно произойти, - это просто случайность. Документация всегда является лучшей отправной точкой, поэтому вы можете прочитать это, которое является хорошим объяснением того, как использовать потоки для обработки перекрывающихся событий. Последовательная Связь | Microsoft Docs[^]

1 Ответов

Рейтинг:
10

Rick York

Две вещи кажутся мне странными. Во-первых, ваши выходные сообщения говорят, что есть x байтов для чтения, но вы никогда не показываете столько байтов, которые были прочитаны. Что это значит на самом деле? Может быть, вам стоит сделать их колдовскую свалку или что-то в этом роде.

Во-вторых, я никогда не использовал перекрывающийся ввод-вывод. Я всегда устанавливаю порт в режим блокировки с коротким таймаутом, например 500 мс, чтобы периодически видеть, должен ли поток завершиться. В тот единственный раз, когда мы использовали перекрывающийся ввод-вывод, у нас был процесс, ожидающий нескольких событий, одним из которых был последовательный порт. Насколько я помню, было четыре возможных события : порт связи, внутреннее сообщение, клавиатура и завершение. В любом случае, вы можете подумать об этом.

В вашем случае вы устанавливаете порт в режим перекрытия (передавая структуру перекрытия), а затем никогда не обслуживаете события. Вы получаете персонажей из порта, потому что они случайно ждут вас там. Как уже упоминал pwasser, прочтите документацию по этой теме для получения дополнительной информации.