Помощь - Поиск - Пользователи - Календарь
Полная версия: Обмен данными между параллельными процессами с помощью каналов.
Форум «Всё о Паскале» > Современный Паскаль и другие языки > Делфи
dron4ik
Всем привет! Задание: Оба приложения через фиксированные промежутки времени передают друг другу строку текста, включающую дескриптор приложения и текущее время.
Требование к заданию:
1) Используя средства языка Object Pascal, разработать в соответствии с индивидуальным заданием приложения ПI и П2 (П2 - «дочернее»).
2) Для организации автоматической записи/чтения данных используйте стан-дартный системный компонент-таймер TTimer с интервалом «срабатывания» не бо¬лее 2-4 с.
3) Обратите внимание, текст сообщения об ошибке, возникающей при работе с каналом можно получить с помощью вызова SysErTorMessage(GetLastError).

Немного теории по заданию:

Для создания анонимных каналов (далее просто «каналы») в ОС Windows ис¬пользуется функция CreatePipe. Она возвращает два дескриптора, имеющие права GENERIC_READ (OENERIC_WRJTE) и SYNCHRONIZE на доступ к «входу» и «выходу» канала. Для записи и чтения данных необходимо использовать соответственно функции WriteFile и ReadFile. Как только канал перестаёт быть необходимым, его необходимо уничтожить с помощью функции CIoseHandle.
Типичное использование анонимного канала состоит в создании канала пере¬дачи данных между родительским и дочерним процессом. В руководстве [1] для этого рекомендуется выполнять переназначение стандартного ввода и вывода в дочер¬нем процессе. Рассмотрим общую схему такого переназначения:
1) Получить дескриптор стандартного входного/выходного потока с помощью функции OctSldHandle и сохранить его для дальнейшего использования.
2) Создать канал, вызвав функцию CreatePipe.
3) Вызвать функцию SetStdHandle, чтобы переназначить стандартные потоки чте-ния и записи.
4) Создать дочерний процесс с помощью функции CrcaieProcess. При этом необхо¬димо указать, что дочерний процесс должен наследовать дескрипторы процесса, в том числе потоки ввода/вывода.
Рассмотрим программный фрагмент, реализующий данную схему (с двухсто¬ронним доступом к каналу):
var 01d_Input, 01d_Output: THandle;
hReadPipe, hWritePipe: Cardinal; //Дескрипторы канала: «выход», «вход»
PipeAttributes: TSecurityAttributes;
si: TStartupInfo;
pi: TProcessInformation;
// Код, выполняемый при инициализации головной программы // - Шаг 1
01d_Input :- GetStdHandle(STD_INPUT_HANDLE); 01d_Output :- GetStdHandle(STO_OUTPUT_HANDLE) ; // - Шаг 2
PipeAttributes.nLength :- SizeOf(TSecurityAttributes);
PipeAttributes.IpSecurityDescriptor :- Hil;
PipeAttributes.blnheritHandle := True; // Разрешение «наследовать»
// дескрипторы канала
// Создание канала размером 100 байт
if Not CreatePipe(hReadPipe,hWritePipe,ePipeAttributes,100)
then „ // Обработка ситуации, когда канал не был создан // - Шаг 3
SetStdHandle(STD_INPUT_HANDLE, hReadPipe); SetStdHandle(STD_OUTPUT_HANDLE, hWritePipe); // - Шаг 4
si.cb:-si2eof(TStartupInfo);
sl.lpReserved :- Nil;
si.lpDesktop :- Hil;
si.lpTitle :- Mil;
si.dwFlags :- 0;
si.cbReserved2 :- 0;
si.lpReserved2 :=■ Nil; // - Шаг 5
if Hot CreateProcess('ChildTask.exe',Nil,Nil,Nil, True, 0,Nil,Nil, si,pi)
than // Обработка ситуации, когда дочерний процесс не был создан SetStdHandle(STD_INPOT_HANDLE, 01d_Input) ; SetStdHandle (STD__OUTPUT_HANDLE, OldJDutput) ;
// Код, выполняемый при инициализации дочерней программы hReadPipe :- GetStdHandle(STD_INPUT_HANDLE) ; hWritePipe :- GetStdHandle(STD_OUTPUT_HANDLE);
// Код, выполняемый при записи данных в канал
var nNumberOfBytesToWrite, NumberOfBytesWritten: Cardinal;
Buffer: array(0..5O) of char; begin
Screen.Cursor :- crHourGlass; try
StrLCopy(Buffer,pChar(Editl.Text),50); // Записываем не более 50 байт
nNumberOfBytesToWrite :- Length(Edit 1.Text)+1;
if Not WriteFile(hWritePipe,Buffer,nNumberOfBytesToWrite,
NumberOfBytesWritten,Nil) than _. // Обработка ситуации, когда // запись в канал оказывается невозможной.
finally
Screen-Cursor := crDefault; and; end;
// Код, выполняемый при чтении данных из канала var Buffer: array[0..10] of char;
nNumberOfBytesToRead, NumberOfBytesRead: Cardinal; begin
Screen.Cursor :- crHourGlass; try
nNumberOfBytesToRead :- 10; // Считываем no 10 байт if ReadFile(hReadPipe,Buffer,nNumberOfBytesToRead,
NumberOfBytesRead,Nil) then Memol.Lines.Add(String(Buffet)) else „ // Обработка ситуации, когда чтение
// из канала оказывается невозможным.
finally
Screen.Cursor := crDefault; end; end;
// Код, выполняемый при завершении головной программы
CIoseHandle(hReadPipe); CIoseHandle(hWritePipe); // Уничтожение канала



Нашел задание--Приложение П1 непрерывно через фиксированные промежутки времени выводит пользовательский текст в канал. Приложение П2 записывает данные из канала в файл-протокол.(все требованию такие же). Как можно переделать вот этот код под своё задание?

Приложение 1. Листинг программы
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, StdCtrls;

type
TForm1 = class(TForm)
Memo1: TMemo;
Label1: TLabel;
Timer1: TTimer;
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure Timer1Timer(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

var Old_Input, Old_Output: THandle;
hReadPipe, hWritePipe: Cardinal; // Дескрипторы канала: "выход", "вход"
PipeAttributes: TSecurityAttributes;
si: TStartupInfo;
pi: TProcessInformation;

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
Old_Input := GetStdHandle(STD_INPUT_HANDLE);
Old_Output := GetStdHandle(STD_OUTPUT_HANDLE);

PipeAttributes.nLength := SizeOf(TSecurityAttributes);
PipeAttributes.lpSecurityDescriptor := Nil;
PipeAttributes.bInheritHandle := True; // Разрешение "наследовать"
// дескрипторы канала

if Not CreatePipe(hReadPipe,hWritePipe,@PipeAttributes,100)
then ShowMessage('Невозможно создать канал!');

SetStdHandle(STD_INPUT_HANDLE, hReadPipe);
SetStdHandle(STD_OUTPUT_HANDLE, hWritePipe);

si.cb:=sizeof(TStartupInfo);
si.lpReserved := Nil;
si.lpDesktop := Nil;
si.lpTitle := Nil;
si.dwFlags := 0;
si.cbReserved2 := 0;
si.lpReserved2 := Nil;

if Not CreateProcess('..\П2\Project2.exe',Nil,Nil,Nil,True,0,Nil,Nil,si,pi)
then ShowMessage('П2 не запущено!');

SetStdHandle(STD_INPUT_HANDLE, Old_Input);
SetStdHandle(STD_OUTPUT_HANDLE, Old_Output);

end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
CloseHandle(hReadPipe);
CloseHandle(hWritePipe);
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var nNumberOfBytesToWrite, NumberOfBytesWritten: Cardinal;
Buffer: array[0..50] of char;
begin
Screen.Cursor := crHourGlass;
try
StrLCopy(Buffer,pChar(Memo1.Lines.Text),50);
nNumberOfBytesToWrite := Length(Memo1.Lines.Text)+1;
if Not WriteFile(hWritePipe,Buffer,nNumberOfBytesToWrite,
NumberOfBytesWritten,Nil) then ShowMessage('Невозможна запись в канал!');

finally
Screen.Cursor := crDefault;
end;
end;

end.


Ведомое приложение:
unit Unit2;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;

type
TForm1 = class(TForm)
Memo1: TMemo;
Label1: TLabel;
Timer1: TTimer;
procedure FormCreate(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

var // Old_Input, Old_Output: THandle;
hReadPipe, hWritePipe: Cardinal; // Дескрипторы канала: "выход", "вход"
PipeAttributes: TSecurityAttributes;
si: TStartupInfo;
pi: TProcessInformation;


{$R *.dfm}

procedure WFile (str,patch: string); forward;

procedure TForm1.FormCreate(Sender: TObject);
begin
hReadPipe := GetStdHandle(STD_INPUT_HANDLE);
hWritePipe := GetStdHandle(STD_OUTPUT_HANDLE);
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var Buffer: array[0..30] of char;
nNumberOfBytesToRead, NumberOfBytesRead: Cardinal;
begin
Screen.Cursor := crHourGlass;
try
nNumberOfBytesToRead := 30; // Считываем по 10 байт
if ReadFile(hReadPipe,Buffer,nNumberOfBytesToRead,
NumberOfBytesRead,Nil)
then
begin
Memo1.Lines.Add(String(Buffer));
Memo1.Lines.SaveToFile('Read.txt');
WFile(String(Buffer),'Read1.txt');
end
else Memo1.Lines.Add('Невозможно чтение!');
finally
Screen.Cursor := crDefault;
end;
end;

procedure WFile (str,patch: string);
var f: TextFile;
begin
AssignFile(f,patch);
if FileExists(patch) then
begin
append(f);
writeln(f,str);
end
else
begin
rewrite(f);
writeln(f,str);
end;
close(f);
end;

end.
volvo
Создай два пайпа (через CreateNamedPipe), в один будет писАть П1, читать П2, второй - наоборот, пишет П2, читает П1. И по таймеру читаешь тот пайп, в который второе приложение записало свой ID и время, и пишешь в свой пайп свои опознавательные знаки, чтоб другое приложение могло их прочесть по своему таймеру.

Или тебе обязательно со стандартными потоками извращаться (я про STD_INPUT_HANDLE/STD_OUTPUT_HANDLE)?
dron4ik
надо обязательно извращаться(
volvo
В таком случае у тебя проблема - анонимные пайпы - они однонаправленные, вот в чем дело. А тебе нужны двунаправленные, поскольку ты не только хочешь с одной стороны писать, а с другой - читать, но и наоборот.
dron4ik
А как это реализовать тогда?
volvo
Я написал выше: использовать именованные пайпы, которые могут работать в режиме FULL_DUPLEX, то есть в них можно одновременно добавлять/извлекать данные с обоих сторон. В MSDN, если не ошибаюсь, был пример использования named pipes для организации IPC (InterProcess Communication, в смысле, МежПроцессного Взаимодействия)
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.