Перейти к содержанию
    

Раньше никогда не сталкивался с необходимостью осваивать WinAPI в части работы с COM-портами, другие способы коммуникации как-то больше волновали. А теперь вот не могу справится с простейшей на мой первоначальный взгляд задачей - реализация асинхронного обмена через "банальный" COM-порт. К COM-порту подключен модем, "понимающий" стандартный набор AT-команд. Вот и решил я далеко не ходить и не разгребать глюки готовых классов и модулей, реализованных другими разработчиками. Лицензионные продукты для своей простой задачи не вижу смысла покупать. Вот пал выбор на WinAPI. Вроде бы и информации в сети валом и msdn внимательно почитал, но у меня то ли неправильно выстроился алгоритм программы то ли дядюшка билли со своими извращенными алгоритмами всевозможных ограничений прав доступа к памяти мешает. Прошу помощи.

 

Успешно открываю порт

 

-- kernel32 - подгружаемая библиотека WinAPI-функций
-- comid - номер открываемого порта
-- handle - итоговый указатель на порт
handle = kernel32.CreateFileA("\\\\.\\COM" .. comid, GENERIC_READ+GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0)

 

Далее я создаю и заполняю структуры BCD и COMMTIMEOUTS. Сохраняю настройки с помощью функций SetCommState, SetCommTimeouts - обе возвращают TRUE.

 

DCB.BaudRate = CBR_9600;
DCB.ByteSize = 8;
DCB.Parity = NOPARITY;
DCB.StopBits = ONESTOPBIT;
DCB.fBinary = 1;
DCB.fOutxCtsFlow = 0;
DCB.fOutxDsrFlow = 0;
DCB.fDtrControl = DTR_CONTROL_DISABLE;
DCB.fDsrSensitivity = 0;
DCB.fNull = 0;
DCB.fRtsControl = RTS_CONTROL_DISABLE;
DCB.fAbortOnError = 0;

COMMTIMEOUTS.ReadIntervalTimeout = 10;
COMMTIMEOUTS.ReadTotalTimeoutMultiplier = 1;
COMMTIMEOUTS.ReadTotalTimeoutConstant = 100;
COMMTIMEOUTS.WriteTotalTimeoutMultiplier = 0;
COMMTIMEOUTS.WriteTotalTimeoutConstant = 0;

 

Далее пытаюсь отправить данные в порт

 

-- data - массив с данными
-- b - указатель на переменную типа DWORD "счетчик байт"
-- t - указатель на буфер с данными
-- n -  кол-во данных в массиве data
local n = Table.Count(data);
local b = MemoryEx.Allocate(4);
local t = MemoryEx.Allocate(n);
if (b and t) then
    -- сброс счетчика байт
    MemoryEx.Fill(b, 4, 0, MEMEX_BYTE);
    -- заполнение буфера данными из массива data
    local c = 1;
    repeat
        MemoryEx.Byte(t + c - 1, data[c]);
        c = c + 1;
    until c > n;
    -- запись в порт
    if (kernel32.WriteFile(handle, t, n, b, 0) == 0) then
        local e = kernel32.GetLastError();
        Dialog.Message("error", e);
    end
end

 

После попытки записать что-либо в порт функция возвращает 5, т.е. ACCESS DENIED. Что я упустил?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Попробуйте заменить

FILE_FLAG_OVERLAPPED

на

FILE_FLAG_NO_BUFFERING

 

А еще конкретизируйте номер порта.

Т.е.

"\\\\.\\COM"

замените на COM с номером, типа:

"\\\\.\\COM1"

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Поскольку Вы при открытии файла указали флаг FILE_FLAG_OVERLAPPED, то при вызове процедуры WriteFile должны обязательно передавать указатель на OVERLAPPED структуру (этот указатель не может принимать значение NULL).

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Xenia, я пробовал играться с этим флагом. Я так понял вариантов для com-портов (не файлов) всего два: 0 (для синхронного обмена, он проще) и FILE_FLAG_OVERLAPPED (для асинхронного). Ни тот ни другой, ни предложенный Вами не меняют итоговый результат - ACCESS DENIED.

 

Дополнительно отмечу несколько моментов:

 

- мое приложение после компиляции запускается от имени администратора (ОС Windows 7 x64);

 

- память под буфер передачи (данных) я формирую аналогично выделению памяти под упомянутые структуры (т.е. данные участки памяти реально доступны для модификации, я перепроверял несколько раз различными способами);

 

- com-порт не используется другими процессами (т.е. работе с ним ничто не мешает);

 

- пробовал работать с портом программами типа putty - все получается (и открыть и отправить и принять);

 

Есть несколько подозрительных вопросов:

 

1. Я самостоятельно могу запросить кусок памяти своими средствами для буфера приема/передачи или я должен пользоваться какими-то дополнительными WinAPI-функциями для задания такой области памяти под буферизацию? В моем коде я "сам" выделяю память и передаю ее поинтер в функцию WriteFile.

 

2. Необходимо ли для простой отправки данных типа "Hello world" создание каких либо дополнительных структур типа OVERLAPED или структур отвечающих за ошибки обмена и события при обмене? Может из-за отсутствия этих или каких-то либо других дополнительных структур функция WriteFile возвращает ошибку доступа?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Поскольку Вы при открытии файла указали флаг FILE_FLAG_OVERLAPPED, то при вызове процедуры WriteFile должны обязательно передавать указатель на OVERLAPPED структуру (этот указатель не может принимать значение NULL).

 

Спасибо за ответ. Я пробовал сбрасывать этот флаг до значения 0. При этом ошибка ACCESS DENIED все равно возвращается функцией WriteFile.

 

Ну, а номер COM-порту вы назначили?

 

Обижаете, конечно. Порт успешно открывается. Во-первых возвращается корректный handle (не 0 и не -1). Дополнительно после открытия порта делал проверку - запускал putty и убеждался в занятости порта. Номер порта в моем случае 52, я передаю этот номер в функцию CreateFileA.

 

замените на COM с номером, типа:

"\\\\.\\COM1"

 

Для универсальности я беру строку "\\\\.\\COM" и "прилепляю" к концу этой строки номер порта. Т.е. задаю параметр comid = 52 и передаю в функцию. В моем посте #1 в первом куске кода это видно. Я пока играюсь в visual-среде, "движок" которой написан на Lua.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Неужели ни у кого нет опыта написания библиотеки/модуля для работы с com-портом с использованием WinAPI? Если есть опыт, подскажите перечень обязательных структур/переменных, которые необходимо создать/заполнить/передать/вернуть, чтобы можно было отправить данные и принять ответ. Хотя бы в синхронном "топорном" режиме для начала, можно без обработки событий и прочих доп.функций (с ними я по ходу дела ознакомлюсь). В инете ничего не могу найти, кроме обрывков, которые не дают отчетливого представления хотя бы для того, чтобы отправить в порт "Hello world". Хелп!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Да есть опыт... Но ничего такого не видно, почему бы оно не должно у Вас работать....

 

hc=CreateFile(port,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,NU
LL);

....
BuildCommDCB(DEFDCB_STRING,&dcb);
memset(&cc,0,sizeof(cc));
cc.dwSize=sizeof(cc);
cc.wVersion=1;
cc.dwProviderSubType=PST_RS232;
memcpy(&cc.dcb,dcb,sizeof(DCB));
  
rc=SetCommConfig(hc,&cc,sizeof(cc));

....

  memset(&ct,0,sizeof(ct));
  ct.ReadIntervalTimeout=200;
  ct.ReadTotalTimeoutConstant=250;
  SetCommTimeouts(hc,&ct);

....

  EscapeCommFunction(hc,SETDTR);
  EscapeCommFunction(hc,SETRTS);

....

WriteFile(hc,ovr->data.buf,bytes,&dummy,(LPOVERLAPPED) ovr);

 

И все арбайтен. Ну то, что ссылка на буфер данных хранится в структуре OVERLAPPED, это уже нюансы реализации. Это совершенно не обязательно.

 

И что NULL перенесло на NU и LL - это не я, это форум глючит.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

До кучи :)

 

bool CComm6055::exchangeData( QByteArray & cmd )
{
COMMTIMEOUTS ct;
DCB dcb;
HANDLE h;
BOOL rc;
bool result = false;
DWORD bytesWritten, bytesRead;

//
h = CreateFile(
	(LPCWSTR)sPort.unicode()
	, GENERIC_READ | GENERIC_WRITE
	, 0
	, 0
	, OPEN_EXISTING
	, 0
	, 0 );
//
if ( INVALID_HANDLE_VALUE != h )
{
	//
	ZeroMemory( &dcb, sizeof(dcb) );
	dcb.BaudRate = CBR_9600;
	dcb.fBinary = TRUE;
	dcb.ByteSize = 8;
	rc = SetCommState( h, &dcb );
	//
	ct.ReadIntervalTimeout = 64;
	ct.ReadTotalTimeoutConstant = 0;
	ct.ReadTotalTimeoutMultiplier = 0;
	ct.WriteTotalTimeoutConstant = 0;
	ct.WriteTotalTimeoutMultiplier = 0;
	rc = SetCommTimeouts( h, &ct );
	//
	makeCheckSum( cmd );
	//
	if ( WriteFile(
		h
		, cmd.data()
		, cmd.size()
		, &bytesWritten
		, 0 )
		)
	{
		//
		if ( cmd.size() == bytesWritten )
		{
			//
			cmd.resize( 32 );
			//
			if ( ReadFile(
				h
				, cmd.data()
				, 32
				, &bytesRead
				, 0 )
				)
			{
				//
				cmd.resize( bytesRead );
				//
				result = true;
			}
		}
	}
	//
	CloseHandle( h );
}
//
return result;
}

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Неужели ни у кого нет опыта написания библиотеки/модуля для работы с com-портом с использованием WinAPI?
Опыт есть. Один раз настроил и забыл. Копипаст из проекта в проект. Работает как часы.

 

bool MainWindow::openComPort(bool is19200)
{
    std::string nameComPort = comPort->currentText().toStdString();
    std::wstring wComPort;
    wComPort.resize(nameComPort.size());
    for(unsigned int i=0; i<nameComPort.size(); i++)
        wComPort[i] = (WCHAR)nameComPort.at(i);
    wComPort.insert(0, L"\\\\.\\");
    port=CreateFile( wComPort.c_str(), GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
    if(port == INVALID_HANDLE_VALUE)
    {
        QApplication::restoreOverrideCursor();
        QMessageBox::critical(this, "Spyhole error", QString("Can not open com port %1").arg(QString::fromStdString(nameComPort)));
        return false;
    }
    else
    {
        GetCommState(port,&dcb);
        //memset(&dcb, 0, sizeof(DCB));
        dcb.DCBlength=sizeof(DCB);
        std::wstring config;

        std::string tConfig = is19200 ? "baud=19200 parity=N data=8 stop=1" : "baud=115200 parity=N data=8 stop=1";
        config.resize(tConfig.size());
        for(unsigned int i=0; i<tConfig.size(); i++)
            config[i] = (WCHAR)tConfig.at(i);

        BuildCommDCB(config.c_str(),&dcb);
        dcb.fNull=FALSE;
        ct.ReadIntervalTimeout=1000;
        ct.ReadTotalTimeoutMultiplier=0;
        ct.ReadTotalTimeoutConstant=5000;
        if( !SetCommState(port,&dcb))
        {
            CloseHandle(port);
            port = NULL;
            QApplication::restoreOverrideCursor();
            QMessageBox::critical(this, "Spyhole error", QString("Cann't set config of  %1").arg(QString::fromStdString(nameComPort)));
            return false;
        }
        SetCommTimeouts(port,&ct);
        PurgeComm(port,PURGE_TXCLEAR|PURGE_RXCLEAR);
        SetupComm(port,4500,4500);
    }
    return true;
}

 

bool MainWindow::readCurve(CURVE curve, unsigned char *replay)
{
    unsigned char command[20];
...

    WriteFile(port,command, 8, &BytesCnt, NULL);

    ReadFile(port, replay, 4000, &BytesCnt, NULL);
    if(BytesCnt != 4000 )
    {
        QApplication::restoreOverrideCursor();
        QMessageBox::critical(this, "Spyhole error", QString("Can not receive 4000 byte. Received %1 bytes").arg(BytesCnt));
        return false;
    }
    return true;
}

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В общем приходится периодически возвращаться к этой теме :) видимо, хочешь не хочешь, а освоить работу с com-портами мне придется. На текущий момент повспоминал все что было, решил создать простенькое windowed приложение для теста com-портов. Так вот, возвращаясь к ранее обсуждаемой на этом форуме проблеме, сообщаю, что как и ранее открыть порт удается, настроить тоже (проверяю записаны ли настройки непосредственно по поинтеру зарезервированного под них куска памяти). Записать данные удалось (вроде бы тут должно быть "уррра!!!"), но только в одном случае - при открытии порта функцией CreateFile я передаю ей параметр dwDesiredAccess равный GENERIC_WRITE. Если при вызове этой функции я указываю GENERIC_READ + GENERIC_WRITE, то запись в порт не происходит. Порт открываю пока обычном режиме (NOT OVERLAPPED). Вопрос: если я открываю порт в НЕ асинхронном режиме, то я могу открыть его только "в одностороннем режиме", т.е. открываю порт для записи, потом закрываю порт и если мне нужно получить ответ от устройства, то я снова открываю порт но уже в режиме чтения и читаю данные? Бредовенько как-то.. В инете куча примеров где в 100% случаев права при вызове функции CreateFile указываются именно как "GENERIC_READ | GENERIC_WRITE", что за фигня у меня, у кого какие мысли?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

что за фигня у меня, у кого какие мысли?

Есть такая мысль, что (GENERIC_READ + GENERIC_WRITE) в вашем случае оказывается не равно (GENERIC_READ | GENERIC_WRITE).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Есть такая мысль, что (GENERIC_READ + GENERIC_WRITE) в вашем случае оказывается не равно (GENERIC_READ | GENERIC_WRITE).

хмм...

#define GENERIC_READ                     0x80000000
#define GENERIC_WRITE                    0x40000000
#define GENERIC_EXECUTE                  0x20000000
#define GENERIC_ALL                      0x10000000

 

будут они равны, хоть ОРь, хоть складывай

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

будут они равны, хоть ОРь, хоть складывай

При сложении будет учтен знак, если константы signed.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

При сложении будет учтен знак, если константы signed.

Ну константы как-то не предполагают знак

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...