Using ThreadPoolIO function with IO completion port

I am trying to do the following:

  1. From usermode , create a thread pool which waits for asynchronous operation(on a file) to complete.
  2. The threads will wait for async operation to complete using IO completion port.

However, When implementing the logic, I get a runtime error when calling CreateThreadPoolIO function. The function returns NULL and upon calling GetLastError() shows error code 87(which means parameter passed to it is incorrect).
I am not sure why it generates this error and I could not find an example on web which demonstrates what I have been trying to do. I am currently referring to MSDN documentation
to implement most of my code.

code:

VOID CALLBACK mycompletionRoutine(
    PTP_CALLBACK_INSTANCE pInstance, // See "Callback Termination Actions" section
    PVOID pvContext,
    PVOID pOverlapped,
    ULONG IoResult,
    ULONG_PTR NumberOfBytesTransferred,
    PTP_IO pIo)
{
    std::cout << "Completion routine is invoked" << std::endl;
}

int main()
{
    // Open handle to existing file in overlapped mode
    HANDLE hFile = CreateFile(L"C:\\test\\text.txt",                // name of the write
        GENERIC_READ,          // open for read
        0,                      // do not share
        NULL,                   // default security
        OPEN_EXISTING,             // ppen existing file only
        FILE_FLAG_OVERLAPPED,  // normal file
        NULL);                  // no attr. template

    if (hFile == INVALID_HANDLE_VALUE)
    {
        std::cout << "Invalid Handle value" << std::endl;
        return -1;
    }

    // Create a new completion port
    HANDLE Iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,NULL,0);
    if (Iocp == NULL)
    {
        DWORD status = GetLastError();
        std::cout << "Iocp is NULL" << status<< std::endl;
        return -1;
    }

    // Associate completion port with file handle
    ULONG ckey = 2;
    HANDLE newIocp = CreateIoCompletionPort(hFile, Iocp, ckey, 0);
    if (newIocp != Iocp)
    {
        DWORD status = GetLastError();
        std::cout << "newIocp is NULL" << status << std::endl;
        return -1;
    }

    // Create a thread pool object and associate it with a file handle.
    PTP_IO thread_pool_obj =  CreateThreadpoolIo(hFile, mycompletionRoutine, NULL, NULL);
    if (thread_pool_obj == NULL)
    {
        DWORD status = GetLastError();
        std::cout << "**Thread pool obj is NULL**" << status << std::endl;
        return -1;
    }

    std::cout << "Program ran successfully" << std::endl;

    return 0;
}

I believe you EITHER call CreateIoCompletionPort, or you call CreateThreadpoolIo. My understanding is that CreateThreadpoolIo will create the completion port as part of its duties.

I may have misunderstood the docs, so I’ll delete this if challenged.

Maybe your theory is correct. When I delete either piece of code(CreateThreadpoolIo / CreateCompletion port), the program works successfully. But I still don’t understand how many threads will be created when I use CreateThreadpoolIo since there is no explicit parameter(similar to CreateCompletionPort) which allows you to specify the number of concurrent thread that will be created

correct on both. CreateThreadPoolIo is used on its own, not with a user IOCP (underneath the covers it has used an IOCP in the past, I don’t know the current implementation). You don’t control the number of threads, you let the thread pool manage it (hence, thread pool) and scale it as needed based on use.

You cannot use both the thread pool APIs and a completion port with the same HANDLE