c#

C#によるActiveWindowの監視


要件
  ユーザーが使用している任意のアプリケーションの起動・終了時間の把握
 

実装内容
 1. Windowsサービスにて実装
 2. ActiveWindowを監視してWindowTitleを取得
 3. ActiveWindow取得時にHookは未使用(Timerによる監視)
 4. WM_CBT/WM_SHELLともに、Windowsサービスでは監視用Windowを持たない為NG
 5. ActiveWindowを取得後、そのProcessが終了した時点でProcess名などのプロパティを取得



[ myProcessWatch.cs ]
-------------
using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace pcLogWatch {
    public class myProcessWatch{
        private static Process process;
        public static ArrayList g_list;

        [DllImport("user32.dll")]
        public static extern IntPtr GetForegroundWindow();

        [DllImport("user32.dll")]
        public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);

        [DllImport("user32.dll")]
        public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpStr, int nMaxCount);

        [DllImport("user32.dll")]
        public static extern int GetWindowTextLength(IntPtr hWnd);

        [DllImport("user32.dll")]
        public static extern bool IsWindowVisible(IntPtr hWnd);

        private static string windowTitle;

        public myProcessWatch() {
            g_list = new ArrayList();
        }
        [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
        private void processExited(object sender, EventArgs e) {
        
            //Java側で実装したサービス・モデル(JavaBean)
            ims.processModel model = new ims.processModel();

            Process process = (Process)sender;
            model.startTime = process.StartTime.ToString();
            model.exitTime = process.ExitTime.ToString();
            model.productName = process.ProcessName;
            model.windowTitle = process.MainWindowTitle;
            g_list.Add(model);
        }

        [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
        internal void getFocusMessage() {
            // アクティブなウィンドウハンドルの取得
            IntPtr hWnd = GetForegroundWindow();

            if (IsWindowVisible(hWnd)) {

                int winLen = GetWindowTextLength(hWnd);

                StringBuilder sb = new StringBuilder(winLen+1);
                GetWindowText(hWnd, sb, sb.Capacity);

                if (windowTitle != sb.ToString()) {
                    int id = 0;
                    // ウィンドウハンドルからプロセスIDを取得
                    GetWindowThreadProcessId(hWnd, out id);

                    try {
                        if (id != Process.GetCurrentProcess().Id) {
                            process = Process.GetProcessById(id);
                            string message = process.MainWindowTitle;

                            if (message != "") {
                                process.EnableRaisingEvents = true;
                                process.Exited += new EventHandler(processExited);
                            }
                        }
                    } catch (Exception e) {
                        Console.WriteLine(e.StackTrace);
                    }

                    windowTitle = sb.ToString();
                }
            }
        }
    }
}
[ EOF ]
-------------


[ Program.cs ]
-------------
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using pcLogWatch;

namespace pcLogWatch {
    class Program : System.ServiceProcess.ServiceBase {

        //データ送信用Timer
        //private static System.Threading.Timer sendDataTimer;
        
        //データ送信用Controller
        //private static Controller ctrl = new Controller();
        
        //WindowTitle監視用Timer
        private static System.Threading.Timer callbackTimer;
        private static myProcessWatch pWatch = new myProcessWatch();

        static void Main(string[] args) {
            System.ServiceProcess.ServiceBase[] ServicesToRun = new System.ServiceProcess.ServiceBase[] { new Program() };
            System.ServiceProcess.ServiceBase.Run(ServicesToRun);
            Console.ReadLine();
        }
        protected override void OnStart(string[] args) {
            base.OnStart(args);

            //デフォルトは100msで監視
            //まぁ500msくらいでも充分でしょうね。
            TimerCallback timerDelegate = new TimerCallback(OnWatchingTimer);
            callbackTimer = new System.Threading.Timer(timerDelegate, null, 0, 100);
            
            //まぁ後はテキトーに別のTimerCallbackを使用して、適度な時間間隔でサーバー側に送信すればおk
            //TimerCallback sendDataDelegate = new TimerCallback(OnDataSendTimer);
            //sendDataTimer = new Timer(sendDataDelegate, null, 0, ((1000 * 60) * int.Parse(Properties.Settings.Default.SendDataTimer)));

        }
        protected override void OnStop() {
            callbackTimer = null;
            //sendDataTimer= null;
            //ctrl = null;
            base.OnStop();
        }
        private void OnWatchingTimer(object o) {
            pWatch.getFocusMessage();
        }
        /*
        private void OnDataSendTimer(object o) {
            if(myProcessWatch.g_list.Count > 0){
                ctrl.sendData(System.Environment.MachineName, bean, myProcessWatch.g_list);
                myProcessWatch.g_list.Clear();
            }
        }
        */
    }
}
[ EOF ]
-------------


というかですね、WM_CBT Hook/WM_SHELL Hookともにやってみたのですが、Timer使った方がキレイに取得出来るんですよね。
だから無理してAPI Hookする必要も無いのかな...と。
クリップボードの監視とかだとHookする必要が出てきますね。

このページの先頭へ