需要一个相当"rude"的程序退出

Need a fairly "rude" program exit

本文关键字:rude 退出 程序 一个      更新时间:2023-10-16

环境为Windows 7。我们有一个程序(用c++编写,使用一个相当老的[2000年左右]版本的Qt),它需要监控同一台PC上的一个服务(用java编写)。该程序打开一个与服务的套接字连接,两者持续地聊天,没有任何特别的内容(套接字连接本身表示omnia praeclara)。

如果该服务通过关闭套接字或其他方式发出警报,我要求c++程序显示一条消息,然后停止并终止。如果这样做能尽可能地引人注目就好了(我知道,不是你的常规票价)。

我想学习的是如何让程序产生一个对话框或一些描述的窗口,占据整个屏幕,我可以在上面打印消息。同样可行的是,以UAC对话框的方式出现一个对话框(这有点使屏幕变暗并接管,迫使你处理它)。超时后(将在那里停留几分钟),它将毫不客气地终止程序。Kersplat !

我知道使用MessageBox(或QMessageBox)来显示少量的信息,用Ok按钮来确认,但这些既不是全屏也不是(想不出这个词,但它们不是"接管界面")。

我知道这是一个粗鲁和可怕的方式来处理程序退出…我有意让它成为。机器放置在用户可能已经离开的地方,并且需要出现某种形式的刺激,最终迫使他们寻求帮助。

我建议调用具有相当大的文本的FatalAppExit,以便确定结果消息框的大小。

此消息框是系统模式的,位于除开始屏幕之外的所有内容的顶部。当然,你也可以创建一个最顶层的窗口(参见CreateWindowEx的窗口样式)。但是FatalAppExit是Windows提供的方法。

所以,这似乎不是真正可行的,在一个标准的方式,使用一个很好的API。似乎微软不喜欢模态对话框和暂停系统(有充分的理由)。我们需要这样做,所以我能想出的最好的办法是,基于我在这里的发现。创建第二个桌面,切换到该桌面并弹出消息框。我更喜欢没有交互性的对话(可能会在5-10分钟后超时——很不礼貌!),但现在这样就可以了。甚至使一个暗淡的背景在UAC对话框。

这真的应该放到一个模态对话框类中以供一般使用。这个程序使用的是Qt,因此在TerminateProgram方法的第一部分中出现了信号。

// This, and the background window proc, are declared static in header.
HDC ServiceMonitorThread::hBKDC;
void ServiceMonitorThread::TerminateProgram()
{
   unsigned int errorCode = 0x0a000000;
   string caption = "Error";
   string message = "In defiance of the gods, an error occurred!";
   // Emit the signal which will instruct the other parts of the program to stop doing what they are doing.
   emit ErrorOccurred();
   // Create the background for the error desktop
   CreateBackgroundBitmap();
   // Save the handle to the current desktop
   HDESK hDeskOld = GetThreadDesktop(GetCurrentThreadId());
   // Create a new desktop
   HDESK hDesk = CreateDesktop("ErrorDialogDT", NULL, NULL, 0, GENERIC_ALL, NULL);
   SwitchDesktop(hDesk);
   // Assign new desktop to the current thread
   SetThreadDesktop(hDesk);
   // Adjust the background to be not black.
   CreateBackgroundWindow();
   // Create and display the error dialog on the new desktop.
   MessageBox(NULL, 
              message.c_str(),
              caption.c_str(), 
              MB_SYSTEMMODAL | MB_ICONSTOP | MB_OK);
   // Switch back to the initial desktop
   SwitchDesktop(hDeskOld);
   CloseDesktop(hDesk);
   ::exit(errorCode);
}
// Takes a screenshot of the current desktop, dims it, then stores it 
// for display on the secondary desktop.
void ServiceMonitorThread::CreateBackgroundBitmap()
{
   // Retrieve desktop size
   RECT rcDesktop = {0};
   GetWindowRect(GetDesktopWindow(), &rcDesktop);
   // Prepare context for background
   HDC hDesktopDC = GetDC(GetDesktopWindow());
   hBKDC = CreateCompatibleDC(hDesktopDC);
   HGDIOBJ hBitmapBKOld = SelectObject(hBKDC, CreateCompatibleBitmap(hDesktopDC, rcDesktop.right, rcDesktop.bottom));
   // Grab a screenshot from current desktop
   BitBlt(hBKDC, 0, 0, rcDesktop.right, rcDesktop.bottom, hDesktopDC, 0, 0, SRCCOPY);
   // Reduce lighting
   HDC hBKDC2L = CreateCompatibleDC(hDesktopDC);
   HGDIOBJ hBitmapBK2LOld = SelectObject(hBKDC2L, CreateCompatibleBitmap(hDesktopDC, rcDesktop.right, rcDesktop.bottom));
   ReleaseDC(GetDesktopWindow(), hDesktopDC);
   FillRect(hBKDC2L, &rcDesktop, (HBRUSH)GetStockObject(BLACK_BRUSH));
   BLENDFUNCTION bf = {AC_SRC_OVER, 0, 128};
   BOOL b = AlphaBlend(hBKDC, 0, 0, rcDesktop.right, rcDesktop.bottom, hBKDC2L, 0, 0, rcDesktop.right, rcDesktop.bottom, bf);
}
// Window procedure of the background window
LRESULT CALLBACK ServiceMonitorThread::BackgroundWindowProc(HWND hWnd, UINT nMessage, WPARAM wParam, LPARAM lParam)
{
   LRESULT res = FALSE;
   switch (nMessage)
   {
   // Implement WM_PAINT message handler
   case WM_PAINT:
      {
         PAINTSTRUCT ps = {0};
         HDC hDC = BeginPaint(hWnd, &ps);
         RECT rcDesktop = {0};
         GetWindowRect(GetDesktopWindow(), &rcDesktop);
         BitBlt(hDC, 0, 0, rcDesktop.right, rcDesktop.bottom, hBKDC, 0, 0, SRCCOPY);
         EndPaint(hWnd, &ps);
         res = TRUE;
      }
      break;
   // Ignore WM_CLOSE event
   case WM_CLOSE:
      break;
   default:
      res = DefWindowProc(hWnd, nMessage, wParam, lParam);
   }
   return res;
}
// Creates a background window on the current desktop (which will 
// actually be the secondary desktop).
void ServiceMonitorThread::CreateBackgroundWindow()
{
   // Create Window Class
   WNDCLASS wc = {0};
   wc.lpfnWndProc = (WNDPROC)ServiceMonitorThread::BackgroundWindowProc;
   wc.hInstance = GetModuleHandle(NULL);
   wc.lpszClassName = "ErrorDialogBK";
   RegisterClass(&wc);
   // Retrieve desktop size
   RECT rcDesktop = {0};
   GetWindowRect(GetDesktopWindow(), &rcDesktop);
   // Create Background Window
   HWND hBKWindow = CreateWindow("ErrorDialogBK", "",
      WS_VISIBLE | WS_POPUP | WS_DISABLED,
      rcDesktop.left, rcDesktop.top, rcDesktop.right, rcDesktop.bottom,
      NULL, NULL, wc.hInstance, NULL);
}