2010年12月19日日曜日

Setup.exeで詳細ログを出力する方法

msiで詳細ログを出力できることは知っていましたが、Setup.exeでも可能なようです。
リリースする場合は、InstallShieldなどで単一のSetup.exeにまとめている場合が
多いと思いますが、何か問題が発生した場合の解析に便利です。

まずはmsiの場合の復習。

msiexec /i Installer.msi /L*v "C:\Install.log"

setup.exeの場合は次の通り。

setup.exe /v"/L*v C:\Install.log"

/vオプションに続けてダブルクォーテーションで囲んだ形で指定すればOKのようです。

2010年12月8日水曜日

Google Chrome 8でMicrosoft Forefront TMGへ接続できない問題の回避方法

Google Chrome 8にアップデート後、Microsoft Forefront TMGへのアクセスができなくなり、
いろいろと苦労したので、忘れないようにメモ。

現象としては、TMGへアクセスするとずっとロード中のままになります。
Windows XPではキャッシュとクッキーのクリアで直ったのですが、Windows 7の場合
再インストールしても復旧せず困っていました。

いろいろ検索したところ、Chromeのフォーラムに答えを発見しました。
起動オプションとして、--use-system-sslを使えば良いみたいです。
具体的には、Chromeのショートカットのプロパティを開いて、リンク先の後ろに
これを付ければよいです。



【フォーラムの該当エントリ】

2010年11月5日金曜日

Visual Studioのセットアップ プロジェクトでアドバタイズショートカットを作らない方法

Visual Studio付属のセットアップ プロジェクトで、スタートメニューにショートカットを
作成しようとすると、必ずアドバタイズショートカットになってしまいます。
これには、いろいろと弊害があり、意図しない状況で修復インストールが動いてしまったりします。

厄介なことに、Visual Studioのオプションではどうすることも出来ません。(項目が無い)
かといって、リリース毎にOrcaで設定をいじるのも面倒です。
なので、なにか良い方法が無いか考えてみました。

結論からいえば、セットアップ プロジェクトのポストビルドイベントで何とかできます。

(1) 例によって、スクリプトファイルを入手するため、Windows Installer SDKをインストールします。
    (これはPlatform SDKの中に含まれています)

(2)*.vdprojがあるプロジェクトフォルダ直下にWiRunSQL.vbsをコピーします。
    WiRunSQL.vbsは、デフォルトでは下記の場所にあるはずです。

C:\Program Files\Microsoft Platform SDK\Samples\SysMgmt\Msi\Scripts\WiRunSQL.vbs

(3)ポストビルドイベントを次のように設定します。(マクロを使っていますのでコピペでいけるはず)

cscript "$(ProjectDir)WiRunSQL.vbs" "$(BuiltOuputPath)" "INSERT INTO Property(Property,Value) VALUES('DISABLEADVTSHORTCUTS', '1')"
































【参考サイト】
[MSDN] Execute SQL Statements

[DOBON.NET] アドバタイズショートカットではなく、普通のショートカットを作成する

[DOBON.NET] MSIファイルのWindows Installerデータベースをプログラムで編集する

2010年10月20日水曜日

Chrome 拡張機能を作ってみました (Last Tab Keeper)

タブの制御を行う、簡単な拡張機能を作ってみました。

Chromeって、最後のタブを閉じると自動的に終了するのですが、ウインドウ自体は閉じて欲しくない場合ありませんか?
その場合、先に新規タブを出しておいて、開いているWebページを閉じることになると思います。
それを少し自動化できます。

最後のタブを閉じた時のイベントを拾って、新しいタブを作ればよいと気楽に考えていたのですが、
どうもそのタイミングでは新しいタブが作れない模様。
試行錯誤して、開いているWebページが残り1タブになったら、自動的に新しいタブを作ることにしました。
人によって、合う合わないあるかと思いますが、ご興味持たれた方は是非使ってみてください。

Chrome拡張機能ギャラリーに登録してありますので、下記リンクからアクセスできます。
ご意見、ご感想などありましたら、twitterかコメント欄でメッセージ頂ければ嬉しいです。

Last Tab Keeper

※名前はちょっと微妙だったかも・・・。アイコンもデフォルトのままですみません。

2010年10月15日金曜日

終了していないスレッドを調べるクラス

ワーカースレッドが終了していないため、メインウインドウは消えてもプロセスが残ってしまう場合があります。
その場合、原因のスレッドを突き止めて終わらない原因を修正しなければいけません。
しかし、開発環境では再現できないケースなどもあると思います。その場合のデバッグ用クラスを作ってみました。

基本的に、"new Thread"を"TraceableThread.CreateThread"に置換して、メインウインドウの
FormClosedイベントあたりでTimerProcTraceを呼んであげるだけです。
そうすると、指定ミリ秒毎に終了していないスレッドがある場合、作成時のスタックトレース付きでデバッグ出力に出します。
開発環境が入っていない(入れられない)場合でも、DebugViewなどで確認できます。

VS2008で作りましたが、多分.NET2.0以降なら動くと思います。

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Diagnostics;
using System.Reflection;

namespace Diagnostics
{
    /// <summary>
    /// スレッドの追跡を行えます。
    /// 終了していないスレッドを調べるのに役立ちます。
    /// </summary>
    public static class TraceableThread
    {
        private static List<Thread> m_listThread;
        private static List<StackTrace> m_listStackTrace;
        private static Timer m_timerRemove;
        private static Timer m_timerTrace;
        private static Object m_sync;

        /// <summary>
        /// 静的コンストラクタ
        /// </summary>
        static TraceableThread()
        {
            // 初期化
            m_listThread = new List<Thread>();
            m_listStackTrace = new List<StackTrace>();
            m_sync = new Object();

            // スレッド終了監視タイマー
            m_timerRemove = new Timer(new TimerCallback(TimerProcRemove), null, 1000, 1000);
        }

        /// <summary>
        /// スレッドの終了を監視し、スレッドリストを更新します。
        /// </summary>
        private static void TimerProcRemove(Object obj)
        {
            lock (m_sync)
            {
                List<Thread> remove = new List<Thread>();

                // 終了しているスレッドを探す
                foreach (Thread trd in m_listThread)
                {
                    try
                    {
                        if (trd.Join(0) == true)
                        {
                            remove.Add(trd);
                        }
                    }
                    catch (ThreadStateException)
                    {
                        // スレッドが開始されていない場合
                        continue;
                    }
                }

                // リストから削除する
                foreach (Thread trd in remove)
                {
                    int nIndex = m_listThread.IndexOf(trd);
                    m_listThread.RemoveAt(nIndex);
                    m_listStackTrace.RemoveAt(nIndex);
                }
            }
        }

        /// <summary>
        /// new Thread(ParameterizedThreadStart start)の代わりに呼んでください
        /// </summary>
        public static Thread CreateThread(ParameterizedThreadStart start)
        {
            StackTrace trace = new StackTrace(true);
            Thread thread;

            lock (m_sync)
            {
                m_listStackTrace.Add(trace);

                thread = new Thread(start);

                m_listThread.Add(thread);
            }

            return thread;
        }

        /// <summary>
        /// new Thread(ThreadStart start)の代わりに呼んでください
        /// </summary>
        public static Thread CreateThread(ThreadStart start)
        {
            StackTrace trace = new StackTrace(true);
            Thread thread;

            lock (m_sync)
            {
                m_listStackTrace.Add(trace);

                thread = new Thread(start);

                m_listThread.Add(thread);
            }

            return thread;
        }

        /// <summary>
        /// new Thread(ParameterizedThreadStart start, int maxStackSize)の代わりに呼んでください
        /// </summary>
        public static Thread CreateThread(ParameterizedThreadStart start, int maxStackSize)
        {
            StackTrace trace = new StackTrace(true);
            Thread thread;

            lock (m_sync)
            {
                m_listStackTrace.Add(trace);

                thread = new Thread(start, maxStackSize);

                m_listThread.Add(thread);
            }

            return thread;
        }

        /// <summary>
        /// new Thread(ThreadStart start, int maxStackSize)の代わりに呼んでください
        /// </summary>
        public static Thread CreateThread(ThreadStart start, int maxStackSize)
        {
            StackTrace trace = new StackTrace(true);
            Thread thread;

            lock (m_sync)
            {
                m_listStackTrace.Add(trace);

                thread = new Thread(start, maxStackSize);

                m_listThread.Add(thread);
            }

            return thread;
        }

        /// <summary>
        /// スレッドの終了追跡を開始します。
        /// メインウインドウのFormClosedイベントで呼び出すことで、
        /// 終了していないスレッドを検出することができます。
        /// </summary>
        public static void TraceThreadTermination(int interval)
        {
            if (m_timerTrace == null)
            {
                m_timerTrace = new Timer(new TimerCallback(TimerProcTrace), null, interval, interval);
            }
        }

        /// <summary>
        /// 終了していないスレッドをTraceに出力します。
        /// </summary>
        private static void TimerProcTrace(object obj)
        {
            lock (m_sync)
            {
                for (int i = 0; i < m_listThread.Count; i++)
                {
                    Trace.WriteLine("");
                    Trace.WriteLine(DateTime.Now.ToLongTimeString());
                    Trace.WriteLine("■終了していないスレッドを検出しました!");
                    Trace.WriteLine("Name = " + m_listThread[i].Name + ", ManagedThreadId = " + m_listThread[i].ManagedThreadId.ToString());
                    Trace.WriteLine("スレッド作成時のスタックトレースは次の通りです。");
                    Trace.WriteLine(m_listStackTrace[i].ToString());
                }
            }
        }
    }
}

2010年9月9日木曜日

Process.WaitForExit()は本当の意味で無期限待機しない

MSDNを見ると、Process.WaitForExitメソッドの引数無しバージョンの説明は次のようになっています。 
関連付けられたプロセスが終了するまで無期限に待機するように Process コンポーネントに指示します。
しかし、実際の動作はProcess.WaitForExit(Timeout.Infinite)と同じですので注意しましょう。
要するに、0x7FFFFFFFミリ秒後(25日後くらい)に、プロセスが終了していなくてもリターンします。(詳細はこちらを参照)

確かに、プログラム的には無期限ともいえますが、説明が紛らわしいですね。
戻り値がvoidなのもいやらしい。

サーバーでプロセスの異常終了を監視してメール通知するプログラムを作ったのですが、
再起動メールが来て焦ってログ確認したら問題無し。原因はこれでした。

2010年3月10日水曜日

Windows7でWebDAVサーバーを構築する方法

Windows7に搭載されているIIS7.5で、テスト用のWebDAVサーバーを構築しながら手順をまとめてみました。

構築するのは、基本認証に対応した書き込み可能なテスト用WebDAVサーバーです。ここでは手順は紹介しませんが、公開する場合はSSLも検討した方が良いでしょう。(基本認証はパスワードが平文で流れるため)
  1. WebDAV機能のインストール
  2. まずは、IISにWebDAVと基本認証機能をいれます。下図のように、「WebDAV発行」と「基本認証」を選択して追加します。(IISをデフォルトでインストールした状態では入っていません)
    注) Windows Vista / Windows Server 2008に搭載されているIIS7.0で行う場合、標準機能には含まれていませんので、ここからWebDAV拡張を入れることで対応できます。

  3. WebDAVユーザーを作成する
  4. WebDAVサーバーにアクセスするときに使うユーザーを作成しておきます。コントロールパネルのユーザーアカウントから、パスワードを設定した標準ユーザーを作成してください。ここではwebdavというユーザーを作りました。

  5. WebDAVを有効にする
  6. インストール直後、WebDAV機能は無効になっています。コントロールパネルの管理ツールの中からIISマネージャを起動します。左側のツリーを展開して"Default Web Site"を選択すると、右側に「WebDAVオーサリング規則」というアイコンがあると思いますので、ダブルクリックして開きます。
    右側に、「WebDAVの有効化」というボタンがあると思いますので押します。

  7. WebDAVルートの作成
  8. 次に"Default Web Site"の右クリックメニューから「仮想ディレクトリの追加」を選択します。
    今回は、"WebDAV"という名前で作成してみます。
    ちなみに、物理パスにはネットワークフォルダを指定することも出来ます。

  9. NTFSのアクセス権を設定する
  10. 標準ユーザー(Usersグループのメンバー)は読み取りアクセス権しか持っていませんので、追加したwebdavというユーザーが書き込みアクセスできるようにします。左側のツリーで、先ほど作成したWebDAVという仮想ディレクトリを選択し、右クリックメニューから「アクセス許可の編集」を選びます。
    「セキュリティ」タブを選択して、「編集」ボタンを押します。
    「追加」ボタンから、webdavユーザーを追加し、フルコントロールをチェックします。

  11. WebDAVのアクセス権を設定する
  12. 最後にWebDAVのアクセス権の設定を行います。新しく作った仮想ディレクトリ"WebDAV"を選択して、「WebDAVオーサリング規則」をダブルクリックします。そして、右上の「オーサリング規則の追加」ボタンを押します。
    「指定されたユーザー」を選択し、2.で作ったユーザー名webdavを入力します。アクセス許可には、「読み込み」と「書き込み」にチェックを入れます。
    これで設定完了です!

  13. アクセスしてみる
  14. うまく設定できたかを確認するには、WebDAVクライアントソフトを使うのが便利です。たとえばCarotDAVなどをつかって接続確認してみましょう。

IIS7.0で管理コンソールが大幅に変わったり、WebDAV拡張モジュールが標準で入っていなかったりとIISを敬遠していた方もいるかと思いますが、この機会に試してみてはどうでしょうか。