お世話になります
CreateProcessで他アプリを起動させているのですが、
StartupInfoの設定が反映されていないようでウインドウの位置やサイズが
思い通りになりません。
下記サイトを参考にしました
http://www.eva.hi-ho.ne.jp/taketani/delphi/system.shtml
ここのコードをそのまま使用してもやはり駄目でした
なぜ?・・・
お分かりになる方 宜しくお願い致します
D7 WinXP
Mr.XRAYです.
>なぜ?・・・
具体的にどのようなコードで,どんなアプリやウインドウが対象なのかは不明ですが,
そのコードの機能に問題がないとしても,アプリやウィンドウの起動時に必ずしも期待通り
になるとは限りません.
「自作のアプリを,外部からサイズや位置を変更できないようにするにはどうしたらいいですか」
という質問があったとします.
これに対応したコーディングをしたプログラムではできないことになります.
(当然,コーディングにもよりますが)
たとえば,パワーポイント2007のスライドショーでは,表示開始時には位置とサイズは
変更できません(表示終了後であれば可能です).
いろいろな他のアプリやウィンドウでテストしてみてはいかがでしょうか.
例えば以下のリスト1では,メモ帳の位置とサイズを固定して,
外部から変更できないようにしています.
アプリケーションの起動を知る・起動阻止
http://mrxray.on.coocan.jp/Delphi/plSamples/280_HookCBTActivate.htm#Activate
Mr.XRAY 様 ありがとうございます
>具体的にどのようなコードで,どんなアプリやウインドウが対象なのかは不明ですが,
>そのコードの機能に問題がないとしても,アプリやウィンドウの起動時に必ずしも期待通り
>になるとは限りません.
先に挙げたURLのサンプルコードをそのままコピペで対象はメモ帳です
そのサンプルのタイトルが「ウィンドウの位置と大きさを指定して実行」だったので出来るもの思い込んでいました
ありがとうございました
>いろいろな他のアプリやウィンドウでテストしてみてはいかがでしょうか.
もっとも簡単なテストは,フォーム1つだけのアプリを作成し,
それを制御してみることでしょう.
すでにテスト済みでしたらご容赦ください.
>対象はメモ帳です
メモ帳,つまりNotepad.exeですね.メモ帳の場合,CreateProcessで起動しても
メモ帳のインスタンスができますが,トップレベルの(外部から操作可能な)ウィンドウ
ハンドルが取得できません.
したがって,起動後にメモ帳のハンドルを取得して,そのハンドルで位置とサイズを
指定すれば可能と思われます.
具体的なコードは提示できませんが,以下を参考にしてください.
起動したプロセスのトップレベルウィンドウのハンドルを取得
http://mrxray.on.coocan.jp/Halbow/Notes/N002.html
>したがって,起動後にメモ帳のハンドルを取得して,そのハンドルで位置とサイズを
>指定すれば可能と思われます.
http://mrxray.on.coocan.jp/Halbow/Notes/N002.html
例えば,上のコードのEnumTopWindow関数で,ウィンドウハンドルが見つかったら
MoveWindow関数でセットするとかですね.
もちろん,他にも方法はいろいろあります.
どんなアプリに組み込むかで検討してください.
function EnumTopWindow(hTopWnd: HWND; lp: LPARAM): BOOL; stdcall;
var
p: LPDWORD;
processId: DWORD;
begin
result := true;
GetWindowThreadProcessId(hTopWnd, @processId);
p := LPDWORD(lp);
if (processId = p^) and (GetWindow(hTopWnd,GW_OWNER) = 0)
and IsWindowVisible(hTopWnd) then begin
p^ := hTopWnd;
MoveWindow(hTopWnd,0,0,400,500,True); //ここを追加
result := false;
end;
end;
もしうまく動作したら,この掲示板を見ている方にも参考になるように,
テストプログラムを作成して,コードを見せてください.
Mr.XRAYです.
上のコードの動作確認環境は,
Windows XP(SP3) + Delphi 7 Proです.
Mr.XRAY 様 何から何までありがとうございます
結局 私の環境では StartupInfo を使ったウインドウ位置の設定は出来ませんでした。
色々なアプリで試しました。(手抜きはしていませんよ)
・・・で
Mr.XRAY 様の示してくださったコードを実行したところ、メモ帳ではうまく動作しました。(成功率100%)
しかし、他のアプリ(具体的にはEmEditor)を起動すると成功率は50%以下といったところです。
>もしうまく動作したら,この掲示板を見ている方にも参考になるように,
>テストプログラムを作成して,コードを見せてください.
なんかハードルが上がってしまった気がしますが・・・
しばらくお待ち下さい。
Mr.XRAYです.今日は夜更かしの予定なので...
>起動すると成功率は50%以下といったところです。
そうですか.ハンドル取得のタイミングがよくないのかも知れません.
また,この方法では,一旦デフォルトの位置に表示されてしまうので,
体裁はあまりよくないですね.
>テストプログラムを作成して,コードを見せてください.
ハハハッ.無理強いはしません.ただここは情報共有の場ですから.
決してサポートセンターではありません.
それと,CrettePrecessで位置とサイズを指定して起動可能にするためには,
その,位置とサイズを指定したいアプリ自身が,ウインドウを生成する時に,
SW_SHOWDEFAULT というフラグを指定していないとできません.
メモ帳などはこのフラグを指定していないということでしょうね.
以下を参考にしてください.
1−3−4 ウィンドウの表示
http://mrxray.on.coocan.jp/Halbow/Chap01.html#Chap1-3-4
どうしても位置とサイズを指定して起動したければ,前に提示したページのコードの
ように,フックをかけるしかありません.Window XPであれば使用可能です.
(Vistaではできません)
アプリケーションの起動を知る・起動阻止
http://mrxray.on.coocan.jp/Delphi/plSamples/280_HookCBTActivate.htm#Activate
>ハンドル取得のタイミングがよくないのかも知れません.
コードはあくまで参考です.いろいろ工夫してみてください.
procedure TForm1.Button1Click(Sender: TObject);
var
hMainWnd : HWND;
begin
hMainWnd := ExecAndGetWindow('Notepad.exe');
MoveWindow(hMainWnd,0,0,400,500,True);
end;
とか.
参考リンク
http://mrxray.on.coocan.jp/Halbow/Notes/N002.html
Mr.XRAYです.
大変失礼しました.先のリンクのページの EnumTopWindowは,見えるウィンドウハンドルだけ
検索するようになっていますね.
そこで,メモ帳を非表示でCreateProcessし,ハンドルを取得したら表示するようにすれば
体裁いいですね.もちろん,その前に位置を指定しておきます.
参考リンク
http://mrxray.on.coocan.jp/Halbow/Notes/N002.html
を参考にしたコードです(そのままですが...).
動作確認環境は前と同じです.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
function EnumTopWindow(hTopWnd: HWND; lp: LPARAM): BOOL; stdcall;
var
p: LPDWORD;
processId: DWORD;
begin
result := true;
GetWindowThreadProcessId(hTopWnd, @processId);
p := LPDWORD(lp);
if (processId = p^) and (GetWindow(hTopWnd,GW_OWNER) = 0) then
begin
p^ := hTopWnd;
result := false;
end;
end;
function ExecAndGetWindow(ExeAppStr:string):HWND;
var
SI:TStartupInfo;
PI:TProcessInformation;
ret,dw: DWORD;
WaitCount:integer;
begin
result := 0;
FillChar(SI,SizeOf(SI),#0);
SI.cb := SizeOf(TStartupInfo);
//以下の4行とdwFlagsにSTARTF_USERPOSITION,STARTF_USESIZEを追加
SI.dwX := 0;
SI.dwY := 0;
SI.dwXSize :=500;
SI.dwYSize :=600;
SI.dwFlags := STARTF_USESHOWWINDOW or
STARTF_USEPOSITION or
STARTF_USESIZE;
SI.wShowWindow := SW_HIDE;
if not CreateProcess(nil,
PChar(ExeAppStr),
nil,
nil,
false,
NORMAL_PRIORITY_CLASS,
nil,
nil,
SI,
PI) then
exit
else begin
repeat
ret := WaitForInputIdle(PI.hProcess,50);
Application.ProcessMessages;
until ret <> WAIT_TIMEOUT;
WaitCount := 0;
repeat
Sleep(100);
if ret = 0 then begin
dw := PI.dwProcessId;
EnumWindows(@EnumTopWindow, LPARAM(@dw));
if dw <> PI.dwProcessId then result := dw;
end;
Inc(WaitCount);
until IsWindow(dw) or (WaitCount > 5);
CloseHandle(PI.hThread);
CloseHandle(PI.hProcess);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
hMainWnd : HWND;
begin
hMainWnd := ExecAndGetWindow('Notepad.exe');
MoveWindow(hMainWnd,0,0,400,500,True);
ShowWindow(hMainWnd,SW_SHOW);
end;
end.
またまた失礼しました.
CreateProcessで位置とサイズを指定しても無効なので,その部分のコードは不要でした.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
function EnumTopWindow(hTopWnd: HWND; lp: LPARAM): BOOL; stdcall;
var
p: LPDWORD;
processId: DWORD;
begin
result := true;
GetWindowThreadProcessId(hTopWnd, @processId);
p := LPDWORD(lp);
if (processId = p^) and (GetWindow(hTopWnd,GW_OWNER) = 0) then
begin
p^ := hTopWnd;
result := false;
end;
end;
function ExecAndGetWindow(ExeAppStr:string):HWND;
var
SI:TStartupInfo;
PI:TProcessInformation;
ret,dw: DWORD;
WaitCount:integer;
begin
result := 0;
FillChar(SI,SizeOf(SI),#0);
SI.cb := SizeOf(TStartupInfo);
SI.dwFlags := STARTF_USESHOWWINDOW;
SI.wShowWindow := SW_HIDE;
if not CreateProcess(nil,PChar(ExeAppStr),nil,nil,false,0,nil,nil,SI,PI) then
exit
else begin
repeat
ret := WaitForInputIdle(PI.hProcess,50);
Application.ProcessMessages;
until ret <> WAIT_TIMEOUT;
WaitCount := 0;
repeat
Sleep(100);
if ret = 0 then begin
dw := PI.dwProcessId;
EnumWindows(@EnumTopWindow, LPARAM(@dw));
if dw <> PI.dwProcessId then result := dw;
end;
Inc(WaitCount);
until IsWindow(dw) or (WaitCount > 5);
CloseHandle(PI.hThread);
CloseHandle(PI.hProcess);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
hMainWnd : HWND;
begin
hMainWnd := ExecAndGetWindow('Notepad.exe');
MoveWindow(hMainWnd,0,0,400,500,True);
ShowWindow(hMainWnd,SW_SHOW);
end;
end.
Mr.XRAY 様 ありがとうございます
完璧な動作になりました。
参考リンクのコードと比較しましたが・・・
なるほど といえば なるほど なのですが
非表示にしただけで全て解決してしまった
というか
ハンドル取得のタイミングの問題は「見えてなくてもいいよ」としただけで解決のようですね。
本当にありがとうございました
Mr.XRAYです.たまさんの環境でも動作したようでよかったのですが,
Notepad.exeならうまくいくのですが,他のアプリではうまくいくとか限りません.
あしからず.
というのは,アプリのメインウィンドウかそれとも他のウィンドウの操作が必要か.
にもよります.
また,表示されている状態でないとうまくいかないアプリもあると思います.
いずれにしても思考錯誤が必要と思われます.
他のアプリの操作関係は,プログラムとしては難しい方だと思います.少なくても私には.
がんばってください.
>いずれにしても思考錯誤が必要と思われます.
少し試行錯誤してみました.参考になれば.
位置とサイズを指定してアプリを起動
http://mrxray.on.coocan.jp/Delphi/plSamples/266_App_CreateOpen.htm
Mr.XRAY 様 すばらしいサンプルをありがとうございます
今後参考にさせていただきます
ただ今回は・・・
クラス名での判定は無理そうです
今回の対象はエディタなのですが ユーザーによってさまざまなエディタを使用されると思います
ですのでクラス名の決め打ちは出来ないです・・・多分
数種類 登録しておいて この中から選んで使いなさい
とか
自分でクラス名を調べて設定しなさい
などとは言えないでしょうから
・・・と 書いてるうちに
クラス名を取得する機能を付けとけば可能かな・・・と思い始めてます
やはり今回 参考にさせて頂くかもしれません
ありがとうございました
Mr.XRAYです.
大変,うまい方法を考えつきました.
>クラス名を取得する機能を付けとけば可能かな・・・と思い始めてます
クラス名がわかれば確実ですが,
前のサンプルでは,SW_HIDEで実行した場合,ウインドウハンドルが正常に取得できない
ことがあります.
こちらをご覧ください.
http://mrxray.on.coocan.jp/Delphi/plSamples/266_App_CreateOpen.htm#04
これは,SW_HIDEではなく,ウインドウを最小化して起動する方法です.
これですと,IsWindowVisible(hTopWnd)で検出可能です.
もちろん,これでも,最初から最大化して開くようなもの,例えばパワーポイントビューワ
では,一度最大化してから,設定した位置とサイズになります.そのようなアプリでは,
http://mrxray.on.coocan.jp/Delphi/plSamples/266_App_CreateOpen.htm#03
を使用せざるをえないと思います.
今回は,いろいろ勉強になりました.
ありがとうございます.
まさか不特定のアプリ(エディタでしたったけ)を制御したいなんて要望があるなんて
思いもしませんでした.いい機会ですので挑戦してみました.
まだテストが十分でないと思いますので,いろいろ不具合が出るかも知れません.
ツイート | ![]() |