реклама на сайте
подробности

 
 
> API функция WriteFile
LAS9891
сообщение Aug 26 2015, 06:47
Сообщение #1


Местный
***

Группа: Участник
Сообщений: 206
Регистрация: 18-06-15
Пользователь №: 87 194



Привет всем. Пробую написать приложение для общения с COM-портом через API функции. Есть такой код в C++ Builder 6:
CODE
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "COMport.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------

HANDLE Com_Handle;




void __fastcall TForm1::ConnectBtnClick(TObject *Sender)
{
//-----------------------------------------------------------------
LPCTSTR lpFileName = "COM1";
DWORD dwDesiredAccess = GENERIC_READ|GENERIC_WRITE; // 0xС0000000
DWORD dwShareMode = NULL;
LPSECURITY_ATTRIBUTES lpSecurityAttributes = NULL;
DWORD dwCreationDistribution = OPEN_EXISTING; // 0x00000003
DWORD dwFlagsAndAttributes = NULL;
HANDLE hTemplateFile = NULL;
//-----------------------------------------------------------------


Com_Handle = CreateFile(lpFileName,
dwDesiredAccess,
dwShareMode,
lpSecurityAttributes,
dwCreationDistribution,
dwFlagsAndAttributes,
hTemplateFile);
}
//---------------------------------------------------------------------------




void __fastcall TForm1::SendBtnClick(TObject *Sender)
{

BOOL SendState ;

LPCVOID lpBuffer; // Указатель на буфер, содержащий данные, которые будут записаны в файл.
DWORD nNumberOfBytesToWrite ;//= 1;// Число байтов, которые будут записаны в файл.
LPDWORD lpNumberOfBytesWritten; // Указатель на переменную, которая получает число записанных байтов
LPOVERLAPPED lpOverlapped; // Указатель на структуру OVERLAPPED



SendState = WriteFile(Com_Handle,
lpBuffer,
nNumberOfBytesToWrite,
lpNumberOfBytesWritten,
lpOverlapped);


//CloseHandle(Com_Handle);

}
//---------------------------------------------------------------------------




void __fastcall TForm1::Button3Click(TObject *Sender)
{

}
//---------------------------------------------------------------------------


На форме расположены 3 кнопки. Кнопка ConnectBtn открывает COM-порт. А кнопкой SendBtn пробую что-нибудь отправить. Проект компилируется и запускается, но при нажатии на кнопку SendBtn возникает error вот в таком окне:
Прикрепленное изображение


Что я делаю не так в функции WriteFile? Как исправить?
Go to the top of the page
 
+Quote Post
 
Start new topic
Ответов
LAS9891
сообщение Sep 3 2015, 04:06
Сообщение #2


Местный
***

Группа: Участник
Сообщений: 206
Регистрация: 18-06-15
Пользователь №: 87 194



Получилось запустить в синхронном режиме. Стало ясно, что в этом режиме, вызывая функцию чтения данных ReadFile(), я не знаю заранее есть ли данные для чтения. Можно циклически проверять их наличие, но это приводит к дополнительным расходам времени ЦП. Поэтому на практике часто удобней использовать асинхронный режим.
Я нашел вот такую статью http://piclist.ru/S-COM-THREAD-RUS/S...AD-RUS.html#22 и решил попробовать написать похожую программу в C++ Builder 6. Код такой:

CODE

#include <vcl.h>
#pragma hdrstop

#include <io.h> // для работы с файлами
#include <fcntl.h> // для работы с файлами
#include <sys\stat.h> // для работы с файлами


#include "COMport.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------




//-Объявления глобальных переменных-----------------------------------------------------------------------------------------
#define BUFSIZE 255 //ёмкость буфера

unsigned char bufrd[BUFSIZE], bufwr[BUFSIZE]; //приёмный и передающий буферы

HANDLE COMport; //дескриптор порта

//структура OVERLAPPED необходима для асинхронных операций, при этом для операции чтения и записи нужно объявить разные структуры
//эти структуры необходимо объявить глобально, иначе программа не будет работать правильно
OVERLAPPED overlapped; //будем использовать для операций чтения (см. поток ReadThread)
OVERLAPPED overlappedwr; //будем использовать для операций записи (см. поток WriteThread)

int handle; //дескриптор для работы с файлом с помощью библиотеки <io.h>

bool fl=0; //флаг, указывающий на успешность операций записи (1 - успешно, 0 - не успешно)

unsigned long counter; //счётчик принятых байтов, обнуляется при каждом открытии порта



char *buf_out;// = "Test string";
char *buf_in;
DWORD bc;
DCB *dcb;


char NumOut = 0;
char NumIn = 0;

String portname;

//--------------------------------------------------------------------------------------------------------------------------------




//-Объявления потоков---------------------------------------------------------------------------------------------------------------
HANDLE reader; //дескриптор потока чтения из порта
HANDLE writer; //дескриптор потока записи в порт

DWORD WINAPI ReadThread(LPVOID);
DWORD WINAPI WriteThread(LPVOID);
//--------------------------------------------------------------------------------------------------------------------------------





//-Поток ReadThead--------------------------------------------------------------------------------------------------------------------

void ReadPrinting(void);

//---------------------------------------------------------------------------

//главная функция потока, реализует приём байтов из COM-порта
DWORD WINAPI ReadThread(LPVOID)
{
COMSTAT comstat; // структура текущего состояния порта, в данной программе используется для определения количества принятых в порт байтов
DWORD btr, temp, mask, signal; // переменная temp используется в качестве заглушки

overlapped.hEvent = CreateEvent(NULL, true, true, NULL); // создать сигнальный объект-событие для асинхронных операций
SetCommMask(COMport, EV_RXCHAR); // установить маску на срабатывание по событию приёма байта в порт
while(1) // пока поток не будет прерван, выполняем цикл
{
WaitCommEvent(COMport, &mask, &overlapped); //ожидать события приёма байта (это и есть перекрываемая операция)
signal = WaitForSingleObject(overlapped.hEvent, INFINITE); //приостановить поток до прихода байта

if(signal == WAIT_OBJECT_0) //если событие прихода байта произошло
{
if(GetOverlappedResult(COMport, &overlapped, &temp, true)) //проверяем, успешно ли завершилась перекрываемая операция WaitCommEvent
if((mask & EV_RXCHAR)!=0) //если произошло именно событие прихода байта
{
ClearCommError(COMport, &temp, &comstat); // нужно заполнить структуру COMSTAT
btr = comstat.cbInQue; // и получить из неё количество принятых байтов
if(btr) // если действительно есть байты для чтения
{
ReadFile(COMport, bufrd, btr, &temp, &overlapped); // прочитать байты из порта в буфер программы
counter+=btr; // увеличиваем счётчик байтов
ReadPrinting(); // вызываем функцию для вывода данных на экран и в файл
}
}
}
}
}


//---------------------------------------------------------------------------

//выводим принятые байты на экран
void ReadPrinting()
{
Form1->MemoOut->Lines->Add((char*)bufrd); //выводим принятую строку в Memo
memset(bufrd, 0, BUFSIZE); //очистить буфер (чтобы данные не накладывались друг на друга)

}





//-Поток WriteThead-----------------------------------------------------------------------------------------------------------------------

//главная функция потока, выполняет передачу байтов из буфера в COM-порт
DWORD WINAPI WriteThread(LPVOID)
{

DWORD temp, signal; //temp - переменная-заглушка

overlappedwr.hEvent = CreateEvent(NULL, true, true, NULL); //создать событие
while(1)
{
WriteFile(COMport, bufwr, strlen(bufwr), &temp, &overlappedwr); //записать байты в порт (перекрываемая операция!)
signal = WaitForSingleObject(overlappedwr.hEvent, INFINITE); //приостановить поток, пока не завершится перекрываемая операция WriteFile

if((signal == WAIT_OBJECT_0) && (GetOverlappedResult(COMport, &overlappedwr, &temp, true))) //если операция завершилась успешно
{
Form1->StatusBar->Panels->Items[0]->Text = "Передача прошла успешно"; //вывести сообщение об этом в строке состояния
}

else
{
Form1->StatusBar->Panels->Items[0]->Text = "Ошибка передачи"; // иначе вывести в строке состояния сообщение об ошибке
}

SuspendThread(writer);

}
}
//-----------------------------------------------------------------------------------------------------------------------------------










void __fastcall TForm1::ConnectBtnClick(TObject *Sender)
{
COMMTIMEOUTS timeouts;


//-Открытие порта----------------------------------------------------------------------------------------------------------------
portname = ChoiceCOM->Text;
COMport=CreateFile(portname.c_str(), GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
//здесь:
// - portname.c_str() - имя порта в качестве имени файла,
// c_str() преобразует строку типа String в строку в виде массива типа char, иначе функция не примет
// - GENERIC_READ | GENERIC_WRITE - доступ к порту на чтение/записть
// - 0 - порт не может быть общедоступным (shared)
// - NULL - дескриптор порта не наследуется, используется дескриптор безопасности по умолчанию
// - OPEN_EXISTING - порт должен открываться как уже существующий файл
// - FILE_FLAG_OVERLAPPED - этот флаг указывает на использование асинхронных операций
// - NULL - указатель на файл шаблона не используется при работе с портами

if(COMport==INVALID_HANDLE_VALUE)
{
MemoOut->Lines-> Add("Не удалось открыть последовательный порт.");
return;
}
//-------------------------------------------------------------------------------------------------------------------------------------------



//-Инициализация порта----------------------------------------------------------------------------------------------------------------
dcb=(DCB*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(DCB)); // Выделение области памяти для DCB из кучи и заполнение этой области нулями.

if(!GetCommState(COMport, dcb)) // Эта функция заполняет DCB информацией о текущем состоянии устройства, точнее о его настройках.
{// Если не удалось - закрыть порт и вывести сообщение об ошибке в строке состояния.
HeapFree(GetProcessHeap(),0,dcb); // Освобождение памяти из кучи.
CloseHandle(COMport);
MemoOut->Text = "Не удалось считать DCB";
return;
}

//-Инициализация структуры DCB.
dcb->BaudRate = StrToInt(ChoiceSpeed->Text); // задаём скорость передачи 115200 бод
dcb->fBinary = TRUE; // включаем двоичный режим обмена
dcb->fOutxCtsFlow = FALSE; // выключаем режим слежения за сигналом CTS
dcb->fOutxDsrFlow = FALSE; // выключаем режим слежения за сигналом DSR
dcb->fDtrControl = DTR_CONTROL_DISABLE; // отключаем использование линии DTR
dcb->fDsrSensitivity = FALSE; // отключаем восприимчивость драйвера к состоянию линии DSR
dcb->fNull = FALSE;//TRUE // разрешить приём нулевых байтов
dcb->fRtsControl = RTS_CONTROL_DISABLE; // отключаем использование линии RTS
dcb->fAbortOnError = FALSE; // отключаем остановку всех операций чтения/записи при ошибке
dcb->ByteSize = 8; // задаём 8 бит в байте
dcb->Parity = 0; // отключаем проверку чётности
dcb->StopBits = 0; // задаём один стоп-бит
//dcb->DCBlength=sizeof(DCB);

//-Загрузить структуру DCB в порт.
if(!SetCommState(COMport, dcb))
{// Если не удалось - закрыть порт и вывести сообщение об ошибке в строке состояния.
HeapFree(GetProcessHeap(),0,dcb); // Освобождение памяти из кучи.
CloseHandle(COMport);
MemoOut->Text = "Не удалось установить DCB.";
return;
}

//-Установка таймаутов.
timeouts.ReadIntervalTimeout = 0; // таймаут между двумя символами
timeouts.ReadTotalTimeoutMultiplier = 0; // общий таймаут операции чтения
timeouts.ReadTotalTimeoutConstant = 0; // константа для общего таймаута операции чтения
timeouts.WriteTotalTimeoutMultiplier = 0; // общий таймаут операции записи
timeouts.WriteTotalTimeoutConstant = 0; // константа для общего таймаута операции записи

//-Загрузить структуру таймаутов в порт.
if(!SetCommTimeouts(COMport, &timeouts))
{// Если не удалось - закрыть порт и вывести сообщение об ошибке в строке состояния.
CloseHandle(COMport);
MemoOut->Text = "Не удалось установить тайм-ауты.";
return;
}

//-Установить размеры очередей приёма и передачи
// SetupComm(COMport,2000,2000);
//---------------------------------------------------------------------------------------------------------------------------------------------


PurgeComm(COMport,PURGE_RXCLEAR); // Очистка буферов порта.

ConnectBtn->Enabled = false;
DisconnectBtn->Enabled = true;



//-Создание потоков на WINAPI:

// Cоздаём поток чтения, который сразу начнёт выполняться (предпоследний параметр = 0).
reader = CreateThread(NULL, 0, ReadThread, NULL, 0, NULL);

// Cоздаём поток записи в остановленном состоянии (предпоследний параметр = CREATE_SUSPENDED).
writer = CreateThread(NULL, 0, WriteThread, NULL, CREATE_SUSPENDED, NULL);


counter = 0;

//------------------------------------------------------------------------------------------------------------------------------------------------------
}





//--------------------------------------------------------------------------------------------------------------------------------------------
void __fastcall TForm1::DisconnectBtnClick(TObject *Sender)
{

//Примечание: так как при прерывании потоков, созданных с помощью функций WinAPI, функцией TerminateThread
// поток может быть прерван жёстко, в любом месте своего выполнения, то освобождать дескриптор
// сигнального объекта-события, находящегося в структуре типа OVERLAPPED, связанной с потоком,
// следует не внутри кода потока, а отдельно, после вызова функции TerminateThread.
// После чего нужно освободить и сам дескриптор потока.

if(writer) //если поток записи работает, завершить его; проверка if(writer) обязательна, иначе возникают ошибки
{
TerminateThread(writer,0);
CloseHandle(overlappedwr.hEvent); //нужно закрыть объект-событие
CloseHandle(writer);
}

if(reader) //если поток чтения работает, завершить его; проверка if(reader) обязательна, иначе возникают ошибки
{
TerminateThread(reader,0);
CloseHandle(overlapped.hEvent); //нужно закрыть объект-событие
CloseHandle(reader);
}

CloseHandle(COMport); //закрыть порт
COMport=0; //обнулить переменную для дескриптора порта

ConnectBtn->Enabled = true;
DisconnectBtn->Enabled = false;

NumIn=0;
NumOut=0;
}
//--------------------------------------------------------------------------------------------------------------------------------




//---------------------------------------------------------------------------
void __fastcall TForm1::SendBtnClick(TObject *Sender)
{
memset(bufwr,0,BUFSIZE); // очистить программный передающий буфер, чтобы данные не накладывались друг на друга
PurgeComm(COMport, PURGE_TXCLEAR); // очистить передающий буфер порта
strcpy(bufwr,MemoOut->Text.c_str()); // занести в программный передающий буфер строку из Edit1
ResumeThread(writer); // активировать поток записи данных в порт
}

При запуске программы, отправка вроде работает, но на входе ничего нет. Мне кажется, что ошибка где-то в функции DWORD WINAPI ReadThread(LPVOID), а именно не выполняется условие if(signal == WAIT_OBJECT_0). Подскажите, где я накосячил?
Go to the top of the page
 
+Quote Post



Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 25th August 2025 - 06:34
Рейтинг@Mail.ru


Страница сгенерированна за 0.01398 секунд с 7
ELECTRONIX ©2004-2016