Discussion:
External Application from within a Delphi Form
(too old to reply)
NYCDave
2004-06-23 22:47:46 UTC
Permalink
I know how to run an external application from within Delphi.. what I need
to know is it possible to have an external program run inside a Delphi
form?

I use this very cumbersome application at work. Its a mainfraim emulator.
There are at least 12 different screens I access numerous times a day. I
thought that I could write a simple shell program with tabs so I could run
the mainframe emulator within the shell and have different instances of
the program for each tab. Is this possible with Delphi?

Thanks,
Dave
J French
2004-06-24 07:42:28 UTC
Permalink
Post by NYCDave
I know how to run an external application from within Delphi.. what I need
to know is it possible to have an external program run inside a Delphi
form?
I use this very cumbersome application at work. Its a mainfraim emulator.
There are at least 12 different screens I access numerous times a day. I
thought that I could write a simple shell program with tabs so I could run
the mainframe emulator within the shell and have different instances of
the program for each tab. Is this possible with Delphi?
I've never done it with Delphi, but have in VB - just experimentally

You can 're-parent' another process inside a Delphi Form

Are these emulators DOS, Console or Windows based ?
NYCDave
2004-06-24 15:03:55 UTC
Permalink
The mainframe emulator I use is a Windows based application. You mentioned
"re-parent". I'll look through my books to see if Delphi has anything
like that. Thanks
Zakath
2004-06-24 15:28:55 UTC
Permalink
Post by NYCDave
The mainframe emulator I use is a Windows based application. You mentioned
"re-parent". I'll look through my books to see if Delphi has anything
like that. Thanks
I think you'll have to use Windows API for this... Check out the
SetParent function on msdn or whatever, it's in the Windows unit.
J French
2004-06-25 08:58:43 UTC
Permalink
Post by NYCDave
The mainframe emulator I use is a Windows based application. You mentioned
"re-parent". I'll look through my books to see if Delphi has anything
like that. Thanks
Just out of curiousity I have ported the routine to Delphi

It seems to work quite nicely.
Here is a Demo
- Followed by a few comments.

unit Unit1;

// Launch App and Re-Parent in a Panel
// 25th June 2004 - J French

// Add Two Buttons and One Large Panel

interface

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

type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Panel1: TPanel;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
mHWnd :HWND; // hWnd of Slave App
public
{ Public declarations }
Constructor Create( AOwner:TComponent );Override;
end;

var
Form1: TForm1;

implementation

{$R *.DFM}

constructor TForm1.Create(AOwner: TComponent);
begin
Inherited;
Button1.Caption := 'Launch Notepad';
Button2.Caption := 'Terminate Notepad';
end;

{
Get hWnd from a Process ID
}
Function InstanceToWnd( Const TgtPID:DWORD):HWND;
Var
ThisHWnd :HWND;
ThisPID :DWORD;
Begin
Result := 0;
ThisPID := 0;
// Find the first window
ThisHWnd := FindWindow( Nil, Nil);
While ThisHWnd <> 0 Do
Begin
//Check if the window isn't a child
If GetParent( ThisHWnd ) = 0 Then
Begin
//Get the window's thread & ProcessId
GetWindowThreadProcessId( ThisHWnd, Addr(ThisPID) );
If ThisPID = TgtPID Then
Begin
Result := ThisHWnd;
Break;
End;
End;
// 'retrieve the next window
ThisHWnd := GetWindow( ThisHWnd, GW_HWNDNEXT );
End;

End;{InstanceToWnd}

{
Launch App and return hWnd
}
Function ExecCmd( Const cmdline:String):HWND;
Var
PI :PROCESS_INFORMATION;
SI :STARTUPINFO;
Ret :LONGBOOL;
Begin;
Result := 0;
ZeroMemory( Addr(PI), SizeOf(PI) );
ZeroMemory( Addr(SI), SizeOf(SI) );
// Initialize the STARTUPINFO structure
SI.cb := SizeOf(SI);

// Start the shelled application:
Ret := CreateProcessA( Nil, PChar( cmdline ),
Nil, Nil, True,
NORMAL_PRIORITY_CLASS,
Nil, Nil,
SI, PI);
// --- let it start - this seems important
WaitForSingleObject( PI.hProcess, 500 );
If Ret Then
Begin
Result := InstanceToWnd( PI.dwProcessID );
CloseHandle( PI.hThread );
CloseHandle( PI.hProcess );
End;

End;{ExecCmd}


procedure TForm1.Button1Click(Sender: TObject);
begin
// Lock the window update - prevent flashing
// Does not work under XP
LockWindowUpdate( GetDesktopWindow );

// Execute notepad.Exe - Get its hWnd
mHWnd := ExecCmd( 'notepad.exe' );
If mHWnd = 0 Then
ShowMessage( 'Error starting the app' );

// Set the notepad's parent
If mHWnd <> 0 Then
Begin
Windows.SetParent( mHWnd, Panel1.Handle );
Windows.MoveWindow(mHWnd, 0, 0,
Panel1.ClientWidth,
Panel1.ClientHeight, True);
// Put the focus on notepad
Windows.SetFocus( mHWnd );
End;

// Unlock window update
LockWindowUpdate( 0 );
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
SendMessage(mHWnd, WM_CLOSE, 0, 0);
end;

end.

==== End of Code ====

Under Win95 the LockWindowUpdate() prevents the App showing up as
'normal' just before it is Re-Parented

This does not work under XP
A workaround would be to start it minimized and then resize it

WHILE DEBUGGING comment out the LockWindowUpdate

Notice that the Slave App does not appear in the Taskbar
- this may or may not be a problem

Slave Apps do appear in the Task Manager under Win95
Under XP they appear in the list of Processes not Running Apps

I have only (so far) tested this under Delphi using NotePad as the
slave App.

Also notice that the Delphi 'Host' loses focus when the Slave has
focus. This can be spoofed by trapping WM_ACTIVATEAPP

It would be an idea for someone else to give this code a once over,
just in case I've slipped up somewhere.

Hope this is useful
NYCDave
2004-06-25 15:38:42 UTC
Permalink
Thanks!!! I'll give this a try when I get back from my vacation.
NYCDave
2004-07-06 20:11:08 UTC
Permalink
The code works great. Thanks!!!

Loading...