Click here to Skip to main content
16,019,136 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I am currently working on an idle timer that is designed to handle multiple I/O operations, and I partially made it work. However, I am encountering issues with the loop functionality; it tends to either enter an infinite loop (if I remove the break statement) or accept only a single input (if I keep the break statement). I suspect that the underlying issue lies with the ReadFile buffer (chBuffer in my program) not being reset after each input.

For more information here's my code:
C++
#include <Windows.h>
#include <iostream>

DWORD waitThread(HANDLE hThread);

DWORD WINAPI threadFileOperations(LPVOID lpParam);

int main() {
    HANDLE  hThread{};
    LPDWORD dwThreadId{};
    CHAR chBuffer[251]{};

    // Create a thread to handle I/O operations
    hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)threadFileOperations, 
                   &chBuffer, 0, dwThreadId);

    if (hThread == NULL) {
        ExitProcess(2);
    }
    
    // While the function's return value is different from WAIT_TIMEOUT 
    // continue to wait on the thread
    while (!waitThread(hThread)) {
        switch (chBuffer[0]) {
        case '1': std::cout << "hello option 1" << std::endl;
            // Do something else...
            break;
        case '2': std::cout << "hello option 2" << std::endl;
            // Do something else...
            break;
        case '3': std::cout << "hello option 3" << std::endl;
            // Do something else...
            break;
        case '4': std::cout << "hello option 4" << std::endl;
            // Do something else...
            break;
        case '5': std::cout << "exit loop" << std::endl;
            break;
        default: std::cout << "invalid option" << std::endl;
            break;
        }
        // We get stuck in an infinite loop if we erase the break below
        break;
    }

    CloseHandle(hThread);

    return 0;
}

DWORD waitThread(HANDLE hThread) {
    DWORD dwTimeOut{ 10'000 };
   
    switch (WaitForSingleObject(hThread, dwTimeOut)) {
    case WAIT_TIMEOUT:
        break;
    case WAIT_OBJECT_0: // return WaitForSingleObject(hThread, dwTimeOut) == WAIT_OBJECT_0;
        break;
    case WAIT_ABANDONED: std::cout << 
         "WaitForSingleObject: Error WAIT_ABANDONED" << std::endl;
        return FALSE;
    case WAIT_FAILED: std::cout << 
          "WaitForSingleObject: Error WAIT_FAILED" << std::endl;
        return FALSE;
    }
}

DWORD WINAPI threadFileOperations(LPVOID lpParam) {
    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
    
    LPCSTR lpszPrompt1{ "Option: " };
    DWORD dwBytesWritten{ 0 };
    DWORD dwBytesRead{ 0 };
    
    // Initialize the lpParam to chBuffer[251]
    CHAR* readBuffer;
    readBuffer = static_cast<CHAR*>(lpParam);

    if (hStdOut == INVALID_HANDLE_VALUE || hStdIn == INVALID_HANDLE_VALUE) {
        std::cout << "I/O Handles: Error " << GetLastError() << std::endl;
        return 1;
    }

    if (!WriteFile(
        hStdOut,
        lpszPrompt1,
        lstrlenA(lpszPrompt1),
        &dwBytesWritten,
        NULL)) {
        std::cout << "WriteFile: Error " << GetLastError() << std::endl;
        return 1;
    }

    if (!ReadFile(
        hStdIn,
        readBuffer,
        250,
        &dwBytesRead,
        NULL)) {
        std::cout << "ReadFile: Error " << GetLastError() << std::endl;
        return 1;
    }
    else {
        return readBuffer[0];
    }
}


What I have tried:

I searched for ways to reset the ReadFile buffer; however, I was unable to find any meaningful or practical answers. The closest solution that I found was the FlushFileBuffers function (https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-flushfilebuffers), but when I tried to use it, it didn't work (perhaps I failed to implement it correctly.)

Any recommendations for resetting the buffer are greatly appreciated.
Posted
Updated 14-Sep-23 9:58am
v2
Comments
Richard MacCutchan 14-Sep-23 12:46pm    
Once your while loop starts there is no way to break out of it, and you never call the thread process again so it will just continue processing the same input value forever. But I am not sure why you are still using this design as the thread process is not really doing anything useful.

Try this:
C++
#include <Windows.h>
#include <iostream>

DWORD waitThread(HANDLE hThread) {
    DWORD dwTimeOut{ 10000 };
    const char* emsg{};
   
    switch (WaitForSingleObject(hThread, dwTimeOut))
    {
    case WAIT_OBJECT_0:
        return true;

    case WAIT_TIMEOUT:
        emsg = "WAIT_TIMEOUT";
        break;
    case WAIT_ABANDONED:
        emsg = "WAIT_ABANDONED";
        break;
    case WAIT_FAILED:
        emsg = "WAIT_FAILED";
        break;
    }
    std::cout << "WaitForSingleObject: Error " << emsg << std::endl;

    return false;
}


DWORD WINAPI threadFileOperations(LPVOID lpParam)
{
    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
    
    LPCSTR lpszPrompt1{ "Option: " };
    DWORD dwBytesWritten{ 0 };
    DWORD dwBytesRead{ 0 };
    
    char* readBuffer = reinterpret_cast<char*>(lpParam);

    if (hStdOut == INVALID_HANDLE_VALUE || hStdIn == INVALID_HANDLE_VALUE) {
        std::cout << "I/O Handles: Error " << GetLastError() << std::endl;
        return 1;
    }

    if (!WriteFile(
        hStdOut,
        lpszPrompt1,
        lstrlen(lpszPrompt1),
        &dwBytesWritten,
        NULL)) {
        std::cout << "WriteFile: Error " << GetLastError() << std::endl;
        return 1;
    }

    if (!ReadFile(
        hStdIn,
        readBuffer,
        250,
        &dwBytesRead,
        NULL)) {
        std::cout << "ReadFile: Error " << GetLastError() << std::endl;
        return 1;
    }

    return 0; // successful termination
}

int main() {
    HANDLE  hThread{};
    DWORD dwThreadId{};
    CHAR chBuffer[251]{};

    // Create a thread to handle I/O operations
    // While the function's return value is different from WAIT_TIMEOUT 
    // continue to wait on the thread
    while (hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)threadFileOperations, 
                   chBuffer, 0, &dwThreadId))
    {
        if (!waitThread(hThread))
        {
            return 1;
        }
        switch (chBuffer[0]) {
        case '1': std::cout << "hello option 1" << std::endl;
            // Do something else...
            break;
        case '2': std::cout << "hello option 2" << std::endl;
            // Do something else...
            break;
        case '3': std::cout << "hello option 3" << std::endl;
            // Do something else...
            break;
        case '4': std::cout << "hello option 4" << std::endl;
            // Do something else...
            break;
        case '5': std::cout << "exit loop" << std::endl;
            ExitProcess(0);
        default: std::cout << "invalid option" << std::endl;
            break;
        }
        CloseHandle(hThread);
    }

    return 0;
}

The main point is that you need to call the thread process each time before checking the input value. And on return you must wait for the thread to terminate. If it is successful then you can check the value in the input buffer. If the thread terminates abnormally then the program closes.
 
Share this answer
 
v2
Comments
RexyCode 15-Sep-23 9:43am    
Apologies for the delay, but thank you very much for your answer; it seems to work perfectly!
Richard MacCutchan 15-Sep-23 10:54am    
You are welcome. It was an interesting challenge.
RexyCode 15-Sep-23 11:58am    
By the way, I was wondering what made you choose reinterpret_cast over static_cast and const char* emsg over std::string emsg?
Richard MacCutchan 15-Sep-23 12:23pm    
I use reinterpret_cast a lot so it is just habit really. And using const char* rather than std::string, makes sense when you are only using the text as a constant. Using std::string would mean that the constant would be created at compile time and then passed in to the string constuctor every time that function is called during execution.
RexyCode 15-Sep-23 13:36pm    
Thank you for the information!
Your thread procedure is trying to read 250 characters but your primary thread expects just one and waitThread is going to wait for the thread procedure to terminate which requires 250 characters. I think you really want to have ReadFile wait for just one character. In that case, it makes little sense to have a thread do that for you. I think you would be better off using overlapped I/O or WaitForMultipleObjects to wait for multiple events. That is, if the main thread needs to wait for anything else. If not then it can call ReadFile itself.

I might be helpful to read the documentation on ReadFile : ReadFile function - Win32 apps | Microsoft Learn[^]

ETA: I don't think resetting the buffer has anything to do with your issue.
 
Share this answer
 
v3
Comments
Richard MacCutchan 14-Sep-23 13:46pm    
If you run the code it prints the prompt "Option:" and accepts a single digit. It then gooes into the main loop forever. Take a look at OP's previous questions on this subject.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900