C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[7]: インストーラー(他のプロセスのインストール完了?キャンセル)


(過去ログ 28 を表示中)

[トピック内 8 記事 (1 - 8 表示)]  << 0 >>

■13359 / inTopicNo.1)  インストーラー(他のプロセスのインストール完了?キャンセル)
  
□投稿者/ セイン (60回)-(2008/01/28(Mon) 17:36:05)

分類:[.NET 全般] 

先日はありがとうございました。

現在インストーラーの作成をしていますが、別の問題が発生しました。

他のプロセスの起動をし、
(インテルのmsi)プロセスの終了を待つところまでは良いのですが、
その後、キャンセルボタンが押されたのかインストールが完了したのかを
戻値として得ることができるのかできないのか悩んでおります。

もしできないのであれば、再度インストール済みチェックをしようと考えています。

ご教授願います。

参考
プロセスを起動し、終了を待つhttp://hp.vector.co.jp/authors/VA007941/program/no2060.html
引用返信 編集キー/
■13360 / inTopicNo.2)  Re[1]: インストーラー(他のプロセスのインストール完了?キャンセル)
□投稿者/ セイン (61回)-(2008/01/28(Mon) 17:42:40)
んんん。。。

そもそもmsi形式のファイルに戻り値があるのか、
インテルさんはどうやって戻り値を返しているのかがわからないと無理ですかねぇ?
引用返信 編集キー/
■13362 / inTopicNo.3)  Re[2]: インストーラー(他のプロセスのインストール完了?キャンセル)
□投稿者/ とっちゃん (237回)-(2008/01/28(Mon) 18:23:44)
とっちゃん さんの Web サイト
No13360 (セイン さん) に返信

msi の実行をどのように行っているか?で変わりますが、戻り値はあると思いますよ。
先のスレでも少しだけ触れてますが、具体的な戻り値は、Product.xml などにも表記されてます。<MS製ランタイムインストーラの場合だけ

まま、もうちょっと具体的に書いてもらえると、アドバイスの仕方もあるわけですが...
exe で呼び出す場合は...
1.勝手にリブートされないオプションをつけて呼び出す(重要)
2.ShellExecuteEx API を使って、プロセスハンドルを待機できるようにして実行する(CreateProcessはVistaだとまずい)
3.MsgWaitForMultipleObjects API などを利用し、起動したインストーラの終了を待つ(実行条件次第では別のAPIでもOK)
4.GetExitCodeProcess で終了プロセスの終了コードを取得する(これ以外のAPIは存在しない)

という流れになります(これ以外に、エラー処理も必要ですがねw)。

msi を直接呼び出したいという場合は、
MsiInstallProduct API を用いることで、同様に行えます。
こちらは、APIを呼び出したら、終了するまで帰ってこないので、処理としては若干楽ですけどw
#当然、同一プロセス場なので、いろいろとお約束が出ることにはなりますがねw

具体的な戻り値は、インストーラによって若干異なりますが...
0(ERROR_SUCCESS)なら、成功。
3010(ERROR_SUCCESS_REBOOT_REQUIRED)なら、インストール成功だが、マシンリブートが必要。
1600〜1699までの間なら、msi の実行エラーコード(キャンセルを含む)
となります。

msi(msiexecでの呼び出しを含む)の戻り値については MSDNライブラリにもドキュメントがあります。
Webならこちらですね。
http://msdn2.microsoft.com/en-us/library/aa368542(VS.85).aspx

引用返信 編集キー/
■13364 / inTopicNo.4)  Re[3]: インストーラー(他のプロセスのインストール完了?キャンセル)
□投稿者/ セイン (62回)-(2008/01/28(Mon) 19:09:49)
2008/01/28(Mon) 19:30:13 編集(投稿者)
2008/01/28(Mon) 19:24:37 編集(投稿者)
<pre><pre>2008/01/28(Mon) 19:23:24 編集(投稿者)
2008/01/28(Mon) 19:14:20 編集(投稿者)

とっちゃんさんのアドバイスのおかげで終了コードが無事取れました。
具体的にということでしたので、ソースを添付します。
こんな感じでばっちりではないでしょうか?
throw 1としているので、環境によって戻値で返すようにすれば良いのではないでしょうか?

(どこか目をつぶるほどひどい部分あれば、解決済み消してでも突っ込みいれてくださいm(_ _)m)

MsiInstallProductもためしてみました。
コマンドラインにNULLを渡して起動したところ、ようこそ画面無し(画面無し)
でインストールができたので、びっくりです。
こちらは随時勉強して行こうと思います。

ありがとうございました。

/*---------------------------------------------*/
/** Msiインストーラー実行
* @ return 終了コード
* 0 :(ERROR_SUCCESS)成功
* 3010 :(ERROR_SUCCESS_REBOOT_REQUIRED)なら、インストール成功だが、マシンリブートが必要。
* 1600〜1699:msi の実行エラーコード(キャンセルを含む)
*/
/*---------------------------------------------*/
DWORD MsiInstallCmd( LPCTSTR pszCmd )
{
SHELLEXECUTEINFO sei = { 0 };
//構造体のサイズ
sei.cbSize = sizeof(SHELLEXECUTEINFO);
//起動側のウインドウハンドル
sei.hwnd = NULL;
//起動後の表示状態
sei.nShow = SW_SHOWNORMAL;
//このパラメータが重要で、セットしないとSHELLEXECUTEINFO構造体のhProcessメンバがセットされない。
sei.fMask = SEE_MASK_NOCLOSEPROCESS;
//起動プログラム
sei.lpFile = (LPCSTR)pszCmd;
//プロセス起動
if(!ShellExecuteEx(&sei) || sei.hInstApp <= (HINSTANCE)32){
throw 1;
}
//終了を待つ
WaitForSingleObject( sei.hProcess, INFINITE );
// 終了コード取得
DWORD ret;
if(!GetExitCodeProcess ( sei.hProcess, &ret )) {
throw 1;
}
return ret;
}

ありがとぅ!</pre></pre>
解決済み
引用返信 編集キー/
■13365 / inTopicNo.5)  Re[4]: インストーラー(他のプロセスのインストール完了?キャンセル)
□投稿者/ とっちゃん (238回)-(2008/01/28(Mon) 19:59:26)
とっちゃん さんの Web サイト
No13364 (セイン さん) に返信

> 具体的にということでしたので、ソースを添付します。
あ、ソースをということじゃなくて、msi の実行方法は?という意味だったんですよw
#ネタ的にコードがないとわからんというものじゃないので

> MsiInstallProductもためしてみました。
> コマンドラインにNULLを渡して起動したところ、ようこそ画面無し(画面無し)
> でインストールができたので、びっくりです。
> こちらは随時勉強して行こうと思います。
>
こちらは、MsiInstallProduct の前に
MsiSetInternalUI とか、MsiSetExternalUI とかを呼び出す必要があります。
前者は、msi の持つUIの表示方法の指定で、msiexec /q? のオプションに相当します。
後者は、外部UIの指定で、エラーハンドリングとかをどうするか?という指定になります。
#このあたりまでもろもろやると凝ったインストーラが作れるんですが...正直しんどいというレベルを超えますw



前後しますがw
> (どこか目をつぶるほどひどい部分あれば、解決済み消してでも突っ込みいれてくださいm(_ _)m)
>
ということなので...
ま、大きな問題は、1か所だけなので大半はついで突っ込みですがw

> if(!ShellExecuteEx(&sei) || sei.hInstApp <= (HINSTANCE)32){
> throw 1;
> }
ShellExecuteExが成功(!=FALSE)を返した場合は、hInstApp は、32より大きな値をセットしてきます。
逆に、失敗した場合は具体的なエラーコード(ShellExecuteと同じエラー値)がセットされてきます。
なので、hInstApp を改めてチェックする必要はありません。
エラーコードをチェックしたい場合は、GetLastError() を返せばよいかと。

むしろ、SHELLEXECUTEINFO 構造体のヘルプにもあるように、hProcess のNULLチェックのほうが必要です。
#NULLの場合は後続のAPIが呼べないので...

それと、hProcess がNULLではない場合は、CloseHandle() して明示的に閉じる必要があります。
とじない場合、プロセスハンドルをリークします。
#結果、プロセスが完全にはとじない状態となり、システムリソースを食いつぶしていきます。
こちらは、致命的な状態に陥ることになりかねないので修正が必要です。


エラーの状態が2種類あるので、それをうまく切り分けできるといいのかな?
というか、例外を投げるのであれば、GetLastError() をうまく組み込むことを考えたほうがいいですね。

いずれのAPI呼び出しも、GetLastError() で適切なエラーコードをセットしてくれるので、
どこかで失敗したら、戻り値は、GetLastError() の値を基にした例外(独自でも既存のものでも何でもよい)を発行、
最終的に成功した場合は、GetExitCodeProcess の値を返すとすれば、より汎用的な実行ルーチンとして使えると思います。

これ以外では特に、気にするようなところはないかと。

引用返信 編集キー/
■13366 / inTopicNo.6)  Re[5]: インストーラー(他のプロセスのインストール完了?キャンセル)
□投稿者/ セイン (63回)-(2008/01/28(Mon) 20:33:36)
2008/01/28(Mon) 20:38:23 編集(投稿者)
>これ以外では特に、気にするようなところはないかと。
すいません。お時間とらせて^^;
たしかにCloseHandle()がないのは問題ありでした。
throw 1 する前にも、チェックが必要ですので、んーーーー。
こんな感じでいかがでしょうか。
ret -1; より GetLastError?

/*---------------------------------------------*/
/** Msiインストーラー起動
 * @ return 終了コード
 *    0        :(ERROR_SUCCESS)成功
 *    -1	   : 予期しないエラー
 *    3010     :(ERROR_SUCCESS_REBOOT_REQUIRED)なら、インストール成功だが、マシンリブートが必要。
 *   1600〜1699:msi の実行エラーコード(キャンセルを含む)
 */
/*---------------------------------------------*/
DWORD MsiInstallCmd( LPCTSTR pszCmd )
{
	SHELLEXECUTEINFO	sei = { 0 };
	DWORD				ret = ERROR_SUCCESS;
	//構造体のサイズ
	sei.cbSize = sizeof(SHELLEXECUTEINFO);
	//起動側のウインドウハンドル
	sei.hwnd = NULL;
	sei.hProcess = NULL;
	//起動後の表示状態
	sei.nShow = SW_SHOWNORMAL;
	//このパラメータが重要で、セットしないとSHELLEXECUTEINFO構造体のhProcessメンバがセットされない。
	sei.fMask = SEE_MASK_NOCLOSEPROCESS;
	//起動プログラム
	sei.lpFile = (LPCSTR)pszCmd;

	try{
		//プロセス起動
		if(!ShellExecuteEx(&sei)){	
			throw 1;
		}
		//NULLチェック
		if(sei.hProcess == NULL) {
			throw 1;
		}
		//終了を待つ
		WaitForSingleObject(sei.hProcess, INFINITE);
		//終了コード取得
		if(!GetExitCodeProcess(sei.hProcess, &ret)) {
			throw 1;
		}
	}catch(...) {
		ret = -1;
	}
	//開放
	if(sei.hProcess != NULL) {
		CloseHandle(sei.hProcess);
	}
	return ret;
}

引用返信 編集キー/
■13372 / inTopicNo.7)  Re[6]: インストーラー(他のプロセスのインストール完了?キャンセル)
□投稿者/ とっちゃん (239回)-(2008/01/28(Mon) 22:06:07)
とっちゃん さんの Web サイト
No13366 (セイン さん) に返信
> たしかにCloseHandle()がないのは問題ありでした。
> throw 1 する前にも、チェックが必要ですので、んーーーー。
> こんな感じでいかがでしょうか。
> ret -1; より GetLastError?
>
例外(throw)と、エラーコードでどう区別をつけるか?
ですね。

呼び出したアプリの戻り値と、GetLastError は区別できないと問題ありなので
戻り値に呼び出し側のエラーコードだと、ちょっとまずいと思います。

なので、自身のロジック上のエラーは例外というのは選択肢として間違っているとは思いません。
#中には例外を使えない状況もあるので、一概に例外ならよいというものでもありませんが。

個人的には、throw 1 のようななんだかわからんオブジェクトを発行する例外ではなく
何らかのクラス(既存のフレームワークのものでよい)を例外オブジェクトとして
投げるほうが良いとは思いますが。


自分では、ATLをフレームワークに利用しているので、この手のエラーはみんな
ATL の例外オブジェクトで投げちゃってるんですけどねw


引用返信 編集キー/
■13389 / inTopicNo.8)  Re[7]: インストーラー(他のプロセスのインストール完了?キャンセル)
□投稿者/ セイン (64回)-(2008/01/29(Tue) 09:05:00)
おはようございます。
昨日は今年初めての積もるぐらいの雪で、
帰宅時怖かったです。


>呼び出したアプリの戻り値と、GetLastError は区別できないと問題ありなので
>戻り値に呼び出し側のエラーコードだと、ちょっとまずいと思います。

なるほど。確かにそのとおりです。ret -1;でいきますね。

>自分では、ATLをフレームワークに利用しているので、この手のエラーはみんな
>ATL の例外オブジェクトで投げちゃってるんですけどねw

その手がありましたか。勉強して挑戦してみます。


ソースまで見ていただき本当にありがとうございました。
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -