我写了一个简单的测试程序(TestProgram.exe)来学习如何处理CTRL_CLOSE_EVENT,这是我的观察和我的问题:
1)当我双击TestProgram.exe启动它时,如果我现在转到任务管理器,TestProgram.exe列在“应用程序”下。当我在TestProgram.exe上执行“结束任务”时,我的CTRL_CLOSE_EVENT处理程序被调用。
BUT
2)当我打开命令提示符并启动TestProgram.exe时,它在“任务管理器”下的“后台进程”下列出,并且对其执行“结束任务”不会产生CTRL_CLOSE_EVENT。
我的真实应用程序如上面的案例2)中所述使用。当用户在我的应用程序上执行结束任务时(我在任务管理器中的后台进程下列出),我想做一些清理工作。
谢谢, 克里希纳
答案 0 :(得分:7)
通常,当一个进程被列为“应用程序”时,它意味着任务管理器已检测到该进程具有GUI,并且GUI上的“结束任务”将首先尝试通过标准{{优雅地关闭GUI 1}}和/或WM_CLOSE
消息之前通过WM_QUIT
求助于GUI进程的暴力终止。另一方面,在“后台进程”上执行“结束任务”将立即执行暴力终止。
因此,在您的情况下,双击.exe文件会导致新的专用控制台进程自动运行您的应用程序,因此控制台的GUI会被标记为“应用程序”,但是当您打开控制台时首先是窗口并通过命令行执行.exe,您的应用程序在现有控制台中运行并共享控制台的原始GUI,因此您的应用程序没有自己的GUI,因此被标记为“后台进程”。
答案 1 :(得分:2)
当一个进程终止(未关闭)时,除非你通过在任务管理器进程中挂钩TerminateProcess
或NtTerminateProcess
开始做一些挂钩,否则无法进行任何操作,例如它是如何工作的:
#include <windows.h>
#include <assert.h>
BOOL WINAPI MyTerminateProcess(HANDLE hProcess, UINT uExitCode ) {
MessageBox(NULL, TEXT("Do some cleanup"), NULL, MB_OK);
ExitProcess(0);
return TRUE;
}
#pragma pack(1)
typedef struct __PATCHDATA {
BYTE push;
DWORD address;
BYTE ret;
} PATCHDATA;
#pragma pack()
int main(int argc, char **argv) {
HMODULE hModule;
DWORD written;
// This struct contains assembly instruction that do:
// push address ; 0x68 MyTerminateProcess
// ret ; 0xc3
// so the execution will return to our hook
PATCHDATA patch = {0x68, (DWORD) MyTerminateProcess, 0xc3};
// remove this code, the program will terminate itself.
// TODO: check the memory protection and modify it.
WriteProcessMemory(GetCurrentProcess(),
TerminateProcess,
&patch,
sizeof(PATCHDATA),
&written);
TerminateProcess(NULL, 0);
return 0;
}
这在同一进程中挂钩TerminateProcess
,你需要在DLL中发送它,并在任务Maneger进程中inject,没有测试它。但是这种方法过度劳累并且不安全,有些AV产品可能会将其视为有害程序。
一个简单的解决方案是清理程序启动,正如@Martin James建议的那样。在程序启动时创建文件或使用注册表存储一些值,如0
,如果程序已关闭,如果是GUI则收到WM_CLOSE
,如果关闭命令则收到CTRL_CLOSE_EVENT
提示,您进行清理并存储1
。
在下次启动时,你会检查该值是否仍为0
,这意味着程序未正确关闭,请进行清理,如果1
没有必要清理,存储0
并继续前进。
许多程序使用此方法来检测程序是否已正确关闭。