元は別トピック(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>
>> 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/
> 当方の環境
> 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
+=========+=========+=========+=========+=========+=========+=========+
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>
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>
>> 取り急ぎ、クラス名が '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 としなくて大丈夫ですか?
この辺↓じゃないでしょうか
http://www.ipentec.com/document/document.aspx?page=document-development-enviroment
エクスプローラーやIEはオプションで別プロセスで起動を選択していないと、
OSと同じプロセスで動く仕様だったと思います。はずしていたらすみません。
--
K.Yamagami <sa...@m5.kcn.ne.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>
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>
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>