The Windows Implementation Libraries (WIL) are a collection of header-only libraries used across Windows source code to make life easier for developers on Windows through readable type-safe C++ interfaces for common Windows coding patterns. For example, the WIL resource wrappers include "unique_ptr-like" wrappers for internal types, like HANDLEs and events.
Features
For the coolest features, see:
- Error handling helpers: easy-to-use macros to make handling error codes a breeze.
- WinRT and COM wrappers:
unique_ptr
-like wrappers for COM and WinRT classes - RAII resource wrappers:
unique_ptr
-like wrappers for common Windows resources (likeHANDLE
s and events) - String helpers: simple helper functions for working with Windows strings
- (new!) Registry helpers: simple functions to read from, write to, and watch the registry
Supported error handling styles
WIL intends on being equally usable from both exception-based and error-code based code whenever possible. WIL also supports and encourages the use of fail-fast.
The names of functions and classes reflect the error handling style.
Error-code based functions and classes always end with a _nothrow
suffix. Fail-fast based functions and classes always end with a
_failfast
suffix. The best names (without a suffix) are reserved for
exception-based routines and routines that do not produce errors.
Neutral (no errors)
When possible, WIL prefers error-handling–neutral code. Specifically, this refers to classes and functions that will never propagate an error (either through exceptions or the return of error codes). For example, take the following WIL call:
wil::detach_to_opt_param(outParam, smartPtr);
This detaches the raw resource from a smart pointer if the given optional out param is non-null. The routine cannot fail at runtime; if you were trying to use an unsupported smart pointer type, it would simply static_assert and fail at compile time.
Exceptions
Prefer exception-based error handling over error code-based handling when possible; it both enables the use of modern libraries and reduces error handling clutter. Exceptions also enable some patterns that aren't possible using error codes:
THROW_IF_FAILED(wil::com_query<IPersistFile>(collection)->Save(name, FALSE));
Notice that the use of WIL's exception-based com_query
helper allows
acquisition of the interface and the actual call to the method can be
folded together on the same line.
Exception-based code can cleanly interop with error-code based code (including ABIs or system callbacks) through the use of exception guards defined within WIL's Error Handling Helpers.
WIL's exception-based classes and routines are hidden from code that
does not have exceptions enabled through the _CPPUNWIND
define which
is set based on your compiler options.
See Exception policy for details on enabling or disabling WIL's exception-based helpers.
Error codes
WIL normalizes on HRESULT
as its error code currency. Nearly all WIL
classes that can produce an error from one or more methods have a
version of the class named with a _nothrow
suffix whose methods return
HRESULT
on failure:
wil::unique_event_nothrow taskReadyEvent;
RETURN_IF_FAILED(taskReadyEvent.open(m_taskReadyEventName, EVENT_ALL_ACCESS));
Global functions that can produce an error will also have a version of
the function with a _nothrow
suffix that returns an HRESULT
on
failure:
RETURN_IF_FAILED(wil::com_query_to_nothrow(m_agileReference, IID_PPV_ARGS(listener)));
Some global functions only produce an error due to an allocation
failure. The error-code based version of those functions are also named
with a _nothrow
suffix, but expect the caller to validate that the
returned object is not null:
auto undo = wil::make_unique_nothrow<CApplyPropertiesUndo>();
RETURN_IF_NULL_ALLOC(undo);
Fail fast
Nearly all WIL classes that can produce an error from one or more
methods have a version of the class named with a _failfast
suffix
whose methods will fail fast on failure. In the following example, the
create()
call will fail fast if unable to create the event:
wil::unique_event_failfast pong_received;
pong_received.create();
Similarly, global functions that can produce an error will also have a
version of the function with a _failfast
suffix that will terminate
the process on failure:
auto serializer = wil::make_unique_failfast<JSONSerializer>();
Note that WIL internally will utilize fail fast to prevent callers from making some programming errors and to terminate a process when an unrecoverable invariant has been violated (for example, failure of a synchronization primitive).
Namespaces and naming conventions
WIL has a small handful of namespaces it uses to isolate code and uses specific naming conventions for macros and error handling style.
Note that WIL's namespaces are specifically designed to be short and
typed with each use. Reference functions and typenames directly with the
namespace qualification: wil::unique_event
. Avoid removing these
namespaces (avoid using using namespace wil;
or using namespace wistd;
, for example) as the namespaces easily collide with STL and
global Windows functions making your code more susceptible to naming
conflicts and making it more difficult make WIL or STL changes.
wil::
All WIL classes and functions are, by default, available behind the
wil
namespace.
wistd::
Some aspects of the STL are core language concepts that should be used
from all C++ code, regardless of whether exceptions are enabled in the
component. WIL selectively pulls in necessary exception-neutral
functionality from STL with almost no modification. This functionality
is available behind the wistd
namespace and exactly mirrors the same
functionality behind the std
namespace.
Note that callers able to use exceptions should always prefer the std
counterparts over wistd
.
MACROS
WIL prefixes macros with the WI_
prefix to help ensure that its
macros don't have name collisions with other similarly named constructs.
Example:
constexpr MyApplicationFlags c_applicationFlags =
WI_COMPILETIME_COMBINE_FLAGS(MyApplicationFlags::Hidden, MyApplicationFlags::Indication);
Macros will normally be in WI_ALL_CAPS
to signify that they are macros
and may not behave quite like a normal function call. WIL prefixes
macros that emulate function calls in every way with the same WI_
prefix, but uses PascalCase for their naming. These can be used as
though they were simple functions. Example:
if (WI_IsFlagSet(fileAttributes, FILE_ATTRIBUTE_DIRECTORY))
{
// Code ...
}
Note that the error handling
helpers library chooses to stick
with a non-prefixed, but longer and unique macro names like
THROW_IF_WIN32_BOOL_FALSE
to maximize readability given the frequency
with which those macros are expected to be used.
Error handling style
WIL reserves the best names for error-code neutral code and for
exception-based code. To distinguish functions and classes that instead
return error codes on failure or fail fast on failure, WIL uses the
_nothrow
and _failfast
suffixes.
For example, the unique_event
class also has unique_event_failfast
with the exact same contract (fail fast replacing exceptions) and
unique_event_nothrow
with the contract altered for methods that can
fail to return HRESULTs. Class methods are not suffixed, only the class
names are.
Global methods are similarly suffixed. For example, the
make_event_watcher
routine also has make_event_watcher_nothrow
and
make_event_watcher_failfast
.
WIL supports multiple error handling policies in order to accommodate a wide variety of clients. Consumers will typically decide whether the consumer's overall policy is error-code-based or exception-based, and use that policy throughout (perhaps with some fail-fast sprinkled in where applicable). Consumers are not expected to support all error handling styles; support just the style you need.
File and library organization
WIL headers are created based upon two key factors: dependencies and/or functionality.
You can think of dependencies pivot as the area being covered. Specifically, these are headers like:
- stl.h
- winrt.h
- com.h
- filesystem.h
The goals of these headers are really to create a set of wrappers; functions and classes that operate all with a common set of dependencies (nothing added to any of these locations should ever add a new dependency). Most code within these headers offer simple usability wrappers or interoperability wrappers across specific system or library constructs.
The other pivot is by functionality. These are headers like:
- result.h
- safecast.h
This is where we have a large piece of independent functionality (either a single class or a collection of routines that together define something interesting); new dependencies are unlikely, but could be added to handle the key functionality being delivered. These generally are not simple wrappers around existing system constructs, but provide something more.
Kernel mode
WIL is primarily consumed in user-mode C++, but some libraries have been enlightened for use from Kernel components:
- The resource wrappers library can be used from Kernel mode C++ code.
Deprecation
WIL includes a mechanism for deprecating things that should no longer be used. If a component is deprecated, it will be marked clearly with "deprecated" comments. There is a system of ifdefs to identify deprecated features (and when they were deprecated) and enable clients to opt into only using the non-deprecated parts of WIL.
NuGet package
The Windows Implementation Library is available on NuGet.org under the package name Microsoft.Windows.ImplementationLibrary.