Windows Web Server 2008 中的 CTRL_CLOSE_EVENT 事件未调用 CtrlHandler

发布于 2024-10-04 14:19:21 字数 652 浏览 0 评论 0原文

我有一个控制台应用程序,它使用 SetConsoleCtrlHandler 设置一个处理 CTRL_CLOSE_EVENT 的处理程序。该处理程序仅返回 TRUE,这将导致出现一个对话框并提示用户继续关闭或取消。

该软件在Windows XP SP3 和Windows Web Server 2008 SP2 上运行。

在 XP 上,当单击控制台窗口上的“X”时,我的控制处理程序将被调用,并按预期出现提示。在 Server 2008 上,关闭控制台窗口不会调用我的控制处理程序,并且应用程序会在没有提示的情况下关闭。

为了检查控制处理程序是否设置正确,我添加了 CTRL_C_EVENT 的情况。我可以看到代码被 Ctrl-C 调用。

Server 2008 中处理关闭事件的方式有什么不同吗?看起来他们根本不经过 ctrl 处理程序。

编辑:查看SetConsoleCtrlHandler的MSDN页面,我找不到任何有关Vista及更高版本中不再处理CTRL_CLOSE_EVENT的信息。

如果您正在处理窗口(HWND)而不是控制台ctrl事件,是否可以获取发送到控制台窗口的关闭消息并处理它?

I have a console application which uses SetConsoleCtrlHandler to set a handler which handles and CTRL_CLOSE_EVENT. The handler simply returns TRUE which will cause a dialog box to appear and prompt the user to continue shutdown or cancel.

The software runs on Windows XP SP3 and Windows Web Server 2008 SP2.

On XP, when the 'X' on the console window is clicked, my control handler gets called and a prompt appears as expected. On Server 2008 closing the console window does not call my control handler and the application closes down without prompting.

To check that the control handler is being set correctly I have added a case for CTRL_C_EVENT. I can see the code get called for Ctrl-C.

Are there any differences in the way close events are handled in Server 2008? It seems like they do not go through the ctrl handlers at all.

EDIT: Looking at the MSDN page for SetConsoleCtrlHandler I can't find any information about CTRL_CLOSE_EVENT no longer being handled in Vista and later.

If you are dealing with windows (HWND) instead of console ctrl events, is it possible to get the close messages sent to the console window and handle that?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

半暖夏伤 2024-10-11 14:19:21

以下是我在控制台应用程序(在 Windows 7 上运行)中执行的操作

:创建隐藏窗口以等待关闭/注销通知。 重要:为其消息循环提供自己的线程

void interrupt::start()
{
  WNDCLASSEX wc = {};
  HINSTANCE hi = GetModuleHandle(NULL);

  wc.cbSize        = sizeof(WNDCLASSEX);
  wc.lpfnWndProc   = WndProc;
  // . . . etc

  if(!RegisterClassEx(&wc))
    return;

  hwnd_ = CreateWindowEx(WS_EX_CLIENTEDGE, class_name, "Waiting for user logoff event",
    WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, NULL, NULL, hi , NULL);

  ShowWindow(hwnd_, SW_HIDE);
  UpdateWindow(hwnd_);

  MSG msg = {};
  while(GetMessage(&msg, NULL, 0, 0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  // call internal function for sending "stop" notification to rest of program
  ctrl.stop(CTRL_CLOSE_EVENT);
}

ii。 在窗口消息处理程序中实现“特殊事件”的处理

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  switch(msg)
  {
  case WM_ENDSESSION:
    if (lParam)
    {
      // call internal function for sending "stop" notification to rest of program
      ctrl.stop(lParam == ENDSESSION_LOGOFF ? (int) interrupt::logoff : CTRL_CLOSE_EVENT);
    }
    break;
  case WM_CLOSE:
    DestroyWindow(hwnd);
    break;
  case WM_DESTROY:
    PostQuitMessage(0);
    break;
  default:
    return DefWindowProc(hwnd, msg, wParam, lParam);
  }
  return 0;
}

iii. 。实现处理“停止”请求的内部函数。它必须处理 2 个特殊情况

:当从窗口消息线程调用时,发送WM_CLOSE到窗口并等待其线程退出

b。当从主线程调用时,等待静态变量(至少一个)的终止。

这是因为 CtrlHandler 退出后,Windows 将无条件终止您的进程,而不给您的代码任何清理的机会。静态变量在主线程上被销毁,因此这个等待至少可以保证 int main() 已经退出。您可以在静态变量的构造函数中捕获主线程的线程ID(可能与启动“影子”窗口的相同)。

这是我在代码中的做法:

void interrupt::stop(int signal)
{
  // . . .

  // Set exit signal
  InterlockedExchange(&stage_, 2L);

  // Close shadow window if notification is from elsewhere
  if (hwnd_ && GetCurrentThreadId() != thread_.id())
  {
    PostMessage(hwnd_, WM_CLOSE, 0, 0);
    thread_.wait();
  }

  // Wait for completion of own destructor on main thread
  if (GetCurrentThreadId() != main_thread_id_)
    while(stage_ != 3L)
      Sleep(10);
}

// My static variable to wait for
interrupt ctrl;

Here is what I do in my console application (running on Windows 7):

i. Create hidden window to wait for close/logoff notification. Important: give it own thread for its message loop

void interrupt::start()
{
  WNDCLASSEX wc = {};
  HINSTANCE hi = GetModuleHandle(NULL);

  wc.cbSize        = sizeof(WNDCLASSEX);
  wc.lpfnWndProc   = WndProc;
  // . . . etc

  if(!RegisterClassEx(&wc))
    return;

  hwnd_ = CreateWindowEx(WS_EX_CLIENTEDGE, class_name, "Waiting for user logoff event",
    WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, NULL, NULL, hi , NULL);

  ShowWindow(hwnd_, SW_HIDE);
  UpdateWindow(hwnd_);

  MSG msg = {};
  while(GetMessage(&msg, NULL, 0, 0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  // call internal function for sending "stop" notification to rest of program
  ctrl.stop(CTRL_CLOSE_EVENT);
}

ii. Implement handling of "special events" in your window message handler

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  switch(msg)
  {
  case WM_ENDSESSION:
    if (lParam)
    {
      // call internal function for sending "stop" notification to rest of program
      ctrl.stop(lParam == ENDSESSION_LOGOFF ? (int) interrupt::logoff : CTRL_CLOSE_EVENT);
    }
    break;
  case WM_CLOSE:
    DestroyWindow(hwnd);
    break;
  case WM_DESTROY:
    PostQuitMessage(0);
    break;
  default:
    return DefWindowProc(hwnd, msg, wParam, lParam);
  }
  return 0;
}

iii. Implement internal function for handling "stop" requests. It must handle 2 special conditions:

a. when not called from window message thread, send WM_CLOSE to window and wait for its thread to exit

b. when not called from main thread, wait for termination of static variables (at least one).

This is because after CtrlHandler exit, Windows will unconditionally terminate your process without giving your code any chance for cleanup. Static variables are destroyed on main thread, so this wait gives you at least guarantee that int main() has exited. You can capture thread id of main thread in the constructor of a static variable (possibly the same which started "shadow" window).

Here is how I did it in my code:

void interrupt::stop(int signal)
{
  // . . .

  // Set exit signal
  InterlockedExchange(&stage_, 2L);

  // Close shadow window if notification is from elsewhere
  if (hwnd_ && GetCurrentThreadId() != thread_.id())
  {
    PostMessage(hwnd_, WM_CLOSE, 0, 0);
    thread_.wait();
  }

  // Wait for completion of own destructor on main thread
  if (GetCurrentThreadId() != main_thread_id_)
    while(stage_ != 3L)
      Sleep(10);
}

// My static variable to wait for
interrupt ctrl;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文