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 CreateEvent #70

Merged
merged 17 commits into from
Apr 28, 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
4 changes: 3 additions & 1 deletion docs/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ Notable enhancements and changes are:

* Fixed a bug where :func:`pywincffi.checks.input_check` might raise
``ffi.error`` in :issue:`73`

* Several enhancements bringing :issue:`69` closer to closure.
* Addition of the :func:`pywincffi.kernel32.events.CreateEvent` and
:func:`pywincffi.kernel32.events.OpenEvent` functions in :issue:`70`.

0.2.0
~~~~~
Expand Down
15 changes: 15 additions & 0 deletions pywincffi/core/cdefs/headers/constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,21 @@
#define ERROR_PATH_NOT_FOUND ...
#define ERROR_IO_PENDING ...

// Events
#define DELETE ...
#define READ_CONTROL ...
#define WRITE_DAC ...
#define WRITE_OWNER ...
#define EVENT_ALL_ACCESS ...
#define EVENT_MODIFY_STATE ...
#define MUTEX_ALL_ACCESS ...
#define MUTEX_MODIFY_STATE ...
#define SEMAPHORE_ALL_ACCESS ...
#define SEMAPHORE_MODIFY_STATE ...
#define TIMER_ALL_ACCESS ...
#define TIMER_MODIFY_STATE ...
#define TIMER_QUERY_STATE ...

// 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
4 changes: 4 additions & 0 deletions pywincffi/core/cdefs/headers/functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,7 @@ HANDLE GetStdHandle(DWORD);
DWORD WaitForSingleObject(HANDLE, DWORD);
BOOL GetHandleInformation(HANDLE, LPDWORD);
BOOL SetHandleInformation(HANDLE, DWORD, DWORD);

// Events
HANDLE CreateEvent(LPSECURITY_ATTRIBUTES, BOOL, BOOL, LPCTSTR);
HANDLE OpenEvent(DWORD, BOOL, LPCTSTR);
2 changes: 1 addition & 1 deletion pywincffi/core/checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def input_check(name, value, allowed_types=None, allowed_values=None):
mapping.cname.match(typeof.cname)):
raise TypeError

except (TypeError, ffi.error):
except (ffi.error, TypeError):
raise InputError(name, value, allowed_types)

elif allowed_types is Enums.UTF8:
Expand Down
1 change: 1 addition & 0 deletions pywincffi/kernel32/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@
from pywincffi.kernel32.process import (
GetProcessId, GetCurrentProcess, OpenProcess, GetExitCodeProcess,
pid_exists)
from pywincffi.kernel32.events import CreateEvent, OpenEvent
108 changes: 108 additions & 0 deletions pywincffi/kernel32/events.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
"""
Events
------

A module containing Windows functions for working with events.
"""

from six import string_types, integer_types

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


def CreateEvent(
bManualReset, bInitialState, lpEventAttributes=None, lpName=None):
"""
Creates or opens an named or unnamed event object.

.. seealso::

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

:param bool bManualReset:
If True then this function will create a manual reset
event which must be manually reset with :func:`ResetEvent`. Refer
to the msdn documentation for full information.

:param bool bInitialState:
If True the initial state will be 'signaled'.

:keyword struct lpEventAttributes:
A pointer to a ``SECURITY_ATTRIBUTES`` structure. If not provided
then by default the handle cannot be inherited by a subprocess.

:keyword str lpName:
The optional case-sensitive name of the event. If not provided then
the event will be created without an explicit name.

:returns:
Returns a handle to the event. If an event by the given name already
exists then it will be returned instead of creating a new event.
"""
input_check("bManualReset", bManualReset, bool)
input_check("bInitialState", bInitialState, bool)

ffi, library = dist.load()

if lpName is None:
lpName = ffi.NULL
else:
input_check("lpName", lpName, string_types)
lpName = string_to_cdata(lpName)

if lpEventAttributes is None:
lpEventAttributes = ffi.NULL
else:
input_check(
"lpEventAttributes", lpEventAttributes,
allowed_types=Enums.SECURITY_ATTRIBUTES)

handle = library.CreateEvent(
lpEventAttributes,
ffi.cast("BOOL", bManualReset),
ffi.cast("BOOL", bInitialState),
lpName
)

try:
error_check("CreateEvent")
except WindowsAPIError as error:
if error.errno != library.ERROR_ALREADY_EXISTS:
raise

return handle


def OpenEvent(dwDesiredAccess, bInheritHandle, lpName):
"""
Opens an existing named event.

.. seealso::

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

:param int dwDesiredAccess:
The access desired for the event object.

:param bool bInheritHandle:
:param str lpName:

:return:
Returns the
"""
input_check("dwDesiredAccess", dwDesiredAccess, integer_types)
input_check("bInheritHandle", bInheritHandle, bool)
input_check("lpName", lpName, string_types)

ffi, library = dist.load()

handle = library.OpenEvent(
ffi.cast("DWORD", dwDesiredAccess),
ffi.cast("BOOL", bInheritHandle),
string_to_cdata(lpName)
)
error_check("OpenEvent")
return handle
68 changes: 68 additions & 0 deletions tests/test_kernel32/test_events.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import sys

from mock import patch

from pywincffi.core import dist
from pywincffi.dev.testutil import TestCase
from pywincffi.exceptions import WindowsAPIError, InputError
from pywincffi.kernel32 import events # used by mocks
from pywincffi.kernel32 import CloseHandle, CreateEvent, OpenEvent


# These tests cause TestPidExists to fail under Python 3.4 so for now
# we skip these tests. Because we're only testing CreateEvent, and
# TestPidExists worked before TestCreateEvent exists, we'll skip these
# for now.
# Traceback (most recent call last):
# [...]
# File "c:\python34\lib\subprocess.py", line 754, in __init__
# _cleanup()
# File "c:\python34\lib\subprocess.py", line 474, in _cleanup
# res = inst._internal_poll(_deadstate=sys.maxsize)
# File "c:\python34\lib\subprocess.py", line 1147, in _internal_poll
# if _WaitForSingleObject(self._handle, 0) == _WAIT_OBJECT_0:
# OSError: [WinError 6] The handle is invalid
# TODO: Need to figure out why this happens ^^^
class TestCreateEvent(TestCase):
"""
Tests for :func:`pywincffi.kernel32.CreateEvent`
"""
def setUp(self):
super(TestCreateEvent, self).setUp()
if sys.version_info[0:2] == (3, 4):
self.skipTest("Not compatible with Python 3.4")

def test_create_event_valid_handle(self):
handle = CreateEvent(False, False)
CloseHandle(handle) # will raise exception if the handle is invalid

def test_creating_duplicate_event_does_not_raise_error(self):
# Windows raises set the last error to ERROR_ALREADY_EXISTS
# if an event object with the same name already exists. The
# pywincffi API ignores this error and returns the handle
# object.
name = "pywincffi-%s" % self.random_string(5)
handle1 = CreateEvent(False, False, lpName=name)
self.addCleanup(CloseHandle, handle1)
handle2 = CreateEvent(False, False, lpName=name)
self.addCleanup(CloseHandle, handle2)

def test_raises_non_error_already_exists(self):
def raise_(*_):
raise WindowsAPIError("CreateEvent", "", -1)

with patch.object(events, "error_check", side_effect=raise_):
with self.assertRaises(WindowsAPIError):
CreateEvent(False, False)

def test_can_retrieve_named_event(self):
_, library = dist.load()
name = "pywincffi-%s" % self.random_string(5)
handle = CreateEvent(False, False, lpName=name)
self.addCleanup(CloseHandle, handle)
opened_event = OpenEvent(library.EVENT_ALL_ACCESS, True, name)
self.addCleanup(CloseHandle, opened_event)

def test_check_lpeventattributes_type(self):
with self.assertRaises(InputError):
CreateEvent(False, False, lpEventAttributes="")