![](/img/trans.png)
[英]How an application can start its own service after it closes in Delphi?
[英]How to Start an application and obtain a handle to it with Delphi?
我想从 Delphi 启动一个应用程序,并获得它的句柄,这样我就可以将所述应用程序的主窗口嵌入到 TFrame 类型的框架中。 到目前为止,我已经尝试过:
Function TFrmEmbeddedExe.StartNewApplication : Boolean;
var
SEInfo: TShellExecuteInfo;
ExitCode : DWORD;
begin
FillChar(SEInfo, SizeOf(SEInfo), 0) ;
SEInfo.cbSize := SizeOf(TShellExecuteInfo) ;
with SEInfo do
begin
fMask := SEE_MASK_NOCLOSEPROCESS;
Wnd := self.Handle;
lpFile := PChar(self.fexecuteFileName) ;// Example could be 'C:\Windows\Notepad.exe'
nShow := SW_SHOWNORMAL;//SW_HIDE;
end;
if ShellExecuteEx(@SEInfo) then
begin
sleep(1500);
self.fAppWnd := FindWindow(nil, PChar(self.fWindowCaption)); //Example : 'Untitled - Notepad'
if self.fAppWnd <> 0 then
begin
Windows.SetParent(self.fAppWnd, SEInfo.Wnd);
ShowWindow(self.fAppWnd, SW_SHOWMAXIMIZED);
result := true;
end
else
result := false;
end
else
result := false;
end ;
上面的代码实际上有效,但 findWindow 会找到我启动的应用程序的任何给定实例。 我想嵌入我 Shell 执行的确切实例。 因此,如果记事本已经启动了几次,我就无法使用 FindWindow 获得正确的记事本。
我试过了:
Function TfrmEmbeddedExe.CreateProcessNewApplication : Boolean;
var
zAppName: array[0..512] of char;
StartupInfo: TStartupInfo;
ProcessInfo: TProcessInformation;
Res : DWORD;
DoWait : Boolean;
begin
DoWait := False;
StrPCopy(zAppName, self.fexecuteFileName); //'C:\Windows\Notepad.exe'
FillChar(StartupInfo, Sizeof(StartupInfo), #0);
StartupInfo.cb := Sizeof(StartupInfo);
StartupInfo.dwFlags := STARTF_USESHOWWINDOW;
StartupInfo.wShowWindow := SW_SHOWNORMAL;
if CreateProcess (zAppName,
nil, { pointer to command line string }
nil, { pointer to process security attributes }
nil, { pointer to thread security attributes }
false, { handle inheritance flag }
CREATE_NEW_CONSOLE or { creation flags }
NORMAL_PRIORITY_CLASS,
nil, { pointer to new environment block }
nil, { pointer to current directory name }
StartupInfo, { pointer to STARTUPINFO }
ProcessInfo) then { pointer to PROCESS_INF }
begin
if DoWait then //just set it to false... so it will never enter here
begin
WaitforSingleObject(ProcessInfo.hProcess, INFINITE);
GetExitCodeProcess(ProcessInfo.hProcess, Res);
end
else
begin
self.fAppWnd := ProcessInfo.hProcess;
Windows.SetParent(self.fAppWnd, self.Handle);
ShowWindow(self.fAppWnd, SW_SHOWMAXIMIZED);
CloseHandle(ProcessInfo.hProcess);
CloseHandle(ProcessInfo.hThread);
end;
result := true;
end
else begin
Result := false;
end;
end;
请不要在代码之上运行! 它会产生奇怪的结果,包括在所有正在运行的应用程序中的任何地方选择一个看似随机的窗口并嵌入它(甚至是 Windows 开始菜单中的菜单项......)
所以基本上我需要的是如何启动应用程序,并获取应用程序主窗口的句柄。
任何帮助都受到高度赞赏
问候
延斯·福吉
这是您需要做的大致轮廓。 我会把编码留给你:
ShellExecuteEx
或CreateProcess
启动您的流程。 这将产生一个进程句柄。 WaitForInputIdle
。 这使进程有机会加载并启动其消息循环。 GetProcessId
以获取进程ID。 EnumWindows
枚举顶级窗口。 GetWindowThreadProcessId
以检查是否找到了目标进程的顶级窗口。 完成后,不要忘记关闭进程句柄。
当按下按钮时,我的程序“Master”最多可打开(运行)4 个其他程序(“Slaves”)。 当按下“Close-All”- 按钮时,所有“Slaves”再次关闭。 所以我遇到了同样的问题,即找到以 ShellExecute 启动的程序的窗口句柄。 我意识到,当你运行一个程序时,它会打开一个带有新窗口句柄的新窗口(有焦点),所以为什么不这样做:
获取当前聚焦窗口的句柄
使用 ShellExecute 打开新窗口
运行一个 Repeat-Until- 循环,读取当前聚焦的窗口句柄,直到它改变
这就是你想要的手柄。 所以这是执行此操作的函数 >>
函数 TfmSWSM.RunProgramAndReturnHandle(ProgName : string): HWND; var PrevH, NewH : HWND; iSE : 整数; 开始结果:= 0; // Handle = 0 表示:无法打开程序 PrevH := GetForegroundWindow; // 保存之前的句柄以与新打开的窗口进行比较 iSE := ShellExecute(Handle, 'open', PChar(ProgName), nil,nil, SW_SHOWNORMAL); if (iSE > 32) then // 程序打开成功 >> 找到新窗口的句柄 >> 开始重复 // 等待新窗口变为活动状态 NewH := GetForegroundWindow; 直到 (NewH <> PrevH); // 新句柄意味着新窗口 Result := NewH; // Slave的句柄-程序结束; 结尾;
要打开记事本,你可以这样:
Slave1H := RunProgramAndReturnHandle('Notepad.exe'); // Slave1H is the handle to the program window
要再次关闭它,你可以这样:
if Slave1H <> 0 then PostMessage(Slave1H, WM_CLOSE, 0, 0);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.