全國最多中醫師線上諮詢網站-台灣中醫網
發文 回覆 瀏覽次數:3118
推到 Plurk!
推到 Facebook!

ShellExecute開啟外部程式,再用GetForegroundWindow()抓取問題

答題得分者是:aftcast
ttnnrsseb
中階會員


發表:30
回覆:77
積分:51
註冊:2004-11-22

發送簡訊給我
#1 引用回覆 回覆 發表時間:2011-07-28 11:58:14 IP:220.229.xxx.xxx 訂閱
請教各位先進,
我用ShellExecute開啟一個外部程式,因為無法從視窗的名稱去抓(有可能不固定),所以我用GetForegroundWindow()去抓最上層的視窗。
ShellExecute呼叫完,通常也要Delay個500ms等它開啟才能抓的到。
如果這段期間去點到別的視窗,變成最上層的話,就會抓錯視窗了!

不知有無方法可以避免這個情況的發生?
例如ShellExecute開啟後就是StayOnTop,或是可以準確知道視窗開啟的時間?

謝謝!
aftcast
站務副站長


發表:81
回覆:1482
積分:1762
註冊:2002-11-21

發送簡訊給我
#2 引用回覆 回覆 發表時間:2011-07-29 05:04:13 IP:122.126.xxx.xxx 訂閱
你好,
請參考!


[code cpp]
BOOL CALLBACK EnumWindowsCallBackFnc(HWND hwnd, LPARAM lParam)
{

::SetWindowPos(hwnd, //handle of window
HWND_TOPMOST,
0,
0,
0,
0,
SWP_NOSIZE);
::SetWindowTextA(hwnd, "被蕭沖改變了!");
return false;

}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)
{
STARTUPINFO si;
PROCESS_INFORMATION pi;

// LPTSTR szCmdline="mspaint.exe"; //小畫家
LPTSTR szCmdline="notepad.exe";

::GetStartupInfo(&si);

// Start the child process.
if( !CreateProcess( NULL,
szCmdline,
NULL,
NULL,
FALSE,
0,
NULL,
NULL,
&si,
&pi )
)
{
ShowMessage(String().sprintf("CreateProcess failed (%d).\n", GetLastError()));
return;
}

// WaitForInputIdle對小畫家這程式來說,看來是沒用的,所以還是要sleep一下
if (WAIT_TIMEOUT==WaitForInputIdle(pi.hProcess, 5000L))
{
ShowMessage("Over Time");
// 若超過 5000 MS 的時間時
}
//::Sleep(500);
// #define STRICT 下面的方法才會不comipler錯誤 EnumWindowsCallBackFnc
// define 在 project option裡。用 (WNDENUMPROC) 強轉也行,但個人覺得不好
BOOL bResult = ::EnumThreadWindows( pi.dwThreadId, EnumWindowsCallBackFnc, NULL);


// Close process and thread handles.
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );


}
//---------------------------------------------------------------------------

[/code]


===================引 用 ttnnrsseb 文 章===================
請教各位先進,
我用ShellExecute開啟一個外部程式,因為無法從視窗的名稱去抓(有可能不固定),所以我用GetForegroundWindow()去抓最上層的視窗。
ShellExecute呼叫完,通常也要Delay個500ms等它開啟才能抓的到。
如果這段期間去點到別的視窗,變成最上層的話,就會抓錯視窗了!

不知有無方法可以避免這個情況的發生?
例如ShellExecute開啟後就是StayOnTop,或是可以準確知道視窗開啟的時間?

謝謝!
------



蕭沖
--All ideas are worthless unless implemented--

C++ Builder Delphi Taiwan G+ 社群
http://bit.ly/cbtaiwan
ttnnrsseb
中階會員


發表:30
回覆:77
積分:51
註冊:2004-11-22

發送簡訊給我
#3 引用回覆 回覆 發表時間:2011-07-29 09:07:19 IP:220.229.xxx.xxx 訂閱
 感謝先進的回覆,

我開notepad測試是OK的。
但如果我要抓的是mmsys.cpl的視窗,它的開啟是用

[code cpp]
LPTSTR szCmdline="C:\\windows\\system32\\control.exe mmsys.cpl";
[/code]

所以 pi.dwThreadId 應該是屬於 control.exe 的,找不到 mmsys.cpl 的HWND。
沒先說明,不好意思!


aftcast
站務副站長


發表:81
回覆:1482
積分:1762
註冊:2002-11-21

發送簡訊給我
#4 引用回覆 回覆 發表時間:2011-07-29 09:45:36 IP:210.64.xxx.xxx 訂閱
你好,可以明確的講你要開的是哪個嗎?
"C:\\windows\\system32\\control.exe mmsys.cpl";
這是否也只是一個例子? 因為我還有別的辦法,但要明確的知道你要開的是什麼,不好給我例子。
因為你說標題會變來變去,關於這點…很少的程式如此,多數會如此都是一些防毒、修護程式之類的才會換來換去。
如果你可以給我明確的你要開的命令 (即使我沒那個程式),或許我還有解決的辦法喔!




===================引 用 ttnnrsseb 文 章===================
感謝先進的回覆,

我開notepad測試是OK的。
但如果我要抓的是mmsys.cpl的視窗,它的開啟是用

[code cpp]
LPTSTR szCmdline="C:\\windows\\system32\\control.exe mmsys.cpl";
[/code]

所以 pi.dwThreadId 應該是屬於 control.exe 的,找不到 mmsys.cpl 的HWND。
沒先說明,不好意思!


------



蕭沖
--All ideas are worthless unless implemented--

C++ Builder Delphi Taiwan G+ 社群
http://bit.ly/cbtaiwan
ttnnrsseb
中階會員


發表:30
回覆:77
積分:51
註冊:2004-11-22

發送簡訊給我
#5 引用回覆 回覆 發表時間:2011-07-29 10:01:28 IP:220.229.xxx.xxx 訂閱
不好意思,我是要開mmsys.cpl。
不過程式有可能在不同語系的OS執行,所以不能抓名稱是指這個意思。

我在想是不是有什麼方式可以先幫它的視窗改個特定的名字,就可以用特定名字去FindWindow ?

編輯記錄
ttnnrsseb 重新編輯於 2011-07-28 20:05:06, 註解 無‧
aftcast
站務副站長


發表:81
回覆:1482
積分:1762
註冊:2002-11-21

發送簡訊給我
#6 引用回覆 回覆 發表時間:2011-07-29 11:59:00 IP:210.64.xxx.xxx 訂閱
你好,有一個可能不是最完美的解。但請試看看!

請於shellexecute 或createprocess後執行下面


[code cpp]
#include "psapi.h"
#pragma link "psapi.lib"

char buf[256] = {0};
HWND hwnd = 0;
HANDLE hProc = 0;
DWORD pId = 0;
while (1)
{
::ZeroMemory(buf,256);
hwnd = ::FindWindow("#32770",NULL);
::GetWindowText(hwnd,buf,255);
//ShowMessage(buf);
::ZeroMemory(buf,256);
GetWindowThreadProcessId(hwnd, &pId);
hProc = OpenProcess(PROCESS_ALL_ACCESS, false, pId);
::GetModuleBaseName(hProc,0,buf,sizeof(buf));
//ShowMessage(buf);
if (AnsiString(buf) != AnsiString("rundll32.exe"))
::SendMessage(hwnd,WM_CLOSE,0,0);
else
break;
}

::SetWindowPos(hwnd, //handle of window
HWND_TOPMOST,
0,
0,
0,
0,
SWP_NOSIZE);

::SetWindowTextA(hwnd, "被蕭沖改變了!");

[/code]



===================引 用 ttnnrsseb 文 章===================
不好意思,我是要開mmsys.cpl。
不過程式有可能在不同語系的OS執行,所以不能抓名稱是指這個意思。

我在想是不是有什麼方式可以先幫它的視窗改個特定的名字,就可以用特定名字去FindWindow ?

------



蕭沖
--All ideas are worthless unless implemented--

C++ Builder Delphi Taiwan G+ 社群
http://bit.ly/cbtaiwan
ttnnrsseb
中階會員


發表:30
回覆:77
積分:51
註冊:2004-11-22

發送簡訊給我
#7 引用回覆 回覆 發表時間:2011-07-29 15:50:52 IP:220.229.xxx.xxx 訂閱
感謝前輩的指導,初步測試OK了!
我再消化一下程式!

您指的不完美是如果有別的cpl也是開啟的情況下會誤判嗎?
編輯記錄
ttnnrsseb 重新編輯於 2011-07-29 02:00:58, 註解 無‧
系統時間:2017-12-17 6:22:41
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!