Текущий архив: 2007.02.25;
Скачать: CL | DM;
ВнизЗапись в COM-порт - WriteFile Найти похожие ветки
← →
Mr. D. (2007-01-29 21:40) [0]Мне нужно асинхронно записывать в COM-порт, то есть послать данные и чтобы управление сразу вернулась, а Windows там уже сама решила когда эти данные можно отправить.
В интернете встречается такой код:OverWrite: TOverlapped;
WriteBytes: array of Byte;
...
OverWrite.hEvent := CreateEvent(nil, True, False, nil);
WriteFile(hPort, WriteBytes, SizeOf(WriteBytes), dwWrite, @OverWrite);
В общем то, что нужно... Но ведь каждому CreateEvent нужно делать соответственно CloseHandle. А когда его делать?
Я же не могу написать так:OverWrite: TOverlapped;
WriteBytes: array of Byte;
...
OverWrite.hEvent := CreateEvent(nil, True, False, nil);
WriteFile(hPort, WriteBytes, SizeOf(WriteBytes), dwWrite, @OverWrite);
CloseHandle(OverWrite.hEvent);
Созданноемной событие может использоваться? А тогда когда мне делать CloseHandle?
Я конечно могу сделать аля:if not WaitCommEvent(hPort, dwMask, @OverWrite) then
WaitForSingleObject(OverWrite.hEvent, INFINITE);
CloseHandle(OverWrite.hEvent);
но это глупо, тем самым повторю синхронную запись...
← →
Kolan © (2007-01-29 21:45) [1]«[0] Mr. D. (29.01.07 21:40)
Мыло дай я тебе кину то что я делал…
← →
Mr. D. (2007-01-29 21:47) [2]roast_84|а тута вот собачка должна быть точно|mail.ru
Но мне огромный компонент и не нужен, мне бы только то место асинхронной записи с CreateEvent и что с ним делать...
← →
Kolan © (2007-01-29 21:55) [3]«Но мне огромный компонент и не нужен»
Ну тогда:function TAnisochronousComm.Write(const Buffer: array of Byte;
NumberOfBytesToWrite: Cardinal): Cardinal;
begin
Result := 0;
if FPortHandle <> INVALID_HANDLE_VALUE then
begin
WriteFile(FPortHandle, Buffer, NumberOfBytesToWrite, Cardinal(Result),
@FOverlappedWrite);
end;
end;
Помогло?
Класс выглядит так:TAnisochronousComm = class
private
FPortHandle: THandle;
FDcb: TDCB;
FCommTimeOuts: TCommTimeouts;
FCommStat: TComStat;
FOverlappedRead: TOverlapped;
FOverlappedWrite: TOverlapped;
FPortName: string;
FBaudRate: Cardinal;
FPartity: Byte;
FStopBits: Byte;
FByteSize: Byte;
FReadIntervalTimeout: Cardinal;
FReadTotalTimeoutMultiplier: Cardinal;
FReadTotalTimeoutConstant: Cardinal;
FWriteTotalTimeoutMultiplier: Cardinal;
FWriteTotalTimeoutConstant: Cardinal;
FInQueueSize, FOutQueueSize: Cardinal;
FComOpenEvent: THandle;
function GetPortName: string;
procedure SetPortName(const Value: string);
function GetBaudRate: Cardinal;
procedure SetBaudRate(const Value: Cardinal);
function GetPartity: Byte;
procedure SetPartity(const Value: Byte);
function GetStopBits: Byte;
procedure SetStopBits(const Value: Byte);
procedure SetReadIntervalTimeout(const Value: Cardinal);
procedure SetReadTotalTimeoutMultiplier(const Value: Cardinal);
procedure SetReadTotalTimeoutConstant(const Value: Cardinal);
procedure SetWriteTotalTimeoutConstant(const Value: Cardinal);
procedure SetWriteTotalTimeoutMultiplier(const Value: Cardinal);
function GetByteSize: Byte;
procedure SetByteSize(const Value: Byte);
function GetIsPortOpenned: Boolean;
protected
public
procedure SetUserComState;
procedure SetUserComTimeOuts;
function Open: Boolean;
procedure Close;
procedure Purge;
procedure ShowSettingsWindow(WindowHandle: THandle);
procedure SetRTSSignal;
procedure ClearRTSSignal;
function WriteByte(B: Byte): Boolean;
function ReadByte(var B: Byte; TimeOut: Cardinal):Boolean;
function GetInQueueSize: Cardinal;
function Read(var Buffer: array of Byte; NumberOfBytesToRead: Cardinal;
TimeOut: Cardinal; var IsTimeOutExpire: Boolean): Cardinal;
function ReadAll(var Buffer: array of Byte; TimeOut: Cardinal;
var IsTimeOutExpire: Boolean): Cardinal;
function WaitAndRead(var Buffer: array of Byte; Count: Word): Cardinal;
function Write(const Buffer: array of Byte; NumberOfBytesToWrite: Cardinal): Cardinal;
procedure SetCommQueueSize(InQueue, OutQueue: Cardinal);
constructor Create;
destructor Destroy; override;
property PortName: string read GetPortName write SetPortName;
property BaudRate: Cardinal read GetBaudRate write SetBaudRate;
property Partity: Byte read GetPartity write SetPartity;
property StopBits: Byte read GetStopBits write SetStopBits;
property ByteSize: Byte read GetByteSize write SetByteSize;
property ReadIntervalTimeout: Cardinal
read FReadIntervalTimeout write SetReadIntervalTimeout;
property ReadTotalTimeoutMultiplier: Cardinal
read FReadTotalTimeoutMultiplier write SetReadTotalTimeoutMultiplier;
property ReadTotalTimeoutConstant: Cardinal
read FReadTotalTimeoutConstant write SetReadTotalTimeoutConstant;
property WriteTotalTimeoutMultiplier: Cardinal
read FWriteTotalTimeoutMultiplier write SetWriteTotalTimeoutMultiplier;
property WriteTotalTimeoutConstant: Cardinal
read FWriteTotalTimeoutConstant write SetWriteTotalTimeoutConstant;
property PortHandle: THandle read FPortHandle;
property IsPortOpened: Boolean read GetIsPortOpenned;
property ComOpenEvent: THandle read FComOpenEvent;
end;
← →
Mr. D. (2007-01-29 22:03) [4]Kolan, то есть, ты во всех операция записи используешь одну и ту же структуру FOverlappedWrite...
Закрываешь FOverlappedWrite.hEvent видать только при уничтожении компонента?
Все нормально работает? Компонент абсолютно корректно отработает две подряд идущие комманды:AnisochronousComm1.Write([$00, $01, $02], 3);
AnisochronousComm1.Write([$00, $02, $03], 3);
Это я чисто для примера... Все ок будет?
P.S. Кстати, интересно зачем ты передаешь число байтов для чтения, у динамических массивов и так выяснить можно: Length(Buffer) ...
← →
Kolan © (2007-01-29 22:07) [5]
constructor TAnisochronousComm.Create;
begin
FPortHandle := INVALID_HANDLE_VALUE;
FOverlappedRead.hEvent := INVALID_HANDLE_VALUE;
FOverlappedWrite.hEvent := INVALID_HANDLE_VALUE;
FPortName := "COM1";
FBaudRate := CBR_115200;
FPartity := NOPARITY;
FStopBits := ONESTOPBIT;
FByteSize := 8;
FReadIntervalTimeout := 0;
FReadTotalTimeoutConstant := 0;
FComOpenEvent := CreateEvent(nil, True, False, nil);
end;
procedure TAnisochronousComm.Close;
begin
if FPortHandle <> INVALID_HANDLE_VALUE then
begin
//Purge;
CloseHandle(FPortHandle);
FPortHandle := INVALID_HANDLE_VALUE;
if FOverlappedRead.hEvent <> INVALID_HANDLE_VALUE then
CloseHandle(FOverlappedRead.hEvent);
if FOverlappedWrite.hEvent <> INVALID_HANDLE_VALUE then
CloseHandle(FOverlappedWrite.hEvent);
ResetEvent(FComOpenEvent);
end;
end;
Если честно код этот в 90% моих приложений. Я его год назад написал. уже не помню что там да как, по статье разбирался…
Нужно скажу что за статья…
← →
Kolan © (2007-01-29 22:07) [6]
function TAnisochronousComm.Open: Boolean;
begin
Result := False;
if FPortHandle <> INVALID_HANDLE_VALUE then
Close;
FPortHandle := CreateFile(PAnsiChar(FPortName), GENERIC_READ or GENERIC_WRITE,
0, nil, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
if FPortHandle = INVALID_HANDLE_VALUE then
Exit;
ZeroMemory(@FOverlappedRead, SizeOf(@FOverlappedRead));
ZeroMemory(@FOverlappedWrite, SizeOf(@FOverlappedWrite));
FOverlappedRead.hEvent := CreateEvent(nil, False, False, nil);
FOverlappedWrite.hEvent := CreateEvent(nil, False, False, nil);
SetUserComState;
SetUserComTimeOuts;
Purge;
Result := True;
SetEvent(FComOpenEvent);
end;
← →
Kolan © (2007-01-29 22:07) [7]Грил давай на мыло :)
← →
Mr. D. (2007-01-29 22:31) [8]Да в общем все понятно... Только, имхо, у тебя один косяк - все завязано на таймаутах при чтении. А я хочу по другому реализовать - пришла порция данных, вызывается определенное событие.
За код спасибо!
← →
Mr. D. (2007-01-29 22:35) [9]Все таки меня терзают смутные сомнения, что будет если два раза подряд вызвать:
AnisochronousComm1.Write([$00, $01, $02], 3);
AnisochronousComm1.Write([$00, $02, $03], 3);
Ведь во WriteFile будет передана одна и таже структура FOverlappedWrite.
Тогда что вернетGetOverlappedResult(..., @FOverlappedWrite, Len, FALSE);
сколько запислось в первый раз или во второй? Или как?
← →
tesseract © (2007-01-29 22:50) [10]
> Ведь во WriteFile будет передана одна и таже структура FOverlappedWrite.
Про очереди не слышал ? Подобное творчество не допусимо.
Записывай данные в очередь и посылай в порт в отдельном потоке.
← →
Mr. D. (2007-01-30 02:06) [11]то есть, при работе с COM нужно два потока, один на запись, другое на чтение?
← →
Германн © (2007-01-30 02:39) [12]
> r. D. (30.01.07 02:06) [11]
>
> то есть, при работе с COM нужно два потока, один на запись,
> другое на чтение?
Хочешь почувствовать все прелести "нахождения в ж@@е у "негра"" - добро пожаловать в потоки:) !!!
← →
tesseract © (2007-01-30 10:17) [13]
> Mr. D. (30.01.07 02:06) [11]
> то есть, при работе с COM нужно два потока, один на запись,
> другое на чтение?
Нет! Поток на запись/чтение один. Просто в нём надо реализовать очередь записи/чтения. Например функция "записать" помещает данные в буфер,а поток потом запишет сам, когда время будет.
← →
Mr. D. (2007-01-30 12:30) [14]tesseract, но как это сделать? Смотри, например, мы ждем чтения, соответственно в асинхронном режиме вызвали ReadFile, потом сделали WaitForSingleObject на событие... ВСЕ, поток то завис в ожидании чтения.
А если в это время нужно что-то записать? Ну будут помещены в буфер данные, но они не отправятся пока на порт не придет какая-нибудь информаця, то есть не произойдет чтения. Нелогично.
Получается, чтобы организовать действительно асинхронную передачу - на каждый порт нужно по 2 потока дополнительных. Один поток при приходе данных будет делать синхронизацию и отправлять данные в основной поток, а основной поток будет помещать данные на запись в некий буфер, из которого пишущий поток своевременно будет данные забирать и отсылать их на порт.
Странно, что это не реализовано в самой Windows, как это сделано с сокетами, например.
← →
Mr. D. (2007-01-30 12:31) [15]И еще странно, ведь Kolan утверждает, что его компонент во многих программах, наверняка бы всплыли косяки, значит все работает...?
← →
tesseract © (2007-01-30 14:56) [16]
> ВСЕ, поток то завис в ожидании чтения.
Угу, а как процесс завершать-то будешь с таким параметром?
Без знания про CancelIO ?
Да и Запись и чтение одновременно нечасто выполняються.
> аверняка бы всплыли косяки, значит все работает...?
И у меня всё работает.
В одном потоке, два можно - но их нужно синхронизировать, а это запросто гемор можно накопить при частом обмене. И не понятно, что может произойти при одновременной попытке считать/записать. в 90% случаях сначала пишем - потом ждём ответа, причем строго ограниченное время. Потом считываем очередь и смотрим, что делать дальше.
И WaitForSingleObject нужно не на Read ставить, а на WaitComEvent.
← →
matt © (2007-01-30 15:12) [17]>>Kolan © (29.01.07 22:07) [7]
Можешь кинуть модуль на yogmurand<at>bk.ru?
← →
Mr. D. (2007-01-30 21:45) [18]tesseract, ну ты изначально полагаешь на связь с неким известным устройством, а если это не так?
Ну представим, что я хочу написать собственный терминал, программу для теста.
С одной стороны, мне нужно все время читать из порта, чтобы как только поступили данные - выдать их на консоль. И в тоже время нужно быть готовым отправить данные с консоли.
Как такое реализовать? Как только читающий поток зависнет на WaitForSingleObject - записать нельзя получится, пока он не освободится.
← →
Kolan © (2007-01-30 22:06) [19]«А я хочу по другому реализовать — пришла порция данных,
вызывается определенное событие.»
Так и работает для этого я использую доп. поток чтения.
Может это и не правильно, но работает нормально…
← →
Kolan © (2007-01-30 22:09) [20]«Можешь кинуть модуль на yogmurand<at>bk.ru?»
Могу :)
← →
Kolan © (2007-01-30 22:10) [21]«Странно, что это не реализовано в самой Windows, как это
сделано с сокетами, например.»
Ты еще с RS485 не работал :)
← →
koha © (2007-01-30 22:14) [22]Я думаю - это зависит от конечного устройства способно ли оно работать в дуплексе и от контролера устройства. И что вообще под COM понимать в данном случае? Физический порт сис. платы или порт виндовс?
← →
koha © (2007-01-30 22:25) [23]> Mr. D. (30.01.07 12:30) [14]
> Странно, что это не реализовано в самой Windows, как это сделано с сокетами, например.
Что странного сокеты могут, как известноработать в двух режимах : в блокирующем и не блокирующем, А COM как? Я так полагаю он инициализируется как файл и думаю, что это и есть блокирующий режим. И думаю, что на API это не удастся.
← →
Kolan © (2007-01-30 22:38) [24]«Можешь кинуть модуль на yogmurand<at>bk.ru?»
yogmurand@ukr.net
SMTP error from remote mailer after RCPT TO:<yogmurand@ukr.net>:
host mxs.ukr.net [195.214.192.100]: 550 mailbox is not used more than 2 months, mail rejected
← →
tesseract © (2007-01-30 22:52) [25]> Как такое реализовать? Как только читающий поток зависнет
> на WaitForSingleObject - записать нельзя получится, пока
> он не освободится.
Ещё раз - а как ты собираешься ЗАВЕРШИТЬ поток зависший на чтении, будешь ждать пока что нибудь не придёт? А если связи нет? Имей в виду RS232 может работать, как в полнодуплексном, так и в полудуплексном режиме. А для переключения режима чтения/записи и проверки наличия подключения существуют CTS/RTS и DTR/DTS.
ЗЫ : Как правило используется WaitForMultipleObjects для того стобы завести ещё одно событие, на выруб потока.
>
> С одной стороны, мне нужно все время читать из порта, чтобы
> как только поступили данные - выдать их на консоль. И в
> тоже время нужно быть готовым отправить данные с консоли.
Ну и ? Надо отправить данные - отправляем, потом переключаемся в чтение, не волнуйся данные пришедшие за это время осядут в буфере. Зато не придёться развлекаться с синхронизацией данных.
← →
Kolan © (2007-01-30 23:00) [26]«Ещё раз — а как ты собираешься ЗАВЕРШИТЬ поток зависший
на чтении, будешь ждать пока что нибудь не придёт? А если
связи нет? »
Я знаю что это ты не мне :)
Но вот как я делаю. Все мои приложения работают в режиме запрос-ответ(хотя делал и НЕ запрос ответ, это не важно). При записи я указываю сколько ждать ответ, если его нет, то «таймаут».
Связь обеспечивает целая система — объектов 10 наверно.
1 поток постоянно читает из порта. При обнаружении 1 байта он вычитывает все что пришло и отсылает это потоку — который выделяет пакеты.
Поток «выделения пакетов» найдя пакет отсылает его дальше.
А запись — непроблемма имхо. Отправил, а куда там что делось какая разница. Проблема прочитать.
← →
tesseract © (2007-01-30 23:02) [27]> Что странного сокеты могут, как известноработать в двух
> режимах : в блокирующем и не блокирующем,
Это только для Windows справедливо, *nix только блокирующие. И редко кто серьёзно пользуется неблокирующими сокетами.
> А COM как?
Попробуй открыть его второй раз и посмотри GetLastError.
> Физический порт сис. платы или порт виндовс?
Устройство Windwos, Ты же не через I/O к нему обращаешься?
Есть ведь и USB-RS232 и Ehternet-Rs232 конвертеры, они через виртуальные порты работают.
> Ты еще с RS485 не работал :)
Да не сильно они отличаються, API то же.
← →
tesseract © (2007-01-30 23:05) [28]>
> Но вот как я делаю. Все мои приложения работают в режиме
> запрос-ответ(хотя делал и НЕ запрос ответ, это не важно)
> . При записи я указываю сколько ждать ответ, если его нет,
> то «таймаут».
Лучше CancelIO при выходе из потока, или сбросить второе событие в WaitForMultipleObject.
> 1 поток постоянно читает из порта.
Маску поставить проще, меньше проблем, выше производительность.
← →
matt © (2007-01-31 00:56) [29]
> Kolan © (30.01.07 22:38) [24]
> 550 mailbox is not
> used more than 2 months, mail rejected
>
это мейл ру неадекватен... мейл получил, спасибо огромное!
← →
Германн © (2007-01-31 01:12) [30]
> tesseract © (30.01.07 23:02) [27]
>
> > Что странного сокеты могут, как известноработать в двух
> > режимах : в блокирующем и не блокирующем,
>
>
> Это только для Windows справедливо, *nix только блокирующие.
> И редко кто серьёзно пользуется неблокирующими сокетами.
>
Последнюю строку прокомментируй пож-ста.
P.S. И ещё раз, моё имхо. Работать с COM-портом в синхронном (aka блокирующем) режиме - есть очень большая ошибка в подавляющем большинстве случаев! Да, имхо это касается не только COM-порта, но и любого устройства, которое использует аппаратные прерывания!
← →
Kolan © (2007-02-01 14:25) [31]> Маску поставить проще, меньше проблем, выше производительность.
Я просто не умел :). Стал искать, нашел вот что:
http://kladovka.net.ru/delphibase/?action=viewfunc&topic=hardsystem&id=10452
Код настолько похож на мой, что без изменений(кроме возможности изменения имени порта) подошел к моим проектам. :)
← →
Prohodil Mimo © (2007-02-01 15:13) [32]Kolan © (07.02.01 14:25) [31]
Код настолько похож на мой, что без изменений(кроме возможности изменения имени порта) подошел к моим проектам. :)
Код VCL на мой совсем не похож, но к моим проектам подходит вообще без изменений :о)
← →
tesseract © (2007-02-01 15:21) [33]
> Kolan © (01.02.07 14:25) [31]
Мой код тут светился столько раз, что уже лень копировать :-)
Я там ещё и прямое обращение к драфверам использую.
> Да, имхо это касается не только COM-порта, но и любого устройства,
> которое использует аппаратные прерывания!
Почитай про IRP - не фига это не ошибка. Он всё равно прерывание не захватывает.
ЗЫ: А где у сокетов прерывание ?
Страницы: 1 вся ветка
Текущий архив: 2007.02.25;
Скачать: CL | DM;
Память: 0.56 MB
Время: 0.047 c