ホーム | プログラミングTClock |
2003/11/20更新。031114以降のソースに基づいています。
ここでは、TClock Lightでの終了処理の手順について紹介します。TClock Lightの終了処理には、次の3+1パターンがあります。
void OnTClockCommand(HWND hwnd, WORD wID, WORD wCode)
{
switch(wID)
{
(中略)
case IDC_EXIT: // Exit
PostMessage(g_hwndClock, CLOCKM_EXIT, 0, 0);
break;
case CLOCKM_EXIT: // clean up all
EndClock(hwnd);
return 0;
関数EndClockでは、サブクラス化の解除など各種終了処理を行ったあとで、メッセージWM_CLOSEをtclock.exeのメインウィンドウにポストします(dll/main2.c)。
void EndClock(HWND hwnd)
{
(中略)
// restore window procedure
SetWindowLong(hwnd, GWL_WNDPROC, (LONG)g_oldWndProc);
g_oldWndProc = NULL;
RefreshTaskbar(hwnd); // taskbar.c
// close window of tclock.exe
PostMessage(g_hwndTClockMain, WM_CLOSE, 0, 0);
}
void OnDestroy(HWND hwnd)
{
ClearTClockMain(hwnd);
PostQuitMessage(0);
}
void ClearTClockMain(HWND hwnd)
{
(中略)
if(m_bHook) HookEnd(); // dll/main.c - uninstall the hook
m_bHook = FALSE;
}
void WINAPI HookEnd(void)
{
g_bHookEnable = FALSE;
// EndClock() will be called
if(g_hwndHook && IsWindow(g_hwndHook))
SendMessage(g_hwndHook, CLOCKM_EXIT, 0, 0);
// uninstall my hook
if(g_hhook != NULL)
UnhookWindowsHookEx(g_hhook);
g_hhook = NULL;
}
メッセージWM_COMMAND(wParam=IDC_EXIT)が送られた段階でtclock.exeにWM_CLOSEを投げずに、tcdll.tclock→tclock.exeの順で終了処理をさせています。そのほうが、なぜだかWindows 95/98/Meでエラーが出なくなるからです。
他のアプリケーションからtclock.exeのメインウィンドウにWM_CLOSEが直接送られる場合もあります。そのときは、tclock.exe→tcdll.tclockの順で終了処理が行われます。
Windowsで「電源を切る」「再起動」「ログオフ」を選ぶと、tclock.exeのメインウィンドウにメッセージWM_ENDSESSIONが送られてきます。WM_CLOSEやWM_DESTROYは送られてきません。
WM_ENDSESSIONが送られると関数ClearTClockMainを呼び出し、ClearTClockMainではtcdll.tclockのAPI関数HookEndを呼び出します。メッセージWM_CLOSEが送られたときと同じ手順になります(exe/wndproc.c)。
case WM_ENDSESSION:
if(wParam) ClearTClockMain(hwnd);
break;
上記の「Windowsがシャットダウン(ログオフ)されるとき」は、Windows NT4/2000/XPおよびIE4なしのWindows 95の話です。IE4以上がインストールされたWindows 95、98、Meでは、tclock.exeのメインウィンドウにWM_ENDSESSIONが送られる前に、時計ウィンドウにメッセージWM_DESTROYが送られてきます。終了処理がなされる前に、時計ウィンドウが破棄されてしまいます。
tcdll.tclockでは、WM_DESTROYが送られたら関数OnDestroyを呼び出します(dll/wndproc.c)。
case WM_DESTROY:
OnDestroy(hwnd); // main2.c
break;
関数OnDestoryでは、サブクラス化の解除などの終了処理を行わず、リソースを解放する関数だけを呼び出します(dll/main2.c)。
void OnDestroy(HWND hwnd)
{
ClearStartButtonResource(); // startbtn.c
ClearDrawing(); // drawing.c
}
その後、tclock.exeにWM_ENDSESSIONが送られ、関数ClearTClockMain→HookEndが呼ばれますが、時計ウィンドウが破棄されたあとなので、関数EndClockは実行されません。
1997年始めのTClockのプログラムでは、時計のサブクラス化プロシージャで、WM_DESTROYが送られてきたときも、終了処理のために関数EndClockを呼び出してました。
case WM_DESTROY:
EndClock(hwnd);
break;
関数EndClockには、次のようなコードがありました。
SetWindowLong(hwnd, GWL_WNDPROC, (LONG)g_oldWndProc);
g_oldWndProc = NULL;
するとどうなるかというと、WM_DESTROYのcase文にはbreakがあるので……
return CallWindowProc(g_oldWndProc, hwnd, message, wParam, lParam);
CallWindowProcに値がNULLのg_oldWndProcが渡されて、Explorerがクラッシュするということになります。おそらくこれが「IE 4を入れたらTClockのせいでExplorerが落ちるようになった」の原因ではなかったかと思われます。