Jump to content
dummzeuch

Passing back a string from an external program

Recommended Posts

2 hours ago, PeterPanettone said:

Or - if Program2 is a console type program: Simply (programmatically) read the console output of Program2.

It's not so simple in Delphi though.

Share this post


Link to post

I always used this code, which works fine. The only issue is if the other application is expecting an input (even a keypress to finish) because ReadFile will wait endlessly as far as I recall. It is easy to finetune it though.

function GetDosOutput(CommandLine: string; var ReturnString: String; var ReturnCode: Cardinal): Boolean;
const
  LOGON_WITH_PROFILE = $00000001;
var
  SA: TSecurityAttributes;
  SI: TStartupInfo;
  PI: TProcessInformation;
  StdOutPipeRead, StdOutPipeWrite: THandle;
  WasOK: Boolean;
  Buffer: array[0..255] of AnsiChar;
  BytesRead: Cardinal;
  WorkDir: string;
  Handle: Boolean;
begin
  Handle := False;
  ReturnCode := 255;
  Result := False;
  ReturnString := '';
  SA.nLength := SizeOf(SA);
  SA.bInheritHandle := True;
  SA.lpSecurityDescriptor := nil;
  CreatePipe(StdOutPipeRead, StdOutPipeWrite, @SA, 0);
  Try
   With SI Do Begin
              FillChar(SI, SizeOf(SI), 0);
              cb := SizeOf(SI);
              dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
              wShowWindow := SW_HIDE;
              //wShowWindow := SW_MINIMIZE;
              hStdInput := GetStdHandle(STD_INPUT_HANDLE); // don't redirect stdin
              hStdOutput := StdOutPipeWrite;
              hStdError := StdOutPipeWrite;
              End;
   WorkDir := '.';
   Handle := CreateProcess(nil, PChar(CommandLine), nil, nil, True, CREATE_NEW_PROCESS_GROUP or CREATE_NEW_CONSOLE, nil, PChar(WorkDir), SI, PI);
   CloseHandle(StdOutPipeWrite);
   If Handle Then Try
                   Repeat
                    WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil);
                    If BytesRead > 0 Then Begin
                                          Buffer[BytesRead] := #0;
                                          ReturnString := ReturnString + Buffer;
                                          End;
                   Until Not WasOK Or (BytesRead = 0);
                   WaitForSingleObject(PI.hProcess, INFINITE);
                   GetExitCodeProcess(PI.hProcess, ReturnCode);
                   While (Length(ReturnString)<>0) And ((ReturnString[Length(ReturnString)]=#10) Or (ReturnString[Length(ReturnString)]=#13)) Do
                    Delete(ReturnString,Length(ReturnString),1);
                   Result := True;
                  Finally
                   CloseHandle(PI.hThread);
                   CloseHandle(PI.hProcess);
                  End;
  Finally
   CloseHandle(StdOutPipeRead);
  End;
end;

 

  • Thanks 2

Share this post


Link to post
Quote

Converting program2 into a DLL or Package is not an option in this case, neither is integrating the source code of program2 to program1.

You don't need to convert Program2 into DLL. Just exports your functions and consume them directly from Program1 (just like DLL).

 

// ========================================== Program1 ==========================================
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
  System.SysUtils,
  WinApi.Windows;
const
  Program2 = 'Project2.exe';
type
  TGetProgram2Result = function(Param1, Param2: Integer): string;
var
  LIB: HMODULE;
  GetProgram2Result: TGetProgram2Result = nil;
  s: string = '';
begin
  try
    LIB := LoadLibrary(Program2);
    @GetProgram2Result := GetProcAddress(LIB, 'GetProgram2Result');
    s := GetProgram2Result(5, 2);
    Writeln(s);
    Readln;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

// ========================================== Program2 ==========================================
program Project2;
{$APPTYPE CONSOLE}
{$R *.res}
uses
  System.SysUtils,
  WinApi.Windows;
function GetProgram2Result(Param1, Param2: Integer): string;
begin
  Result := Format('%d+%d=%d', [Param1, Param2, Param1 + Param2]);
end;
// ------------- EXPORTS -------------
exports GetProgram2Result;
begin
  try
    Writeln('This is Program2');
    Writeln(GetProgram2Result(5, 2));
    Readln;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

 

Share this post


Link to post
34 minutes ago, Mahdi Safsafi said:

You don't need to convert Program2 into DLL. Just exports your functions and consume them directly from Program1 (just like DLL).

 


// ========================================== Program1 ==========================================
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
  System.SysUtils,
  WinApi.Windows;
const
  Program2 = 'Project2.exe';
type
  TGetProgram2Result = function(Param1, Param2: Integer): string;
var
  LIB: HMODULE;
  GetProgram2Result: TGetProgram2Result = nil;
  s: string = '';
begin
  try
    LIB := LoadLibrary(Program2);
    @GetProgram2Result := GetProcAddress(LIB, 'GetProgram2Result');
    s := GetProgram2Result(5, 2);
    Writeln(s);
    Readln;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

// ========================================== Program2 ==========================================
program Project2;
{$APPTYPE CONSOLE}
{$R *.res}
uses
  System.SysUtils,
  WinApi.Windows;
function GetProgram2Result(Param1, Param2: Integer): string;
begin
  Result := Format('%d+%d=%d', [Param1, Param2, Param1 + Param2]);
end;
// ------------- EXPORTS -------------
exports GetProgram2Result;
begin
  try
    Writeln('This is Program2');
    Writeln(GetProgram2Result(5, 2));
    Readln;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

 

Nope, that doesn't work. You can't expect to load an exe file with LoadLibrary. Also, even if you could, don't go exporting string across a module boundary.

 

I'm astounded at all the posting here for a trivial problem with a standard solution.

Share this post


Link to post

@David Heffernan

Quote

Nope, that doesn't work. You can't expect to load an exe file with LoadLibrary. Also, even if you could, don't go exporting string across a module boundary.

You can use LoadLibrary with exe file ! The only things is that you need to keep an eye on initialisation.  

Share this post


Link to post
6 minutes ago, Mahdi Safsafi said:

@David Heffernan

You can use LoadLibrary with exe file ! The only things is that you need to keep an eye on initialisation.  

No. Because there is no DllMain and so nothing is initialized. And then there are the lack of relocations. 

 

What's wrong with Delphi programmers. Read the standard output already! 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×