インターフェイス部分。ウインドウはいらないのでただ常駐すればいい、でもコンソールにしたらDOS窓が出てきてうざいので、最初は窓なしWindowsアプリにしたんだけど、ウインドウメッセージなりシグナル(?)なりのハンドラーを作りたかったので、SW_HIDEでWindowを生成することにしました。
2重起動防止
2重で起動してても意味がないので、防止するにはどうしたらいいのか。ロックファイルを作る、プロセス一覧から同じ名前のプロセスを探す、とか色々考えたけど、調べてみるとMutexを使うのが一番簡単そう。
// check already-running hMutex = CreateMutex(FALSE, 0, "MUTEX_GETAP_CHECK"); if(GetLastError() == ERROR_ALREADY_EXISTS){ ReleaseMutex(hMutex); CloseHandle(hMutex); return FALSE; }
これだけ。
シャットダウンのときとかにログを出しておきたい
I/Oの負担になるので、デフォルトでは10分おきとかに出力させてるんだけど、シャットダウンのときとか終了時にはログを出しておきたい*1。まあ、10分くらいならあきらめるってのもアリだけどね・・・。コンソールアプリだとSetConsoleCtrlHandler()でコントロールイベント(っていうのか?)をトラップできるらしい。でもこれはWindowsアプリなので、何も表示させないウインドウを作って、そこにメッセージを送ることにします。
シャットダウンやリブートのときにはWM_QUERYENDSESSIONが全てのウインドウに送られるらしいので、それをキャッチしてログを出させる。
LRESULT CALLBACK WindowProc(HWND hwnd,UINT message,WPARAM wparam,LPARAM lparam) { switch(message){ case WM_DESTROY: PostQuitMessage(0); return 0; // case WM_POWERBROADCAST: case WM_QUERYENDSESSION: ap.fileoutput(); return TRUE; } return DefWindowProc(hwnd,message,wparam,lparam); }
全ソースコードはこんなかんじ。
#include <stdio.h> #include <stdlib.h> #include <time.h> #include <windows.h> #include "ActiveProcInfo.h" #define PLEN 256 #define UPDATE_TIME 1000 // 1 second #define LOGGING_TIME 10*60*UPDATE_TIME HWND CreateMinimalWindow(void); LRESULT CALLBACK WindowProc(HWND,UINT,WPARAM,LPARAM); VOID CALLBACK callback1(HWND, UINT, UINT, DWORD); VOID CALLBACK callback2(HWND, UINT, UINT, DWORD); ActiveProcInfo ap; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow){ MSG msg; HWND hwnd; time_t timer; struct tm *t_st; int logging_time; char path[PLEN], drive[PLEN], dir[PLEN], file[PLEN], file2[PLEN], ext[PLEN]; HANDLE hMutex; // check already-running hMutex = CreateMutex(FALSE, 0, "MUTEX_GETAP_CHECK"); if(GetLastError() == ERROR_ALREADY_EXISTS){ ReleaseMutex(hMutex); CloseHandle(hMutex); return FALSE; } // set logging-time interval logging_time = atoi(lpCmdLine) * UPDATE_TIME; if(logging_time < UPDATE_TIME){ logging_time = LOGGING_TIME; } // set logfile name time(&timer); t_st = localtime(&timer); GetModuleFileName(NULL, path, sizeof(path)/sizeof(char)); _splitpath(path, drive, dir, file, ext); sprintf(file2, "%s_%04d%02d%02d%02d%02d%02d", file, t_st->tm_year+1900, t_st->tm_mon+1, t_st->tm_mday, t_st->tm_hour, t_st->tm_min, t_st->tm_sec); _makepath(path, drive, dir, file2, ".log"); ap.setfilename(path); // set timer SetTimer(NULL, 1, UPDATE_TIME, callback1); SetTimer(NULL, 2, logging_time, callback2); // open hidden window if((hwnd=CreateMinimalWindow()) == NULL) return 0; ShowWindow(hwnd, SW_HIDE); // looping... while(GetMessage(&msg, NULL, 0, 0)){ TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } VOID CALLBACK callback1(HWND hwnd, UINT nMsg, UINT idEvent, DWORD dwTime) { ap.update(); } VOID CALLBACK callback2(HWND hwnd, UINT nMsg, UINT idEvent, DWORD dwTime) { ap.fileoutput(); } #define WCLASSNAME "getapMinimalWindow" HWND CreateMinimalWindow(void) { WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WindowProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = NULL; wc.hIcon = LoadIcon(NULL,IDI_APPLICATION); wc.hCursor = LoadCursor(NULL,IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wc.lpszMenuName = NULL; wc.lpszClassName = WCLASSNAME; if(RegisterClass(&wc) == 0) return NULL; return CreateWindow(WCLASSNAME, "",WS_OVERLAPPEDWINDOW,0,0,0,0,NULL,NULL,NULL,0); } LRESULT CALLBACK WindowProc(HWND hwnd,UINT message,WPARAM wparam,LPARAM lparam) { switch(message){ case WM_DESTROY: PostQuitMessage(0); return 0; // case WM_POWERBROADCAST: case WM_QUERYENDSESSION: ap.fileoutput(); return TRUE; } return DefWindowProc(hwnd,message,wparam,lparam); }
ソースは短いんだけど、Windows APIを駆使して作ると何だかWindowsハックしてるような気持ちになりますね。
まあ何にしても、目的のものができてよかった。案外フリーウェアでこういうのがないんだよなあ・・・。