views:

1273

answers:

2

In Delphi I've used ShellExecute for years to launch (and optionally wait for) other applications. Now though, I need to have one of these applications appear in one of my Delphi app forms. I've tried the code below as a simple test to open notepad (which it does) and to display the result within PAnel1 on my form (which it doesnt). Can some kind person put me on the right track? Thanks

var
  Rec          : TShellExecuteInfo;
  wnd : HWnd;
const
  AVerb = 'open';
  AParams = '';
  AFileName = 'Notepad.exe';
  ADir = '';
begin
  FillChar(Rec, SizeOf(Rec), #0);

  Rec.cbSize       := SizeOf(Rec);
  Rec.fMask        := SEE_MASK_NOCLOSEPROCESS;
  Rec.lpVerb       := PChar( AVerb );
  Rec.lpFile       := PChar( AfileName );
  Rec.lpParameters := PChar( AParams );
  Rec.lpDirectory  := PChar( Adir );
  Rec.nShow        := sw_Show;

  ShellExecuteEx(@Rec);

  wnd := Windows.FindWindow( 'Notepad', nil );
  Windows.SetParent( Wnd, PAnel1.Handle );

end;
A: 

That will be a tricky one, if it's even possible.

I've seen approaches that will work for text-based applications - they generally capture the standard output of the process as it happens and put it into a text control.

But what you're talking about is a fully fledged graphical application (Notepad, despite working on text, display pixels, not character codes).

So, unless Notepad provides an interface where you can:

  • request arbitrary characters in the buffer; and
  • send arbitrary keystrokes to the program, I'd say you're flat out of luck.

Definitely a kludge, but one option is to continuously monitor the Notepad windows and ensure it's always superimposed over your forms client area. That's pretty horrible since you have to stop it moving, resizing, minimizing and so on, and maintain its z-order to be just above your applications. I wouldn't wish those requirements on my worst enemy.

Have you thought of using an editor control built specifically for Delphi (or an ActiveX editor that you could embed)? That might be a better approach.

paxdiablo
I take it that Notepad is just a placeholder for other programs to try this technique on. And while I agree with your general sentiment of not doing this if there's another way, simply setting the parent HWND and doing the positioning isn't that hard.
mghie
An ActiveX/OLE control would definitely be preferable. However, it's certainly possible to put another application's window inside of your application. I've done something like that in VB, but I think I used an MDI form.And it should be relatively easy to capture and send keystrokes to another windows form through subclassing. But it's been nearly a decade since I messed with that kind of stuff.
Calvin
+11  A: 

All error checking omitted, but this should get you started:

procedure TForm1.Button1Click(Sender: TObject);
var
  Rec: TShellExecuteInfo;
const
  AVerb = 'open';
  AParams = '';
  AFileName = 'Notepad.exe';
  ADir = '';
begin
  FillChar(Rec, SizeOf(Rec), #0);

  Rec.cbSize       := SizeOf(Rec);
  Rec.fMask        := SEE_MASK_NOCLOSEPROCESS;
  Rec.lpVerb       := PChar( AVerb );
  Rec.lpFile       := PChar( AfileName );
  Rec.lpParameters := PChar( AParams );
  Rec.lpDirectory  := PChar( Adir );
  Rec.nShow        := SW_HIDE;

  ShellExecuteEx(@Rec);
  WaitForInputIdle(Rec.hProcess, 5000);

  fNotepadHandle := Windows.FindWindow( 'Notepad', nil );
  Windows.SetParent( fNotepadHandle, Handle );

  Resize;
  ShowWindow(fNotepadHandle, SW_SHOW);
end;

procedure TForm1.FormResize(Sender: TObject);
begin
  if IsWindow(fNotepadHandle) then begin
    SetWindowPos(fNotepadHandle, 0, 0, 0, ClientWidth, ClientHeight,
      SWP_ASYNCWINDOWPOS);
  end;
end;

What you should definitely do is enumerate the windows of the new process, instead of simply using any window handle that FindWindow() returns.

mghie
Brilliant, thanks. I just wish I understood in detail what is happening.....! Off now to delete some bits to see what happens. Thanks again. Bri
Brian Frost
I'm curious - does that handle minimizing, maximizing changing z order and such? Or should you add some more TForm1.Form* functions for that? It looks pretty good.
paxdiablo
The only problem is that the caption bar buttons of Notepad are preserved, so one can still minimize, maximize or close Notepad. For a proper solution probably the whole caption bar should be removed. Depending on the requirements more might be necessary, but the problem in the question is solved by above code.
mghie