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());
                }
            }
        }
    }
}