Click here to Skip to main content
15,887,485 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I'm trying to make an asynchronous IO operation work. However, both the WriteFile and ReadFile functions do not work; WriteFile doesn't print out prompt1 and ReadFile doesn't return chBuffer[200] so consequently the switch statement returns the default output "option not valid!". Moreover the functions fail to return ERROR_IO_PENDING.

Here is my code for more information:
C++
#include <Windows.h>
#include <iostream>
#include <string>

class Timer {
public:
    Timer() = default;
    ~Timer() {};

    BOOL writeOperation(HANDLE& hFile, LPCSTR lpszPrompt1) {
        if (!WriteFile(
            hFile,
            lpszPrompt1,
            lstrlenA(lpszPrompt1),
            NULL,
            &ov1)) {
            if (GetLastError() != ERROR_IO_PENDING) {
                std::cout << "****WriteFile: Error " << GetLastError() << "****" << std::endl;
                ExitProcess(0);
            }
            else {
                // completes asynchronously
                return TRUE;
            }
        }
        else {
            // completes synchronously
            return FALSE;
        }
    }

    CHAR readOperation(HANDLE& hFile) {
        if (!ReadFile(
            hFile,
            chBuffer,
            dwRead,
            NULL,
            &ov2)) {
            if (GetLastError() != ERROR_IO_PENDING) {
                std::cout << "****ReadFile: Error " << GetLastError() << "****" << std::endl;
                ExitProcess(0);
            }
            else {
                return chBuffer[200];
            }
        }
    }

    BOOL waitOperation(HANDLE& hFile) {
        if (WaitForSingleObject(hFile, dwTimeOut) == WAIT_FAILED) {
            std::cout << "****WaitForSingleObject: Error " << GetLastError() << "****" << std::endl;
        }
        else {
            return WaitForSingleObject(hFile, dwTimeOut) == WAIT_OBJECT_0;
        }
    }

public:
    CHAR chBuffer[201]{};

private:
    DWORD dwRead{ 200 };
    DWORD dwTimeOut{ 5'000 };

    OVERLAPPED ov1 = { 0, 0, 0, 0, CreateEvent(NULL, FALSE, FALSE, TEXT("OverlappedOne")) };
    OVERLAPPED ov2 = { 0, 0, 0, 0, CreateEvent(NULL, FALSE, FALSE, TEXT("OverlappedTwo")) };
};

int main() {
    Timer tm;

    LPCSTR lpszPrompt1{ "Option: " };

    HANDLE hFile = CreateFileA(
        "C:\\Temp\\MainFile.txt",
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
        NULL
    );

    if (hFile == INVALID_HANDLE_VALUE) {
        std::cout << "****CreateFileA: Error " << GetLastError() << "****" << std::endl;
        return 1;
    }

    tm.writeOperation(hFile, lpszPrompt1);

    //do {
    //    
    //    // tm.readOperation(hFile);
    //
    //    switch (tm.chBuffer[0]) {
    //    case '1': std::cout << "option 1" << std::endl;
    //        break;
    //    case '2': std::cout << "option 2" << std::endl;
    //        break;
    //    case '3': std::cout << "exit" << std::endl;
    //        break;
    //    default: std::cout << "option not valid!" << std::endl;
    //        break;
    //    }
    //} while (tm.waitOperation(hFile));

    CloseHandle(hFile);
    return 0;
}


What I have tried:

I tried changing multiple things such as the CreateFile's file name, the OVERLAPPED structures' values, as well as the parameters of the WriteFile and ReadFile functions, however nothing works except WaitForSingleObject.

Can someone please tell me how to make these async functions work together?
Posted
Updated 9-Aug-23 0:17am
v2
Comments
Richard MacCutchan 9-Aug-23 5:46am    
I just ran that code and it worked. So what is the new problem?
RexyCode 9-Aug-23 5:52am    
Do you actually see "Option: " being printed it out to the terminal? Because like I previously stated WriteFile does have ERROR_IO_PENDING but it doesn't print out the actual string given to it.

Richard MacCutchan 9-Aug-23 6:19am    
When I ran the code it printed the result of the read operation ("Option: "). You can also check to see what the text file contains. See also my latest Solution below.
Richard MacCutchan 9-Aug-23 7:19am    
I have reworked Solution to so it is much easier to see how the operations should be managed.

The main problem that I can see is that you call the ioOperations which will return as soon as the WriteFile has started, since WriteFile returns false for overlapped IO. Returning the value of GetLastError from that call is pointless as your main code does not check it. The main loop is only checking the value of the read buffer which will never contain anything because the read never gets actioned. And even if it did the first byte of the buffer will not contain 1, 2, or 3 since those characters are never written to the file. There may be other logic issues, but i will leave that for you to check.

One other item worth mentioning is in the following code:
C++
LPCSTR prompt1{ "Option: " };

DWORD dwRead{ 200 };
DWORD bytesPrompt1{ sizeof(prompt1) };

The variable prompt1 is a pointer (to a charactger array) so the sizeof(prompt1) will always be 4, as it is the size of the pointer, not the size of the array.
 
Share this answer
 
Comments
RexyCode 13-Jul-23 8:53am    
Thank you for your input. I changed sizeof(prompt1) to lstrlenA(prompt1) and I also changed the ioOperations function by giving the ReadFile function its own function. However the problem still remains for both the WriteFile and ReadFile functions.

BOOL writeOperation() {
if (WriteFile(hFile, prompt1, lstrlenA(prompt1), NULL, &ov1) != ERROR_INVALID_HANDLE) {
std::cout << "TEXT PRINTED" << std::endl; // Successfully prints out the aforementioned string.
}
else {
std::cout << "****WriteFile: Handle Error****" << std::endl;
return GetLastError();
}
}

BOOL readOperation() {
if (ReadFile(hFile, chBuffer, dwRead, NULL, &ov2) != ERROR_INVALID_HANDLE) {
return chBuffer[200];
}
else {
std::cout << "****ReadFile: Handle Error****" << std::endl;
return GetLastError();
}
}
Richard MacCutchan 13-Jul-23 9:04am    
The WriteFile function does not return an error code, it returns TRUE or FALSE as explained in the documentation at WriteFile function (fileapi.h) - Win32 apps | Microsoft Learn[^]. You should remove most of the code from your program and just test the write function, and examine exactly what gets written to the file. Until you get that working correctly you will go round in circles.
RexyCode 2-Aug-23 8:38am    
After tinkering a bit longer with the WriteFile function, I was able to get ERROR_IO_PENDING. However, I still can't manage to print out "Option: ", perhaps you have an idea of what the issue could be. By the way, if you would like I can provide you with the new code. Thank you in advance!
Richard MacCutchan 2-Aug-23 9:58am    
Yes, because you are testing for a value which does not exist, as I already suggested. The statement:
        switch (tm.chBuffer[0]) {

is meaningless, until all Write and Read operations have completed successfully. So you need to rewrite your main method to check the results of all the I/O before examining any data area that you are (trying to) reading into.

Also, the definitions of your methods are incorrect. You declare them all as returning a BOOL value, but then you return any one of BOOL. Error code, character or void. So strictly speaking this code should not even compile successfully.
RexyCode 8-Aug-23 7:46am    
Well like I previously stated I got WriteFile to return ERROR_IO_PENDING and it does write to the file when I check it in my file explorer. However, it can't print the string out to the console terminal (by the way, the new version of my program returns proper BOOL values and not error messages and tm.chBuffer[0] is commented out for now until I get the rest working).
I have reworked this to simplify the code so you can see how to manage overlapped operations, and capture the results correctly.

C++
#include <Windows.h>
#include <iostream>
#include <string>

int main()
{
    HANDLE hFile = CreateFileA(
        "MainFile.txt",
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
        NULL
    );

    if (hFile == INVALID_HANDLE_VALUE) {
        std::cout << "****CreateFileA: Error " << GetLastError() << "****" << std::endl;
        return 1;
    }

    LPCSTR lpszPrompt1{ "Option: " };
    DWORD dwWritten;
    DWORD dwError;

    OVERLAPPED ovWrite = { 0, 0, 0, 0, CreateEvent(NULL, FALSE, FALSE, "ovWrite") };

    if (!WriteFile(
        hFile,
        lpszPrompt1,
        lstrlen(lpszPrompt1),
        &dwWritten,
        &ovWrite))
    {
        dwError = GetLastError();
        if (dwError != ERROR_IO_PENDING)
        {
            std::cout << "****WriteFile: Error " << GetLastError() << "****" << std::endl;
            ExitProcess(0);
        }
        else
        {
            // wait for it to complete
            std::cout << "WriteFile returned ERROR_IO_PENDING" << std::endl;
            BOOL bw = GetOverlappedResult(hFile, &ovWrite, &dwWritten, TRUE);
            if (!bw)
            {
                // something went wrong
                std::cout << "GetOverlappedResult failed" << std::endl;
            }
        }
    }
    std::cout << "WriteFile wrote " << dwWritten << " bytes" << std::endl;

    DWORD dwRead;
    CHAR chBuffer[200]{};
    OVERLAPPED ovRead = { 0, 0, 0, 0, CreateEvent(NULL, FALSE, FALSE, "ovRead") };
    if (!ReadFile(
        hFile,
        chBuffer,
        200,
        &dwRead,
        &ovRead))
    {
        dwError = GetLastError();
        if (dwError != ERROR_IO_PENDING)
        {
            std::cout << "****ReadFile: Error " << GetLastError() << "****" << std::endl;
            ExitProcess(0);
        }
        else
        {
            // wait for it to complete
            std::cout << "ReadFile returned ERROR_IO_PENDING" << std::endl;
            BOOL bw = GetOverlappedResult(hFile, &ovRead, &dwRead, TRUE);
            if (!bw)
            {
                // something went wrong
                std::cout << "GetOverlappedResult failed" << std::endl;
            }
        }
    }
    std::cout << "ReadFile read " << dwRead << " bytes: ";
    std::cout << "\"" << chBuffer << "\"" << std::endl;


    CloseHandle(hFile);
    return 0;
}


I have run tests on the above code and the output is:
WriteFile returned ERROR_IO_PENDING
WriteFile wrote 8 bytes
ReadFile returned ERROR_IO_PENDING
ReadFile read 8 bytes: "Option: "
 
Share this answer
 
v2
Comments
RexyCode 9-Aug-23 7:50am    
Thank you very much for this solution, I appreciate the help. Consequently, if I change perhaps a couple of things, would I be able to create an I/O stream capable of handling user input (in a switch statement for example)?
Richard MacCutchan 9-Aug-23 7:56am    
Technically yes, but that is really a different question. For example you could do something like:
int option;
cout << "Please select your option: ";
cin >> option;
switch (option)
{
case 1:
    // option 1 processing
    break;
case 2:
    // option 2 processing
    break;
case 3:
    // option 3 processing
    break;
default:
    cout << "Illegal option" << endl;
}

But that has nothing to do with async I/O.
RexyCode 9-Aug-23 8:13am    
The all reason why I didn't use cin was the fact that it can't work with a timer function like WaitForSingleObject, which is a major problem for what I'm trying to do. Therefore, I don't really know if I just stick with async I/O (since you don't think it's good for what I'm trying to do) or if I just use sync I/O but with Windows' threads. What do you think is the best option?
Richard MacCutchan 9-Aug-23 8:17am    
Sorry, no idea, as I really do not know what you are trying to do, or why you think you need async I/O.
RexyCode 9-Aug-23 8:27am    
The main problem I'm facing is the following: I'm trying to create a timer function that kicks the user out of the program if they do not give any input during the amount of time given, just like McDonald's screen ordering system which resets everything if the user doesn't click on anything. And so I'm using async I/O because everyone on Stack Overflow said to use it, yet, they never provided me with any examples of how to do it; therefore I ran into some problems.

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