Skip to content

Commit

Permalink
Merge pull request #115 from exvito/get-ov-result
Browse files Browse the repository at this point in the history
GetOverlappedResult implementation
  • Loading branch information
opalmer authored Aug 1, 2016
2 parents 1b39e7b + 4842a58 commit 3a43d36
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 1 deletion.
14 changes: 14 additions & 0 deletions pywincffi/core/cdefs/headers/functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,20 @@ BOOL WINAPI CreateProcess(
_Out_ LPPROCESS_INFORMATION lpProcessInformation
);


///////////////////////
// Overlapped
///////////////////////

// https://msdn.microsoft.com/en-us/ms683209
BOOL WINAPI GetOverlappedResult(
_In_ HANDLE hFile,
_In_ LPOVERLAPPED lpOverlapped,
_Out_ LPDWORD lpNumberOfBytesTransferred,
_In_ BOOL bWait
);


// Used internally to reset the last error to 0
// in cases where pywincffi is the cause of the
// error and we choose to ignore the error.
Expand Down
1 change: 1 addition & 0 deletions pywincffi/kernel32/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@
from pywincffi.kernel32.events import CreateEvent, OpenEvent, ResetEvent
from pywincffi.kernel32.comms import ClearCommError
from pywincffi.kernel32.synchronization import WaitForSingleObject
from pywincffi.kernel32.overlapped import GetOverlappedResult
3 changes: 2 additions & 1 deletion pywincffi/kernel32/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,8 @@ def WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite=None, lpOverlapped=None):
wintype_to_cdata(hFile), lpBuffer, nNumberOfBytesToWrite,
bytes_written, wintype_to_cdata(lpOverlapped)
)
error_check("WriteFile", code=code, expected=NON_ZERO)
expected = NON_ZERO if lpOverlapped is None else 0
error_check("WriteFile", code=code, expected=expected)

return bytes_written[0]

Expand Down
66 changes: 66 additions & 0 deletions pywincffi/kernel32/overlapped.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"""
Overlapped
----------
A module containing Windows functions for working with OVERLAPPED objects.
"""

from pywincffi.core import dist
from pywincffi.core.checks import NON_ZERO, input_check, error_check
from pywincffi.wintypes import HANDLE, OVERLAPPED, wintype_to_cdata


def GetOverlappedResult(hFile, lpOverlapped, bWait):
"""
Retrieves the results of an overlapped operation on the specified file,
named pipe, or communications device. To specify a timeout interval or
wait on an alertable thread, use GetOverlappedResultEx.
.. seealso::
https://msdn.microsoft.com/en-us/library/ms683209
:param pywincffi.wintypes.HANDLE hFile:
A handle to the file, named pipe, or communications device.
This is the same handle that was specified when the overlapped
operation was started by a call to the ReadFile, WriteFile,
ConnectNamedPipe, TransactNamedPipe, DeviceIoControl, or WaitCommEvent
function.
:param pywincffi.wintypes.OVERLAPPED lpOverlapped:
The an OVERLAPPED object that was specified when the overlapped
operation was started
:param bool bWait:
If this parameter is TRUE, and the Internal member of the lpOverlapped
structure is STATUS_PENDING, the function does not return until the
operation has been completed. If this parameter is FALSE and the
operation is still pending, the function returns FALSE and the
GetLastError function returns ERROR_IO_INCOMPLETE
:returns:
The number of bytes that were actually transferred by a read or write
operation. For a TransactNamedPipe operation, this is the number of
bytes that were read from the pipe. For a DeviceIoControl operation,
this is the number of bytes of output data returned by the device
driver. For a ConnectNamedPipe or WaitCommEvent operation, this value
is undefined.
"""
input_check("hFile", hFile, HANDLE)
input_check("lpOverlapped", lpOverlapped, OVERLAPPED)
input_check("bWait", bWait, allowed_values=(True, False))

ffi, library = dist.load()

lpNumberOfBytesTransferred = ffi.new("DWORD[1]")

result = library.GetOverlappedResult(
wintype_to_cdata(hFile),
wintype_to_cdata(lpOverlapped),
lpNumberOfBytesTransferred,
ffi.cast("BOOL", bWait),
)

error_check("GetOverlappedResult", result, NON_ZERO)

return int(lpNumberOfBytesTransferred[0])
66 changes: 66 additions & 0 deletions tests/test_kernel32/test_overlapped.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import os
import shutil
import tempfile

from six import text_type

from pywincffi.dev.testutil import TestCase

from pywincffi.core import dist

from pywincffi.kernel32 import (
CreateFile, WriteFile, CloseHandle, CreateEvent, GetOverlappedResult)
from pywincffi.wintypes import OVERLAPPED


class TestOverlappedWriteFile(TestCase):
"""
Tests for :func:`pywincffi.kernel32.GetOverlappedResult`
"""
def test_overlapped_write_file(self):
# Test outline:
# - Create a temp dir.
# - CreateFile for writing with FILE_FLAG_OVERLAPPED.
# - WriteFile in overlapped mode.
# - Use GetOverlappedResult to wait for IO completion.

temp_dir = tempfile.mkdtemp(prefix="pywincffi-test-ovr-")
self.addCleanup(shutil.rmtree, temp_dir, ignore_errors=True)

filename = text_type(os.path.join(temp_dir, "overlapped-write-file"))
file_contents = b"hello overlapped world"

_, lib = dist.load()
handle = CreateFile(
lpFileName=filename,
dwDesiredAccess=lib.GENERIC_WRITE,
dwCreationDisposition=lib.CREATE_NEW,
dwFlagsAndAttributes=lib.FILE_FLAG_OVERLAPPED,
)

# Prepare overlapped write
ovr = OVERLAPPED()
ovr.hEvent = CreateEvent(bManualReset=True, bInitialState=False)

# Go for overlapped WriteFile. Should result in:
# - num_bytes_written == 0
# - GetLastError() == ERROR_IO_PENDING

# HOWEVER, https://msdn.microsoft.com/en-us/library/aa365683 states:
# "Further, the WriteFile function will sometimes return TRUE with a
# GetLastError value of ERROR_SUCCESS, even though it is using an
# asynchronous handle (which can also return FALSE with
# ERROR_IO_PENDING).
# Test strategy:
# - Disregard WriteFile return result.
# - Assert GetLastError is either ERROR_IO_PENDING or ERROR_SUCCESS.
# - Later validate that the correct number of bytes was written.

_ = WriteFile(handle, file_contents, lpOverlapped=ovr)
self.maybe_assert_last_error(lib.ERROR_IO_PENDING)

# Block until async write is completed.
num_bytes_written = GetOverlappedResult(handle, ovr, bWait=True)
self.assertEqual(num_bytes_written, len(file_contents))

CloseHandle(handle)

0 comments on commit 3a43d36

Please sign in to comment.