11

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;
Brian Frost
  • 13,334
  • 11
  • 80
  • 154

3 Answers3

17

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
  • 32,028
  • 6
  • 87
  • 129
  • 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 Apr 28 '09 at 09:31
  • 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 Apr 28 '09 at 09:54
  • 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 Apr 28 '09 at 10:15
  • What is the `fNotepadHandle` and the `Windows` types? – NaN Jun 27 '13 at 19:21
  • @EASI: Windows is the unit name, necessary for proper name resolution (the form has a method of same name, different signature). The other one is a `HWND`, but you could simply look it up yourself, in the documentation of the FindWindow() API function. – mghie Jun 27 '13 at 19:32
  • I tried. Maybe it changed at Delphi XE3. Did it not? Yes, to WinApi.Windows.FindWindows() :-) Just Find it now. – NaN Jun 27 '13 at 19:46
  • You are leaking the process handle – David Heffernan May 03 '15 at 11:57
1
var
  URL: string;
begin
  URL:= DBMemoURL.Text;
  // ShellExecute(self.WindowHandle,'open', PChar(URL), nil, nil, SW_SHOW); //default browser
     ShellExecute(self.WindowHandle,'open','chrome.exe', PChar(URL), nil, SW_SHOW); 
chown
  • 51,908
  • 16
  • 134
  • 170
françois
  • 19
  • 1
0

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
  • 854,327
  • 234
  • 1,573
  • 1,953
  • 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 Apr 28 '09 at 09:14
  • 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 Apr 28 '09 at 09:18