Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement MsgWaitForMultipleObjects #80

Merged
merged 8 commits into from
May 1, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Notable enhancements and changes are:
* :issue:`76` - :func:`pywincffi.kernel32.process.TerminateProcess`
* :issue:`78` - :func:`pywincffi.kernel32.handle.DuplicateHandle`
* :issue:`79` - :func:`pywincffi.kernel32.process.ClearCommError`
* :issue:`80` - :func:`pywincffi.user32.synchronization.MsgWaitForMultipleObjects`

0.2.0
~~~~~
Expand Down
18 changes: 18 additions & 0 deletions pywincffi/core/cdefs/headers/constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
// Return values of WaitForSingleObject
// https://msdn.microsoft.com/en-us/library/ms687032
#define WAIT_ABANDONED ...
#define WAIT_ABANDONED_0 ...
#define WAIT_OBJECT_0 ...
#define WAIT_TIMEOUT ...
#define WAIT_FAILED ...
Expand Down Expand Up @@ -199,6 +200,23 @@
#define CE_PTO ...
#define CE_TXFULL ...

// MsgWaitForMultipleObjects
#define MAXIMUM_WAIT_OBJECTS ...
#define QS_ALLEVENTS ...
#define QS_ALLINPUT ...
#define QS_ALLPOSTMESSAGE ...
#define QS_HOTKEY ...
#define QS_INPUT ...
#define QS_KEY ...
#define QS_MOUSE ...
#define QS_MOUSEBUTTON ...
#define QS_MOUSEMOVE ...
#define QS_PAINT ...
#define QS_POSTMESSAGE ...
#define QS_RAWINPUT ...
#define QS_SENDMESSAGE ...
#define QS_TIMER ...

// For the moment, we can't define this here. When cffi
// parses the header this returns -1 and cffi seems to
// only handle positive integers right now.
Expand Down
1 change: 1 addition & 0 deletions pywincffi/core/cdefs/headers/functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ DWORD WaitForSingleObject(HANDLE, DWORD);
BOOL GetHandleInformation(HANDLE, LPDWORD);
BOOL SetHandleInformation(HANDLE, DWORD, DWORD);
BOOL DuplicateHandle(HANDLE, HANDLE, HANDLE, LPHANDLE, DWORD, BOOL, DWORD);
DWORD MsgWaitForMultipleObjects(DWORD, HANDLE, BOOL, DWORD, DWORD);

// Events
HANDLE CreateEvent(LPSECURITY_ATTRIBUTES, BOOL, BOOL, LPCTSTR);
Expand Down
7 changes: 5 additions & 2 deletions pywincffi/core/dist.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
SOURCE_FILES = (
resource_filename(
"pywincffi", join("core", "cdefs", "sources", "main.c")), )
LIBRARIES = ("kernel32", "user32")


class Module(object): # pylint: disable=too-few-public-methods
Expand Down Expand Up @@ -144,7 +145,9 @@ def _read(*paths):
return output


def _ffi(module_name=MODULE_NAME, headers=HEADER_FILES, sources=SOURCE_FILES):
def _ffi(
module_name=MODULE_NAME, headers=HEADER_FILES, sources=SOURCE_FILES,
libraries=LIBRARIES):
"""
Returns an instance of :class:`FFI` without compiling
the module. This function is used internally but also
Expand All @@ -168,7 +171,7 @@ def _ffi(module_name=MODULE_NAME, headers=HEADER_FILES, sources=SOURCE_FILES):

ffi = FFI()
ffi.set_unicode(True)
ffi.set_source(module_name, source)
ffi.set_source(module_name, source, libraries=libraries)
ffi.cdef(header)

return ffi
Expand Down
5 changes: 3 additions & 2 deletions pywincffi/kernel32/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@
ReadFile, WriteFile, FlushFileBuffers, MoveFileEx, CreateFile, LockFileEx,
UnlockFileEx)
from pywincffi.kernel32.handle import (
CloseHandle, GetStdHandle, WaitForSingleObject, handle_from_file,
GetHandleInformation, SetHandleInformation, DuplicateHandle)
CloseHandle, GetStdHandle, GetHandleInformation, SetHandleInformation,
DuplicateHandle, handle_from_file)
from pywincffi.kernel32.pipe import (
CreatePipe, PeekNamedPipe, PeekNamedPipeResult, SetNamedPipeHandleState)
from pywincffi.kernel32.process import (
GetProcessId, GetCurrentProcess, OpenProcess, GetExitCodeProcess,
pid_exists, TerminateProcess)
from pywincffi.kernel32.events import CreateEvent, OpenEvent, ResetEvent
from pywincffi.kernel32.comms import ClearCommError
from pywincffi.kernel32.synchronization import WaitForSingleObject
35 changes: 1 addition & 34 deletions pywincffi/kernel32/handle.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
-------

A module containing general functions for working with handle
objects.
objects. The functions provided here are part of the ``kernel32`` library.
"""

from six import integer_types
Expand Down Expand Up @@ -89,39 +89,6 @@ def CloseHandle(hObject):
error_check("CloseHandle", code=code, expected=Enums.NON_ZERO)


def WaitForSingleObject(hHandle, dwMilliseconds):
"""
Waits for the specified object to be in a signaled state
or for ``dwMiliseconds`` to elapse.

.. seealso::

https://msdn.microsoft.com/en-us/library/ms687032

:param handle hHandle:
The handle to wait on.

:param int dwMilliseconds:
The time-out interval.
"""
input_check("hHandle", hHandle, Enums.HANDLE)
input_check("dwMilliseconds", dwMilliseconds, integer_types)

ffi, library = dist.load()
result = library.WaitForSingleObject(
hHandle, ffi.cast("DWORD", dwMilliseconds)
)

if result == library.WAIT_FAILED:
raise WindowsAPIError(
"WaitForSingleObject", "Wait Failed", ffi.getwinerror()[-1],
return_code=result, expected_return_code="not %s" % result)

error_check("WaitForSingleObject")

return result


def GetHandleInformation(hObject):
"""
Returns properties of an object handle.
Expand Down
3 changes: 2 additions & 1 deletion pywincffi/kernel32/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
from pywincffi.core import dist
from pywincffi.core.checks import Enums, input_check, error_check
from pywincffi.exceptions import WindowsAPIError, PyWinCFFINotImplementedError
from pywincffi.kernel32.handle import CloseHandle, WaitForSingleObject
from pywincffi.kernel32.handle import CloseHandle
from pywincffi.kernel32.synchronization import WaitForSingleObject

RESERVED_PIDS = set([0, 4])

Expand Down
51 changes: 51 additions & 0 deletions pywincffi/kernel32/synchronization.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""
Synchronization
---------------

This module contains general functions for synchronizing objects and
events. The functions provided in this module are parts of the ``kernel32``
library.

.. seealso::

:mod:`pywincffi.user32.synchronization`
"""

from six import integer_types

from pywincffi.core import dist
from pywincffi.core.checks import Enums, input_check, error_check
from pywincffi.exceptions import WindowsAPIError


def WaitForSingleObject(hHandle, dwMilliseconds):
"""
Waits for the specified object to be in a signaled state
or for ``dwMiliseconds`` to elapse.

.. seealso::

https://msdn.microsoft.com/en-us/library/ms687032

:param handle hHandle:
The handle to wait on.

:param int dwMilliseconds:
The time-out interval.
"""
input_check("hHandle", hHandle, Enums.HANDLE)
input_check("dwMilliseconds", dwMilliseconds, integer_types)

ffi, library = dist.load()
result = library.WaitForSingleObject(
hHandle, ffi.cast("DWORD", dwMilliseconds)
)

if result == library.WAIT_FAILED:
raise WindowsAPIError(
"WaitForSingleObject", "Wait Failed", ffi.getwinerror()[-1],
return_code=result, expected_return_code="not %s" % result)

error_check("WaitForSingleObject")

return result
9 changes: 9 additions & 0 deletions pywincffi/user32/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"""
User32 Sub-Package
==================

Provides functions, constants and utilities that wrap functions provided by
``user32.dll``.
"""

from pywincffi.user32.synchronization import MsgWaitForMultipleObjects
90 changes: 90 additions & 0 deletions pywincffi/user32/synchronization.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
"""
Synchronization
---------------

This module contains general functions for synchronizing objects and
events. The functions provided in this module are parts of the ``user32``
library.

.. seealso::

:mod:`pywincffi.kernel32.synchronization`
"""

from six import integer_types

from pywincffi.core import dist
from pywincffi.core.checks import Enums, input_check
from pywincffi.exceptions import WindowsAPIError


def MsgWaitForMultipleObjects(
pHandles, bWaitAll, dwMilliseconds, dwWakeMask, nCount=None):
"""
Waits until one or all of the specified objects are in a singled state
or the timeout elapses.

.. seealso::

https://msdn.microsoft.com/en-us/library/ms684242

:param list pHandles:
A list of objects to wait on. See Microsoft's documentation for
more information about the contents of this variable.

:param bool bWaitAll:
If True then this function will return when the states of all
objects in ``pHandles`` are signaled.

:param int dwMilliseconds:
The timeout interval in milliseconds.

:param int dwWakeMask:
The input types for which an input event object handle will be added
to the array of handles. See Microsoft's documentation for more
detailed information.

:keyword int nCount:
The number of object handles in ``pHandles``. By default this will
be determined by checking the length of the input to ``pHandles``.

:raises WindowsAPIError:
Raised if the underlying Windows function returns ``WAIT_FAILED``.

:rtype: int
:return:
Returns the value of the event which caused the function to
return. See Microsoft's documentation for full details on what
this could be.
"""
input_check("pHandles", pHandles, (list, tuple))

if nCount is None:
nCount = len(pHandles)

input_check("bWaitAll", bWaitAll, bool)
input_check("dwMilliseconds", dwMilliseconds, integer_types)
input_check("dwWakeMask", dwWakeMask, integer_types)
input_check("nCount", nCount, integer_types)

# Iterate over all of the object in pHandles. Each object
# should be a handle.
for i, item in enumerate(pHandles):
input_check("pHandles[%d]" % i, item, Enums.HANDLE)

ffi, library = dist.load()

code = library.MsgWaitForMultipleObjects(
nCount,
ffi.new("const PHANDLE[%d]" % nCount, pHandles),
bWaitAll,
dwMilliseconds,
dwWakeMask
)

if code == library.WAIT_FAILED:
code, message = ffi.getwinerror()
raise WindowsAPIError(
"MsgWaitForMultipleObjects", message, code)

return code
8 changes: 4 additions & 4 deletions tests/test_core/test_dist.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
from mock import Mock, patch

from pywincffi.core.dist import (
MODULE_NAME, HEADER_FILES, SOURCE_FILES, Module, _import_path, _ffi,
_compile, _read, load)
MODULE_NAME, HEADER_FILES, SOURCE_FILES, LIBRARIES, Module, _import_path,
_ffi, _compile, _read, load)
from pywincffi.dev.testutil import TestCase
from pywincffi.exceptions import ResourceNotFoundError

Expand Down Expand Up @@ -132,7 +132,7 @@ def test_default_source_files(self):
_ffi(module_name=self.module_name)

mocked_set_source.assert_called_once_with(
self.module_name, _read(*SOURCE_FILES))
self.module_name, _read(*SOURCE_FILES), libraries=LIBRARIES)

def test_default_cdefs(self):
with patch.object(FFI, "cdef") as mocked_cdef:
Expand All @@ -147,7 +147,7 @@ def test_alternate_source_files(self):
_ffi(module_name=self.module_name, sources=[path])

mocked_set_source.assert_called_once_with(
self.module_name, _read(*[path]))
self.module_name, _read(*[path]), libraries=LIBRARIES)


class TestCompile(TestCase):
Expand Down
50 changes: 3 additions & 47 deletions tests/test_kernel32/test_handle.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@

from pywincffi.core import dist
from pywincffi.dev.testutil import TestCase
from pywincffi.exceptions import InputError, WindowsAPIError
from pywincffi.exceptions import InputError
from pywincffi.kernel32 import (
GetStdHandle, CloseHandle, OpenProcess, WaitForSingleObject,
handle_from_file, GetHandleInformation, SetHandleInformation,
DuplicateHandle, GetCurrentProcess, CreateEvent)
GetStdHandle, CloseHandle, handle_from_file, GetHandleInformation,
SetHandleInformation, DuplicateHandle, GetCurrentProcess, CreateEvent)

try:
WindowsError
Expand Down Expand Up @@ -86,49 +85,6 @@ def test_opens_correct_file_handle(self):
self.fail("Expected os.close(%r) to fail" % fd)


class TestWaitForSingleObject(TestCase):
"""
Tests for :func:`pywincffi.kernel32.WaitForSingleObject`
"""
def test_wait_failed(self):
# This should cause WAIT_FAILED to be returned by the underlying
# WaitForSingleObject because we didn't request the SYNCHRONIZE
# permission.
process = self.create_python_process("import time; time.sleep(3)")
_, library = dist.load()

hProcess = OpenProcess(
library.PROCESS_QUERY_INFORMATION, False, process.pid)
self.addCleanup(CloseHandle, hProcess)

with self.assertRaises(WindowsAPIError) as exec_:
WaitForSingleObject(hProcess, 3)
self.assertEqual(exec_.code, library.WAIT_FAILED)

def test_wait_on_running_process(self):
process = self.create_python_process("import time; time.sleep(1)")
_, library = dist.load()

hProcess = OpenProcess(
library.PROCESS_QUERY_INFORMATION | library.SYNCHRONIZE,
False, process.pid)
self.addCleanup(CloseHandle, hProcess)
self.assertEqual(
WaitForSingleObject(hProcess, 0), library.WAIT_TIMEOUT)

def test_process_dies_before_timeout(self):
process = self.create_python_process("import time; time.sleep(1)")
_, library = dist.load()

hProcess = OpenProcess(
library.PROCESS_QUERY_INFORMATION | library.SYNCHRONIZE,
False, process.pid)
self.addCleanup(CloseHandle, hProcess)
self.assertEqual(
WaitForSingleObject(hProcess, library.INFINITE),
library.WAIT_OBJECT_0)


class TestGetHandleInformation(TestCase):
"""
Tests for :func:`pywincffi.kernel32.GetHandleInformation`
Expand Down
Loading