4

我有一个无窗口应用程序,其唯一目的是安装 32 位挂钩 DLL 文件并等待父程序(64 位程序)退出。64 位程序是用 C# 编写的,无窗口应用程序是用 C++ 编写的。我最初有一个保持程序打开的 GetMessage 循环:

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

我正在使用 C# 中的 Process.Kill 方法关闭 C++ 应用程序,但我发现这不允许 C++ 应用程序干净地关闭。此外,如果 C# 应用程序崩溃,C++ 应用程序将永远保持打开状态。我让 C++ 应用程序检查 C# 应用程序是否仍在运行,使用以下循环:

while(true)
{
    if(PeekMessage(&msg, NULL, 0, 0, true))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    if(!isMainProgramRunning())
        break;

    Sleep(1000);
}

由于某种原因,睡眠会导致问题。DLL文件安装的钩子是WH_CBT和WH_KEYBOARD。每当我在 C++ 应用程序使用此循环运行时按下一个键,这些键就会被吃掉。删除 Sleep 使其工作正常,但正如预期的那样,它使用 100% CPU,这是我不想要的。我尝试完全删除消息循环,而是在线程上使用具有无限超时的 WaitForSingleObject,该线程将在 isMainProgramRunning 返回 false 时结束。这基本上锁定了整个计算机。

我真的不明白为什么 GetMessage,据我所见,从未返回,而是无限期地暂停主线程,但没有导致这些问题,但 WaitForSingleObject 会导致每个应用程序在我单击它时冻结。如何让 C++ 应用程序保持打开状态,直到 C# 应用程序关闭?

编辑:

既然有人向我指出在消息泵中休眠是不好的,让我问一下:有没有办法指定消息等待的超时时间,所以程序不会无限期地等待消息,而是会等待大约 250 毫秒,超时,让我运行 isMainProgramRunning 方法,然后再等一下?

编辑2:

我尝试使用 MsgWaitForMultipleObjects,尽管与 Leo 建议的方式有些不同。这是我使用的循环:

while(MsgWaitForMultipleObjects (0, NULL, true, 250, QS_ALLPOSTMESSAGE) != WAIT_FAILED)
{
    if(PeekMessage(&msg, NULL, 0, 0, true))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    if(!isMainProgramRunning())
        break;
}

我再次遇到了与睡眠相同的问题。我还尝试暂停主线程并让另一个线程恢复它。同样的问题。GetMessage 是做什么的,它允许它等待而不会导致这些问题?也许这应该是另一篇文章的主题,但为什么当安装钩子的 C++ 应用程序休眠或挂起时,钩子中的所有处理似乎也挂起?

编辑3:

这是 C++ 应用程序调用以安装挂钩的 DLL 方法:

extern "C" __declspec(dllexport) void install()
{
    cbtHook = SetWindowsHookEx(WH_CBT, hookWindowEvents, hinst, NULL);

    if(cbtHook == NULL)
        MessageBox(NULL, "Unable to install CBT hook", "Error!", MB_OK);

    keyHook = SetWindowsHookEx(WH_KEYBOARD, LowLevelKeyboardProc, hinst, NULL);

    if(keyHook == NULL)
        MessageBox(NULL, "Unable to install key hook", "Error!", MB_OK);
}
4

4 回答 4

4

你有两个不同的问题:

  1. 如果 C# 程序退出(例如崩溃),如何使无窗口 C++ 程序自动退出。

    在 C++ 程序中,打开 C# 程序的句柄。由于 C# 程序运行 C++ 程序,因此 C# 程序将其自己的 PID 作为参数传递;然后,C++ 程序可以使用OpenProcess打开该进程的句柄。

    然后在消息循环中使用MsgWaitForMultipleObjects 。如果 C# 程序退出您必须处理的句柄,它将收到信号并唤醒您。(您也可以WaitForSingleObject(hProcess,0)==WAIT_OBJECT_0用来检查进程是否收到信号,例如验证您被唤醒的原因,尽管 MsgWaitForMultipleObjects 结果也会告诉您。)

    您应该在退出时关闭进程句柄(尽管操作系统会在您退出时为您执行此操作,但如果您在不同的上下文中重新使用此代码,这是一个很好的做法)。请注意,句柄比它所代表的进程寿命更长,这就是您可以等待它的原因。

  2. 如何让 C# 程序指示 C++ 程序退出。

    如果你让#1 工作,你可能不需要这个,但如果你愿意,你可以让 C# 程序向 C++ 程序发布一条消息。

    不要使用 PostQuitMessage,也不要跨线程或进程发布或发送 WM_QUIT。

    相反,使用PostThreadMessage发布两个应用程序同意的其他消息(例如 WM_APP+1)。

于 2010-12-20T08:23:51.610 回答
1

您可以创建一个命名事件并使用:

MsgWaitForMultipleObjects

在您的消息循环中。

C# 应用程序只需打开并引发此事件即可告诉您的应用程序退出。

这是一种最小的进程间通信。

于 2010-12-20T08:18:12.983 回答
0

您应该通过向其发布 WM_QUIT 消息并正确处理它来退出该过程,如Raymond Chen的这篇文章 (Modality)中所述。不要在不处理消息的情况下睡在循环中——这是错误的。您的应用应该正在处理消息或等待新消息。

于 2010-12-20T05:12:19.787 回答
-1

GetMessage 永远不会返回是因为您没有创建窗口!

要使用消息队列,您必须有一些 GUI。例如,您可以创建一个隐藏窗口。

于 2010-12-20T15:17:38.470 回答