简体   繁体   中英

C++ Builder 10.2: Thread blocks WaitForInputIdle

I've got the following scenario: An Application B is supposed to start an application A and then to wait for A becoming idle. For this purpose, B uses CreateProcess in combination with WaitForInputIdle, and unfortunately the timeout of this command has been set to INFINITE.

This is the corresponding part of B's source code:

void StartA(void){
    STARTUPINFO         siInfo;
    PROCESS_INFORMATION piInfo;

    ZeroMemory(&siInfo, sizeof(siInfo));
    ZeroMemory(&piInfo, sizeof(piInfo));

    CreateProcess(L"A.exe", L"", 0, 0, 
        false, CREATE_DEFAULT_ERROR_MODE, 0, 0,
        &siInfo, &piInfo);

    WaitForInputIdle(piInfo.hProcess, INFINITE); // A will block this command!

    CloseHandle(piInfo.hProcess);
    CloseHandle(piInfo.hThread);
}

When I call my application A from this application B, B will be blocked by its WaitForInputIdle command forever due to the SendMessage command invoked by A's TestThread. If I create the TestThread only after the FormShow event, then WaitForInputIdle will return as expected. That's a solution and though I'm aware of the article WaitForInputIdle waits for any thread, which might not be the thread you care about , I like to understand what actually happens here.

This is the simplified source code of application A. It just consists out of a TForm and the class TestThread, which is derived from TThread.

class TestThread : public TThread {
    public:
        __fastcall TestThread(HWND in_msg) : msg(in_msg), TThread(false) {};
        virtual __fastcall ~TestThread(){};
    private:
        void __fastcall Execute(){
            // Next line leads to WaitForInputIdle blocking
            SendMessage(msg, WM_USER, NULL, NULL); 
            while(!Terminated) Sleep(1);
        }

        HWND msg;
};

class TFormA : public TForm{
    private:
        TestThread * testthread_p;
    public:
        __fastcall TFormA(TComponent* Owner){
            testthread_p = new TestThread(Handle);
        }
        virtual __fastcall ~TFormA(){}
}; 

Why does the command WaitForInputIdle fail to detect the idle state of application A?

The launched app never has a chance to enter an "input idle" state.

Assuming TFormA is the app's MainForm , it gets created at app startup, and thus creates the thread, before the VCL's main UI message loop starts running (when Application->Run() is called by WinMain() ).

Even though the worker thread is using SendMessage() and not PostMessage() , it is sending messages across thread boundaries, so each message is not dispatched to the window until the receiving thread (in this case, the main UI thread) calls (Peek|Get)Message() . This is stated in the SendMessage() documentation :

Messages sent between threads are processed only when the receiving thread executes message retrieval code . The sending thread is blocked until the receiving thread processes the message.

By the time the main UI message loop starts running, one message from the thread has already been sent to the window and is waiting to be dispatched. Subsequent messages are being sent with a 1ms delay between them. So, the first time the main UI message loop attempts to retrieve a message from the queue, there is already a message waiting. By the time the loop dispatches that message for processing and goes back to the queue to wait for a message, there is already a new message waiting for it.

The WaitForInputIdle() documentation says:

The WaitForInputIdle function enables a thread to suspend its execution until the specified process has finished its initialization and is waiting for user input with no input pending . If the process has multiple threads, the WaitForInputIdle function returns as soon as any thread becomes idle .

The main UI thread has continuous messages pending from the thread, so it can't become "input idle". And the worker thread does not retrieve inbound messages of its own, so it can't become "input idle", either (see WaitForInputIdle waits for any thread, which might not be the thread you care about ).

Thus, the app process as a whole never becomes "input idle", so WaitForInputIdle() blocks until its timeout elapses, which in this case is INFINITE , so it blocks indefinately.

The Form's OnShow event is not fired until well after the main UI message loop is running and has already processed several other window messages, so the app has time to go "input idle", unblocking WaitForInputIdle() , before the thread gets created and starts sending messages to the window.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM