Git Product home page Git Product logo

wil's Introduction

Windows Implementation Libraries (WIL)

Build Status

The Windows Implementation Libraries (WIL) is a header-only C++ library created to make life easier for developers on Windows through readable type-safe C++ interfaces for common Windows coding patterns.

Some things that WIL includes to whet your appetite:

  • include/wil/resource.h (documentation): Smart pointers and auto-releasing resource wrappers to let you manage Windows API HANDLEs, HWNDs, and other resources and resource handles with RAII semantics.
  • include/wil/win32_helpers.h (documentation): Wrappers for API functions that save you the work of manually specifying buffer sizes, calling a function twice to get the needed buffer size and then allocate and pass the right-size buffer, casting or converting between types, and so on.
  • include/wil/registry.h (documentation): Type-safe functions to read from, write to, and watch the registry. Also, registry watchers that can call a lambda function or a callback function you provide whenever a certain tree within the Windows registry changes.
  • include/wil/result.h (documentation): Preprocessor macros to help you check for errors from Windows API functions, in many of the myriad ways those errors are reported, and surface them as error codes or C++ exceptions in your code.
  • include/wil/Tracelogging.h: This file contains the convenience macros that enable developers define and log telemetry. These macros use TraceLogging API to log data. This data can be viewed in tools such as Windows Performance Analyzer.

WIL can be used by C++ code that uses C++ exceptions as well as code that uses returned error codes to report errors. All of WIL can be used from user-space Windows code, and some (such as the RAII resource wrappers) can even be used in kernel mode.

Documentation

This project is documented in its GitHub wiki. Feel free to contribute to it!

Consuming WIL

WIL follows the "live at head" philosophy, so you should feel free to consume WIL directly from the GitHub repo however you please: as a GIT submodule, symbolic link, download and copy files, etc. and update to the latest version at your own cadence. Alternatively, WIL is available using a few package managers, mentioned below. These packages will be updated periodically, likely to average around once or twice per month.

Consuming WIL via NuGet

WIL is available on nuget.org under the name Microsoft.Windows.ImplementationLibrary. This package includes the header files under the include directory as well as a .targets file.

Consuming WIL via vcpkg

WIL is also available using vcpkg under the name wil. Instructions for installing packages can be found in the vcpkg GitHub docs. In general, once vcpkg is set up on the system, you can run:

C:\vcpkg> vcpkg install wil:x86-windows
C:\vcpkg> vcpkg install wil:x64-windows

Note that even though WIL is a header-only library, you still need to install the package for all architectures/platforms you wish to use it with. Otherwise, WIL won't be added to the include path for the missing architectures/platforms. Execute vcpkg help triplet for a list of available options.

Building/Testing

Prerequisites

To get started contributing to WIL, first make sure that you have:

  • The latest version of Visual Studio or Build Tools for Visual Studio with the latest MSVC C++ build tools and Address Sanitizer components included.
  • The most recent Windows SDK
  • Nuget downloaded and added to PATH
  • vcpkg available on your system. Follow their getting started guide to get set up. You'll need to provide the path to vcpkg when initializing with CMake by passing -DCMAKE_TOOLCHAIN_FILE=[path to vcpkg]/scripts/buildsystems/vcpkg.cmake. Note that if you use the init.cmd script (mentioned below), this path can be specified or auto-detected if you:
    1. Manually specify the path to the root of your vcpkg clone via the -p or --vcpkg argument,
    2. Have the VCPKG_ROOT environment variable set to the root of your vcpkg clone. You can use the setx command to have this variable persist across shell sessions,
    3. Have the path to the root of your vcpkg clone added to your PATH (i.e. the path to vcpkg.exe), or
    4. If your vcpkg clone is located at the root of the same drive as your WIL clone (e.g. C:\vcpkg if your WIL clone is on the C: drive)

If you are doing any non-trivial work, also be sure to have:

  • A recent version of Clang
    • (winget install -i llvm.llvm and select Add LLVM to the system path for all users)

Initial configuration

Once everything is installed (you'll need to restart Terminal if you updated PATH and don't have this 2023 fix), open a VS native command window (e.g. x64 Native Tools Command Prompt for VS 2022 [not Developer Command Prompt for VS2022]).

  • If you are familiar with CMake you can get started building normally.
  • Otherwise, or if you prefer to skip all of the boilerplate, you can use the init.cmd script in the scripts directory. For example:
    C:\wil> scripts\init.cmd -c clang -g ninja -b debug
    You can execute init.cmd --help for a summary of available options. The scripts/init_all.cmd script will run the init.cmd script for all combinations of Clang/MSVC and Debug/RelWithDebInfo. Note that for either script, projects will only be generated for the architecture of the current VS command window.

To set up Visual Studio with IntelliSense, see below. If you used the init.cmd script, the corresponding build output directory should contain a compile_commands.json file that describes the commands used to compile each input file. Some editors such as Visual Studio Code can be configured to use this file to provide better auto-complete, tooltips, etc. Visual Studio Code, in particular should auto-detect the presence of this file and prompt you to use it for better IntelliSense. If you are not auto-prompted, this can be manually configured in the workspace's C/C++ properties under the property name compileCommands.

Visual Studio setup

To generate a Visual Studio solution with IntelliSense:

C:\wil> scripts\init.cmd -c msvc -g msbuild

That will create a .sln file in the corresponding build/ subdirectory (e.g. build/msvc64debug). You can open this solution in Visual Studio to develop and build, or you can invoke MSBuild directly.

Important! When using MSVC as the generator, the build type (-b argument to init.cmd) is mostly ignored by Visual Studio (since you can change the build type in the IDE), however this selection may still have an impact on project generation due to logic in the CMake files.

You can also get decent IntelliSense just by opening the repo directory in Visual Studio; VS should auto-detect CMake. You'll have to compile and run tests in a terminal window, though.

Inner loop

The scripts use a common directory pattern of build/$(compiler)$(arch)$(type) for the build output root. E.g. build/clang64debug when using Clang as the compiler, x64 as the architecture, and Debug as the build type. It is this directory where you will want to build from.

For example, if you initialized using the command above (scripts\init.cmd -c clang -g ninja -b debug), you can build the tests like so:

C:\wil\build\clang64debug> ninja

Or, if you want to only build a single test (e.g. for improved compile times):

C:\wil\build\clang64debug> ninja witest.noexcept

The output is a number of test executables. If you used the initialization script(s) mentioned above, or if you followed the same directory naming convention of those scripts, you can use the runtests.cmd script, which will execute any test executables that have been built, erroring out - and preserving the exit code - if any test fails. Note that MSBuild will modify the output directory names, so this script is only compatible with using Ninja as the generator.

Build everything

If you are at the tail end of of a change, you can execute the following to get a wide range of coverage:

C:\wil> scripts\init_all.cmd
C:\wil> scripts\build_all.cmd
C:\wil> scripts\runtests.cmd

Note that this will only test for the architecture that corresponds to the command window you opened. You will want to repeat this process for the other architecture (e.g. by using the x86 Native Tools Command Prompt for VS 2022 in addition to x64).

Formatting

This project has adopted clang-format as the tool for formatting our code. Please note that the .clang-format at the root of the repo is a copy from the internal Windows repo with few additions. In general, please do not modify it. If you find that a macro is causing bad formatting of code, you can add that macro to one of the corresponding arrays in the .clang-format file (e.g. AttributeMacros, etc.), format the code, and submit a PR.

NOTE: Different versions of clang-format may format the same code differently. In an attempt to maintain consistency between changes, we've standardized on using the version of clang-format that ships with the latest version of Visual Studio. If you have LLVM installed and added to your PATH, the version of clang-format that gets picked up by default may not be the one we expect. If you leverage the formatting scripts we have provided in the scripts directory, these should automatically pick up the proper version provided you are using a Visual Studio command window.

Before submitting a PR to the WIL repo we ask that you first run clang-format on your changes. There is a CI check in place that will fail the build for your PR if you have not run clang-format. There are a few different ways to format your code:

1. Formatting with git clang-format

Important! Git integration with clang-format is only available through the LLVM distribution. You can install LLVM through their GibHub releases page, via winget install llvm.llvm, or through the package manager of your choice.

Important! The use of git clang-format additionally requires Python to be installed and available on your PATH.

The simplest way to format just your changes is to use clang-format's git integration. You have the option to do this continuously as you make changes, or at the very end when you're ready to submit a PR. To format code continuously as you make changes, you run git clang-format after staging your changes. For example:

C:\wil> git add *
C:\wil> git clang-format --style file

At this point, the formatted changes will be unstaged. You can review them, stage them, and then commit. Please note that this will use whichever version of clang-format is configured to run with this command. You can pass --binary <path> to specify the path to clang-format.exe you would like the command to use.

If you'd like to format changes at the end of development, you can run git clang-format against a specific commit/label. The simplest is to run against upstream/master or origin/master depending on whether or not you are developing in a fork. Please note that you likely want to sync/merge with the master branch prior to doing this step. You can leverage the format-changes.cmd script we provide, which will use the version of clang-format that ships with Visual Studio:

C:\wil> git fetch upstream
C:\wil> git merge upstream/master
C:\wil> scripts\format-changes.cmd upstream/master

2. Formatting with clang-format

Important! The path to clang-format.exe is not added to PATH automatically, even when using a Visual Studio command window. The LLVM installer has the option to add itself to the system or user PATH if you'd like. If you would like the path to the version of clang-format that ships with Visual Studio added to your path, you will need to do so manually. Otherwise, the run-clang-format.cmd script mentioned below (or, equivalently, building the format target) will manually invoke the clang-format.exe under your Visual Studio install path.

An alternative, and generally easier option, is to run clang-format either on all source files or on all source files you've modified. Note, however, that depending on how clang-format is invoked, the version used may not be the one that ships with Visual Studio. Some tools such as Visual Studio Code allow you to specify the path to the version of clang-format that you wish to use when formatting code, however this is not always the case. The run-clang-format.cmd script we provide will ensure that the version of clang-format used is the version that shipped with your Visual Studio install:

C:\wil> scripts\run-clang-format.cmd

Additionally, we've added a build target that will invoke this script, named format:

C:\wil\build\clang64debug> ninja format

Please note that this all assumes that your Visual Studio installation is up to date. If it's out of date, code unrelated to your changes may get formatted unexpectedly. If that's the case, you may need to manually revert some modifications that are unrelated to your changes.

NOTE: Occasionally, Visual Studio will update without us knowing and the version installed for you may be newer than the version installed the last time we ran the format all script. If that's the case, please let us know so that we can re-format the code.

Contributing

This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.microsoft.com.

When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.

This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact [email protected] with any additional questions or comments.

wil's People

Contributors

0xc0000054 avatar 0xflotus avatar asklar avatar ataridreams avatar chrisguzak avatar citelao avatar cookieplmonster avatar dmachaj avatar docjay avatar drustheaxe avatar dunhor avatar jackboosy avatar jlaanstra avatar jonwis avatar keith-horton avatar lhecker avatar m417z avatar matbech avatar michelecicciottiwork avatar microsoftopensource avatar natimiller avatar nibanks avatar noelcross avatar oldnewthing avatar paulcam206 avatar razvan-pricope avatar sylveon avatar talagrand avatar tusharpm avatar umu618 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wil's Issues

Documentation says unique_variant is a unique_any but it's a unique_struct.

The wiki says that unique_variant is derived from unique_any, but it's actually derived from unique_struct. Among other things, this means that to get a raw VARIANT the correct thing to do is a static_cast, because get() is not implemented. (And there is no shared_variant, for obvious reasons.)

I would make the change to the wiki myself, but I'm not sure that there aren't other similar issues with other types, and I'm not sure how unique_variant (and any other wrappers with the same issue) should be mentioned relative to the current documentation for unique_struct.

ExpandEnvAndSearchPath not /permissive- clean

Tested against 4361351

Test file (in root of repo for simplicity)

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <cstdint> // for SIZE_MAX in resource.h
#include "include/wil/win32_helpers.h"
int main() {}
> cl testwil.cpp /W4 /WX /permissive-
Microsoft (R) C/C++ Optimizing Compiler Version 19.20.27508.1 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

testwil.cpp
[...]/include/wil/win32_helpers.h(190): error C2760: syntax error: unexpected token 'identifier', expected ';'
[...]/include/wil/win32_helpers.h(190): error C7510: 'unique_cotaskmem_string': use of dependent type name must be prefixed with 'typename'

That appears to be the only blocker to making that header compile with /permissive-.

Add a RETURN_IF_FALSE macro

I'm curious if there's a reason there's no RETURN_IF_FALSE macro in result_macros.h. Our use-case is early returning false if a function call returned false in a simple one-liner.

The nearest equivalent is RETURN_IF_WIN32_BOOL_FALSE, but that returns the value of GLE, not the bool itself.

#define RETURN_IF_WIN32_BOOL_FALSE(win32BOOL) __WI_SUPPRESS_4127_S do { const auto __boolRet = wil::verify_BOOL(win32BOOL); if (!__boolRet) { __RETURN_GLE_FAIL(#win32BOOL); }} __WI_SUPPRESS_4127_E while ((void)0, 0)

The implementation we've been using is as follows

#define RETURN_IF_FALSE(b)                    \
    do                                        \
    {                                         \
        BOOL __boolRet = wil::verify_bool(b); \
        if (!__boolRet)                       \
        {                                     \
            return b;                         \
        }                                     \
    } while (0, 0)

Though it looks like there might be some other warning suppression that might need to be included, since this was inspired by an older version of wil. If there's no objection, I'm happy to open a PR ๐Ÿ˜„

_nothrow and failfast

Hi,

I would like to use a wrapper for Win32 Event in the code of some "optional" plugin for some application. (Imagine Visual Studio and its Intellisense feature as an example of such application). I would not like to fail the whole app if something goes wrong with this plugin. I would prefer to just gracefully stop the plugin instead.
For this reason I tried to use the "_nothrow" flavor of this wrapper:
wil::unique_event_nothrow evt;

But while reading its code I noticed that it can still fail the whole app with failfast if, for example, I call evt.SetEvent without creating the event first.
I would like to handle such failures in some other way rather than failing the application with failfast.
Don't you consider this behavior as inconsistent and unexpected for the _nothrow flavors of the API?

Multiple files not /analyze clean

Tested against 4361351 with local fix for #26.

Test file (in root of repo for simplicity)

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "include/wil/com.h"
#include "include/wil/registry.h"
#include "include/wil/filesystem.h"
#include "include/wil/win32_helpers.h"
int main() {}
> cl testwil.cpp /W4 /WX /permissive- /analyze
Microsoft (R) C/C++ Optimizing Compiler Version 19.20.27508.1 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

[see link below]

Compiler output: https://gist.github.com/zx64/563b366873fd8063bb72a44ec5839e52

str_raw_ptr use is not /permissive- clean

The issue is that the str_raw_ptr is intended to be a overloadable, however the issue is that it's used before all definitions/declarations are available, which is not two-phase name lookup clean. E.g. the overloads for PCWSTR and unique_any_t appear towards the top of resource.h and is used pretty much right after with str_concat, etc., however the overload for HSTRING doesn't appear until later in the file and the overload for std::wstring appears in stl.h, defined after including resource.h. Thus, neither of these overloads should be considered during function overload resolution in functions like str_concat.

I tried to address this when porting the tests over, but ran into a number of build issues with the Windows build. The somewhat obvious thing to do is to define a function template that can be specialized, but this doesn't work cleanly if the type itself is a class template (since there's no partial specializations for function templates). Another option is class template specialization, which is what I tried, but the issue there is that there are dependencies on overload resolution behavior. E.g. it's assumed to work with both things that are convertible to PCWSTR and things that are convertible to std::wstring (e.g. std::filesystem::path). Unfortunately there are issues around trying to specialize types like path since there are inconsistencies in which filesystem header is available in the Windows build...

This mostly leaves trying to replicate overload resolution to some degree. This isn't exactly clean or easy. E.g. trying to conditionally specialize on things like is_convertible_v don't work by themselves since PCWSTR is convertible to std::wstring. One possible option may be to describe the restriction more explicitly (e.g. is_convertible_v<T, wstring> && !is_convertible_v<T, PCWSTR>), but that's a bit messy to use as anything other than a last resort. Another possible option I threw around might be to explicitly check for operator PCWSTR, etc. I'm a bit skeptical of that, however since I'm not sure it would work cleanly with cv/refs.

RETURN_HR_MSG and "%s" failure

While doing a couple of case studies on how to use wil, I tested the following simple case which doesn't work for me

constexpr char failstr[] = "fail";
HRESULT FailingFunc(bool dofail)
{
	if (dofail)
		RETURN_HR_MSG(E_INVALIDARG, "%s is not a valid string", failstr);
	return E_INVALIDARG;
}

Outputs:

..\src\testwil.cpp(9)\wiltest.exe!00007FF7CD7016F7: (caller: 00007FF7CD70314F) ReturnHr(2) tid(11ca4) 80070057 Felaktig parameter.
    Msg:[?? is not a valid string] [FailingFunc(E_INVALIDARG)]

wil::com_ptr typedef is silently omitted if WI_ENABLE_EXCEPTIONS isn't defined

If exceptions aren't enabled using wil::com_ptr results in an error that isn't immediately clear without digging through the source.
error C2039: 'com_ptr': is not a member of 'wil'
It should be pretty easy to add a struct that takes a compile time message and static_assert(false, message) if that class is instantiated to produce a clearer error message when using throwing types in binaries that don't support exceptions.

Investigate/fix unreachable code warning suppression (C4702)

Copied from ADO task 15917057

The original bug talked about warnings coming from the tests directory and so subsequently warning suppression was added in a few places (do a search for 4702). I did a quick and dirty test of removing these suppressions and didn't encounter any warnings/errors. It's possible that the issue has either been fixed or the original issue was a compiler bug that has since been fixed. Probably just need to find a good time to test out its removal on a full build to see if any errors get spit out.

WI_AreAllFlagsSet doesn't compile for C++/WinRT defined enum flags

If you want to deal with C++/WinRT's projected enum flags and using WI_AreAllFlagsSet, it doesn't compile. The reason is C++/WinRT defines its enum operator in global namespace but enum is defined in their own namespace. So ADL doesn't work, then WIL have several enum operators (for its own type) in wil::details or wil namespaces so it doesn't go hunting for global namespace.

The fix from WIL side would be stopping relying on enum operator and just cast it to underlying integral type, using WI_EnumValue, just like WI_IsAnyFlagSet does

Enable '-fno-delayed-template-parsing' when building with Clang

There's a few outstanding issues that I've yet to address, so it can't be turned on just yet, but we should get this complete when we can. Both the "normal" and "cpplatest" tests build successfully with this set, but there's still work to do for the "app" and "noexcept" variants.

It may also be worth our while to address what we can with the -fno-ms-compatibility switch turned on while we're at it.

Update fail fast handling (FAIL_FAST, FAIL_FAST_IF, etc.) to use RoFailFastWithErrorContext to improve diagnosis of failures and Watson bucketing

There has been feedback that WIL FailFast should use stowed exceptions to get better blaming. This indeed could be useful as part of FAIL_FAST_IF_FAILED calls.

In addition, the POF logic could also be useful as in the FAIL_FAIL_FAST_IF_FAILED calls that did RoOriginate, you can end up with a crash dump from the point where it actually failed.

We should rationalize what the best way to achieve this is (i.e. this could be just calling RoFailFastWithErrorContext as part of wil/result_originate.h if there is a stowed exception).

Visual C++ Code Analysis broken by commit 99c425a

Microsoft Visual C++ 2019 version 16.3.3, following simple module:

#include "wil/com.h"

int main() {}

Code Analysis works fine with f74a30b

1>------ Build started: Project: wil_analyze, Configuration: Debug Win32 ------
1>wil_analyze.cpp
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

but fails with 99c425a #80

1>------ Build started: Project: wil_analyze, Configuration: Debug Win32 ------
1>wil_analyze.cpp
1>E:\Source\wil_analyze\wil\include\wil\result_macros.h(3617,1): error C2059: syntax error: '<L_GRAMMAR_template_head_l>'
1>E:\Source\wil_analyze\wil\include\wil\result_macros.h(3618,1): error C2143: syntax error: missing ';' before '{'
1>E:\Source\wil_analyze\wil\include\wil\result_macros.h(3618,1): error C2447: '{': missing function header (old-style formal list?)
1>E:\Source\wil_analyze\wil\include\wil\result_macros.h(3626,1): error C2059: syntax error: ''template<''
1>E:\Source\wil_analyze\wil\include\wil\result_macros.h(3628,1): error C2143: syntax error: missing ';' before '{'
...
1>E:\Source\wil_analyze\wil\include\wil\result_macros.h(5301,31): error C2039: 'ReportFailure_NtStatus': is not a member of 'wil::details'
1>E:\Source\wil_analyze\wil\include\wil\result_macros.h(5301,53): fatal error C1003: error count exceeds 100; stopping compilation
1>Done building project "wil_analyze.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

I wondered why Code Analysis works fine in https://github.com/microsoft/terminal - but WIL is held back there on working version e8c599b

Tests take an excessive amount of time to build

The tests were copied mostly verbatim from the OS depot. These were already pretty bad in terms of build time, but undoubtedly made worse by the change over to Catch2. Most of the excessive build time comes from ComTests.cpp, likely through the excessive amount of template instantiation that it does. Some if not much if not most of that is likely redundant, and if we can reduce it down we certainly should. WinRTTests.cpp and wiTest.cpp are also possible offenders, but not nearly to the degree that the COM tests is. For now I've disabled Release builds on the CI machines to try and improve the state of things (15-ish min compile time for Debug compared to 50-ish min compile time for Release).

Is this the Windows Internal Library rebranded?

Trying to determine if this is a subset of the Windows Internal Library (with a new name) or a completely different project. It appears to be a subset, sans certain types related to staging configuration, velocity, and the like.

Suppressing static initializers causes the fallback telemetry provider callback to not be called

Repro steps:

  1. Define RESULT_SUPPRESS_STATIC_INITIALIZERS
  2. Include wil headers after the define
  3. Use SetResultTelemetryFallback to set a telemetry provider callback
  4. Use an error macro (e.g. throw and catch using CATCH_LOG)

Expected: the telemetry callback should be hit
Actual: callback is not hit

Narrowed it down to the following line never being called in result.h
g_pfnGetContextAndNotifyFailure = GetContextAndNotifyFailure;
WilInitialize_Result should make the global assignment and consumers should invoke WilInitialize_Result. (In the event that static initializers are disabled)

Not very clear documentation

In your documentation you say: "To get started testing WIL, first make sure that you have a recent version of Visual Studio and the most recent Windows SDK installed."

I use Visual Studio 2017 and Win10 SDK - and library can't be compiled because of some unknown attributes in header files. Looks like VS 2017 and/or Win10 SDK is not enough?

Could you please mention in documentation exact version of VS and SDK which are required to use WIL ?

RESULT_DIAGNOSTICS_LEVEL 0

When the diagnostics level is set to 0 using the following define:
#define RESULT_DIAGNOSTICS_LEVEL 0
In a release build (with all optimizations etc), I do expect that Return_Hr is not called in the generated code. However, this is not the case when using the RETURN_IF_FAILED macro:

if ( hr < 0 )
  {
    wil::details::in1diag0::Return_Hr(retaddr, hr);
    return hr;
  }

A possible solution might be do define different helper macros, depending on RESULT_DIAGNOSTICS_LEVEL. E.g.

#if RESULT_DIAGNOSTICS_LEVEL == 0
#undef RETURN_HR_FAIL
#define RETURN_HR_FAIL RETURN_HR_FAIL_EXPECTED
#undef RETURN_IF_WIN32_BOOL_FALSE
#define RETURN_IF_WIN32_BOOL_FALSE RETURN_IF_WIN32_BOOL_FALSE_EXPECTED
#undef RETURN_LAST_ERROR_IF_NULL
#define RETURN_LAST_ERROR_IF_NULL RETURN_LAST_ERROR_IF_NULL_EXPECTED
#undef RETURN_LAST_ERROR_IF
#define RETURN_LAST_ERROR_IF RETURN_LAST_ERROR_IF_EXPECTED
#undef RETURN_IF_WIN32_ERROR
#define RETURN_IF_WIN32_ERROR RETURN_IF_WIN32_ERROR_EXPECTED
#undef RETURN_HR_IF
#define RETURN_HR_IF RETURN_HR_IF_EXPECTED
#endif 

[Request for comments] wil::unique_string

While trying to add support for conversion to std::string_view to unique_any_t when used to hold strings, I faced multiple issues that prevented me from correctly implementing it without creating a dependency on <string_view> or causing a static assertion/ugly basic_string_view error when trying to convert non character types to string_view. To fix this issue (and add more functionality), I have the idea of creating a unique_string type, which would look somehow like this:

template<...>
class unique_string : public unique_any_array_ptr<...>
{
    static_assert(/* that ValueType is a wchar_t or char */);

    // avoid code duplication, reuse constructors from inherited class
    using unique_any_array_ptr<...>::unique_any_array_ptr;

    // somehow enabled conditionally
    operator std::basic_string_view<value_type>() const { return { this->get(), this->size() }; }

    const_pointer c_str() const { return this->get(); }
    size_type length() const { return this->size(); }

    // maybe more string manipulation functions?
}

All the existing type aliases and string helpers would be updated to use this type instead. This would allow for things like correctly embedding null characters, bounds checked string access, and being able to pass the string RAII wrappers to functions accepting string views.

I believe that after updating everything, this shouldn't break normal usage cases, because unique_string will end up exposing the same API surface than unique_any via public inheritance of unique_array_ptr.

As a bonus some Natvis visualizers for unique_string could be made so that it shows up nicely in a debugger.

I'm submitting this as request for comments since this would be a pretty major change, so I want to get comments or check if this would even be accepted before getting to work implementing it.

VS2019 default build fails

(See vcsig email thread)

If you try to build wil with Visual Studio (File > Open >Cmake) using VS2019 (16.1.0 Preview 3.0) it will fail with:
wil\tests\wiTest.cpp(117): warning C4244: 'initializing': conversion from 'long' to 'unsigned short', possible loss of data

This is the line where the error occurs:
unsigned short errorLine = LINE;

LINE has different types in different contexts when compiled with ZI thus the error. The docs mention that it is not a constant, but there is no mention that the type also changes.

https://godbolt.org/z/DlOqtV
https://github.com/MicrosoftDocs/cpp-docs/issues/1002

The modified CMake version shipped with VS2019 changed the default compiler flag from Zi ot Zi. See here: microsoft/CMake#75

Repo is missing a .gitattributes

Turns out the changes I've pushed (like #51) have had Unix-style LF endings instead of Windows-style CRLF endings.

I presume we need to add a .gitattributes containing:

# Disable CRLF-mapping for all files in the depot.
* -text

And then use the sequence here to renormalize broken endings.

wil error handling macros don't work with standards conforming pre-processor /experimental:preprocessor

Copied from ADO task 18587971

I started investigating this a while ago. There's a number of issues - many of which are easily addressible - however there are a few that are not so straightforward. For the most part the pre-processor changes seem to be backwards compatible (that is, using the ##__VA_ARGS__ styntax will work even without the compiler switch). However, I was unable to get the WI_ARGS_COUNT macro working for zero arguments. We can maybe address this piecewise since Clang seems to complain about some of the usage as well.

Tests don't build with Clang for x86 Debug

This is likely a Clang/linker bug, but needs to be looked at more closesly to find out. When you try and build with Clang configured for x86 Debug, there are a ton of errors that look something like error: assembler label '' can not be undefined. I looked into this briefly and was able to narrow it down to the result.h header file, but didn't get much further than that.

Struct helper that zero inits and sets dwSize/cbSize etc.

Filing this as a feature request/suggestion to see if there's interest in it. I've also included my own implementation to more thoroughly demonstrate the idea even if it's not PR ready.

Example use: auto startup_info = InitWinStruct<STARTUPINFO>();. The resulting struct is zeroed and has the first member variable set to the appropriate sizeof value.

namespace detail
{
#define MAKE_HAS_MEMBER(MEMBER) \
    template<class, class = void> constexpr bool has_##MEMBER = false; \
    template<class T> constexpr bool has_##MEMBER<T, std::void_t<decltype(std::declval<T>().##MEMBER)>> = true;

    MAKE_HAS_MEMBER(dwSize);
    MAKE_HAS_MEMBER(cbSize);
    MAKE_HAS_MEMBER(cb);
    MAKE_HAS_MEMBER(nLength);
#undef MAKE_HAS_MEMBER
}

template<class T>
inline auto InitWinStruct()
{
    T result {};
    if constexpr(detail::has_cbSize<T>)
    {
        result.cbSize = sizeof(T);
    }
    else if constexpr(detail::has_dwSize<T>)
    {
        result.dwSize = sizeof(T);
    }
    else if constexpr(detail::has_cb<T>)
    {
        result.cb = sizeof(T);
    }
    else if constexpr(detail::has_nLength<T>)
    {
        result.nLength = sizeof(T);
    }
    else
    {
        static_assert(false, "Don't know what the size member variable is called");
    }
    return result;
}

There's an obvious simplification to the size assignment step that I wasn't sure on the safety of: cast and assign the sizeof value to the first four bytes.

Changing linker order will cause tests to fail

Change cpplatest source listing to:

target_sources(witest.cpplatest PUBLIC
    ${CMAKE_CURRENT_SOURCE_DIR}/../CommonTests.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/../ComTests.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/../FileSystemTests.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/../CppWinRTTests.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
    )

compile and run tests. It will fail.

Analysis warnings when more rules are enabled

The project is mostly clean when analyzed with the NativeRecommenedRules.ruleset ... Some projects enable additional checks (such as 6340 and 6322) for which the project is not as clean.

Specific examples:

  • result_macros.h:1106 - warning C6340 - printf format specificer signedness mismatch
  • result_macros.h:2961 - warning C6322 - empty except block

To resolve this issue, consider finding and adding the .ruleset file for that internal project.

unique_hstring and shared_hstring don't manage HSTRING reference counts properly

unique_hstring is prone to overrelease when used with a raw HSTRING or with WRL's HSTRING wrappers. This is a pernicious issue that is causing memory corruption and crashes in code that is migrating from WRL toward WIL.

{
  Microsoft::WRL::Wrappers source_str = something();
  wil::unique_hstring dest_str =source_str.Get(); // No call to WindowsDuplicateString!
} // At destruction, two calls to WindowsDeleteString are made, causing an overrelease.

shared_hstring also suffers from this problem, but compounds it with a major performance penalty, because it's creating a std::shared_ptr to manage the lifetime of the HSTRING, giving us a redundant reference count, instead of just using WindowsDuplicateString to properly copy strings around.

Unable to use the library within a C++17 project

There is several compilation errors when trying to use the library within a C++17 project (I use the NuGet package and have no problem with std versions <= 14).
My version of VS2017 is 15.9.2.

Reproduction steps:
Create an empty console application, install Nuget Microsoft.Windows.ImplementationLibrary v1.0.190430.13 and just update your main to include wil/resources.h :

#include <windows.h>
#include <wil/resource.h>
int main()
{
}

Here are the error messages I get:

include\wil\wistd_type_traits.h(1011): error C2275: '_Tp': illegal use of this type as an expression
include\wil\wistd_type_traits.h(1010): note: see declaration of '_Tp'
include\wil\wistd_type_traits.h(1011): note: see reference to class template instantiation 'wistd::is_aggregate<_Tp>' being compiled
include\wil\wistd_type_traits.h(1011): error C3861: '__is_aggregate': identifier not found
include\wil\wistd_type_traits.h(1011): error C2975: '__v': invalid template argument for 'wistd::integral_constant', expected compile-time constant expression
include\wil\wistd_type_traits.h(113): note: see declaration of '__v'
include\wil\result_macros.h(2168): error C2440: '=': cannot convert from 'void (__cdecl *)(bool,const wil::FailureInfo &)' to 'void (__cdecl *)(bool,const wil::FailureInfo &) noexcept'
include\wil\result_macros.h(2168): note: Conversion to a function with more restrictive exception specification requires reinterpret_cast
include\wil\result_macros.h(2178): error C2440: '=': cannot convert from 'void (__cdecl *)(const wil::FailureInfo &)' to 'void (__cdecl *)(const wil::FailureInfo &) noexcept'
include\wil\result_macros.h(2178): note: Conversion to a function with more restrictive exception specification requires reinterpret_cast
include\wil\result_macros.h(2191): error C2440: '=': cannot convert from 'void (__cdecl *)(wil::FailureInfo *,PWSTR,::size_t)' to 'void (__cdecl *)(wil::FailureInfo *,PWSTR,::size_t) noexcept'
include\wil\result_macros.h(2191): note: Conversion to a function with more restrictive exception specification requires reinterpret_cast
include\wil\result_macros.h(2201): error C2440: '=': cannot convert from 'HRESULT (__cdecl *)(void)' to 'HRESULT (__cdecl *)(void) noexcept'
include\wil\result_macros.h(2201): note: Conversion to a function with more restrictive exception specification requires reinterpret_cast
include\wil\result_macros.h(2212): error C2440: '=': cannot convert from 'void (__cdecl *)(const wil::FailureInfo &)' to 'void (__cdecl *)(const wil::FailureInfo &) noexcept'
include\wil\result_macros.h(2212): note: Conversion to a function with more restrictive exception specification requires reinterpret_cast

Some messages seem related to this compiler issue: c++ compiler bug with decltype functions that have noexcept.

Add error handling for windows RPC APIs (that use SEH)

A feature request from internal windows teams.

RETURN_IF_RPC_FAILED/THROW_IF_RPC_FAILED/FAIL_FAST_IF_RPC_FAILED macros that would use a lambda to create a function scope that enables the use of the __try/__catch/__finally language extensions needed to consume RPC based APIs.

#define RETURN_IF_RPC_FAILED(hr)
do
{
HRESULT __hrRet = & -> HRESULT
{
RpcTryExcept
{
HRESULT __hrRet = wil::verify_hresult(hr);
if (FAILED(__hrRet))
{
__RETURN_HR_FAIL(__hrRet, #hr);
}
}
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
{
__RETURN_WIN32_FAIL(RpcExceptionCode(), #hr);
}
RpcEndExcept
return S_OK;
}();
}
while (0, 0)

Best practices for using com_ptr to create factory objects?

Hi,
I'm sorry for asking such a basic question, but I'm back to using COM APIs after a decade-long break and trying to adjust to modern best practices.

I'm working on some code that uses Direct2D and I'm looking at WIL to help me with that.

What are the recommended best practices for using com_ptr when calling functions like D2D1CreateFactory or DWriteCreateFactory? The former uses void** as the type of the output parameter, the latter IUnknown**. I've been following the examples in the docs for Direct2D and DirectWrite and using reinterpret_cast -- that seems to work well after upgrading to com_ptr, and looking at the source code there's nothing indicating that there's a better alternative for:

wil::com_ptr<IDWriteFactory2> pDWriteFactory2;
DWriteCreateFactory(
    DWRITE_FACTORY_TYPE_SHARED, 
    __uuidof(IDWriteFactory2), 
    reinterpret_cast<IUnknown**>(&pDWriteFactory2));

but I might be missing something... I think earlier incarnations of smart COM pointers had methods like put_void for getting the underlying pointer cast to void** for purposes like this. Just wanted to make sure there's no better way of doing things that I had somehow missed...

My other question is: how to use wil::CoCreateInstance to obtain a WIC factory object that implements IWICImagingFactory2? The first template argument expects a class, from which it can extract the CLSID using __uuidof, but in this case I only have the GUID CLSID_WICImagingFactory2. Is it a supported scenario, or should I not even bother and stick to the old way of doing things?

RPC call helper swallows conversion exceptions

Per the thread on #51, adopt one of Raymond's suggestions. I personally like the intermediate std::tuple approach. This may require yet another layer of indirection if the tuple instantiation triggers the "no mixing C++ & SEH in a single frame" requirement.

Warning: Function declared 'noreturn' should not return when using RETURN_IF_FAILED macro in clang

Compiling
`
#include "wil\result.h"

volatile bool debug = false;

HRESULT test_func()
{
if(debug)
{
return E_FAIL;
}
return S_OK;
}

int main(void)
{
RETURN_IF_FAILED(test_func());
return 0;
}`

with Clang gives
In file included from ../..\..\wil\result.h:16: ../..\..\wil/result_macros.h(4256,13): warning: function declared 'noreturn' should not return [-Winvalid-noreturn] } ^ ../..\..\wil/result_macros.h(4262,13): warning: function declared 'noreturn' should not return [-Winvalid-noreturn] } ^ ../..\..\wil/result_macros.h(4268,13): warning: function declared 'noreturn' should not return [-Winvalid-noreturn] } ^ ../..\..\wil/result_macros.h(4274,13): warning: function declared 'noreturn' should not return [-Winvalid-noreturn] } ^ ../..\..\wil/result_macros.h(4288,13): warning: function declared 'noreturn' should not return [-Winvalid-noreturn] } ^ ../..\..\wil/result_macros.h(4294,13): warning: function declared 'noreturn' should not return [-Winvalid-noreturn] } ^ ../..\..\wil/result_macros.h(4300,13): warning: function declared 'noreturn' should not return [-Winvalid-noreturn] } ^ ../..\..\wil/result_macros.h(4306,13): warning: function declared 'noreturn' should not return [-Winvalid-noreturn] } ^ ../..\..\wil/result_macros.h(4312,13): warning: function declared 'noreturn' should not return [-Winvalid-noreturn] } ^ ../..\..\wil/result_macros.h(4480,13): warning: function declared 'noreturn' should not return [-Winvalid-noreturn] } ^ ../..\..\wil/result_macros.h(4488,13): warning: function declared 'noreturn' should not return [-Winvalid-noreturn] } ^ ../..\..\wil/result_macros.h(4496,13): warning: function declared 'noreturn' should not return [-Winvalid-noreturn] } ^ ../..\..\wil/result_macros.h(4504,13): warning: function declared 'noreturn' should not return [-Winvalid-noreturn] } ^ ../..\..\wil/result_macros.h(4520,13): warning: function declared 'noreturn' should not return [-Winvalid-noreturn] } ^ ../..\..\wil/result_macros.h(4526,13): warning: function declared 'noreturn' should not return [-Winvalid-noreturn] } ^ ../..\..\wil/result_macros.h(4532,13): warning: function declared 'noreturn' should not return [-Winvalid-noreturn] } ^ ../..\..\wil/result_macros.h(4538,13): warning: function declared 'noreturn' should not return [-Winvalid-noreturn] } ^ ../..\..\wil/result_macros.h(4544,13): warning: function declared 'noreturn' should not return [-Winvalid-noreturn] } ^ ../..\..\wil/result_macros.h(4742,13): warning: function declared 'noreturn' should not return [-Winvalid-noreturn] } ^ ../..\..\wil/result_macros.h(4748,13): warning: function declared 'noreturn' should not return [-Winvalid-noreturn] } ^ ../..\..\wil/result_macros.h(4798,13): warning: function declared 'noreturn' should not return [-Winvalid-noreturn] } ^ ../..\..\wil/result_macros.h(4804,13): warning: function declared 'noreturn' should not return [-Winvalid-noreturn] } ^ 22 warnings generated.

GDI object leak in unique_hdc_paint?

It looks like there is a leak in wil::unique_hdc_paint, ::EndPaint() is called with nullptr instead of HWND handle. Am I doing something wrong?

Repo: https://github.com/rysavyjan/wil_paint_leak

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	case WM_PAINT:
	{
		PAINTSTRUCT ps;
		wil::unique_hdc_paint hDC = wil::BeginPaint(hwnd, &ps);
		FillRect(hDC.get(), &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
		InvalidateRect(hwnd, NULL, FALSE); // request another paint
		Sleep(1);
		return 0;
	}
	return 0;
	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

image

image

str_build_nothrow's use of str_raw_ptr introduces UB when called with a type that's convertible to std::wstring (return by value)

Somewhat related to #8. The str_build_nothrow function captures PCWSTR values from its arguments into an array using the str_raw_ptr function. This introduces UB if a temporary needs to be constructed for the call to str_raw_ptr. E.g. if called with a std::filesystem::path, which is implicitly convertible to a std::wstring through its operator string_type function, which returns by value.

2020 is a leapyear

2020 is a leap year and there are lot of times developers do things like manipulate SYSTEMTIME or FILETIME or convert from one to another, and they do not take into account leap years. C# has AddYears() for this.

WIL should implement safe date manipulation methods like C# so that 2020 isn't a mess.

std::invoke for callables.

C++17 allows some pretty crazy things with templates. Observe the following:

template <typename T>
struct member_parent { };

template<typename Object, typename Return, typename... Args>
struct member_parent<Return(Object:: *)(Args...)> {
	typedef Object type;
};

template<typename T>
using member_parent_t = typename member_parent<T>::type;

template<auto close_fn, typename token_t, token_t invalid_token = token_t()>
using com_token = wil::unique_com_token<member_parent_t<decltype(close_fn)>, token_t, decltype(close_fn), close_fn, invalid_token>;

This code allows to simplify writing a custom COM token:

using app_visibility_sink_cookie = com_token<&IAppVisibility::Unadvise, DWORD>;

However, it can't be easily included in wil without depending on C++17 (we can't overload templated type aliases, C++ determined that would be too easy).

The code should work, but doesn't, because of this line:

close_fn(m_source, m_token);

If that line where to be changed to std::invoke(close_fn, m_source, m_token);, this would work. However, I am aware that wil tries to avoid dependencies on the system standard library, and I am honestly unsure on how I would proceed to add more stuff to wistd.

Ideally, replacing all of the calls of function pointer template parameters like above by calls to wistd::invoke would allow more creativity in the use of wil.

Tests fail when compiled with FrameHandler4 support due to bug in FH4

Still looking into it, but it seems that in the following:

        HRESULT hr = S_OK;
        try
        {
            throw std::bad_alloc();
        }
        catch (...)
        {
            hr = LOG_CAUGHT_EXCEPTION();
            auto hrDirect = wil::ResultFromCaughtException();
            REQUIRE(hr == hrDirect);
        }

The catch (...) handler is not getting executed inside of ResultTests::ExceptionHandling. FWIW something similar is working correctly:

        auto hr = []() -> HRESULT
        {
            try
            {
                throw std::bad_alloc();
            }
            catch (...)
            {
                RETURN_CAUGHT_EXCEPTION();
            }
        }();

The only difference is that the second example is within a lambda. Could be that Catch2 is doing something weird w.r.t. exception handling that's interfering with a newer update in MSVC. Still looking at it

PathCch.h include in win32_helpers.h

PathCch.h is included in win32_helpers.h:
https://github.com/microsoft/wil/blob/master/include/wil/win32_helpers.h#L18

However, the functions declared in this header are only available on Windows 8 or higher. An include guard could be used to cover this case:

#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
#include <PathCch.h>
#endif

But after quickly inspecting win32_helpers.h, I couldn't find any references to the symbols in PathCch.h and it compiles without any errors in my project without this include.

If the PathCch.h include has no function, I propose it should be removed.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.