ShellExecuteで呼び出したcplファイルを速やかに終了させるには?

解決


どら  2008-09-11 23:27:31  No: 68982

毎度お世話になっております。

前回の質問の続きになってしまう形になりますが・・・
IEのプロパティのダイアログの起動を、ShellExecute関数を使ってinetcpl.cpl
を起動するようにしています。

#define INTERNET_CONNECTION_SETTINGS "inetcpl.cpl"

・・・中略

SHELLEXECUTEINFO l_sei;

ZeroMemory(&l_sei, sizeof(SHELLEXECUTEINFO));
l_sei.cbSize = sizeof(SHELLEXECUTEINFO);
l_sei.hwnd = hDlg;
l_sei.nShow = SW_SHOWNORMAL;
l_sei.fMask = SEE_MASK_NOCLOSEPROCESS;
l_sei.lpFile = (LPCSTR)INTERNET_CONNECTION_SETTINGS;
if(ShellExecuteEx(&l_sei) != TRUE)
{
   エラー処理;
}
else
{
   //開けたら処理が終わりまで待機
   WaitForSingleObject(l_sei.hProcess, INFINITE);
   //終わったらハンドルを解放
   CloseHandle(l_sei.hProcess);
}

起動はするのですが、インターネットのプロパティ画面を変更して、OKや適用
ボタンをクリックすると、変更が適用または適用してダイアログが閉じるまで

   1.上記ソースから実行した場合                    約30秒
   2.コマンドラインからinetcpl.cplを実行した場合   約5〜6秒

と大きく時間差が生じてしまいます。
この呼び出し方だと、何か問題があるのでしょうか?

ご存じの方がいらっしゃいましたらご教授願います。
開発・実行環境は、どもに Windows XP SP2、VS.NET2005 + 最新Windows SDKです。

よろしくお願いします>_<


かもねぎ  2008-09-12 00:25:00  No: 68983

http://d.hatena.ne.jp/koori/20041008/1097236615
これですかね?


どら  2008-09-12 02:41:00  No: 68984

かもねぎさん

情報ありがとうございます。
確かにこれみたいですね。

ただ・・・
MsgWaitForMultipleObjects、MsgWaitForMultipleObjectsExで、「inetcpl.cplが
閉じられるまで待機」という制御にしたい場合、恥ずかしながら待機する入力
イベントの種類(両関数のdwWakeMask)、待機フラグ(MsgWaitForMultipleObjectsEx
関数のdwFlags)をどの様に指定すればよいのか見当もつかず・・・

タイムアウト時間はINFINITEにすればいいと思っているのですが、それ自体が
間違っているのでしょうか?

私がやりたいことは、インターネットのプロパティ画面を出し、「OK」または
「キャンセル」「×」ボタンをクリックしてダイアログを閉じるまで待機した
いだけなのですが・・・

フラグの設定などを色々試しているのですが

   ・WaitForSingleObjectの時と同様30秒待たなければならない
   ・閉じる前に待機が終わってしまう

のどちらかにしかならなくて・・・。

おそらく、この関数をきちんと理解していないのだと思います>_<
申し訳ありませんが、ご存じであればヒントだけでもいただけないですか?


かもねぎ  2008-09-12 04:47:59  No: 68985

ブラウザ制御であればSHDocVw::IWebBrowser2Ptrとかありそうですが・・
ShellExecuteExが最善なのでしょうか。
プログラムの全体がわからないのでなんとも・・・
お役に立てなくてすみません・・


どら  2008-09-12 19:42:56  No: 68986

かもねぎさん

レスありがとうございます。
私がやりたいことは、

   IEのプロパティのProxyに関する部分の設定を行いたい

のです。
簡単にいうとネットワークの設定変更ツールみたいなものを作っているのです
が、その中でProxyの設定も一緒に切り替えるようにしたいのです。
(社内だとProxy有効、社外だと無効など)

現状で、これらの設定をコントロールする関数が見つかっておらず、レジスト
リ情報の読み書きで対応するしかないと考えています。

設定の読み書きはこれでいいのですが、その設定を変更するためにも、基本的
にはIEのプロパティ画面を呼び出すしか方法がないと思っています。
(独自でインターフェイスを作る方法もありますが、レジストリにバイナリ値
が合ったりするので、これを完全にカバーするにはこうするしかないかと・・・)。

そこで、ShellExecuteEx関数でinetcpl.cplを起動するようにしています。
他に方法があれば、こんなやり方したくないのですが・・・


かもねぎ  2008-09-12 22:18:43  No: 68987

http://www.geekpage.jp/programming/iphlpapi/
IPHelper API
もしかしたら・・このへんでなんとかなるかも?


かもねぎ  2008-09-12 22:45:55  No: 68988

http://www.admintech.jp/wiki.cgi?page=ProxyCfg.exe%2C+a+Proxy+Configuration+Tool
こんなのも見つかりました・・


どら  2008-09-12 23:02:19  No: 68989

かもねぎさん

何度もご返答ありがとうございます。

説明が下手でごめんなさい。
NICの設定変更に関しては、すでにWMI等を使って実装済なんです。
現在行いたいのはインターネットのプロパティ(IEのツール→インターネット
オプションで開くダイアログ)の「接続」タブの内容の切替を行う部分の実装
です。

IPHelper API系は、基本的にインターフェイスなどPC全体の設定に関するもの
だと思いますが、インターネットのプロパティは、ユーザ毎の設定なので・・・

うまくやる方法って、ないんですかね・・・>_<

もうちょっと調べてみます。


subaru  2008-09-12 23:50:30  No: 68990

>私がやりたいことは、インターネットのプロパティ画面を出し、「OK」または
>「キャンセル」「×」ボタンをクリックしてダイアログを閉じるまで待機した
>いだけなのですが・・・

待機に時間がかかる原因はわかりませんが、inetcpl.cpl の起動は
実際には rundll32 から Control_RunDLL という関数が呼ばれるようですね。
なので Control_RunDLL を直接呼ぶことでも待機できそうです。

typedef void (CALLBACK *EntryPointW)(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow);

・・・中略

HINSTANCE hLib = LoadLibrary(TEXT("shell32.dll"));
if(hLib != NULL) {
    EntryPointW pControl_RunDLLW = (EntryPointW)GetProcAddress(hLib, "Control_RunDLLW");
    if(pControl_RunDLLW != NULL) {
        (*pControl_RunDLLW)(hWnd, GetModuleHandle(NULL), L"inetcpl.cpl", SW_SHOWDEFAULT);
    }
    FreeLibrary(hLib);
}

コンソールアプリなどで親ウインドウがない場合は
デスクトップウインドウのハンドルを渡してみてください。


どら  2008-09-13 00:45:24  No: 68991

subaruさん、ありがとうございます!!

いけました!!
プログラムそのものがマルチバイト文字セットで作成されているのでそれに
合わせてW→Aでやってますが(笑)、あっさりいけました。

こんな方法があったのですね、本当に助かりました!!


subaru  2008-09-13 01:12:19  No: 68992

すみません、Control_RunDLL の第3引数は LPWSTR (AならLPSTR)でした。
なので念のため、

>(*pControl_RunDLLW)(hWnd, GetModuleHandle(NULL), L"inetcpl.cpl", SW_SHOWDEFAULT);

WCHAR sz[100] = L"inetcpl.cpl";
(*pControl_RunDLLW)(hWnd, GetModuleHandle(NULL), sz, SW_SHOWDEFAULT);

に訂正します(^^;

ちなみに rundll32 が呼べる関数の仕様は以下を参考にしました。
http://support.microsoft.com/kb/164787/ja


gak  2008-09-13 02:17:11  No: 68993

解決済み後の回答なので、これに対する返信は特に要らない。

> 待機に時間がかかる原因はわかりませんが、
恐らく inetcpl.cpl が(更新されて)終了時、オーナーウィンドウに
SendMessageTimeout() 辺りを使ってメッセージを投げていると予想。
ただ、どらさんが最初に出しているソースだと WaitForSingleObject() で
(メッセージを処理する機会が無いまま)待機してしまっているので
デッドロックに陥っている、という辺りかと。

> どの様に指定すればよいのか見当もつかず・・・
> おそらく、この関数をきちんと理解していないのだと思います>_<
> 申し訳ありませんが、ご存じであればヒントだけでもいただけないですか?
--------
// WaitForSingleObject(l_sei.hProcess, INFINITE);
for (;;) {
    const DWORD ret = MsgWaitForMultipleObjects(1, &l_sei.hProcess, FALSE, INFINITE, QS_SENDMESSAGE);
    if (ret == WAIT_OBJECT_0) {
        break;
    }
    else if (ret == (WAIT_OBJECT_0 + 1)) {
        MSG msg;
        while (::PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
            if (処理したいメッセージなら) {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
    }
}
--------
上記では SendMessage 系でメッセージが来た場合のみメッセージループにまわしている。

ちなみに、ウィンドウ(メッセージループ)を持つスレッドでは、基本 WaitForSingleObject() より
 MsgWaitForMultipleObjects() を使うべきだと俺は思ってる。
提示ソースで云うと、WaitForSingleObject() で待っているため hDlg の画面再描画を
処理できず hDlg の表示がメチャ2になるが、MsgWaitForMultipleObjects() + QS_PAINT で
待つようにすれば必要に応じて hDlg の表示を更新できる。


どら  2008-09-13 03:46:33  No: 68994

subaruさん

ご丁寧にありがとうございます。
私の方では以下のようにソースを書いています。

typedef void (CALLBACK *EntryPoint)(HWND hwnd, HINSTANCE hinst, LPTSTR lpszCmdLine, int nCmdShow);

中略・・・

HINSTANCE hLib;

中略・・・

hLib = LoadLibrary(TEXT("shell32.dll"));
if(hLib != NULL)
{
   EntryPoint pControl_RunDLL = (EntryPoint)GetProcAddress(hLib, "Control_RunDLLA");
   if(pControl_RunDLL != NULL)
   {
      (*pControl_RunDLL)(hDlg, GetModuleHandle(NULL), "inetcpl.cpl", SW_SHOWDEFAULT);
   }
   FreeLibrary(hLib);
}

gakさん

レスありがとうございます。
MsgWaitForMultipleObjectsを使った場合はこのようになるのですね。
時間があるときにこちらでも確認してみようと思います。

今回は、subaruさんの案で構築してみようと思います。
(Google Chrome などでは、いきなり「接続」タブを開くように作り込まれ
ているみたいで、subaruさんの案でこのようにできないかとも策してみよう
と思います)

かもねぎさん、subaruさんgakさん、本当にありがとうございました。


subaru  2008-09-13 05:53:56  No: 68995

LPTSTR 型の引数の場合は関数側で書き換えられる可能性があります。
文字列は静的な領域に取られるため直接渡さずにヒープかスタック上にあるほうが安全です。
#CreateProcessWなんかは文字列を直接渡すと落ちます。

あと引数の説明読むと第2引数もプロセス側じゃなくてDLLのインスタンスハンドルみたいですね。
見落としてました。

>(Google Chrome などでは、いきなり「接続」タブを開くように作り込まれ
>ているみたいで、subaruさんの案でこのようにできないかとも策してみよう
>と思います)
前回書き込んだリンク先の関連情報にもあるのですが、
パラメーターを "inetcpl.cpl,@0,4" にするだけでいいみたいです。


どら  2008-09-17 02:18:45  No: 68996

subaruさん

何度もありがとうございます。

> パラメーターを "inetcpl.cpl,@0,4" にするだけでいいみたいです。

おぉ、本当だ☆
ありがとうございます!!すごく助かりました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加