C++ 如果立即关闭句柄,则RegisterWaitForSingleObject有时会崩溃

C++ 如果立即关闭句柄,则RegisterWaitForSingleObject有时会崩溃,c++,winapi,C++,Winapi,我有时会在RegisterWaitForSingleObject中遇到崩溃(10分之一)。看起来,尽管RegisterWaitForSingleObject返回,但内部线程池尚未就绪 HANDLE processHandle = OpenProcess (PROCESS_ALL_ACCESS, FALSE, processID); // CRASH IN INTERNAL SOMETIMES RegisterWaitForSingleObject (&hWaitForChild_,pr

我有时会在RegisterWaitForSingleObject中遇到崩溃(10分之一)。看起来,尽管RegisterWaitForSingleObject返回,但内部线程池尚未就绪

HANDLE processHandle = OpenProcess (PROCESS_ALL_ACCESS, FALSE, processID);

// CRASH IN INTERNAL SOMETIMES
RegisterWaitForSingleObject (&hWaitForChild_,processHandle,OnChildProcessExit, 0,INFINITE,WT_EXECUTEONLYONCE);

// If I sleep here, then it seems ok.
//std::this_thread::sleep_for (std::chrono::milliseconds (10));
CloseHandle (processHandle);

我可以在这里用一个简单的例子来复制这个。10次中有1次,它会崩溃。我应该如何在不诉诸睡眠黑客的情况下正确地同步它


根据您的代码spinet:

    // THIS CRASHS HERE SOMETIMES

    if (! RegisterWaitForSingleObject (
        &hWaitForChild_

        ,processHandle
        , OnChildProcessExit
        , 0 //this
        , INFINITE
        , WT_EXECUTEONLYONCE))
    {
        LogDebug ("RegisterWaitForSingleObject failed");
    }

    // If this is enabled, then it won't crash
    //std::this_thread::sleep_for (std::chrono::milliseconds (10));

    if (! CloseHandle (processHandle)) // !!!
        LogDebug ("RegisterWaitForSingleObject  Closehandle failed");
因此,在调用此句柄的
RegisterWaitForSingleObject
之后,您将关闭
processHandle
。但是,如果阅读以下内容:

如果在等待仍处于挂起状态时关闭此句柄,则 函数的行为未定义

如果看得更深入-尝试了解-如何在内部工作
RegisterWaitForSingleObject
?它将
processHandle
传递给某个工作线程。这个线程开始等待这个句柄。但这是(将句柄传递给另一个线程)异步操作——例如,可以在内部以该句柄作为参数启动新线程,也可以通过某个信号将其传递给已经存在的工作线程。但无论如何,工作线程得到了这个句柄,并开始等待一些以后。从另一侧-您只需在
RegisterWaitForSingleObject
返回控件之后关闭
processHandle
。所以在这里,race(第一个)或worked thread begin(工作线程开始)等待句柄(在本例中,所有线程都将工作),或者关闭此句柄。如果您先关闭此句柄-工作线程将尝试等待已经无效的句柄并引发异常-
状态\u THREADPOOL\u handle\u exception

// If this is enabled, then it won't crash
//std::this_thread::sleep_for (std::chrono::milliseconds (10));
当然,通过睡眠,您可以让工作线程有时间开始等待句柄。在这种情况下,他开始等待,然后再关闭手柄

解决方案-在不调用
WAITORTIMERCALLBACK回调之前,不能关闭句柄。您需要分配一些上下文,其中放置
processHandle
,并将该上下文传递给
RegisterWaitForSingleObject
。当您调用回调时,您得到了指向上下文的指针,返回到这里并关闭句柄


还要注意的是,您不需要为子进程打开单独的第二个句柄,但可以使用
CreateProcess

根据您的代码spinet返回的进程句柄:

    // THIS CRASHS HERE SOMETIMES

    if (! RegisterWaitForSingleObject (
        &hWaitForChild_

        ,processHandle
        , OnChildProcessExit
        , 0 //this
        , INFINITE
        , WT_EXECUTEONLYONCE))
    {
        LogDebug ("RegisterWaitForSingleObject failed");
    }

    // If this is enabled, then it won't crash
    //std::this_thread::sleep_for (std::chrono::milliseconds (10));

    if (! CloseHandle (processHandle)) // !!!
        LogDebug ("RegisterWaitForSingleObject  Closehandle failed");
因此,在调用此句柄的
RegisterWaitForSingleObject
之后,您将关闭
processHandle
。但是,如果阅读以下内容:

如果在等待仍处于挂起状态时关闭此句柄,则 函数的行为未定义

如果看得更深入-尝试了解-如何在内部工作
RegisterWaitForSingleObject
?它将
processHandle
传递给某个工作线程。这个线程开始等待这个句柄。但这是(将句柄传递给另一个线程)异步操作——例如,可以在内部以该句柄作为参数启动新线程,也可以通过某个信号将其传递给已经存在的工作线程。但无论如何,工作线程得到了这个句柄,并开始等待一些以后。从另一侧-您只需在
RegisterWaitForSingleObject
返回控件之后关闭
processHandle
。所以在这里,race(第一个)或worked thread begin(工作线程开始)等待句柄(在本例中,所有线程都将工作),或者关闭此句柄。如果您先关闭此句柄-工作线程将尝试等待已经无效的句柄并引发异常-
状态\u THREADPOOL\u handle\u exception

// If this is enabled, then it won't crash
//std::this_thread::sleep_for (std::chrono::milliseconds (10));
当然,通过睡眠,您可以让工作线程有时间开始等待句柄。在这种情况下,他开始等待,然后再关闭手柄

解决方案-在不调用
WAITORTIMERCALLBACK回调之前,不能关闭句柄。您需要分配一些上下文,其中放置
processHandle
,并将该上下文传递给
RegisterWaitForSingleObject
。当您调用回调时,您得到了指向上下文的指针,返回到这里并关闭句柄


还要注意的是,您不需要为子进程打开单独的第二个句柄,但可以使用
CreateProcess

返回的进程句柄。好的,我通过保留句柄直到调用Unregisterwait来解决这个问题。情况似乎稳定。多亏了这些答案。

好的,我设法解决了这个问题,在我调用Unregisterwait之前一直保持手柄不变。情况似乎稳定。感谢您的回答。

我遇到了与您相同的问题,即在Visual Studio中调试时发生异常。我试了很多次,终于找到了原因。如果关闭新创建的进程的句柄,程序将崩溃。我尝试关闭回调函数中的句柄,它工作得非常好,如下所示:

typedef struct {
    LPTSTR pszCmdLine;
    HANDLE hEvent;
} THREAD_PARAM;

typedef struct {
    TCHAR  szCmdLine[1024];
    HANDLE hWaitObject;
    DWORD  dwProcessId;
    HANDLE hProcess;
    DWORD  dwThreadId;
    HANDLE hThread;
} OBJECT_PARAM;

static void CALLBACK WaitObjectCallback(LPVOID lpParam, BOOLEAN TimerOrWaitFired)
{
    OBJECT_PARAM *pobp = static_cast<OBJECT_PARAM *>(lpParam);

    TCHAR szInfo[1024] = { 0 };
    DWORD dwExitCode = 0;

    GetExitCodeProcess(pobp->hProcess, &dwExitCode);
    wnsprintf(szInfo, ARRAYSIZE(szInfo), _T("process %u [%s] exit: %u\n"), pobp->dwProcessId, pobp->szCmdLine, dwExitCode);
    OutputDebugString(szInfo);

    //
    // unregister the wait object handle and close the process handle finally
    //
    UnregisterWait(pobp->hWaitObject);
    CloseHandle(pobp->hProcess);
    CloseHandle(pobp->hThread);

    GlobalFree(lpParam);
}

static DWORD CALLBACK ThreadFunction(LPVOID lpParam)
{
    THREAD_PARAM *pthp = static_cast<THREAD_PARAM *>(lpParam);

    STARTUPINFO si = { sizeof(si) };
    PROCESS_INFORMATION pi = { 0 };
    BOOL bResult;

    bResult = CreateProcess(nullptr, pthp->pszCmdLine, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi);
    if (bResult)
    {
        OBJECT_PARAM *pobp = static_cast<OBJECT_PARAM *>(GlobalAlloc(GPTR, sizeof(OBJECT_PARAM)));

        // make copy of the command line and other informations of the newly created process
        lstrcpyn(pobp->szCmdLine, pthp->pszCmdLine, ARRAYSIZE(pobp->szCmdLine));
        pobp->dwProcessId = pi.dwProcessId;
        pobp->hProcess = pi.hProcess;
        pobp->dwThreadId = pi.dwThreadId;
        pobp->hThread = pi.hThread;

        bResult = RegisterWaitForSingleObject(&pobp->hWaitObject, pi.hProcess, WaitObjectCallback, pobp, INFINITE, WT_EXECUTEONLYONCE);

        // once it failed...
        if (!bResult)
        {
            CloseHandle(pi.hProcess);
            CloseHandle(pi.hThread);
        }
    }

    // Notify the thread creator that the process is created successfully
    SetEvent(pthp->hEvent);

    return 0;
}
typedef结构{
LPTSTR pszCmdLine;
处理hEvent;
}螺纹参数;
类型定义结构{
TCHAR szCmdLine[1024];
处理hWaitObject;
DWORD dwProcessId;
处理hProcess;
德沃德·德维德;
处理hThread;
}对象参数;
静态无效回调WaitObjectCallback(LPVOID lpParam,布尔TimerOrWaitFired)
{
OBJECT_PARAM*pobp=静态_cast(lpParam);
TCHAR szInfo[1024]={0};
DWORD dwExitCode=0;
GetExitCode(pobp->hProcess和dwExitCode);
wnsprintf(szInfo,ARRAYSIZE(szInfo),_T(“进程%u[%s]出口:%u\n”)、pobp->dwProcessId、pobp->szCmdLine、dwExitCode);
OutputDebugString(szInfo);
//
//取消注册等待对象句柄并最终关闭进程句柄
//
取消注册等待(pobp->hWaitObject);
CloseHandle(pobp->HPProcess);
CloseHandle(pobp->hThread);
GlobalFree(lpParam);
}
静态DWORD回调线程函数(LPVOID lpParam)
{
螺纹参数*pthp=静态铸造(lpParam);
STARTUPINFO si={sizeof(si)};
进程信息pi={0};
布尔·布雷苏特;
bResult=CreateProcess(nullptr,pthp->pszCmdLine,nullptr,nullptr,FALSE,0,nullptr,nullptr,&si,&pi);
if(bResult)
{
对象参数*pobp=静态强制转换(全局)