如何在Delphi窗体中调用另一个应用程序的Shell

11

我多年来一直在Delphi中使用ShellExecute启动(并可选择等待)其他应用程序。但是现在,我需要让其中一个应用程序出现在我的Delphi应用程序表单中。我尝试了下面的代码作为一个简单的测试来打开记事本(它确实可以),并在我的表单上的PAnel1中显示结果(但它没有)。有没有好心人能帮我找到正确的方向? 谢谢

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;

你想对记事本的主菜单做什么? - Alex
3个回答

17

忽略了所有的错误检查,但这可以让你开始:

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;

你绝对应该枚举新进程的窗口,而不是简单地使用FindWindow()返回的任何窗口句柄。


太棒了,谢谢。我只希望我能详细地理解正在发生的事情.....!现在要删除一些部分,看看会发生什么。再次感谢。Bri - Brian Frost
fNotepadHandleWindows类型是什么? - NaN
@EASI:Windows是单元名称,必要的正确名称解析(该表单具有相同名称但不同签名的方法)。另一个是HWND,但您可以在FindWindow() API函数的文档中自行查找。 - mghie
我试过了。也许在 Delphi XE3 中有所改变。不是吗?是的,现在使用 WinApi.Windows.FindWindows() :-) 现在就去找它。 - NaN
你正在泄漏进程句柄。 - David Heffernan
显示剩余2条评论

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); 

0

如果可能的话,那将是一个棘手的问题。

我见过一些适用于基于文本的应用程序的方法 - 它们通常捕获进程的标准输出并将其放入文本控件中。

但你所说的是一个完整的图形化应用程序(尽管Notepad在处理文本时显示像素而不是字符代码)。

因此,除非Notepad提供了一个接口,您可以:

  • 请求缓冲区中的任意字符;和
  • 向程序发送任意按键输入,
  • 我会说你运气不好。

肯定是一个临时解决方案,但一个选项是持续监视Notepad窗口,并确保它始终覆盖在您的表单客户区上方。这非常可怕,因为您必须停止它移动、调整大小、最小化等操作,并且保持其z顺序仅在您的应用程序上方。我不会希望我的最坏的敌人有这些要求。

您考虑过使用专门为Delphi构建的编辑器控件(或者您可以嵌入的ActiveX编辑器)吗?这可能是一个更好的方法。


我理解Notepad只是一个占位符,供其他程序尝试这种技术。虽然我同意你的一般观点,即如果有其他方法就不要这样做,但仅仅设置父HWND并进行定位并不难。 - mghie
一个ActiveX/OLE控件肯定是首选。然而,将另一个应用程序的窗口放在你的应用程序中是完全可能的。我在VB中做过类似的事情,但我记得我使用了一个MDI表单。而且,通过子类化相对容易地捕获和发送按键到另一个Windows窗体。但是自从我上次涉及这种东西已经快十年了。 - Calvin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接