Windowsでプロセスの起動・終了を監視する

Windowsでプロセスの起動・終了を監視するプログラムを書いてみました。コマンドライン引数で指定したプログラムが起動・終了すると音が鳴ります。
Haskellで書く利点はまったくありません。まぁFFIの練習みたいなものです。ところでC言語側からHaskell側にリストを返すのってどうやるんでしょう(まだ既存のソースをあんまり調べてません)。それができれば、もうちょっとC言語側でやらせているロジックが少なくなるんだけどなー。
それにしても、Haskellはスリープすら標準なのが無いのかな。うーむ、Haskellでシステムまわりをいじろうとすると結構苦労しますね。
ファイル: proc.c

#include <HsFFI.h>
#include <windows.h>
#include <psapi.h>

HsBool
c_exist_proc(char *pname)
{
    DWORD procs[1024];
    DWORD procbytes;
    int i;
    
    if (!EnumProcesses(procs, sizeof(procs), &procbytes))
        return HS_BOOL_FALSE;
    
    for (i = 0; i < procbytes / sizeof(procs[0]); i++) {
        TCHAR name[MAX_PATH] = "";
        HANDLE hproc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
                                   FALSE, procs[i]);

        if (hproc) {
            HMODULE hmod;
            DWORD modbytes;
            
            if (EnumProcessModules(hproc, &hmod, sizeof(hmod), &modbytes))
                GetModuleBaseName(hproc, hmod, name, sizeof(name) / sizeof(name[0]));
        }

        CloseHandle(hproc);

        if (strcmp(pname, name) == 0)
            return HS_BOOL_TRUE;
    }
    
    return HS_BOOL_FALSE;
}

ファイル: pwatch.hs

module Main (main) where

import Control.Monad (when)
import Data.Word (Word32)
import Foreign.C (CString, withCString)
import System (getArgs)

foreign import stdcall unsafe "Sleep" cSleep :: Word32 -> IO ()
foreign import stdcall unsafe "MessageBeep" cMesssageBeep :: Word32 -> IO ()

cMB_ICONASTERISK         = 64
cMB_ICONEXCLAMATION      = 0x30
cMB_ICONHAND             = 16
cMB_ICONQUESTION         = 32
cMB_OK                   = 0

foreign import ccall unsafe "c_exist_proc" cExistProcess :: CString -> IO Bool

existProcess :: String -> IO Bool
existProcess name = withCString name $ \ cstr -> cExistProcess cstr

main :: IO ()
main = do
  args <- getArgs
  if (null args)
    then putStrLn "usage: pwatch [proc name]"
    else loop (head args) False

loop name prev = do
  now <- existProcess name
  when (now /= prev) $ cMesssageBeep cMB_ICONASTERISK
  cSleep 2000
  loop name now

-- Local Variables:
-- compile-command: "ghc -W -fno-warn-unused-binds -fno-warn-unused-matches -ffi -o pwatch proc.c pwatch.hs -lpsapi -luser32 -lkernel32"
-- End:

ちなみにこのプログラムは、ScanSnapで書籍をPDFに変換しているときに、変換の終了を通知するために作りました。音が鳴らないといつ変換終わったのかわからないので、PCの前から離れられないんですよね… ScanSnapの場合、PDFで文字認識しているときにはSSPprCap.exeというプロセスが起動しているので、こんな感じで監視してやります。

$ pwatch SSPprCap.exe