[Delphi:91190] Explorer.exe を CreateProcess した場合の、Explorer.exe のウインドウハンドルの取得

1,433 views
Skip to first unread message

Fumio Kawamata

unread,
Mar 16, 2009, 10:04:14 AM3/16/09
to Del...@ml.users.gr.jp
川又です。こんばんは。

元は別トピック(Delphi:91145)だったのですが、元のトピックのタイトルと
内容が違うので別トピックにしました。

当方の環境
Windows XP Pro. SP3, Delphi 6 Pro. Update Pack 2

> On Fri, 06 Mar 2009 10:06:56 +0900
> aaa1...@pop06.odn.ne.jp (高木太郎) wrote:
>
> >  CreateProcess で子プロセスを作ると、ProcessInformation 引数の中の
> > dwProcessIdフィールドに子プロセスのプロセス番号が入ります。 そこで
> > ウィンドウハンドルを列挙したら、ExploreWClass または CabinetWClass
> > クラスのものを取り出し、さらに GetWindowThreadProcessId で対応する
> > プロセス番号を割り出して、先の dwProcessId フィールドと一致する
> > ものを取り出すようにすれば確実です。

これを実装するコードを書いてテストしてみたのですが、CreateProcess時の、
プロセスID(TProcessInformation.dwProcessID) と、その直後に、
ExplorerWClass 又はCabinetWClass をクラス名とするウインドウハンドルの
プロセスIDは、一致しませんでした。

似たような苦労をしていた方が他にもいて、質問しているのを見つけました。
http://homepage1.nifty.com/MADIA/vb/vb_bbs2/200308/200308_03080013.html
この記事に対するレスで、TShellWindows を使う方法が書かれていました。
TShellWinodowsが何なのか調べてみると、『エクスプローラとインターネット
エクスプローラを列挙してくれるActiveX系のオブジェクト』
http://k2top.jpn.org/diary/?date=1207
というのを見つけました。

何が得られるか分からないまま、ものは試しと、uses にActiveXを追加し、
var
SW: TShellWindows;
としてみましたが、コンパイル時に「未定義の識別子」エラーになります。
C:\Program Files\Borland\Delphi6\Source 内に、TShellWindows あるいは
ShellWindow 文字列がないか grep で探してみましたがヒットしませんでした。

[Delphi-ML:53508]からはじまるスレッドの中には、ShellWindowsが話題にのぼって
おりますが、TWebBrowserで利用されいている関数で、TShellWindows とは
(名前が似ているが)異なるものと思われます。

Delphi 6 で、自プログラムから起動(Explorer.exeは複数プロセス起動される
訳ではないようなので、起動という表現が正しいのかどうかは分かりませんが)
したExplorer.exe のウインドウハンドルを取得する方法はないものでしょうか?
(別トピックで私が書いた、CreateProcessの前と後で、ウインドウハンドルを
列挙して差を確認する方法は、実用的品質が得られませんでした。)

(Explorer.exe のフォルダオプションの、「別のプロセスでフォルダウインドウ
を開く」はチェック無しの状態です。また、チェック無しのままで、自プログラム
からExplorer.exeを起動して、そのウインドウハンドルを取得したいです。)

何か、ヒント、サジェスチョンがありましたら、ご教示いただけませんでしょうか。

実験した際のコードは、以下のとおりです。

function TFormMain.ExecAndGetWindow(ExeAppStr:string):HWND;
var
I: Integer;
SI:TStartupInfo;
PI:TProcessInformation;
ProcessID: DWORD;
ret: DWORD;
WA: THWndArray;
NumOfWinHandles: Integer;
begin
result := 0;
FillChar(SI,SizeOf(SI),#0);
SI.cb := SizeOf(TStartupInfo);
SI.dwFlags := STARTF_USESHOWWINDOW;
SI.wShowWindow := SW_SHOW;
if not CreateProcess(nil,PChar(ExeAppStr),nil,nil,false,0,nil,nil,SI,PI) then begin
ShowMessage('Error: CreateProcess() API function was failed.');
exit;
end
else begin
repeat
ret := WaitForInputIdle(PI.hProcess,50);
Application.ProcessMessages;
until ret <> WAIT_TIMEOUT;
Memo1.Lines.Add('Created process Id='+IntToStr(PI.dwProcessId)+' (hex:'+IntToHex(PI.dwProcessId, 6)+')');
sleep(50);
NumOfWinHandles:=EnumWindowsOfClassNameAsZOrder(WA, 'ExploreWClass CabinetWClass');
(* ExplorerWClass 又は CabinetWClass のウインドウハンドルを列挙する関数
* WA は、THWndArray = array[0..500] of HWND;
* 戻り値は、ウインドウハンドルの数 (Integer)
*)
Memo1.Lines.Add('Number of Explore Win Handles='+IntToStr(NumOfWinHandles));
if NumOfWinHandles>0 then begin
for I:=1 to NumOfWinHandles do begin
GetWindowThreadProcessId(WA[I], @ProcessId);
Memo1.Lines.Add(IntToStr(I)+': '+IntToStr(WA[I])+' (hex:'+IntToHex(WA[I], 6)+') ProcessID='+IntToStr(ProcessID));
end;
end;
CloseHandle(PI.hThread);
CloseHandle(PI.hProcess);
end;
end;

実行した例は、次のようになります。

Created process Id=3428 (hex:000D64) (1)
Number of Explore Win Handles=1
1: 67210 (hex:01068A) ProcessID=620 (2)

(1)と(2)が、一致しません。
Process Explorerで確認してみると、(2)のプロセスIDは、親(という表現で合ってますで
しょうか)のExplorer.exe のプロセスIDで、(1)のプロセスIDは、どこかに行ってしまった
ような挙動を示します。

--
Fumio Kawamata <fu...@my.email.ne.jp>


高木太郎

unread,
Mar 16, 2009, 6:39:03 PM3/16/09
to Del...@ml.users.gr.jp
 こんにちは、イマジオムの高木です。

>>  CreateProcess で子プロセスを作ると、ProcessInformation 引数の中の
>> dwProcessIdフィールドに子プロセスのプロセス番号が入ります。 そこで
>> ウィンドウハンドルを列挙したら、ExploreWClass または CabinetWClass
>> クラスのものを取り出し、さらに GetWindowThreadProcessId で対応する
>> プロセス番号を割り出して、先の dwProcessId フィールドと一致する
>> ものを取り出すようにすれば確実です。
>
> これを実装するコードを書いてテストしてみたのですが、CreateProcess

> 時の、プロセスID(TProcessInformation.dwProcessID) と、その直後に、


> ExplorerWClass 又はCabinetWClass をクラス名とするウインドウ
> ハンドルのプロセスIDは、一致しませんでした。

 取り急ぎ、クラス名が 'IEFrame' でも一致しませんか?
――――――――――――――――――――――――――――――――――――
株式会社イマジオム 代表取締役 高木太郎
〒316-0024 茨城県 日立市 水木町 1-11-10
電話:0294-28-0147
ファクシミリ:0294-28-0148
電子メール:tarou_...@imageom.co.jp
ホームページ:http://www.imageom.co.jp/

Hikaru Fukushi

unread,
Mar 16, 2009, 9:05:28 PM3/16/09
to Del...@ml.users.gr.jp
川又さん、こんにちは。福士と申します。

> 当方の環境
> Windows XP Pro. SP3, Delphi 6 Pro. Update Pack 2

> (中略)


> 似たような苦労をしていた方が他にもいて、質問しているのを見つけました。
> http://homepage1.nifty.com/MADIA/vb/vb_bbs2/200308/200308_03080013.html
> この記事に対するレスで、TShellWindows を使う方法が書かれていました。
> TShellWinodowsが何なのか調べてみると、『エクスプローラとインターネット
> エクスプローラを列挙してくれるActiveX系のオブジェクト』
> http://k2top.jpn.org/diary/?date=1207
> というのを見つけました。

TShellWindowsはSource\Internet\SHDocVw.pasで定義されています。
Delphi 5以降であれば使用可能だと思われます。


+=========+=========+=========+=========+=========+=========+=========+
東洋テクニカルシステム株式会社 システム開発部 福士 光
Hikaru Fukushi (Toyo Technical System Inc.)
mailto:fuk...@tts-inc.co.jp
+=========+=========+=========+=========+=========+=========+=========+

Fumio Kawamata

unread,
Mar 17, 2009, 8:44:39 AM3/17/09
to Del...@ml.users.gr.jp
こんんばんは。川又です。

On Tue, 17 Mar 2009 07:39:03 +0900
aaa1...@pop06.odn.ne.jp (高木太郎) wrote:

>  取り急ぎ、クラス名が 'IEFrame' でも一致しませんか?

残念ながら、一致しませんでした。

ExploreWClass CabinetWClass IEFrame のどれかにマッチするクラス名の
ウインドウハンドルを列挙するようにしてみたところ、以下のような結果と
なりました。Internet Explorerは事前に起動。Explorerは起動無しの状態で開始。

Created process Id=3776 (hex:000EC0) <-- このプロセスは後で発見できず
Number of Explore Win Handles=2
1: 8389810 (hex:8004B2) ProcessID=3312 <-- 新しく作られたExplorerのウインドウ
2: 263940 (hex:040704) ProcessID=3244 <-- Internet Explorer
Detected WinHandle=0 <-- CreateProcess で作られたウインドウハンドル
-----
1: 8389810 (hex:8004B2) ProcessId=3312 <-- Explorer (1)
-----
1: 8389810 (hex:8004B2) ProcessId=3312 <-- Explorer (2)
-----
1: 263940 (hex:040704) ProcessId=3244 <-- Internet Explorer (3)
-----
1: 263940 (hex:040704) ProcessId=3244 <-- Internet Explorer (4)

CreateProcess実行後、以下のように、クラス名を指定して、列挙してみたのですが、
ボタンをクリックするごとに、Explorer や InternetExplorerが列挙されたり
されなかったりします。 (上記、(1) から (4) の行)

procedure TFormMain.ToolButton2Click(Sender: TObject);
var
Num: integer;
WA: THWndArray;
I: integer;
ProcessID: DWORD;
begin
Memo1.Lines.Add('-----');
Num:=EnumWindowsOfClassNameAsZOrder( WA, 'ExploreWClass CabinetWClass IEFrame');
for I:=1 to Num do begin
GetWindowThreadProcessId(WA[I], @ProcessId);
Memo1.Lines.Add(IntToStr(I)+': '+IntToStr(WA[I])+' (hex:'+IntToHex(WA[I], 6)+') ProcessId='+IntToStr(ProcessID));
end;
end;

ExplorerをCreateProcess したときのプロセスIDはいったい何処に行ってしまうんでしょうか。

--
Fumio Kawamata <fu...@my.email.ne.jp>


Fumio Kawamata

unread,
Mar 17, 2009, 8:45:17 AM3/17/09
to Del...@ml.users.gr.jp
こんばんは。川又です。

On Tue, 17 Mar 2009 10:05:28 +0900
Hikaru Fukushi <fuk...@tts-inc.co.jp> wrote:

> TShellWindowsはSource\Internet\SHDocVw.pasで定義されています。

確かにありました。何で grep したときに見つからなかったのか、履歴を
みたら、ShellWinodws を検索してました。(お恥ずかしい...。)

uses に、SHDocVw を追加し、

var
SW: TShellWindows;
begin
SW:=TShellWindows.Create(Nil);
Memo1.Lines.Add('ShellWinodws count (first)='+IntToStr(SW.Count));
SW.Free;

CreateProcess(省略)

SW:=TShellWindows.Create(Nil);
Memo1.Lines.Add('ShellWinodws count (second)='+IntToStr(SW.Count));
SW.Free;

とか、

var
SW: IShellWindows;
begin
SW:=CoShellWindows.Create;
Memo1.Lines.Add('ShellWinodws count (first)='+IntToStr(SW.Count));

CreateProcess(省略)

SW:=CoShellWindows.Create;
Memo1.Lines.Add('ShellWinodws count (second)='+IntToStr(SW.Count));

というコードで試したのですが、CreateProcess() の前後で値は変わわりませんでした。
今は、ShellWinodws を、自分の用途に使えるのか分からず実験している状態なので、
調査して、再トライします。

SHDocVw.pas の存在を教えていただいて助かりました。
ありがとうございました。

--
Fumio Kawamata <fu...@my.email.ne.jp>


高木太郎

unread,
Mar 17, 2009, 9:55:16 AM3/17/09
to Del...@ml.users.gr.jp
 川又さん、こんにちは。 イマジオムの高木です。

>>  取り急ぎ、クラス名が 'IEFrame' でも一致しませんか?
>
> 残念ながら、一致しませんでした。

 そうですか。 私の環境では、次のコードでウィンドウハンドルが取れるの
ですが……

var WindowHandle:HWND;

function EnumWindowsProc(WindowHandle:HWnd; LParam:LongInt):LongBool; stdcall;

var ProcessId:LongWord;
PClassName:PChar;

begin
GetWindowThreadProcessId(WindowHandle,@ProcessId);
if ProcessId=LongWord(LParam) then
begin
PClassName:=nil;
try
GetMem(PClassName,256);
GetClassName(WindowHandle,PClassName,256);
if PClassName='IEFrame' then
Form1.WindowHandle:=WindowHandle;
finally
FreeMem(PClassName);
end;
end;
Result:=True;
end;


procedure TForm1.Button1Click;

var StartUpInfo:TStartUpInfo;
ProcessInformation:TProcessInformation;

begin
GetStartupInfo(StartUpInfo);
CreateProcess(nil,PChar('C:\Program Files\Internet Explorer\IExplore.Exe'),
nil,nil,False,0,nil,nil,StartUpInfo,ProcessInformation);
repeat
WindowHandle:=0;
EnumWindows(@EnumWindowsProc,LongInt(ProcessInformation.DWProcessId));
until WindowHandle<>0;
WriteLn(WindowHandle);
end;


 ご報告の実験結果については、まだ細かく調べてはいないのですが、
一つ気になることがあります。

> procedure TFormMain.ToolButton2Click(Sender: TObject);
> var
> Num: integer;
> WA: THWndArray;
> I: integer;
> ProcessID: DWORD;
> begin
> Memo1.Lines.Add('-----');
> Num:=EnumWindowsOfClassNameAsZOrder( WA, 'ExploreWClass CabinetWClass IEFrame');
> for I:=1 to Num do begin
> GetWindowThreadProcessId(WA[I], @ProcessId);
> Memo1.Lines.Add(IntToStr(I)+': '+IntToStr(WA[I])+' (hex:'+IntToHex(WA[I], 6)+')
ProcessId='+IntToStr(ProcessID));
> end;
> end;

 for I:=0 to Num-1 do としなくて大丈夫ですか?

K.Yamagami

unread,
Mar 18, 2009, 7:29:16 AM3/18/09
to Del...@ml.users.gr.jp, sa...@m5.kcn.ne.jp
こんばんは、SAWAと申します

この辺↓じゃないでしょうか
http://www.ipentec.com/document/document.aspx?page=document-development-enviroment

エクスプローラーやIEはオプションで別プロセスで起動を選択していないと、
OSと同じプロセスで動く仕様だったと思います。はずしていたらすみません。

--
K.Yamagami <sa...@m5.kcn.ne.jp>


Fumio Kawamata

unread,
Mar 18, 2009, 9:25:38 AM3/18/09
to Del...@ml.users.gr.jp
こんばんは。川又です。

On Wed, 18 Mar 2009 20:29:16 +0900
"K.Yamagami" <sa...@m5.kcn.ne.jp> wrote:

> この辺↓じゃないでしょうか
> http://www.ipentec.com/document/document.aspx?page=document-development-enviroment
>
> エクスプローラーやIEはオプションで別プロセスで起動を選択していないと、
> OSと同じプロセスで動く仕様だったと思います。はずしていたらすみません。

いいえ、はずしてないと思います。私は、以下のように書きましたが、

[Delphi:91190]


> (Explorer.exe のフォルダオプションの、「別のプロセスでフォルダウインドウ
> を開く」はチェック無しの状態です。また、チェック無しのままで、自プログラム
> からExplorer.exeを起動して、そのウインドウハンドルを取得したいです。)

[Delphi:91196]
> ExplorerをCreateProcess したときのプロセスIDはいったい何処に行ってしまうんでしょうか。

SAWAさんのレスを読んでから、上記を読み直すと、とんちんかんなことを書いてますね。

「別のプロセスでフォルダウインドウを開く」をチェックして、プログラムを実行して
みたところ、Explorer.exe をCreateProcess したときの プロセスIDの存在を、
タスクマネージャーで確認できました。(コードをいろいろ修正してしまったので、
Explorer.exe のフォームを自前のフォーム内に貼り付ける部分は未確認です。)

Explorerを別プロセスで起動することを許容するか、CreateProcess とは違う方法で、
自前アプリが起動したExplorer.exe を識別する方法を考えなければならない...と
いうことのようですね。

後者はうまくいかず、前者にシフトしたので、正直、途方にくれてます。

--
Fumio Kawamata <fu...@my.email.ne.jp>


Fumio Kawamata

unread,
Mar 18, 2009, 9:41:20 AM3/18/09
to Del...@ml.users.gr.jp
高木さん、こんばんは。川又です。

On Tue, 17 Mar 2009 22:55:16 +0900
aaa1...@pop06.odn.ne.jp (高木太郎) wrote:

>  そうですか。 私の環境では、次のコードでウィンドウハンドルが取れるの
> ですが……

SAWAさんの書き込みを読んで、Explorer.exe の、Explorer.exe のフォルダオプションの、
「別のプロセスでフォルダウインドウを開く」をチェックしたら、取得したプロセスID
が存在していることをタスクマネージャーで確認できました。(ウインドウハンドルとの
関連づけはこれから確認します。)

>  ご報告の実験結果については、まだ細かく調べてはいないのですが、
> 一つ気になることがあります。


>
> > for I:=1 to Num do begin
>

>  for I:=0 to Num-1 do としなくて大丈夫ですか?

Halbowさんの作られた関数を実際上BlackBoxとして使ってます。宣言自体はBase 0
の配列なのですが、サンプルでも、ループは 1から使用してました。WA[0]には何が
入っているかについては、即答できません。
(お恥ずかしい...。これから確認します。すいません。)

--
Fumio Kawamata <fu...@my.email.ne.jp>


K.Yamagami

unread,
Mar 18, 2009, 5:01:47 PM3/18/09
to Del...@ml.users.gr.jp
こんにちは、SAWAです

Explorerを立ち上げてウィンドウハンドルを取得するのなら、いっそ
こんな感じではいかがでしょうか。SHDocVwをusesに入れてください。

procedure TForm1.Button1Click(Sender: TObject);
var
IE: TInternetExplorer;
begin
IE:=TInternetExplorer.create(self);
IE.Visible := true;
IE.Navigate('c:\');
Memo1.Lines.Add(IntToStr(IE.HWND));
end;

CreateProcessで行くのなら、以下のようにフォルダを列挙して
調べるしかないかも知れません。

procedure TForm1.Button6Click(Sender: TObject);
var
i:integer;
SWindows:TShellWindows;
WB:IWebBrowser2;
ExeName:string;
begin
SWindows := TShellWindows.Create(self);
try
for i := 0 to SWindows.Count - 1 do begin
if SWindows.Item(i).QueryInterface(IWebBrowser2,WB) = s_ok then begin
ExeName := WB.FullName;
if CompareText(ExtractFileName(ExeName),'Explorer.exe') = 0 then begin
Memo1.Lines.Add('is Exploer');
end else begin
Memo1.Lines.Add('is IE');
end;
Memo1.Lines.Add(wb.LocationURL);
Memo1.Lines.Add(IntToStr(WB.HWND));
end;
end;
finally
SWindows.Free;
end;

end;

--
K.Yamagami <sa...@m5.kcn.ne.jp>


Reply all
Reply to author
Forward
0 new messages