我正在尝试通过匿名通道实现进程之间的数据交换。服务器进程创建两个客户端进程的函数CreateProcess
,通过匿名通道交换数据。最初代码是用C++编写的,我将其翻译成Delphi。它在 C++ 中工作得很好,但在 Delphi 中实现有困难:应该首先传输数据的客户端 (1) 看起来像一个空控制台,没有任何输出,并且交换过程停止。请帮我解决问题。
更改:数据交换作为标准 I/O 重定向来实现。在 C++ 中,使用 iostream 作为标准流。我不知道 Delphi 的任何替代方案( writeln 和 readln 函数不合适)。
服务器进程代码:
program pipeserver;
{$APPTYPE CONSOLE}
{$R *.res}
uses
SysUtils, Windows;
var
lpszComLine1, lpszComLine2: array [0..200] of Char;
si: STARTUPINFO;
pi: PROCESS_INFORMATION;
hWritePipe, hReadPipe: THandle;
sa: SECURITY_ATTRIBUTES;
begin
try
// имена исполняемых файлов
StrPCopy(lpszComLine1, 'F:\DELPHI PROJECTS\client1\Win32\Debug\client1.exe'); {здесь необходимо указать полный путь до исполняемого файла}
StrPCopy(lpszComLine2, 'F:\DELPHI PROJECTS\client2\Win32\Debug\client2.exe'); {здесь тоже}
// устанавливаем атрибуты защиты канала
sa.nLength := SizeOf(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor := nil; // защита по умолчанию
sa.bInheritHandle := True; // дескрипторы наследуемые
// создаем анонимный канал
if not CreatePipe(hReadPipe, hWritePipe, @sa, 0) then
begin
WriteLn('Create pipe failed.');
WriteLn('Press any key to finish.');
ReadLn;
Exit;
end;
// устанавливаем атрибуты нового процесса
ZeroMemory(@si, SizeOf(STARTUPINFO));
si.cb := SizeOf(STARTUPINFO);
// использовать стандартные дескрипторы
si.dwFlags := STARTF_USESTDHANDLES;
// устанавливаем стандартные дескрипторы
si.hStdInput := hReadPipe;
si.hStdOutput := hWritePipe;
si.hStdError := hWritePipe;
// запускаем первого клиента
if not CreateProcess(nil, lpszComLine1, nil, nil, True,
CREATE_NEW_CONSOLE, nil, nil, si, pi) then
begin
WriteLn('Create process failed.');
WriteLn('Press any key to finish.');
ReadLn;
Exit;
end;
// закрываем дескрипторы первого клиента
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
// запускаем второго клиента
if not CreateProcess(nil, lpszComLine2, nil, nil, True,
CREATE_NEW_CONSOLE, nil, nil, si, pi) then
begin
WriteLn('Create process failed.');
WriteLn('Press any key to finish.');
ReadLn;
Exit;
end;
// закрываем дескрипторы второго клиента
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
// закрываем дескрипторы канала
CloseHandle(hReadPipe);
CloseHandle(hWritePipe);
WriteLn('The clients are created.');
WriteLn('Press Enter to exit.');
ReadLn;
except
on E: Exception do
begin
Writeln(E.ClassName, ': ', E.Message);
end;
end;
end.
客户端进程代码1:
program client1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
SysUtils, Windows;
var
hReadFloat, hReadText: THandle;
lpszReadFloat, lpszReadText: PChar;
i, j: Integer;
nData: Single;
dwWaitResult: DWORD;
begin
try
// события для синхронизации обмена данными
lpszReadFloat := 'ReadFloat';
lpszReadText := 'ReadText';
hReadFloat := CreateEvent(nil, False, False, lpszReadFloat);
hReadText := CreateEvent(nil, False, False, lpszReadText);
// ждем команды о начале записи в анонимный канал
WriteLn('Press any key to start communication.');
ReadLn;
// пишем целые числа в анонимный канал
for i := 0 to 4 do
begin
Sleep(500);
WriteLn(i);
end;
// ждем разрешения на чтение плавающих чисел из канала
dwWaitResult := WaitForSingleObject(hReadFloat, INFINITE);
if dwWaitResult = WAIT_OBJECT_0 then
begin
// читаем плавающие числа из анонимного канала
for j := 0 to 4 do
begin
ReadLn(nData);
WriteLn(Format('The number %2.1f is read from the pipe.', [nData]));
end;
end;
// отмечаем, что можно читать текст из анонимного канала
SetEvent(hReadText);
// теперь передаем текст
WriteLn('This is a demo sentence.');
WriteLn('The process finished transmission of data.');
WriteLn('Press Enter to exit.');
ReadLn;
CloseHandle(hReadFloat);
CloseHandle(hReadText);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
客户端进程代码2:
program client2;
{$APPTYPE CONSOLE}
{$R *.res}
uses
SysUtils, Windows;
var
hReadFloat, hReadText: THandle;
lpszReadFloat, lpszReadText: PChar;
i, j: Integer;
nData: Integer;
dwWaitResult: DWORD;
lpszInput: array [0..79] of Char;
begin
try
// события для синхронизации обмена данными
lpszReadFloat := 'ReadFloat';
lpszReadText := 'ReadText';
hReadFloat := CreateEvent(nil, False, False, lpszReadFloat);
hReadText := CreateEvent(nil, False, False, lpszReadText);
// читаем целые числа из анонимного канала
for i := 0 to 4 do
begin
ReadLn(nData);
WriteLn(Format('The number %d is read from the pipe.', [nData]));
end;
// разрешаем читать плавающие числа из анонимного канала
SetEvent(hReadFloat);
// пишем плавающие числа в анонимный канал
for j := 0 to 4 do
begin
Sleep(500);
WriteLn((j * 0.1));
end;
// ждем разрешения на чтение текста
dwWaitResult := WaitForSingleObject(hReadText, INFINITE);
if dwWaitResult = WAIT_OBJECT_0 then
begin
Write('The process read the text: ');
// теперь читаем текст
repeat
Sleep(500);
ReadLn(lpszInput);
Write(lpszInput);
Write(' ');
until lpszInput[0] = #0;
end;
WriteLn;
WriteLn('The process finished transmission of data.');
WriteLn('Press Enter to exit.');
ReadLn;
CloseHandle(hReadFloat);
CloseHandle(hReadText);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
问题是可以在C++中实现标准 I/O 重定向,但不能在 Delphi 中实现。
我将客户端保留在C++中,并用 Delphi 重写了服务器。一切正常。我自己解决了这个问题。