add files

This commit is contained in:
2025-07-07 09:39:03 +08:00
commit c9f6571a0f
181 changed files with 100140 additions and 0 deletions

Binary file not shown.

View File

@ -0,0 +1,27 @@
Copyright (C) Microsoft Corporation. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* The name of Microsoft Corporation, or the names of its contributors
may not be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,23 @@
<?xml version="1.0"?>
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- This build/.targets file is used by managed VS projects including win32
apps and UWP apps. -->
<PropertyGroup>
<WebView2ProjectKind>managed</WebView2ProjectKind>
</PropertyGroup>
<PropertyGroup>
<!-- The managed targets file is under build
so the root is one path segment up. -->
<NugetRoot>$(MSBuildThisFileDirectory)..\</NugetRoot>
</PropertyGroup>
<!-- Example logging
<Target Name="WebView2ManagedEntryLog" BeforeTargets="Build">
<Message Text="WebView2 managed .targets file. $(NugetRoot)" Importance="high"/>
</Target>
-->
<Import Project="$(NugetRoot)\build\Common.targets"/>
</Project>

View File

@ -0,0 +1,22 @@
<?xml version="1.0"?>
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- This build\native\.targets file is used by native (C++) VS projects including win32 and UWP. -->
<PropertyGroup>
<WebView2ProjectKind>native</WebView2ProjectKind>
</PropertyGroup>
<PropertyGroup>
<!-- The native targets file is under build\native
so the root is two path segments up. -->
<NugetRoot>$(MSBuildThisFileDirectory)..\..\</NugetRoot>
</PropertyGroup>
<!-- Example logging
<Target Name="WebView2NativeEntryLog" BeforeTargets="Build">
<Message Text="WebView2 native .targets file. $(NugetRoot)" Importance="high"/>
</Target>
-->
<Import Project="$(NugetRoot)\build\Common.targets"/>
</Project>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,144 @@
// Copyright (C) Microsoft Corporation. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef __core_webview2_environment_options_h__
#define __core_webview2_environment_options_h__
#include <objbase.h>
#include <wrl/implements.h>
#include "webview2.h"
#define CORE_WEBVIEW_TARGET_PRODUCT_VERSION L"93.0.961.33"
#define COREWEBVIEW2ENVIRONMENTOPTIONS_STRING_PROPERTY(p) \
public: \
HRESULT STDMETHODCALLTYPE get_##p(LPWSTR* value) override { \
if (!value) \
return E_POINTER; \
*value = m_##p.Copy(); \
if ((*value == nullptr) && (m_##p.Get() != nullptr)) \
return HRESULT_FROM_WIN32(GetLastError()); \
return S_OK; \
} \
HRESULT STDMETHODCALLTYPE put_##p(LPCWSTR value) override { \
LPCWSTR result = m_##p.Set(value); \
if ((result == nullptr) && (value != nullptr)) \
return HRESULT_FROM_WIN32(GetLastError()); \
return S_OK; \
} \
\
protected: \
AutoCoMemString m_##p;
#define COREWEBVIEW2ENVIRONMENTOPTIONS_BOOL_PROPERTY(p) \
public: \
HRESULT STDMETHODCALLTYPE get_##p(BOOL* value) override { \
if (!value) \
return E_POINTER; \
*value = m_##p; \
return S_OK; \
} \
HRESULT STDMETHODCALLTYPE put_##p(BOOL value) override { \
m_##p = value; \
return S_OK; \
} \
\
protected: \
BOOL m_##p = FALSE;
// This is a base COM class that implements ICoreWebView2EnvironmentOptions.
template <typename allocate_fn_t,
allocate_fn_t allocate_fn,
typename deallocate_fn_t,
deallocate_fn_t deallocate_fn>
class CoreWebView2EnvironmentOptionsBase
: public Microsoft::WRL::Implements<
Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
ICoreWebView2EnvironmentOptions> {
public:
CoreWebView2EnvironmentOptionsBase() {
// Initialize the target compatible browser version value to the version of
// the browser binaries corresponding to this version of the SDK.
m_TargetCompatibleBrowserVersion.Set(CORE_WEBVIEW_TARGET_PRODUCT_VERSION);
}
protected:
~CoreWebView2EnvironmentOptionsBase(){};
class AutoCoMemString {
public:
AutoCoMemString() {}
~AutoCoMemString() { Release(); }
void Release() {
if (m_string) {
deallocate_fn(m_string);
m_string = nullptr;
}
}
LPCWSTR Set(LPCWSTR str) {
Release();
if (str) {
m_string = MakeCoMemString(str);
}
return m_string;
}
LPCWSTR Get() { return m_string; }
LPWSTR Copy() {
if (m_string)
return MakeCoMemString(m_string);
return nullptr;
}
protected:
LPWSTR MakeCoMemString(LPCWSTR source) {
const size_t length = wcslen(source);
const size_t bytes = (length + 1) * sizeof(*source);
// Ensure we didn't overflow during our size calculation.
if (bytes <= length) {
return nullptr;
}
wchar_t* result = reinterpret_cast<wchar_t*>(allocate_fn(bytes));
if (result)
memcpy(result, source, bytes);
return result;
}
LPWSTR m_string = nullptr;
};
COREWEBVIEW2ENVIRONMENTOPTIONS_STRING_PROPERTY(AdditionalBrowserArguments)
COREWEBVIEW2ENVIRONMENTOPTIONS_STRING_PROPERTY(Language)
COREWEBVIEW2ENVIRONMENTOPTIONS_STRING_PROPERTY(TargetCompatibleBrowserVersion)
COREWEBVIEW2ENVIRONMENTOPTIONS_BOOL_PROPERTY(
AllowSingleSignOnUsingOSPrimaryAccount)
};
template <typename allocate_fn_t,
allocate_fn_t allocate_fn,
typename deallocate_fn_t,
deallocate_fn_t deallocate_fn>
class CoreWebView2EnvironmentOptionsBaseClass
: public Microsoft::WRL::RuntimeClass<
Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
CoreWebView2EnvironmentOptionsBase<allocate_fn_t,
allocate_fn,
deallocate_fn_t,
deallocate_fn>> {
public:
CoreWebView2EnvironmentOptionsBaseClass() {}
protected:
~CoreWebView2EnvironmentOptionsBaseClass() override{};
};
typedef CoreWebView2EnvironmentOptionsBaseClass<decltype(&::CoTaskMemAlloc),
::CoTaskMemAlloc,
decltype(&::CoTaskMemFree),
::CoTaskMemFree>
CoreWebView2EnvironmentOptions;
#endif // __core_webview2_environment_options_h__

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,375 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>Microsoft.Web.WebView2.WinForms</name>
</assembly>
<members>
<member name="T:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties">
<summary>
This class is a bundle of the most common parameters used to create a <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/>.
Its main purpose is to be set to <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CreationProperties"/> in order to customize the environment used by a <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/> during implicit initialization.
</summary>
<remarks>
This class isn't intended to contain all possible environment customization options.
If you need complete control over the environment used by a WebView2 control then you'll need to initialize the control explicitly by
creating your own environment with <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> and passing it to <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment)"/>
*before* you set the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property to anything.
See the <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/> class documentation for an initialization overview.
</remarks>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.#ctor">
<summary>
Creates a new instance of <see cref="T:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties"/> with default data for all properties.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.BrowserExecutableFolder">
<summary>
Gets or sets the value to pass as the browserExecutableFolder parameter of <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> when creating an environment with this instance.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.UserDataFolder">
<summary>
Gets or sets the value to pass as the userDataFolder parameter of <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> when creating an environment with this instance.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.Language">
<summary>
Gets or sets the value to use for the Language property of the CoreWebView2EnvironmentOptions parameter passed to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> when creating an environment with this instance.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.CreateEnvironmentAsync">
<summary>
Create a <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/> using the current values of this instance's properties.
</summary>
<returns>A task which will provide the created environment on completion.</returns>
<remarks>
As long as no other properties on this instance are changed, repeated calls to this method will return the same task/environment as earlier calls.
If some other property is changed then the next call to this method will return a different task/environment.
</remarks>
</member>
<member name="T:Microsoft.Web.WebView2.WinForms.WebView2">
<summary>
Control to embed WebView2 in WinForms.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.#ctor">
<summary>
Create a new WebView2 WinForms control.
After construction the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> property is <c>null</c>.
Call <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment)"/> to initialize the underlying <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/>.
</summary>
<remarks>
This control is effectively a wrapper around the WebView2 COM API, which you can find documentation for here: https://aka.ms/webview2
You can directly access the underlying ICoreWebView2 interface and all of its functionality by accessing the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> property.
Some of the most common COM functionality is also accessible directly through wrapper methods/properties/events on the control.
Upon creation, the control's CoreWebView2 property will be null.
This is because creating the CoreWebView2 is an expensive operation which involves things like launching Edge browser processes.
There are two ways to cause the CoreWebView2 to be created:
1) Call the <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment)"/> method. This is referred to as explicit initialization.
2) Set the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property. This is referred to as implicit initialization.
Either option will start initialization in the background and return back to the caller without waiting for it to finish.
To specify options regarding the initialization process, either pass your own <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/> to EnsureCoreWebView2Async or set the control's <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CreationProperties"/> property prior to initialization.
When initialization has finished (regardless of how it was triggered) then the following things will occur, in this order:
1) The control's <see cref="E:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2InitializationCompleted"/> event will be invoked. If you need to perform one time setup operations on the CoreWebView2 prior to its use then you should do so in a handler for that event.
2) If a Uri has been set to the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property then the control will start navigating to it in the background (i.e. these steps will continue without waiting for the navigation to finish).
3) The Task returned from <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment)"/> will complete.
For more details about any of the methods/properties/events involved in the initialization process, see its specific documentation.
Accelerator key presses (e.g. Ctrl+P) that occur within the control will
fire standard key press events such as OnKeyDown. You can suppress the
control's default implementation of an accelerator key press (e.g.
printing, in the case of Ctrl+P) by setting the Handled property of its
EventArgs to true. Also note that the underlying browser process is
blocked while these handlers execute, so:
<list type="number">
<item>
You should avoid doing a lot of work in these handlers.
</item>
<item>
Some of the WebView2 and CoreWebView2 APIs may throw errors if
invoked within these handlers due to being unable to communicate with
the browser process.
</item>
</list>
If you need to do a lot of work and/or invoke WebView2 APIs in response to
accelerator keys then consider kicking off a background task or queuing
the work for later execution on the UI thread.
</remarks>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.Dispose(System.Boolean)">
<summary>
Cleans up any resources being used.
</summary>
<param name="disposing"><c>true</c> if managed resources should be disposed; otherwise, <c>false</c>.</param>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.OnPaint(System.Windows.Forms.PaintEventArgs)">
<summary>
Overrides the base OnPaint event to have custom actions
in designer mode
</summary>
<param name="e">The graphics devices which is the source</param>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.WndProc(System.Windows.Forms.Message@)">
<summary>
Overrides the base WndProc events to handle specific window messages.
</summary>
<param name="m">The Message object containing the HWND window message and parameters</param>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.CreationProperties">
<summary>
Gets or sets a bag of options which are used during initialization of the control's <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/>.
This property cannot be modified (an exception will be thrown) after initialization of the control's CoreWebView2 has started.
</summary>
<exception cref="T:System.InvalidOperationException">Thrown if initialization of the control's CoreWebView2 has already started.</exception>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment)">
<summary>
Explicitly trigger initialization of the control's <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/>.
</summary>
<param name="environment">
A pre-created <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/> that should be used to create the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/>.
Creating your own environment gives you control over several options that affect how the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is initialized.
If you pass <c>null</c> (the default value) then a default environment will be created and used automatically.
</param>
<returns>
A Task that represents the background initialization process.
When the task completes then the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> property will be available for use (i.e. non-null).
Note that the control's <see cref="E:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2InitializationCompleted"/> event will be invoked before the task completes
or on exceptions.
</returns>
<remarks>
Calling this method additional times will have no effect (any specified environment is ignored) and return the same Task as the first call.
Calling this method after initialization has been implicitly triggered by setting the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property will have no effect (any specified environment is ignored) and simply return a Task representing that initialization already in progress.
</remarks>
<exception cref="T:System.InvalidOperationException">
Thrown if this instance of <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is already disposed, or if the calling thread isn't the thread which created this object (usually the UI thread). See <see cref="P:System.Windows.Forms.Control.InvokeRequired"/> for more info.
May also be thrown if the browser process has crashed unexpectedly and left the control in an invalid state. We are considering throwing a different type of exception for this case in the future.
</exception>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.InitCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment)">
<summary>
This is the private function which implements the actual background initialization task.
Cannot be called if the control is already initialized or has been disposed.
</summary>
<param name="environment">
The environment to use to create the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Controller"/>.
If that is null then a default environment is created with <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> and its default parameters.
</param>
<returns>A task representing the background initialization process.</returns>
<remarks>All the event handlers added here need to be removed in <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.Dispose(System.Boolean)"/>.</remarks>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.CreateParams">
<summary>
Protected CreateParams property. Used to set custom window styles to the forms HWND.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.OnVisibleChanged(System.EventArgs)">
<summary>
Protected VisibilityChanged handler.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.OnSizeChanged(System.EventArgs)">
<summary>
Protected SizeChanged handler.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.Select(System.Boolean,System.Boolean)">
<summary>
Protected Select method: override this to capture tab direction when WebView control is activated
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.OnGotFocus(System.EventArgs)">
<summary>
Protected OnGotFocus handler.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.IsInitialized">
<summary>
True if initialization finished successfully and the control is not disposed yet.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.GetSitedParentSite(System.Windows.Forms.Control)">
<summary>
Recursive retrieval of the parent control
</summary>
<param name="control">The control to get the parent for</param>
<returns>The root parent control</returns>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2">
<summary>
The underlying CoreWebView2. Use this property to perform more operations on the WebView2 content than is exposed
on the WebView2. This value is null until it is initialized and the object itself has undefined behaviour once the control is disposed.
You can force the underlying CoreWebView2 to
initialize via the <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment)"/> method.
</summary>
<exception cref="T:System.InvalidOperationException">Thrown if the calling thread isn't the thread which created this object (usually the UI thread). See <see cref="P:System.Windows.Forms.Control.InvokeRequired"/> for more info.</exception>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.ZoomFactor">
<summary>
The zoom factor for the WebView.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.Source">
<summary>
The Source property is the URI of the top level document of the
WebView2. Setting the Source is equivalent to calling <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Navigate(System.String)"/>.
Setting the Source will trigger initialization of the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/>, if not already initialized.
The default value of Source is <c>null</c>, indicating that the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized.
</summary>
<exception cref="T:System.ArgumentException">Specified value is not an absolute <see cref="T:System.Uri"/>.</exception>
<exception cref="T:System.NotImplementedException">Specified value is <c>null</c> and the control is initialized.</exception>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Navigate(System.String)"/>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.CanGoForward">
<summary>
Returns true if the webview can navigate to a next page in the
navigation history via the <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.GoForward"/> method.
This is equivalent to the <see cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.CanGoForward"/>.
If the underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized, this property is <c>false</c>.
</summary>
<seealso cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.CanGoForward"/>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.CanGoBack">
<summary>
Returns <c>true</c> if the webview can navigate to a previous page in the
navigation history via the <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.GoBack"/> method.
This is equivalent to the <see cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.CanGoBack"/>.
If the underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized, this property is <c>false</c>.
</summary>
<seealso cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.CanGoBack"/>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.DefaultBackgroundColor">
<summary>
The default background color for the WebView.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.ExecuteScriptAsync(System.String)">
<summary>
Executes the provided script in the top level document of the <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/>.
This is equivalent to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.ExecuteScriptAsync(System.String)"/>.
</summary>
<exception cref="T:System.InvalidOperationException">The underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized.</exception>
<exception cref="T:System.InvalidOperationException">Thrown when browser process has unexpectedly and left this control in an invalid state. We are considering throwing a different type of exception for this case in the future.</exception>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.ExecuteScriptAsync(System.String)"/>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.Reload">
<summary>
Reloads the top level document of the <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/>.
This is equivalent to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Reload"/>.
</summary>
<exception cref="T:System.InvalidOperationException">The underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized.</exception>
<exception cref="T:System.InvalidOperationException">Thrown when browser process has unexpectedly and left this control in an invalid state. We are considering throwing a different type of exception for this case in the future.</exception>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Reload"/>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.GoForward">
<summary>
Navigates to the next page in navigation history.
This is equivalent to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.GoForward"/>.
If the underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized, this method does nothing.
</summary>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.GoForward"/>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.GoBack">
<summary>
Navigates to the previous page in navigation history.
This is equivalent to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.GoBack"/>.
If the underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized, this method does nothing.
</summary>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.GoBack"/>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.NavigateToString(System.String)">
<summary>
Renders the provided HTML as the top level document of the <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/>.
This is equivalent to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.NavigateToString(System.String)"/>.
</summary>
<exception cref="T:System.InvalidOperationException">The underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized.</exception>
<exception cref="T:System.InvalidOperationException">Thrown when browser process has unexpectedly and left this control in an invalid state. We are considering throwing a different type of exception for this case in the future.</exception>
<remarks>The <c>htmlContent</c> parameter may not be larger than 2 MB (2 * 1024 * 1024 bytes) in total size. The origin of the new page is <c>about:blank</c>.</remarks>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.NavigateToString(System.String)"/>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.Stop">
<summary>
Stops any in progress navigation in the <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/>.
This is equivalent to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Stop"/>.
If the underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized, this method does nothing.
</summary>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Stop"/>
</member>
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2InitializationCompleted">
<summary>
This event is triggered either 1) when the control's <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> has finished being initialized (regardless of how it was triggered or whether it succeeded) but before it is used for anything
OR 2) the initialization failed.
You should handle this event if you need to perform one time setup operations on the CoreWebView2 which you want to affect all of its usages
(e.g. adding event handlers, configuring settings, installing document creation scripts, adding host objects).
</summary>
<remarks>
This sender will be the WebView2 control, whose CoreWebView2 property will now be valid (i.e. non-null) for the first time
if <see cref="P:Microsoft.Web.WebView2.Core.CoreWebView2InitializationCompletedEventArgs.IsSuccess"/> is true.
Unlikely this event can fire second time (after reporting initialization success first)
if the initialization is followed by navigation which fails.
</remarks>
</member>
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.NavigationStarting">
<summary>
NavigationStarting dispatches before a new navigate starts for the top
level document of the <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/>.
This is equivalent to the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationStarting"/> event.
</summary>
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationStarting"/>
</member>
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.NavigationCompleted">
<summary>
NavigationCompleted dispatches after a navigate of the top level
document completes rendering either successfully or not.
This is equivalent to the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationCompleted"/> event.
</summary>
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationCompleted"/>
</member>
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.WebMessageReceived">
<summary>
WebMessageReceived dispatches after web content sends a message to the
app host via <c>chrome.webview.postMessage</c>.
This is equivalent to the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.WebMessageReceived"/> event.
</summary>
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.WebMessageReceived"/>
</member>
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.SourceChanged">
<summary>
SourceChanged dispatches after the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property changes. This may happen
during a navigation or if otherwise the script in the page changes the
URI of the document.
This is equivalent to the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.SourceChanged"/> event.
</summary>
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.SourceChanged"/>
</member>
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.ContentLoading">
<summary>
ContentLoading dispatches after a navigation begins to a new URI and the
content of that URI begins to render.
This is equivalent to the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.ContentLoading"/> event.
</summary>
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.ContentLoading"/>
</member>
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.ZoomFactorChanged">
<summary>
ZoomFactorChanged dispatches when the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.ZoomFactor"/> property changes.
This is equivalent to the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2Controller.ZoomFactorChanged"/> event.
</summary>
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2Controller.ZoomFactorChanged"/>
</member>
<member name="F:Microsoft.Web.WebView2.WinForms.WebView2.components">
<summary>
Required designer variable.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.InitializeComponent">
<summary>
Required method for Designer support - do not modify
the contents of this method with the code editor.
</summary>
</member>
</members>
</doc>

View File

@ -0,0 +1,800 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>Microsoft.Web.WebView2.Wpf</name>
</assembly>
<members>
<member name="T:Microsoft.Web.WebView2.Wpf.CoreWebView2CreationProperties">
<summary>
This class is a bundle of the most common parameters used to create a <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/>.
Its main purpose is to be set to <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CreationProperties"/> in order to customize the environment used by a <see cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/> during implicit initialization.
It is also a nice WPF integration utility which allows commonly used environment parameters to be dependency properties and be created and used in markup.
</summary>
<remarks>
This class isn't intended to contain all possible environment customization options.
If you need complete control over the environment used by a WebView2 control then you'll need to initialize the control explicitly by
creating your own environment with <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> and passing it to <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment)"/>
*before* you set the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.Source"/> property to anything.
See the <see cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/> class documentation for an initialization overview.
</remarks>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.CoreWebView2CreationProperties.#ctor">
<summary>
Creates a new instance of <see cref="T:Microsoft.Web.WebView2.Wpf.CoreWebView2CreationProperties"/> with default data for all properties.
</summary>
</member>
<member name="F:Microsoft.Web.WebView2.Wpf.CoreWebView2CreationProperties.BrowserExecutableFolderProperty">
<summary>
The WPF DependencyProperty which backs the <see cref="P:Microsoft.Web.WebView2.Wpf.CoreWebView2CreationProperties.BrowserExecutableFolder"/> property.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.Wpf.CoreWebView2CreationProperties.BrowserExecutableFolder">
<summary>
Gets or sets the value to pass as the browserExecutableFolder parameter of <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> when creating an environment with this instance.
</summary>
</member>
<member name="F:Microsoft.Web.WebView2.Wpf.CoreWebView2CreationProperties.UserDataFolderProperty">
<summary>
The WPF DependencyProperty which backs the <see cref="P:Microsoft.Web.WebView2.Wpf.CoreWebView2CreationProperties.UserDataFolder"/> property.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.Wpf.CoreWebView2CreationProperties.UserDataFolder">
<summary>
Gets or sets the value to pass as the userDataFolder parameter of <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> when creating an environment with this instance.
</summary>
</member>
<member name="F:Microsoft.Web.WebView2.Wpf.CoreWebView2CreationProperties.LanguageProperty">
<summary>
The WPF DependencyProperty which backs the <see cref="P:Microsoft.Web.WebView2.Wpf.CoreWebView2CreationProperties.Language"/> property.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.Wpf.CoreWebView2CreationProperties.Language">
<summary>
Gets or sets the value to use for the Language property of the CoreWebView2EnvironmentOptions parameter passed to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> when creating an environment with this instance.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.CoreWebView2CreationProperties.CreateEnvironmentAsync">
<summary>
Create a <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/> using the current values of this instance's properties.
</summary>
<returns>A task which will provide the created environment on completion.</returns>
<remarks>
As long as no other properties on this instance are changed, repeated calls to this method will return the same task/environment as earlier calls.
If some other property is changed then the next call to this method will return a different task/environment.
</remarks>
</member>
<member name="T:Microsoft.Web.WebView2.Wpf.WebView2">
<summary>
A control to embed web content in a WPF application.
</summary>
<remarks>
This control is effectively a wrapper around the [WebView2 COM
API](https://aka.ms/webview2). You can directly access the underlying
ICoreWebView2 interface and all of its functionality by accessing the
<see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> property. Some of the most common COM
functionality is also accessible directly through wrapper
methods/properties/events on the control.
Upon creation, the control's <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> property will be
<c>null</c>. This is because creating the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> is an
expensive operation which involves things like launching Edge browser
processes. There are two ways to cause the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> to
be created:
<list type="bullet">
<item><description>
Call the <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment)"/> method. This is
referred to as explicit initialization.
</description></item>
<item><description>
Set the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.Source"/> property (which could be done from
markup, for example). This is referred to as implicit initialization.
Either option will start initialization in the background and return
back to the caller without waiting for it to finish.
To specify options regarding the initialization process, either pass
your own <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/> to <see
cref="M:Microsoft.Web.WebView2.Wpf.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment)"/> or set the control's <see
cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CreationProperties"/> property prior to initialization.
</description></item>
</list>
When initialization has finished (regardless of how it was triggered or
whether it succeeded) then the following things will occur, in this
order:
<list type="number">
<item><description>
The control's <see cref="E:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2InitializationCompleted"/> event
will be invoked. If you need to perform one time setup operations on
the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> prior to its use then you should
do so in a handler for that event.
</description></item>
<item><description>
If initialization was successful and a Uri has been set to the <see
cref="P:Microsoft.Web.WebView2.Wpf.WebView2.Source"/> property then the control will start navigating to it in
the background (i.e. these steps will continue without waiting for the
navigation to finish).
</description></item>
<item><description>
The Task returned from <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment)"/> will
complete.
</description></item>
</list>
For more details about any of the methods/properties/events involved in
the initialization process, see its specific documentation.
Because the control's <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> is a very heavyweight
object (potentially responsible for multiple running processes and
megabytes of disk space) the control implements <see
cref="T:System.IDisposable"/> to provide an explicit means to free it.
Calling <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.Dispose(System.Boolean)"/> will release the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/>
and its underlying resources (except any that are also being used by other
WebViews), and reset <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> to <c>null</c>. After <see
cref="M:Microsoft.Web.WebView2.Wpf.WebView2.Dispose(System.Boolean)"/> has been called the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> cannot be
re-initialized, and any attempt to use functionality which requires it
will throw an <see cref="T:System.ObjectDisposedException"/>.
Accelerator key presses (e.g. Ctrl+P) that occur within the control will
fire standard key press events such as OnKeyDown. You can suppress the
control's default implementation of an accelerator key press (e.g.
printing, in the case of Ctrl+P) by setting the Handled property of its
EventArgs to true. Also note that the underlying browser process is
blocked while these handlers execute, so:
<list type="number">
<item>
<description>You should avoid doing a lot of work in these handlers.</description>
</item>
<item><description>
Some of the WebView2 and CoreWebView2 APIs may throw errors if
invoked within these handlers due to being unable to communicate with
the browser process.
</description></item>
</list>
If you need to do a lot of work and/or invoke WebView2 APIs in response to
accelerator keys then consider kicking off a background task or queuing
the work for later execution on the UI thread.
Note that this control extends <see cref="T:System.Windows.Interop.HwndHost"/> in order to embed
windows which live outside of the WPF ecosystem. This has some
implications regarding the control's input and output behavior as well as
the functionality it "inherits" from <see cref="T:System.Windows.UIElement"/> and <see
cref="T:System.Windows.FrameworkElement"/>.
See the <see cref="T:System.Windows.Interop.HwndHost"/> and [WPF/Win32
interop](https://docs.microsoft.com/dotnet/framework/wpf/advanced/wpf-and-win32-interoperation#hwnds-inside-wpf)
documentation for more information.
</remarks>
<seealso cref="T:System.Windows.Interop.HwndHost"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.#ctor">
<summary>
Creates a new instance of a WebView2 control.
Note that the control's <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> will be null until initialized.
See the <see cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/> class documentation for an initialization overview.
</summary>
</member>
<member name="F:Microsoft.Web.WebView2.Wpf.WebView2.CreationPropertiesProperty">
<summary>
The WPF <see cref="T:System.Windows.DependencyProperty"/> which backs the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CreationProperties"/> property.
</summary>
<seealso cref="T:System.Windows.DependencyProperty"/>
</member>
<member name="P:Microsoft.Web.WebView2.Wpf.WebView2.CreationProperties">
<summary>
Gets or sets a bag of options which are used during initialization of the control's <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/>.
Setting this property will not work after initialization of the control's <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> has started (the old value will be retained).
See the <see cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/> class documentation for an initialization overview.
</summary>
<seealso cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.BuildWindowCore(System.Runtime.InteropServices.HandleRef)">
<summary>
This is overridden from <see cref="T:System.Windows.Interop.HwndHost"/> and is called to instruct us to create our HWND.
</summary>
<param name="hwndParent">The HWND that we should use as the parent of the one we create.</param>
<returns>The HWND that we created.</returns>
<seealso cref="M:System.Windows.Interop.HwndHost.BuildWindowCore(System.Runtime.InteropServices.HandleRef)"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.DestroyWindowCore(System.Runtime.InteropServices.HandleRef)">
<summary>
This is overridden from <see cref="T:System.Windows.Interop.HwndHost"/> and is called to instruct us to destroy our HWND.
</summary>
<param name="hwnd">Our HWND that we need to destroy.</param>
<seealso cref="M:System.Windows.Interop.HwndHost.DestroyWindowCore(System.Runtime.InteropServices.HandleRef)"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.WndProc(System.IntPtr,System.Int32,System.IntPtr,System.IntPtr,System.Boolean@)">
<summary>
This is overridden from <see cref="T:System.Windows.Interop.HwndHost"/> and is called to provide us with Win32 messages that are sent to our hwnd.
</summary>
<param name="hwnd">Window receiving the message (should always match our <see cref="P:System.Windows.Interop.HwndHost.Handle"/>).</param>
<param name="msg">Indicates the message being received. See Win32 documentation for WM_* constant values.</param>
<param name="wParam">The "wParam" data being provided with the message. Meaning varies by message.</param>
<param name="lParam">The "lParam" data being provided with the message. Meaning varies by message.</param>
<param name="handled">If true then the message will not be forwarded to any (more) <see cref="E:System.Windows.Interop.HwndHost.MessageHook"/> handlers.</param>
<returns>Return value varies by message.</returns>
<seealso cref="M:System.Windows.Interop.HwndHost.WndProc(System.IntPtr,System.Int32,System.IntPtr,System.IntPtr,System.Boolean@)"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.OnRender(System.Windows.Media.DrawingContext)">
<summary>
Override for painting to draw
</summary>
<param name="dc">The tools to handle the drawing via <see cref="T:System.Windows.Media.DrawingContext"/>.</param>
</member>
<member name="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2">
<summary>
Accesses the complete functionality of the underlying <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/> COM API.
Returns <c>null</c> until initialization has completed.
See the <see cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/> class documentation for an initialization overview.
</summary>
<exception cref="T:System.InvalidOperationException">
Thrown if the calling thread isn't the thread which created this object (usually the UI thread). See <see cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/> for more info.
May also be thrown if the browser process has crashed unexpectedly and left the control in an invalid state. We are considering throwing a different type of exception for this case in the future.
</exception>
<exception cref="T:System.ObjectDisposedException">Thrown if <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.Dispose(System.Boolean)"/> has already been called on the control.</exception>
<seealso cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/>
<seealso cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/>
</member>
<member name="E:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2InitializationCompleted">
<summary>
This event is triggered either
1) when the control's <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> has finished being initialized (regardless of how initialization was triggered) but before it is used for anything, or
2) if the initialization failed.
You should handle this event if you need to perform one time setup operations on the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> which you want to affect all of its usages.
(e.g. adding event handlers, configuring settings, installing document creation scripts, adding host objects).
See the <see cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/> class documentation for an initialization overview.
</summary>
<remarks>
This sender will be the <see cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/> control, whose <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> property will now be valid (i.e. non-null) for the first time
if <see cref="P:Microsoft.Web.WebView2.Core.CoreWebView2InitializationCompletedEventArgs.IsSuccess"/> is <c>true</c>.
Unlikely this event can fire second time (after reporting initialization success first)
if the initialization is followed by navigation which fails.
</remarks>
<seealso cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment)">
<summary>
Explicitly triggers initialization of the control's <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/>.
See the <see cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/> class documentation for an initialization overview.
</summary>
<param name="environment">
A pre-created <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/> that should be used to create the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/>.
Creating your own environment gives you control over several options that affect how the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/> is initialized.
If you pass an environment to this method then it will override any settings specified on the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CreationProperties"/> property.
If you pass <c>null</c> (the default value) and no value has been set to <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CreationProperties"/> then a default environment will be created and used automatically.
</param>
<returns>
A Task that represents the background initialization process.
When the task completes then the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> property will be available for use (i.e. non-null).
Note that the control's <see cref="E:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2InitializationCompleted"/> event will be invoked before the task completes.
</returns>
<remarks>
Calling this method additional times will have no effect (any specified environment is ignored) and return the same Task as the first call.
Calling this method after initialization has been implicitly triggered by setting the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.Source"/> property will have no effect (any specified environment is ignored) and simply return a Task representing that initialization already in progress.
Note that even though this method is asynchronous and returns a Task, it still must be called on the UI thread like most public functionality of most UI controls.
</remarks>
<exception cref="T:System.InvalidOperationException">
Thrown if the calling thread isn't the thread which created this object (usually the UI thread). See <see cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/> for more info.
May also be thrown if the browser process has crashed unexpectedly and left the control in an invalid state. We are considering throwing a different type of exception for this case in the future.
</exception>
<exception cref="T:System.ObjectDisposedException">Thrown if <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.Dispose(System.Boolean)"/> has already been called on the control.</exception>
<seealso cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/>
<seealso cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.Dispose(System.Boolean)">
<summary>
This is called by our base class according to the typical implementation of the <see cref="T:System.IDisposable"/> pattern.
We implement it by releasing all of our underlying COM resources, including our <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/>.
</summary>
<param name="disposing">True if a caller is explicitly calling Dispose, false if we're being finalized.</param>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2_ProcessFailed(System.Object,Microsoft.Web.WebView2.Core.CoreWebView2ProcessFailedEventArgs)">
<summary>
This is an event handler for our CoreWebView2's ProcessFailedEvent
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.BeginInit">
<summary>
Implementation of the ISupportInitialize pattern.
Prevents the control from implicitly initializing its <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> until <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.EndInit"/> is called.
Does *not* prevent explicit initialization of the CoreWebView2 (i.e. <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment)"/>).
Mainly intended for use by interactive UI designers.
</summary>
<remarks>
Note that the "Initialize" in ISupportInitialize and the "Init" in BeginInit/EndInit mean
something different and more general than this control's specific concept of initializing
its CoreWebView2 (explicitly or implicitly). This ISupportInitialize pattern is a general
way to set batches of properties on the control to their initial values without triggering
any dependent side effects until all of the values are set (i.e. until EndInit is called).
In the case of this control, a specific side effect to be avoided is triggering implicit
initialization of the CoreWebView2 when setting the Source property.
For example, normally if you set <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CreationProperties"/> after you've already set Source,
the data set to CreationProperties is ignored because implicit initialization has already started.
However, if you set the two properties (in the same order) in between calls to BeginInit and
EndInit then the implicit initialization of the CoreWebView2 is delayed until EndInit, so the data
set to CreationProperties is still used even though it was set after Source.
</remarks>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.EndInit">
<summary>
Implementation of the ISupportInitialize pattern.
Invokes any functionality that has been delayed since the corresponding call to <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.BeginInit"/>.
Mainly intended for use by interactive UI designers.
</summary>
<remarks>
See the documentation of <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.BeginInit"/> for more information.
</remarks>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.WhenInit_Source(System.Action)">
<summary>
Specifies a Source-related action to be invoked but which must be delayed if BeginInit has already been called but EndInit hasn't.
If the control is currently between BeginInit/EndInit calls then the action will be invoked during EndInit.
Otherwise the action will be invoked immediately.
If this is called multiple times between BeginInit/EndInit then the action passed to the last call is the only one that will be invoked during EndInit.
</summary>
<param name="action">The Source-related action to invoke, delayed until EndInit if necessary.</param>
<remarks>
In the future if we need this delayed-invoke functionality for actions unrelated to Source,
then we should generalize _initAction_Source to a list and generalize this method to take a list index along with the action.
Then we can just assign each index to a different potential action (e.g. 0 for Source), and potentially use constants to name the indices.
For now it didn't seem worth dynamically allocating a list for one thing,
but I still wanted to keep the delay-or-not logic abstracted away from the Source setter.
</remarks>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.SetCurrentValueFromCore(System.Windows.DependencyProperty,System.Object)">
<summary>
Updates one of our dependency properties to match a new value from the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/>.
It both sets the value and remembers (in _propertyChangingFromCore) that it came from the CoreWebView2 rather than the caller,
allowing the property's "on changed" handler to alter its behavior based on where the new value came from.
It's only intended to be called in a CoreWebView2 event handler that's informing us of a new property value.
It's basically just a wrapper around the inherited SetCurrentValue which also maintains _propertyChangingFromCore.
See the comments on <see cref="F:Microsoft.Web.WebView2.Wpf.WebView2._propertyChangingFromCore"/> for additional background info.
One more thing worth explicitly stating is that it wraps SetCurrentValue rather than SetValue,
in order to avoid overwriting any OneWay bindings that are set on the specified properties.
Check the link https://stackoverflow.com/q/4230698 for more information about the difference between SetValue and SetCurrentValue.
</summary>
<param name="property">The property to change due to an equivalent change in the CoreWebView2.</param>
<param name="value">The new value from the CoreWebView2.</param>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.IsPropertyChangingFromCore(System.Windows.DependencyProperty)">
<summary>
Checks if a given property is currently being updated to match an equivalent change in the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/>.
This method should only be called from a property's "on changed" handler; it has no meaning at any other time.
It is used to determine if the property is changing to match the CoreWebView2 or because the caller set it.
Usually this is used in order to decide if the new value needs to be propagated down to the CoreWebView2.
See the comments on <see cref="F:Microsoft.Web.WebView2.Wpf.WebView2._propertyChangingFromCore"/> for additional background info.
</summary>
<param name="property">The property to check.</param>
<returns>True if the property is changing to match the CoreWebView2, or false if the property was changed by the caller.</returns>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.ReparentController(System.IntPtr,System.Boolean)">
<summary>
Changes our controller's ParentWindow to the given HWND, along with any other necessary associated work.
</summary>
<param name="hwnd">The new HWND to set as the controller's parent. IntPtr.Zero means that the controller will have no parent and the CoreWebView2 will be hidden.</param>
<param name="sync">Whether or not to call <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.SyncControllerWithParentWindow"/> as required. Defaults to true. If you pass false then you should call it yourself if required.</param>
<remarks>
Reparenting the controller isn't necessarily as simple as changing its ParentWindow property,
and this method exists to ensure that any other work that needs to be done at the same time gets done.
The reason that SyncControllerWithParentWindow isn't baked directly into this method is because
sometimes we want to call the Sync functionality without necessarily reparenting (e.g. during initialization).
</remarks>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.SyncControllerWithParentWindow">
<summary>
Syncs visual/windowing information between the controller and its parent HWND.
This should be called any time a new, non-null HWND is set as the controller's parent,
including when the controller is first created.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.UIElement_IsVisibleChanged(System.Object,System.Windows.DependencyPropertyChangedEventArgs)">
<summary>
This is a handler for our base UIElement's IsVisibleChanged event.
It's predictably fired whenever IsVisible changes, and IsVisible reflects the actual current visibility status of the control.
We just need to pass this info through to our CoreWebView2Controller so it can save some effort when the control isn't visible.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.OnWindowPositionChanged(System.Windows.Rect)">
<summary>
This is overridden from <see cref="T:System.Windows.Interop.HwndHost"/> and called when our control's location has changed.
The HwndHost takes care of updating the HWND we created.
What we need to do is move our CoreWebView2 to match the new location.
</summary>
</member>
<member name="F:Microsoft.Web.WebView2.Wpf.WebView2.SourceProperty">
<summary>
The WPF <see cref="T:System.Windows.DependencyProperty"/> which backs the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.Source"/> property.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.Wpf.WebView2.Source">
<summary>
The top-level <see cref="T:System.Uri"/> which the WebView is currently displaying (or will display once initialization of its <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> is finished).
Generally speaking, getting this property is equivalent to getting the <see cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.Source"/> property and setting this property (to a different value) is equivalent to calling the <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Navigate(System.String)"/> method.
</summary>
<remarks>
Getting this property before the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/> has been initialized will retrieve the last Uri which was set to it, or null (the default) if none has been.
Setting this property before the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/> has been initialized will cause initialization to start in the background (if not already in progress), after which the <see cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/> will navigate to the specified <see cref="T:System.Uri"/>.
This property can never be set back to null or to a relative <see cref="T:System.Uri"/>.
See the <see cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/> class documentation for an initialization overview.
</remarks>
<exception cref="T:System.ObjectDisposedException">Thrown if <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.Dispose(System.Boolean)"/> has already been called on the control.</exception>
<exception cref="T:System.NotImplementedException">Thrown if the property is set to <c>null</c>.</exception>
<exception cref="T:System.ArgumentException">Thrown if the property is set to a relative <see cref="T:System.Uri"/> (i.e. a <see cref="T:System.Uri"/> whose <see cref="P:System.Uri.IsAbsoluteUri"/> property is <c>false</c>).</exception>
<seealso cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.SourcePropertyValid(System.Object)">
<summary>
This is a callback that WPF calls to validate a potential new Source value.
</summary>
<returns>
True if the value is valid, false if it is not.
If we return false then WPF should respond by throwing an <see cref="T:System.ArgumentException"/>.
</returns>
<remarks>
Note that we unfortunately can't treat null as invalid here because null is valid prior to initialization.
</remarks>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.SourcePropertyChanged(System.Windows.DependencyObject,System.Windows.DependencyPropertyChangedEventArgs)">
<summary>
This is a callback that WPF calls when the WPF Source property's value changes.
This might have been triggered by either:
1) The caller set Source to programmatically trigger a navigation.
2) The CoreWebView changed its own source and we're just updating the dependency property to match.
We use <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.IsPropertyChangingFromCore(System.Windows.DependencyProperty)"/> to distinguish the two cases.
</summary>
</member>
<member name="E:Microsoft.Web.WebView2.Wpf.WebView2.SourceChanged">
<summary>
A wrapper around the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.SourceChanged"/>.
The only difference between this event and <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.SourceChanged"/> is the first parameter that's passed to handlers.
Handlers of this event will receive the <see cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/> control, whereas handlers of <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.SourceChanged"/> will receive the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/> instance.
</summary>
<see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.SourceChanged"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2_SourceChanged(System.Object,Microsoft.Web.WebView2.Core.CoreWebView2SourceChangedEventArgs)">
<summary>
This is an event handler for our CoreWebView2's SourceChanged event.
Unsurprisingly, it fires when the CoreWebView2's source URI has been changed.
Note that there are two distinct triggers for this:
1) The CoreWebView2 was told to navigate programmatically (potentially by us, see SourcePropertyChanged).
2) The user interacted with the CoreWebView2, e.g. clicked a link.
In either of the above cases, this event might trigger several times due to e.g. redirection.
Aside from propagating to our own event, we just need to update our WPF Source property to match the CoreWebView2's.
</summary>
</member>
<member name="E:Microsoft.Web.WebView2.Wpf.WebView2.NavigationStarting">
<summary>
A wrapper around the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationStarting"/>.
The only difference between this event and <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationStarting"/> is the first parameter that's passed to handlers.
Handlers of this event will receive the <see cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/> control, whereas handlers of <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationStarting"/> will receive the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/> instance.
</summary>
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationStarting"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2_NavigationStarting(System.Object,Microsoft.Web.WebView2.Core.CoreWebView2NavigationStartingEventArgs)">
<summary>
This is an event handler for our CoreWebView2's NavigationStarting event.
We just need to propagate the event to WPF.
</summary>
</member>
<member name="E:Microsoft.Web.WebView2.Wpf.WebView2.NavigationCompleted">
<summary>
A wrapper around the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationCompleted"/>.
The only difference between this event and <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationCompleted"/> is the first parameter that's passed to handlers.
Handlers of this event will receive the <see cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/> control, whereas handlers of <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationCompleted"/> will receive the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/> instance.
</summary>
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationCompleted"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2_NavigationCompleted(System.Object,Microsoft.Web.WebView2.Core.CoreWebView2NavigationCompletedEventArgs)">
<summary>
This is an event handler for our CoreWebView2's NavigationCompleted event.
We just need to propagate the event to WPF.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2_HistoryChanged(System.Object,System.Object)">
<summary>
This is an event handler for our CoreWebView2's HistoryChanged event.
We're handling it in order to update our WPF CanGoBack and CanGoForward properties.
</summary>
</member>
<member name="F:Microsoft.Web.WebView2.Wpf.WebView2.CanGoBackProperty">
<summary>
The WPF <see cref="T:System.Windows.DependencyProperty"/> which backs the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CanGoBack"/> property.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.Wpf.WebView2.CanGoBack">
<summary>
Returns <c>true</c> if the WebView can navigate to a previous page in the navigation history.
Wrapper around the <see cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.CanGoBack"/> property of <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/>.
If <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> isn't initialized yet then returns <c>false</c>.
</summary>
<seealso cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.CanGoBack"/>
</member>
<member name="F:Microsoft.Web.WebView2.Wpf.WebView2.CanGoForwardProperty">
<summary>
The WPF <see cref="T:System.Windows.DependencyProperty"/> which backs the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CanGoForward"/> property.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.Wpf.WebView2.CanGoForward">
<summary>
Returns <c>true</c> if the WebView can navigate to a next page in the navigation history.
Wrapper around the <see cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.CanGoForward"/> property of <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/>.
If <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> isn't initialized yet then returns <c>false</c>.
</summary>
<seealso cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.CanGoForward"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.TabIntoCore(System.Windows.Input.TraversalRequest)">
<summary>
This is overridden from <see cref="T:System.Windows.Interop.HwndHost"/> and is called to inform us that tabbing has caused the focus to move into our control/window.
Since WPF can't manage the transition of focus to a non-WPF HWND, it delegates the transition to us here.
So our job is just to place the focus in our external HWND.
</summary>
<param name="request">Information about how the focus is moving.</param>
<returns><c>true</c> to indicate that we handled the navigation, or <c>false</c> to indicate that we didn't.</returns>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.OnGotKeyboardFocus(System.Windows.Input.KeyboardFocusChangedEventArgs)">
<summary>
This is overridden from <see cref="T:System.Windows.UIElement"/> and is called to inform us when we receive the keyboard focus.
We handle this by passing the keyboard focus on to the underlying <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/>.
We never want to land in a state where our window (this.Handle) actually has the keyboard focus.
</summary>
<param name="e">Arguments from the underlying GotKeyboardFocus event.</param>
<remarks>
Note that it's actually possible for us to receive keyboard focus without this method being called.
One known case where that happens is when our parent window is deactivated while we have focus, then reactivated.
We handle that case in <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.WndProc(System.IntPtr,System.Int32,System.IntPtr,System.IntPtr,System.Boolean@)"/>.
</remarks>
<seealso cref="M:Microsoft.Web.WebView2.Wpf.WebView2.WndProc(System.IntPtr,System.Int32,System.IntPtr,System.IntPtr,System.Boolean@)"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2Controller_MoveFocusRequested(System.Object,Microsoft.Web.WebView2.Core.CoreWebView2MoveFocusRequestedEventArgs)">
<summary>
This is an event handler for our CoreWebView2Controller's MoveFocusRequested event.
It fires when the CoreWebView2Controller has focus but wants to move it elsewhere in the app.
E.g. this happens when the user tabs past the last item in the CoreWebView2 and focus needs to return to some other app control.
So our job is just to tell WPF to move the focus on to the next control.
Note that we don't propagate this event outward as a standard WPF routed event because we've implemented its purpose here.
If users of the control want to track focus shifting in/out of the control, they should use standard WPF events.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2Controller_GotFocus(System.Object,System.Object)">
<summary>
This is an event handler for our CoreWebView2Controller's GotFocus event.
We just need to propagate the event to WPF.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2Controller_LostFocus(System.Object,System.Object)">
<summary>
This is an event handler for our CoreWebView2Controller's LostFocus event.
We just need to propagate the event to WPF.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2Controller_AcceleratorKeyPressed(System.Object,Microsoft.Web.WebView2.Core.CoreWebView2AcceleratorKeyPressedEventArgs)">
<summary>
This is an event handler for our CoreWebView2Controller's AcceleratorKeyPressed event.
This is called to inform us about key presses that are likely to have special behavior (e.g. esc, return, Function keys, letters with modifier keys).
WPF can't detect this input because Windows sends it directly to the Win32 CoreWebView2Controller control.
We implement this by generating standard WPF key input events, allowing callers to handle the input in the usual WPF way if they want.
If nobody handles the WPF key events then we'll allow the default CoreWebView2Controller logic (if any) to handle it.
Of the possible options, this implementation should provide the most flexibility to callers.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.OnKeyDown(System.Windows.Input.KeyEventArgs)">
<summary>
This is overridden from <see cref="T:System.Windows.UIElement"/> and called to allow us to handle key press input.
WPF should never actually call this in response to keyboard events because we're hosting a non-WPF window.
When our window has focus Windows will send the input directly to it rather than to WPF's top-level window and input system.
This override should only be called when we're explicitly forwarding accelerator key input from the CoreWebView2 to WPF (in CoreWebView2Controller_AcceleratorKeyPressed).
Even then, this KeyDownEvent is only triggered because our PreviewKeyDownEvent implementation explicitly triggers it, matching WPF's usual system.
So the process is:
<list type="number">
<item><description>CoreWebView2Controller_AcceleratorKeyPressed</description></item>
<item><description>PreviewKeyDownEvent</description></item>
<item><description>KeyDownEvent</description></item>
<item><description>OnKeyDown</description></item>
</list>
.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.OnKeyUp(System.Windows.Input.KeyEventArgs)">
<summary>
See <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.OnKeyDown(System.Windows.Input.KeyEventArgs)"/>.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.OnPreviewKeyDown(System.Windows.Input.KeyEventArgs)">
<summary>
This is the "Preview" (i.e. tunneling) version of <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.OnKeyDown(System.Windows.Input.KeyEventArgs)"/>, so it actually happens first.
Like OnKeyDown, this will only ever be called if we're explicitly forwarding key presses from the CoreWebView2.
In order to mimic WPF's standard input handling, when we receive this we turn around and fire off the standard bubbling KeyDownEvent.
That way others in the WPF tree see the same standard pair of input events that WPF itself would have triggered if it were handling the key press.
</summary>
<seealso cref="M:Microsoft.Web.WebView2.Wpf.WebView2.OnKeyDown(System.Windows.Input.KeyEventArgs)"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.OnPreviewKeyUp(System.Windows.Input.KeyEventArgs)">
<summary>
See <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.OnPreviewKeyDown(System.Windows.Input.KeyEventArgs)"/>.
</summary>
</member>
<member name="F:Microsoft.Web.WebView2.Wpf.WebView2.ZoomFactorProperty">
<summary>
The WPF <see cref="T:System.Windows.DependencyProperty"/> which backs the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.ZoomFactor"/> property.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.Wpf.WebView2.ZoomFactor">
<summary>
The zoom factor for the WebView.
This property directly exposes <see cref="P:Microsoft.Web.WebView2.Core.CoreWebView2Controller.ZoomFactor"/>, see its documentation for more info.
Getting this property before the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/> has been initialized will retrieve the last value which was set to it, or <c>1.0</c> (the default) if none has been.
The most recent value set to this property before the CoreWebView2 has been initialized will be set on it after initialization.
</summary>
<seealso cref="P:Microsoft.Web.WebView2.Core.CoreWebView2Controller.ZoomFactor"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.ZoomFactorPropertyChanged(System.Windows.DependencyObject,System.Windows.DependencyPropertyChangedEventArgs)">
<summary>
This is a callback that WPF calls when our WPF ZoomFactor property's value changes.
This might have been triggered by either:
1) The caller set ZoomFactor to change the zoom of the CoreWebView2.
2) The CoreWebView2 changed its own ZoomFactor and we're just updating the dependency property to match.
We use <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.IsPropertyChangingFromCore(System.Windows.DependencyProperty)"/> to distinguish the two cases.
</summary>
</member>
<member name="E:Microsoft.Web.WebView2.Wpf.WebView2.ZoomFactorChanged">
<summary>
The event is raised when the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.ZoomFactor"/> property changes.
This event directly exposes <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2Controller.ZoomFactorChanged"/>.
</summary>
<seealso cref="P:Microsoft.Web.WebView2.Wpf.WebView2.ZoomFactor"/>
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2Controller.ZoomFactorChanged"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2Controller_ZoomFactorChanged(System.Object,System.Object)">
<summary>
This is an event handler for our CoreWebView2Controller's ZoomFactorChanged event.
Unsurprisingly, it fires when the CoreWebView2Controller's ZoomFactor has been changed.
Note that there are two distinct triggers for this:
1) The value was changed programmatically (potentially by us, see ZoomFactorPropertyChanged).
2) The user interacted with the CoreWebView2, e.g. CTRL + Mouse Wheel.
Aside from propagating to our own event, we just need to update our WPF ZoomFactor property to match the CoreWebView2Controller's.
</summary>
</member>
<member name="F:Microsoft.Web.WebView2.Wpf.WebView2.DefaultBackgroundColorProperty">
<summary>
The WPF <see cref="T:System.Windows.DependencyProperty"/> which backs the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.DefaultBackgroundColor"/> property.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.Wpf.WebView2.DefaultBackgroundColor">
<summary>
The default background color for the WebView.
This property directly exposes <see cref="P:Microsoft.Web.WebView2.Core.CoreWebView2Controller.DefaultBackgroundColor"/>, see its documentation for more info.
Getting this property before the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Controller"/> has been initialized will retrieve the last value which was
set to it, or <c>Color.White</c> (the default) if none has been.
The most recent value set to this property before CoreWebView2Controller has been initialized will be set on it after initialization.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.DefaultBackgroundColorPropertyChanged(System.Windows.DependencyObject,System.Windows.DependencyPropertyChangedEventArgs)">
<summary>
This is a callback that WPF calls when our WPF DefaultBackgroundColor property's value changes.
Since CoreWebView2Controller does not update this property itself, this is only triggered by the
caller setting DefaultBackgroundColor.
</summary>
</member>
<member name="F:Microsoft.Web.WebView2.Wpf.WebView2.DesignModeForegroundColorProperty">
<summary>
The WPF <see cref="T:System.Windows.DependencyProperty"/> which backs the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.DesignModeForegroundColor"/> property.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.Wpf.WebView2.DesignModeForegroundColor">
<summary>
The foreground color to be used in design mode.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.GoBack">
<summary>
Navigates the WebView to the previous page in the navigation history.
Equivalent to calling <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.GoBack"/>
If <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/> hasn't been initialized yet then does nothing.
</summary>
<exception cref="T:System.InvalidOperationException">
Thrown if the calling thread isn't the thread which created this object (usually the UI thread). See <see cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/> for more info.
May also be thrown if the browser process has crashed unexpectedly and left the control in an invalid state. We are considering throwing a different type of exception for this case in the future.
</exception>
<exception cref="T:System.ObjectDisposedException">Thrown if <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.Dispose(System.Boolean)"/> has already been called on the control.</exception>
<seealso cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/>
<seealso cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.CanGoBack"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.GoForward">
<summary>
Navigates the WebView to the next page in the navigation history.
Equivalent to calling <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.GoForward"/>.
If <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/> hasn't been initialized yet then does nothing.
</summary>
<exception cref="T:System.InvalidOperationException">
Thrown if the calling thread isn't the thread which created this object (usually the UI thread). See <see cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/> for more info.
May also be thrown if the browser process has crashed unexpectedly and left the control in an invalid state. We are considering throwing a different type of exception for this case in the future.
</exception>
<exception cref="T:System.ObjectDisposedException">Thrown if <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.Dispose(System.Boolean)"/> has already been called on the control.</exception>
<seealso cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.GoForward"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.Reload">
<summary>
Reloads the current page.
Equivalent to calling <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Reload"/>.
</summary>
<exception cref="T:System.InvalidOperationException">
Thrown if <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> hasn't been initialized yet, or if the calling thread isn't the thread which created this object (usually the UI thread). See <see cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/> for more info.
May also be thrown if the browser process has crashed unexpectedly and left the control in an invalid state. We are considering throwing a different type of exception for this case in the future.
</exception>
<exception cref="T:System.ObjectDisposedException">Thrown if <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.Dispose(System.Boolean)"/> has already been called on the control.</exception>
<seealso cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Reload"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.Stop">
<summary>
Stops all navigations and pending resource fetches.
Equivalent to calling <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Stop"/>.
</summary>
<exception cref="T:System.InvalidOperationException">
Thrown if <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> hasn't been initialized yet, or if the calling thread isn't the thread which created this object (usually the UI thread). See <see cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/> for more info.
May also be thrown if the browser process has crashed unexpectedly and left the control in an invalid state. We are considering throwing a different type of exception for this case in the future.
</exception>
<exception cref="T:System.ObjectDisposedException">Thrown if <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.Dispose(System.Boolean)"/> has already been called on the control.</exception>
<seealso cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Stop"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.NavigateToString(System.String)">
<summary>
Initiates a navigation to htmlContent as source HTML of a new document.
Equivalent to calling <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.NavigateToString(System.String)"/>.
</summary>
<exception cref="T:System.InvalidOperationException">
Thrown if <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> hasn't been initialized yet, or if the calling thread isn't the thread which created this object (usually the UI thread). See <see cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/> for more info.
May also be thrown if the browser process has crashed unexpectedly and left the control in an invalid state. We are considering throwing a different type of exception for this case in the future.
</exception>
<exception cref="T:System.ObjectDisposedException">Thrown if <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.Dispose(System.Boolean)"/> has already been called on the control.</exception>
<remarks>The <c>htmlContent</c> parameter may not be larger than 2 MB (2 * 1024 * 1024 bytes) in total size. The origin of the new page is <c>about:blank</c>.</remarks>
<seealso cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.NavigateToString(System.String)"/>
</member>
<member name="E:Microsoft.Web.WebView2.Wpf.WebView2.ContentLoading">
<summary>
A wrapper around the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.ContentLoading"/>.
The only difference between this event and <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.ContentLoading"/> is the first parameter that's passed to handlers.
Handlers of this event will receive the <see cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/> control, whereas handlers of <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.ContentLoading"/> will receive the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/> instance.
</summary>
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.ContentLoading"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2_ContentLoading(System.Object,Microsoft.Web.WebView2.Core.CoreWebView2ContentLoadingEventArgs)">
<summary>
This is an event handler for our CoreWebView2's ContentLoading event.
We just need to propagate the event to WPF.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.ExecuteScriptAsync(System.String)">
<summary>
Executes JavaScript code from the javaScript parameter in the current top level document rendered in the WebView.
Equivalent to calling <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.ExecuteScriptAsync(System.String)"/>.
</summary>
<exception cref="T:System.InvalidOperationException">
Thrown if <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> hasn't been initialized yet, or if the calling thread isn't the thread which created this object (usually the UI thread). See <see cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/> for more info.
May also be thrown if the browser process has crashed unexpectedly and left the control in an invalid state. We are considering throwing a different type of exception for this case in the future.
</exception>
<exception cref="T:System.ObjectDisposedException">Thrown if <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.Dispose(System.Boolean)"/> has already been called on the control.</exception>
<seealso cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.ExecuteScriptAsync(System.String)"/>
</member>
<member name="E:Microsoft.Web.WebView2.Wpf.WebView2.WebMessageReceived">
<summary>
A wrapper around the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.WebMessageReceived"/>.
The only difference between this event and <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.WebMessageReceived"/> is the first parameter that's passed to handlers.
Handlers of this event will receive the <see cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/> control, whereas handlers of <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.WebMessageReceived"/> will receive the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/> instance.
</summary>
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.WebMessageReceived"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2_WebMessageReceived(System.Object,Microsoft.Web.WebView2.Core.CoreWebView2WebMessageReceivedEventArgs)">
<summary>
This is an event handler for our CoreWebView2's WebMessageReceived event.
We just need to propagate the event to WPF.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.Wpf.WebView2.IsInDesignMode">
<summary>
True when we're in design mode and shouldn't create an underlying CoreWebView2.
</summary>
</member>
</members>
</doc>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,375 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>Microsoft.Web.WebView2.WinForms</name>
</assembly>
<members>
<member name="T:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties">
<summary>
This class is a bundle of the most common parameters used to create a <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/>.
Its main purpose is to be set to <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CreationProperties"/> in order to customize the environment used by a <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/> during implicit initialization.
</summary>
<remarks>
This class isn't intended to contain all possible environment customization options.
If you need complete control over the environment used by a WebView2 control then you'll need to initialize the control explicitly by
creating your own environment with <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> and passing it to <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment)"/>
*before* you set the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property to anything.
See the <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/> class documentation for an initialization overview.
</remarks>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.#ctor">
<summary>
Creates a new instance of <see cref="T:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties"/> with default data for all properties.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.BrowserExecutableFolder">
<summary>
Gets or sets the value to pass as the browserExecutableFolder parameter of <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> when creating an environment with this instance.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.UserDataFolder">
<summary>
Gets or sets the value to pass as the userDataFolder parameter of <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> when creating an environment with this instance.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.Language">
<summary>
Gets or sets the value to use for the Language property of the CoreWebView2EnvironmentOptions parameter passed to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> when creating an environment with this instance.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.CreateEnvironmentAsync">
<summary>
Create a <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/> using the current values of this instance's properties.
</summary>
<returns>A task which will provide the created environment on completion.</returns>
<remarks>
As long as no other properties on this instance are changed, repeated calls to this method will return the same task/environment as earlier calls.
If some other property is changed then the next call to this method will return a different task/environment.
</remarks>
</member>
<member name="T:Microsoft.Web.WebView2.WinForms.WebView2">
<summary>
Control to embed WebView2 in WinForms.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.#ctor">
<summary>
Create a new WebView2 WinForms control.
After construction the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> property is <c>null</c>.
Call <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment)"/> to initialize the underlying <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/>.
</summary>
<remarks>
This control is effectively a wrapper around the WebView2 COM API, which you can find documentation for here: https://aka.ms/webview2
You can directly access the underlying ICoreWebView2 interface and all of its functionality by accessing the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> property.
Some of the most common COM functionality is also accessible directly through wrapper methods/properties/events on the control.
Upon creation, the control's CoreWebView2 property will be null.
This is because creating the CoreWebView2 is an expensive operation which involves things like launching Edge browser processes.
There are two ways to cause the CoreWebView2 to be created:
1) Call the <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment)"/> method. This is referred to as explicit initialization.
2) Set the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property. This is referred to as implicit initialization.
Either option will start initialization in the background and return back to the caller without waiting for it to finish.
To specify options regarding the initialization process, either pass your own <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/> to EnsureCoreWebView2Async or set the control's <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CreationProperties"/> property prior to initialization.
When initialization has finished (regardless of how it was triggered) then the following things will occur, in this order:
1) The control's <see cref="E:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2InitializationCompleted"/> event will be invoked. If you need to perform one time setup operations on the CoreWebView2 prior to its use then you should do so in a handler for that event.
2) If a Uri has been set to the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property then the control will start navigating to it in the background (i.e. these steps will continue without waiting for the navigation to finish).
3) The Task returned from <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment)"/> will complete.
For more details about any of the methods/properties/events involved in the initialization process, see its specific documentation.
Accelerator key presses (e.g. Ctrl+P) that occur within the control will
fire standard key press events such as OnKeyDown. You can suppress the
control's default implementation of an accelerator key press (e.g.
printing, in the case of Ctrl+P) by setting the Handled property of its
EventArgs to true. Also note that the underlying browser process is
blocked while these handlers execute, so:
<list type="number">
<item>
You should avoid doing a lot of work in these handlers.
</item>
<item>
Some of the WebView2 and CoreWebView2 APIs may throw errors if
invoked within these handlers due to being unable to communicate with
the browser process.
</item>
</list>
If you need to do a lot of work and/or invoke WebView2 APIs in response to
accelerator keys then consider kicking off a background task or queuing
the work for later execution on the UI thread.
</remarks>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.Dispose(System.Boolean)">
<summary>
Cleans up any resources being used.
</summary>
<param name="disposing"><c>true</c> if managed resources should be disposed; otherwise, <c>false</c>.</param>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.OnPaint(System.Windows.Forms.PaintEventArgs)">
<summary>
Overrides the base OnPaint event to have custom actions
in designer mode
</summary>
<param name="e">The graphics devices which is the source</param>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.WndProc(System.Windows.Forms.Message@)">
<summary>
Overrides the base WndProc events to handle specific window messages.
</summary>
<param name="m">The Message object containing the HWND window message and parameters</param>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.CreationProperties">
<summary>
Gets or sets a bag of options which are used during initialization of the control's <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/>.
This property cannot be modified (an exception will be thrown) after initialization of the control's CoreWebView2 has started.
</summary>
<exception cref="T:System.InvalidOperationException">Thrown if initialization of the control's CoreWebView2 has already started.</exception>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment)">
<summary>
Explicitly trigger initialization of the control's <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/>.
</summary>
<param name="environment">
A pre-created <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/> that should be used to create the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/>.
Creating your own environment gives you control over several options that affect how the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is initialized.
If you pass <c>null</c> (the default value) then a default environment will be created and used automatically.
</param>
<returns>
A Task that represents the background initialization process.
When the task completes then the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> property will be available for use (i.e. non-null).
Note that the control's <see cref="E:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2InitializationCompleted"/> event will be invoked before the task completes
or on exceptions.
</returns>
<remarks>
Calling this method additional times will have no effect (any specified environment is ignored) and return the same Task as the first call.
Calling this method after initialization has been implicitly triggered by setting the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property will have no effect (any specified environment is ignored) and simply return a Task representing that initialization already in progress.
</remarks>
<exception cref="T:System.InvalidOperationException">
Thrown if this instance of <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is already disposed, or if the calling thread isn't the thread which created this object (usually the UI thread). See <see cref="P:System.Windows.Forms.Control.InvokeRequired"/> for more info.
May also be thrown if the browser process has crashed unexpectedly and left the control in an invalid state. We are considering throwing a different type of exception for this case in the future.
</exception>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.InitCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment)">
<summary>
This is the private function which implements the actual background initialization task.
Cannot be called if the control is already initialized or has been disposed.
</summary>
<param name="environment">
The environment to use to create the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Controller"/>.
If that is null then a default environment is created with <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> and its default parameters.
</param>
<returns>A task representing the background initialization process.</returns>
<remarks>All the event handlers added here need to be removed in <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.Dispose(System.Boolean)"/>.</remarks>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.CreateParams">
<summary>
Protected CreateParams property. Used to set custom window styles to the forms HWND.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.OnVisibleChanged(System.EventArgs)">
<summary>
Protected VisibilityChanged handler.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.OnSizeChanged(System.EventArgs)">
<summary>
Protected SizeChanged handler.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.Select(System.Boolean,System.Boolean)">
<summary>
Protected Select method: override this to capture tab direction when WebView control is activated
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.OnGotFocus(System.EventArgs)">
<summary>
Protected OnGotFocus handler.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.IsInitialized">
<summary>
True if initialization finished successfully and the control is not disposed yet.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.GetSitedParentSite(System.Windows.Forms.Control)">
<summary>
Recursive retrieval of the parent control
</summary>
<param name="control">The control to get the parent for</param>
<returns>The root parent control</returns>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2">
<summary>
The underlying CoreWebView2. Use this property to perform more operations on the WebView2 content than is exposed
on the WebView2. This value is null until it is initialized and the object itself has undefined behaviour once the control is disposed.
You can force the underlying CoreWebView2 to
initialize via the <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment)"/> method.
</summary>
<exception cref="T:System.InvalidOperationException">Thrown if the calling thread isn't the thread which created this object (usually the UI thread). See <see cref="P:System.Windows.Forms.Control.InvokeRequired"/> for more info.</exception>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.ZoomFactor">
<summary>
The zoom factor for the WebView.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.Source">
<summary>
The Source property is the URI of the top level document of the
WebView2. Setting the Source is equivalent to calling <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Navigate(System.String)"/>.
Setting the Source will trigger initialization of the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/>, if not already initialized.
The default value of Source is <c>null</c>, indicating that the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized.
</summary>
<exception cref="T:System.ArgumentException">Specified value is not an absolute <see cref="T:System.Uri"/>.</exception>
<exception cref="T:System.NotImplementedException">Specified value is <c>null</c> and the control is initialized.</exception>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Navigate(System.String)"/>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.CanGoForward">
<summary>
Returns true if the webview can navigate to a next page in the
navigation history via the <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.GoForward"/> method.
This is equivalent to the <see cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.CanGoForward"/>.
If the underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized, this property is <c>false</c>.
</summary>
<seealso cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.CanGoForward"/>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.CanGoBack">
<summary>
Returns <c>true</c> if the webview can navigate to a previous page in the
navigation history via the <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.GoBack"/> method.
This is equivalent to the <see cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.CanGoBack"/>.
If the underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized, this property is <c>false</c>.
</summary>
<seealso cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.CanGoBack"/>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.DefaultBackgroundColor">
<summary>
The default background color for the WebView.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.ExecuteScriptAsync(System.String)">
<summary>
Executes the provided script in the top level document of the <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/>.
This is equivalent to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.ExecuteScriptAsync(System.String)"/>.
</summary>
<exception cref="T:System.InvalidOperationException">The underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized.</exception>
<exception cref="T:System.InvalidOperationException">Thrown when browser process has unexpectedly and left this control in an invalid state. We are considering throwing a different type of exception for this case in the future.</exception>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.ExecuteScriptAsync(System.String)"/>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.Reload">
<summary>
Reloads the top level document of the <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/>.
This is equivalent to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Reload"/>.
</summary>
<exception cref="T:System.InvalidOperationException">The underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized.</exception>
<exception cref="T:System.InvalidOperationException">Thrown when browser process has unexpectedly and left this control in an invalid state. We are considering throwing a different type of exception for this case in the future.</exception>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Reload"/>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.GoForward">
<summary>
Navigates to the next page in navigation history.
This is equivalent to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.GoForward"/>.
If the underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized, this method does nothing.
</summary>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.GoForward"/>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.GoBack">
<summary>
Navigates to the previous page in navigation history.
This is equivalent to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.GoBack"/>.
If the underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized, this method does nothing.
</summary>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.GoBack"/>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.NavigateToString(System.String)">
<summary>
Renders the provided HTML as the top level document of the <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/>.
This is equivalent to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.NavigateToString(System.String)"/>.
</summary>
<exception cref="T:System.InvalidOperationException">The underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized.</exception>
<exception cref="T:System.InvalidOperationException">Thrown when browser process has unexpectedly and left this control in an invalid state. We are considering throwing a different type of exception for this case in the future.</exception>
<remarks>The <c>htmlContent</c> parameter may not be larger than 2 MB (2 * 1024 * 1024 bytes) in total size. The origin of the new page is <c>about:blank</c>.</remarks>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.NavigateToString(System.String)"/>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.Stop">
<summary>
Stops any in progress navigation in the <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/>.
This is equivalent to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Stop"/>.
If the underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized, this method does nothing.
</summary>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Stop"/>
</member>
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2InitializationCompleted">
<summary>
This event is triggered either 1) when the control's <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> has finished being initialized (regardless of how it was triggered or whether it succeeded) but before it is used for anything
OR 2) the initialization failed.
You should handle this event if you need to perform one time setup operations on the CoreWebView2 which you want to affect all of its usages
(e.g. adding event handlers, configuring settings, installing document creation scripts, adding host objects).
</summary>
<remarks>
This sender will be the WebView2 control, whose CoreWebView2 property will now be valid (i.e. non-null) for the first time
if <see cref="P:Microsoft.Web.WebView2.Core.CoreWebView2InitializationCompletedEventArgs.IsSuccess"/> is true.
Unlikely this event can fire second time (after reporting initialization success first)
if the initialization is followed by navigation which fails.
</remarks>
</member>
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.NavigationStarting">
<summary>
NavigationStarting dispatches before a new navigate starts for the top
level document of the <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/>.
This is equivalent to the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationStarting"/> event.
</summary>
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationStarting"/>
</member>
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.NavigationCompleted">
<summary>
NavigationCompleted dispatches after a navigate of the top level
document completes rendering either successfully or not.
This is equivalent to the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationCompleted"/> event.
</summary>
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationCompleted"/>
</member>
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.WebMessageReceived">
<summary>
WebMessageReceived dispatches after web content sends a message to the
app host via <c>chrome.webview.postMessage</c>.
This is equivalent to the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.WebMessageReceived"/> event.
</summary>
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.WebMessageReceived"/>
</member>
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.SourceChanged">
<summary>
SourceChanged dispatches after the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property changes. This may happen
during a navigation or if otherwise the script in the page changes the
URI of the document.
This is equivalent to the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.SourceChanged"/> event.
</summary>
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.SourceChanged"/>
</member>
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.ContentLoading">
<summary>
ContentLoading dispatches after a navigation begins to a new URI and the
content of that URI begins to render.
This is equivalent to the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.ContentLoading"/> event.
</summary>
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.ContentLoading"/>
</member>
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.ZoomFactorChanged">
<summary>
ZoomFactorChanged dispatches when the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.ZoomFactor"/> property changes.
This is equivalent to the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2Controller.ZoomFactorChanged"/> event.
</summary>
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2Controller.ZoomFactorChanged"/>
</member>
<member name="F:Microsoft.Web.WebView2.WinForms.WebView2.components">
<summary>
Required designer variable.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.InitializeComponent">
<summary>
Required method for Designer support - do not modify
the contents of this method with the code editor.
</summary>
</member>
</members>
</doc>

View File

@ -0,0 +1,800 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>Microsoft.Web.WebView2.Wpf</name>
</assembly>
<members>
<member name="T:Microsoft.Web.WebView2.Wpf.CoreWebView2CreationProperties">
<summary>
This class is a bundle of the most common parameters used to create a <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/>.
Its main purpose is to be set to <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CreationProperties"/> in order to customize the environment used by a <see cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/> during implicit initialization.
It is also a nice WPF integration utility which allows commonly used environment parameters to be dependency properties and be created and used in markup.
</summary>
<remarks>
This class isn't intended to contain all possible environment customization options.
If you need complete control over the environment used by a WebView2 control then you'll need to initialize the control explicitly by
creating your own environment with <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> and passing it to <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment)"/>
*before* you set the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.Source"/> property to anything.
See the <see cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/> class documentation for an initialization overview.
</remarks>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.CoreWebView2CreationProperties.#ctor">
<summary>
Creates a new instance of <see cref="T:Microsoft.Web.WebView2.Wpf.CoreWebView2CreationProperties"/> with default data for all properties.
</summary>
</member>
<member name="F:Microsoft.Web.WebView2.Wpf.CoreWebView2CreationProperties.BrowserExecutableFolderProperty">
<summary>
The WPF DependencyProperty which backs the <see cref="P:Microsoft.Web.WebView2.Wpf.CoreWebView2CreationProperties.BrowserExecutableFolder"/> property.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.Wpf.CoreWebView2CreationProperties.BrowserExecutableFolder">
<summary>
Gets or sets the value to pass as the browserExecutableFolder parameter of <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> when creating an environment with this instance.
</summary>
</member>
<member name="F:Microsoft.Web.WebView2.Wpf.CoreWebView2CreationProperties.UserDataFolderProperty">
<summary>
The WPF DependencyProperty which backs the <see cref="P:Microsoft.Web.WebView2.Wpf.CoreWebView2CreationProperties.UserDataFolder"/> property.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.Wpf.CoreWebView2CreationProperties.UserDataFolder">
<summary>
Gets or sets the value to pass as the userDataFolder parameter of <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> when creating an environment with this instance.
</summary>
</member>
<member name="F:Microsoft.Web.WebView2.Wpf.CoreWebView2CreationProperties.LanguageProperty">
<summary>
The WPF DependencyProperty which backs the <see cref="P:Microsoft.Web.WebView2.Wpf.CoreWebView2CreationProperties.Language"/> property.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.Wpf.CoreWebView2CreationProperties.Language">
<summary>
Gets or sets the value to use for the Language property of the CoreWebView2EnvironmentOptions parameter passed to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> when creating an environment with this instance.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.CoreWebView2CreationProperties.CreateEnvironmentAsync">
<summary>
Create a <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/> using the current values of this instance's properties.
</summary>
<returns>A task which will provide the created environment on completion.</returns>
<remarks>
As long as no other properties on this instance are changed, repeated calls to this method will return the same task/environment as earlier calls.
If some other property is changed then the next call to this method will return a different task/environment.
</remarks>
</member>
<member name="T:Microsoft.Web.WebView2.Wpf.WebView2">
<summary>
A control to embed web content in a WPF application.
</summary>
<remarks>
This control is effectively a wrapper around the [WebView2 COM
API](https://aka.ms/webview2). You can directly access the underlying
ICoreWebView2 interface and all of its functionality by accessing the
<see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> property. Some of the most common COM
functionality is also accessible directly through wrapper
methods/properties/events on the control.
Upon creation, the control's <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> property will be
<c>null</c>. This is because creating the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> is an
expensive operation which involves things like launching Edge browser
processes. There are two ways to cause the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> to
be created:
<list type="bullet">
<item><description>
Call the <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment)"/> method. This is
referred to as explicit initialization.
</description></item>
<item><description>
Set the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.Source"/> property (which could be done from
markup, for example). This is referred to as implicit initialization.
Either option will start initialization in the background and return
back to the caller without waiting for it to finish.
To specify options regarding the initialization process, either pass
your own <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/> to <see
cref="M:Microsoft.Web.WebView2.Wpf.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment)"/> or set the control's <see
cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CreationProperties"/> property prior to initialization.
</description></item>
</list>
When initialization has finished (regardless of how it was triggered or
whether it succeeded) then the following things will occur, in this
order:
<list type="number">
<item><description>
The control's <see cref="E:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2InitializationCompleted"/> event
will be invoked. If you need to perform one time setup operations on
the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> prior to its use then you should
do so in a handler for that event.
</description></item>
<item><description>
If initialization was successful and a Uri has been set to the <see
cref="P:Microsoft.Web.WebView2.Wpf.WebView2.Source"/> property then the control will start navigating to it in
the background (i.e. these steps will continue without waiting for the
navigation to finish).
</description></item>
<item><description>
The Task returned from <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment)"/> will
complete.
</description></item>
</list>
For more details about any of the methods/properties/events involved in
the initialization process, see its specific documentation.
Because the control's <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> is a very heavyweight
object (potentially responsible for multiple running processes and
megabytes of disk space) the control implements <see
cref="T:System.IDisposable"/> to provide an explicit means to free it.
Calling <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.Dispose(System.Boolean)"/> will release the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/>
and its underlying resources (except any that are also being used by other
WebViews), and reset <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> to <c>null</c>. After <see
cref="M:Microsoft.Web.WebView2.Wpf.WebView2.Dispose(System.Boolean)"/> has been called the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> cannot be
re-initialized, and any attempt to use functionality which requires it
will throw an <see cref="T:System.ObjectDisposedException"/>.
Accelerator key presses (e.g. Ctrl+P) that occur within the control will
fire standard key press events such as OnKeyDown. You can suppress the
control's default implementation of an accelerator key press (e.g.
printing, in the case of Ctrl+P) by setting the Handled property of its
EventArgs to true. Also note that the underlying browser process is
blocked while these handlers execute, so:
<list type="number">
<item>
<description>You should avoid doing a lot of work in these handlers.</description>
</item>
<item><description>
Some of the WebView2 and CoreWebView2 APIs may throw errors if
invoked within these handlers due to being unable to communicate with
the browser process.
</description></item>
</list>
If you need to do a lot of work and/or invoke WebView2 APIs in response to
accelerator keys then consider kicking off a background task or queuing
the work for later execution on the UI thread.
Note that this control extends <see cref="T:System.Windows.Interop.HwndHost"/> in order to embed
windows which live outside of the WPF ecosystem. This has some
implications regarding the control's input and output behavior as well as
the functionality it "inherits" from <see cref="T:System.Windows.UIElement"/> and <see
cref="T:System.Windows.FrameworkElement"/>.
See the <see cref="T:System.Windows.Interop.HwndHost"/> and [WPF/Win32
interop](https://docs.microsoft.com/dotnet/framework/wpf/advanced/wpf-and-win32-interoperation#hwnds-inside-wpf)
documentation for more information.
</remarks>
<seealso cref="T:System.Windows.Interop.HwndHost"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.#ctor">
<summary>
Creates a new instance of a WebView2 control.
Note that the control's <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> will be null until initialized.
See the <see cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/> class documentation for an initialization overview.
</summary>
</member>
<member name="F:Microsoft.Web.WebView2.Wpf.WebView2.CreationPropertiesProperty">
<summary>
The WPF <see cref="T:System.Windows.DependencyProperty"/> which backs the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CreationProperties"/> property.
</summary>
<seealso cref="T:System.Windows.DependencyProperty"/>
</member>
<member name="P:Microsoft.Web.WebView2.Wpf.WebView2.CreationProperties">
<summary>
Gets or sets a bag of options which are used during initialization of the control's <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/>.
Setting this property will not work after initialization of the control's <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> has started (the old value will be retained).
See the <see cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/> class documentation for an initialization overview.
</summary>
<seealso cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.BuildWindowCore(System.Runtime.InteropServices.HandleRef)">
<summary>
This is overridden from <see cref="T:System.Windows.Interop.HwndHost"/> and is called to instruct us to create our HWND.
</summary>
<param name="hwndParent">The HWND that we should use as the parent of the one we create.</param>
<returns>The HWND that we created.</returns>
<seealso cref="M:System.Windows.Interop.HwndHost.BuildWindowCore(System.Runtime.InteropServices.HandleRef)"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.DestroyWindowCore(System.Runtime.InteropServices.HandleRef)">
<summary>
This is overridden from <see cref="T:System.Windows.Interop.HwndHost"/> and is called to instruct us to destroy our HWND.
</summary>
<param name="hwnd">Our HWND that we need to destroy.</param>
<seealso cref="M:System.Windows.Interop.HwndHost.DestroyWindowCore(System.Runtime.InteropServices.HandleRef)"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.WndProc(System.IntPtr,System.Int32,System.IntPtr,System.IntPtr,System.Boolean@)">
<summary>
This is overridden from <see cref="T:System.Windows.Interop.HwndHost"/> and is called to provide us with Win32 messages that are sent to our hwnd.
</summary>
<param name="hwnd">Window receiving the message (should always match our <see cref="P:System.Windows.Interop.HwndHost.Handle"/>).</param>
<param name="msg">Indicates the message being received. See Win32 documentation for WM_* constant values.</param>
<param name="wParam">The "wParam" data being provided with the message. Meaning varies by message.</param>
<param name="lParam">The "lParam" data being provided with the message. Meaning varies by message.</param>
<param name="handled">If true then the message will not be forwarded to any (more) <see cref="E:System.Windows.Interop.HwndHost.MessageHook"/> handlers.</param>
<returns>Return value varies by message.</returns>
<seealso cref="M:System.Windows.Interop.HwndHost.WndProc(System.IntPtr,System.Int32,System.IntPtr,System.IntPtr,System.Boolean@)"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.OnRender(System.Windows.Media.DrawingContext)">
<summary>
Override for painting to draw
</summary>
<param name="dc">The tools to handle the drawing via <see cref="T:System.Windows.Media.DrawingContext"/>.</param>
</member>
<member name="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2">
<summary>
Accesses the complete functionality of the underlying <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/> COM API.
Returns <c>null</c> until initialization has completed.
See the <see cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/> class documentation for an initialization overview.
</summary>
<exception cref="T:System.InvalidOperationException">
Thrown if the calling thread isn't the thread which created this object (usually the UI thread). See <see cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/> for more info.
May also be thrown if the browser process has crashed unexpectedly and left the control in an invalid state. We are considering throwing a different type of exception for this case in the future.
</exception>
<exception cref="T:System.ObjectDisposedException">Thrown if <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.Dispose(System.Boolean)"/> has already been called on the control.</exception>
<seealso cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/>
<seealso cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/>
</member>
<member name="E:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2InitializationCompleted">
<summary>
This event is triggered either
1) when the control's <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> has finished being initialized (regardless of how initialization was triggered) but before it is used for anything, or
2) if the initialization failed.
You should handle this event if you need to perform one time setup operations on the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> which you want to affect all of its usages.
(e.g. adding event handlers, configuring settings, installing document creation scripts, adding host objects).
See the <see cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/> class documentation for an initialization overview.
</summary>
<remarks>
This sender will be the <see cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/> control, whose <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> property will now be valid (i.e. non-null) for the first time
if <see cref="P:Microsoft.Web.WebView2.Core.CoreWebView2InitializationCompletedEventArgs.IsSuccess"/> is <c>true</c>.
Unlikely this event can fire second time (after reporting initialization success first)
if the initialization is followed by navigation which fails.
</remarks>
<seealso cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment)">
<summary>
Explicitly triggers initialization of the control's <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/>.
See the <see cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/> class documentation for an initialization overview.
</summary>
<param name="environment">
A pre-created <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/> that should be used to create the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/>.
Creating your own environment gives you control over several options that affect how the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/> is initialized.
If you pass an environment to this method then it will override any settings specified on the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CreationProperties"/> property.
If you pass <c>null</c> (the default value) and no value has been set to <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CreationProperties"/> then a default environment will be created and used automatically.
</param>
<returns>
A Task that represents the background initialization process.
When the task completes then the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> property will be available for use (i.e. non-null).
Note that the control's <see cref="E:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2InitializationCompleted"/> event will be invoked before the task completes.
</returns>
<remarks>
Calling this method additional times will have no effect (any specified environment is ignored) and return the same Task as the first call.
Calling this method after initialization has been implicitly triggered by setting the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.Source"/> property will have no effect (any specified environment is ignored) and simply return a Task representing that initialization already in progress.
Note that even though this method is asynchronous and returns a Task, it still must be called on the UI thread like most public functionality of most UI controls.
</remarks>
<exception cref="T:System.InvalidOperationException">
Thrown if the calling thread isn't the thread which created this object (usually the UI thread). See <see cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/> for more info.
May also be thrown if the browser process has crashed unexpectedly and left the control in an invalid state. We are considering throwing a different type of exception for this case in the future.
</exception>
<exception cref="T:System.ObjectDisposedException">Thrown if <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.Dispose(System.Boolean)"/> has already been called on the control.</exception>
<seealso cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/>
<seealso cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.Dispose(System.Boolean)">
<summary>
This is called by our base class according to the typical implementation of the <see cref="T:System.IDisposable"/> pattern.
We implement it by releasing all of our underlying COM resources, including our <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/>.
</summary>
<param name="disposing">True if a caller is explicitly calling Dispose, false if we're being finalized.</param>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2_ProcessFailed(System.Object,Microsoft.Web.WebView2.Core.CoreWebView2ProcessFailedEventArgs)">
<summary>
This is an event handler for our CoreWebView2's ProcessFailedEvent
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.BeginInit">
<summary>
Implementation of the ISupportInitialize pattern.
Prevents the control from implicitly initializing its <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> until <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.EndInit"/> is called.
Does *not* prevent explicit initialization of the CoreWebView2 (i.e. <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment)"/>).
Mainly intended for use by interactive UI designers.
</summary>
<remarks>
Note that the "Initialize" in ISupportInitialize and the "Init" in BeginInit/EndInit mean
something different and more general than this control's specific concept of initializing
its CoreWebView2 (explicitly or implicitly). This ISupportInitialize pattern is a general
way to set batches of properties on the control to their initial values without triggering
any dependent side effects until all of the values are set (i.e. until EndInit is called).
In the case of this control, a specific side effect to be avoided is triggering implicit
initialization of the CoreWebView2 when setting the Source property.
For example, normally if you set <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CreationProperties"/> after you've already set Source,
the data set to CreationProperties is ignored because implicit initialization has already started.
However, if you set the two properties (in the same order) in between calls to BeginInit and
EndInit then the implicit initialization of the CoreWebView2 is delayed until EndInit, so the data
set to CreationProperties is still used even though it was set after Source.
</remarks>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.EndInit">
<summary>
Implementation of the ISupportInitialize pattern.
Invokes any functionality that has been delayed since the corresponding call to <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.BeginInit"/>.
Mainly intended for use by interactive UI designers.
</summary>
<remarks>
See the documentation of <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.BeginInit"/> for more information.
</remarks>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.WhenInit_Source(System.Action)">
<summary>
Specifies a Source-related action to be invoked but which must be delayed if BeginInit has already been called but EndInit hasn't.
If the control is currently between BeginInit/EndInit calls then the action will be invoked during EndInit.
Otherwise the action will be invoked immediately.
If this is called multiple times between BeginInit/EndInit then the action passed to the last call is the only one that will be invoked during EndInit.
</summary>
<param name="action">The Source-related action to invoke, delayed until EndInit if necessary.</param>
<remarks>
In the future if we need this delayed-invoke functionality for actions unrelated to Source,
then we should generalize _initAction_Source to a list and generalize this method to take a list index along with the action.
Then we can just assign each index to a different potential action (e.g. 0 for Source), and potentially use constants to name the indices.
For now it didn't seem worth dynamically allocating a list for one thing,
but I still wanted to keep the delay-or-not logic abstracted away from the Source setter.
</remarks>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.SetCurrentValueFromCore(System.Windows.DependencyProperty,System.Object)">
<summary>
Updates one of our dependency properties to match a new value from the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/>.
It both sets the value and remembers (in _propertyChangingFromCore) that it came from the CoreWebView2 rather than the caller,
allowing the property's "on changed" handler to alter its behavior based on where the new value came from.
It's only intended to be called in a CoreWebView2 event handler that's informing us of a new property value.
It's basically just a wrapper around the inherited SetCurrentValue which also maintains _propertyChangingFromCore.
See the comments on <see cref="F:Microsoft.Web.WebView2.Wpf.WebView2._propertyChangingFromCore"/> for additional background info.
One more thing worth explicitly stating is that it wraps SetCurrentValue rather than SetValue,
in order to avoid overwriting any OneWay bindings that are set on the specified properties.
Check the link https://stackoverflow.com/q/4230698 for more information about the difference between SetValue and SetCurrentValue.
</summary>
<param name="property">The property to change due to an equivalent change in the CoreWebView2.</param>
<param name="value">The new value from the CoreWebView2.</param>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.IsPropertyChangingFromCore(System.Windows.DependencyProperty)">
<summary>
Checks if a given property is currently being updated to match an equivalent change in the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/>.
This method should only be called from a property's "on changed" handler; it has no meaning at any other time.
It is used to determine if the property is changing to match the CoreWebView2 or because the caller set it.
Usually this is used in order to decide if the new value needs to be propagated down to the CoreWebView2.
See the comments on <see cref="F:Microsoft.Web.WebView2.Wpf.WebView2._propertyChangingFromCore"/> for additional background info.
</summary>
<param name="property">The property to check.</param>
<returns>True if the property is changing to match the CoreWebView2, or false if the property was changed by the caller.</returns>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.ReparentController(System.IntPtr,System.Boolean)">
<summary>
Changes our controller's ParentWindow to the given HWND, along with any other necessary associated work.
</summary>
<param name="hwnd">The new HWND to set as the controller's parent. IntPtr.Zero means that the controller will have no parent and the CoreWebView2 will be hidden.</param>
<param name="sync">Whether or not to call <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.SyncControllerWithParentWindow"/> as required. Defaults to true. If you pass false then you should call it yourself if required.</param>
<remarks>
Reparenting the controller isn't necessarily as simple as changing its ParentWindow property,
and this method exists to ensure that any other work that needs to be done at the same time gets done.
The reason that SyncControllerWithParentWindow isn't baked directly into this method is because
sometimes we want to call the Sync functionality without necessarily reparenting (e.g. during initialization).
</remarks>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.SyncControllerWithParentWindow">
<summary>
Syncs visual/windowing information between the controller and its parent HWND.
This should be called any time a new, non-null HWND is set as the controller's parent,
including when the controller is first created.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.UIElement_IsVisibleChanged(System.Object,System.Windows.DependencyPropertyChangedEventArgs)">
<summary>
This is a handler for our base UIElement's IsVisibleChanged event.
It's predictably fired whenever IsVisible changes, and IsVisible reflects the actual current visibility status of the control.
We just need to pass this info through to our CoreWebView2Controller so it can save some effort when the control isn't visible.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.OnWindowPositionChanged(System.Windows.Rect)">
<summary>
This is overridden from <see cref="T:System.Windows.Interop.HwndHost"/> and called when our control's location has changed.
The HwndHost takes care of updating the HWND we created.
What we need to do is move our CoreWebView2 to match the new location.
</summary>
</member>
<member name="F:Microsoft.Web.WebView2.Wpf.WebView2.SourceProperty">
<summary>
The WPF <see cref="T:System.Windows.DependencyProperty"/> which backs the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.Source"/> property.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.Wpf.WebView2.Source">
<summary>
The top-level <see cref="T:System.Uri"/> which the WebView is currently displaying (or will display once initialization of its <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> is finished).
Generally speaking, getting this property is equivalent to getting the <see cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.Source"/> property and setting this property (to a different value) is equivalent to calling the <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Navigate(System.String)"/> method.
</summary>
<remarks>
Getting this property before the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/> has been initialized will retrieve the last Uri which was set to it, or null (the default) if none has been.
Setting this property before the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/> has been initialized will cause initialization to start in the background (if not already in progress), after which the <see cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/> will navigate to the specified <see cref="T:System.Uri"/>.
This property can never be set back to null or to a relative <see cref="T:System.Uri"/>.
See the <see cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/> class documentation for an initialization overview.
</remarks>
<exception cref="T:System.ObjectDisposedException">Thrown if <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.Dispose(System.Boolean)"/> has already been called on the control.</exception>
<exception cref="T:System.NotImplementedException">Thrown if the property is set to <c>null</c>.</exception>
<exception cref="T:System.ArgumentException">Thrown if the property is set to a relative <see cref="T:System.Uri"/> (i.e. a <see cref="T:System.Uri"/> whose <see cref="P:System.Uri.IsAbsoluteUri"/> property is <c>false</c>).</exception>
<seealso cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.SourcePropertyValid(System.Object)">
<summary>
This is a callback that WPF calls to validate a potential new Source value.
</summary>
<returns>
True if the value is valid, false if it is not.
If we return false then WPF should respond by throwing an <see cref="T:System.ArgumentException"/>.
</returns>
<remarks>
Note that we unfortunately can't treat null as invalid here because null is valid prior to initialization.
</remarks>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.SourcePropertyChanged(System.Windows.DependencyObject,System.Windows.DependencyPropertyChangedEventArgs)">
<summary>
This is a callback that WPF calls when the WPF Source property's value changes.
This might have been triggered by either:
1) The caller set Source to programmatically trigger a navigation.
2) The CoreWebView changed its own source and we're just updating the dependency property to match.
We use <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.IsPropertyChangingFromCore(System.Windows.DependencyProperty)"/> to distinguish the two cases.
</summary>
</member>
<member name="E:Microsoft.Web.WebView2.Wpf.WebView2.SourceChanged">
<summary>
A wrapper around the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.SourceChanged"/>.
The only difference between this event and <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.SourceChanged"/> is the first parameter that's passed to handlers.
Handlers of this event will receive the <see cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/> control, whereas handlers of <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.SourceChanged"/> will receive the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/> instance.
</summary>
<see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.SourceChanged"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2_SourceChanged(System.Object,Microsoft.Web.WebView2.Core.CoreWebView2SourceChangedEventArgs)">
<summary>
This is an event handler for our CoreWebView2's SourceChanged event.
Unsurprisingly, it fires when the CoreWebView2's source URI has been changed.
Note that there are two distinct triggers for this:
1) The CoreWebView2 was told to navigate programmatically (potentially by us, see SourcePropertyChanged).
2) The user interacted with the CoreWebView2, e.g. clicked a link.
In either of the above cases, this event might trigger several times due to e.g. redirection.
Aside from propagating to our own event, we just need to update our WPF Source property to match the CoreWebView2's.
</summary>
</member>
<member name="E:Microsoft.Web.WebView2.Wpf.WebView2.NavigationStarting">
<summary>
A wrapper around the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationStarting"/>.
The only difference between this event and <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationStarting"/> is the first parameter that's passed to handlers.
Handlers of this event will receive the <see cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/> control, whereas handlers of <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationStarting"/> will receive the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/> instance.
</summary>
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationStarting"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2_NavigationStarting(System.Object,Microsoft.Web.WebView2.Core.CoreWebView2NavigationStartingEventArgs)">
<summary>
This is an event handler for our CoreWebView2's NavigationStarting event.
We just need to propagate the event to WPF.
</summary>
</member>
<member name="E:Microsoft.Web.WebView2.Wpf.WebView2.NavigationCompleted">
<summary>
A wrapper around the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationCompleted"/>.
The only difference between this event and <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationCompleted"/> is the first parameter that's passed to handlers.
Handlers of this event will receive the <see cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/> control, whereas handlers of <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationCompleted"/> will receive the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/> instance.
</summary>
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationCompleted"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2_NavigationCompleted(System.Object,Microsoft.Web.WebView2.Core.CoreWebView2NavigationCompletedEventArgs)">
<summary>
This is an event handler for our CoreWebView2's NavigationCompleted event.
We just need to propagate the event to WPF.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2_HistoryChanged(System.Object,System.Object)">
<summary>
This is an event handler for our CoreWebView2's HistoryChanged event.
We're handling it in order to update our WPF CanGoBack and CanGoForward properties.
</summary>
</member>
<member name="F:Microsoft.Web.WebView2.Wpf.WebView2.CanGoBackProperty">
<summary>
The WPF <see cref="T:System.Windows.DependencyProperty"/> which backs the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CanGoBack"/> property.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.Wpf.WebView2.CanGoBack">
<summary>
Returns <c>true</c> if the WebView can navigate to a previous page in the navigation history.
Wrapper around the <see cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.CanGoBack"/> property of <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/>.
If <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> isn't initialized yet then returns <c>false</c>.
</summary>
<seealso cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.CanGoBack"/>
</member>
<member name="F:Microsoft.Web.WebView2.Wpf.WebView2.CanGoForwardProperty">
<summary>
The WPF <see cref="T:System.Windows.DependencyProperty"/> which backs the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CanGoForward"/> property.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.Wpf.WebView2.CanGoForward">
<summary>
Returns <c>true</c> if the WebView can navigate to a next page in the navigation history.
Wrapper around the <see cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.CanGoForward"/> property of <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/>.
If <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> isn't initialized yet then returns <c>false</c>.
</summary>
<seealso cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.CanGoForward"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.TabIntoCore(System.Windows.Input.TraversalRequest)">
<summary>
This is overridden from <see cref="T:System.Windows.Interop.HwndHost"/> and is called to inform us that tabbing has caused the focus to move into our control/window.
Since WPF can't manage the transition of focus to a non-WPF HWND, it delegates the transition to us here.
So our job is just to place the focus in our external HWND.
</summary>
<param name="request">Information about how the focus is moving.</param>
<returns><c>true</c> to indicate that we handled the navigation, or <c>false</c> to indicate that we didn't.</returns>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.OnGotKeyboardFocus(System.Windows.Input.KeyboardFocusChangedEventArgs)">
<summary>
This is overridden from <see cref="T:System.Windows.UIElement"/> and is called to inform us when we receive the keyboard focus.
We handle this by passing the keyboard focus on to the underlying <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/>.
We never want to land in a state where our window (this.Handle) actually has the keyboard focus.
</summary>
<param name="e">Arguments from the underlying GotKeyboardFocus event.</param>
<remarks>
Note that it's actually possible for us to receive keyboard focus without this method being called.
One known case where that happens is when our parent window is deactivated while we have focus, then reactivated.
We handle that case in <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.WndProc(System.IntPtr,System.Int32,System.IntPtr,System.IntPtr,System.Boolean@)"/>.
</remarks>
<seealso cref="M:Microsoft.Web.WebView2.Wpf.WebView2.WndProc(System.IntPtr,System.Int32,System.IntPtr,System.IntPtr,System.Boolean@)"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2Controller_MoveFocusRequested(System.Object,Microsoft.Web.WebView2.Core.CoreWebView2MoveFocusRequestedEventArgs)">
<summary>
This is an event handler for our CoreWebView2Controller's MoveFocusRequested event.
It fires when the CoreWebView2Controller has focus but wants to move it elsewhere in the app.
E.g. this happens when the user tabs past the last item in the CoreWebView2 and focus needs to return to some other app control.
So our job is just to tell WPF to move the focus on to the next control.
Note that we don't propagate this event outward as a standard WPF routed event because we've implemented its purpose here.
If users of the control want to track focus shifting in/out of the control, they should use standard WPF events.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2Controller_GotFocus(System.Object,System.Object)">
<summary>
This is an event handler for our CoreWebView2Controller's GotFocus event.
We just need to propagate the event to WPF.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2Controller_LostFocus(System.Object,System.Object)">
<summary>
This is an event handler for our CoreWebView2Controller's LostFocus event.
We just need to propagate the event to WPF.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2Controller_AcceleratorKeyPressed(System.Object,Microsoft.Web.WebView2.Core.CoreWebView2AcceleratorKeyPressedEventArgs)">
<summary>
This is an event handler for our CoreWebView2Controller's AcceleratorKeyPressed event.
This is called to inform us about key presses that are likely to have special behavior (e.g. esc, return, Function keys, letters with modifier keys).
WPF can't detect this input because Windows sends it directly to the Win32 CoreWebView2Controller control.
We implement this by generating standard WPF key input events, allowing callers to handle the input in the usual WPF way if they want.
If nobody handles the WPF key events then we'll allow the default CoreWebView2Controller logic (if any) to handle it.
Of the possible options, this implementation should provide the most flexibility to callers.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.OnKeyDown(System.Windows.Input.KeyEventArgs)">
<summary>
This is overridden from <see cref="T:System.Windows.UIElement"/> and called to allow us to handle key press input.
WPF should never actually call this in response to keyboard events because we're hosting a non-WPF window.
When our window has focus Windows will send the input directly to it rather than to WPF's top-level window and input system.
This override should only be called when we're explicitly forwarding accelerator key input from the CoreWebView2 to WPF (in CoreWebView2Controller_AcceleratorKeyPressed).
Even then, this KeyDownEvent is only triggered because our PreviewKeyDownEvent implementation explicitly triggers it, matching WPF's usual system.
So the process is:
<list type="number">
<item><description>CoreWebView2Controller_AcceleratorKeyPressed</description></item>
<item><description>PreviewKeyDownEvent</description></item>
<item><description>KeyDownEvent</description></item>
<item><description>OnKeyDown</description></item>
</list>
.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.OnKeyUp(System.Windows.Input.KeyEventArgs)">
<summary>
See <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.OnKeyDown(System.Windows.Input.KeyEventArgs)"/>.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.OnPreviewKeyDown(System.Windows.Input.KeyEventArgs)">
<summary>
This is the "Preview" (i.e. tunneling) version of <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.OnKeyDown(System.Windows.Input.KeyEventArgs)"/>, so it actually happens first.
Like OnKeyDown, this will only ever be called if we're explicitly forwarding key presses from the CoreWebView2.
In order to mimic WPF's standard input handling, when we receive this we turn around and fire off the standard bubbling KeyDownEvent.
That way others in the WPF tree see the same standard pair of input events that WPF itself would have triggered if it were handling the key press.
</summary>
<seealso cref="M:Microsoft.Web.WebView2.Wpf.WebView2.OnKeyDown(System.Windows.Input.KeyEventArgs)"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.OnPreviewKeyUp(System.Windows.Input.KeyEventArgs)">
<summary>
See <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.OnPreviewKeyDown(System.Windows.Input.KeyEventArgs)"/>.
</summary>
</member>
<member name="F:Microsoft.Web.WebView2.Wpf.WebView2.ZoomFactorProperty">
<summary>
The WPF <see cref="T:System.Windows.DependencyProperty"/> which backs the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.ZoomFactor"/> property.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.Wpf.WebView2.ZoomFactor">
<summary>
The zoom factor for the WebView.
This property directly exposes <see cref="P:Microsoft.Web.WebView2.Core.CoreWebView2Controller.ZoomFactor"/>, see its documentation for more info.
Getting this property before the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/> has been initialized will retrieve the last value which was set to it, or <c>1.0</c> (the default) if none has been.
The most recent value set to this property before the CoreWebView2 has been initialized will be set on it after initialization.
</summary>
<seealso cref="P:Microsoft.Web.WebView2.Core.CoreWebView2Controller.ZoomFactor"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.ZoomFactorPropertyChanged(System.Windows.DependencyObject,System.Windows.DependencyPropertyChangedEventArgs)">
<summary>
This is a callback that WPF calls when our WPF ZoomFactor property's value changes.
This might have been triggered by either:
1) The caller set ZoomFactor to change the zoom of the CoreWebView2.
2) The CoreWebView2 changed its own ZoomFactor and we're just updating the dependency property to match.
We use <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.IsPropertyChangingFromCore(System.Windows.DependencyProperty)"/> to distinguish the two cases.
</summary>
</member>
<member name="E:Microsoft.Web.WebView2.Wpf.WebView2.ZoomFactorChanged">
<summary>
The event is raised when the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.ZoomFactor"/> property changes.
This event directly exposes <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2Controller.ZoomFactorChanged"/>.
</summary>
<seealso cref="P:Microsoft.Web.WebView2.Wpf.WebView2.ZoomFactor"/>
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2Controller.ZoomFactorChanged"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2Controller_ZoomFactorChanged(System.Object,System.Object)">
<summary>
This is an event handler for our CoreWebView2Controller's ZoomFactorChanged event.
Unsurprisingly, it fires when the CoreWebView2Controller's ZoomFactor has been changed.
Note that there are two distinct triggers for this:
1) The value was changed programmatically (potentially by us, see ZoomFactorPropertyChanged).
2) The user interacted with the CoreWebView2, e.g. CTRL + Mouse Wheel.
Aside from propagating to our own event, we just need to update our WPF ZoomFactor property to match the CoreWebView2Controller's.
</summary>
</member>
<member name="F:Microsoft.Web.WebView2.Wpf.WebView2.DefaultBackgroundColorProperty">
<summary>
The WPF <see cref="T:System.Windows.DependencyProperty"/> which backs the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.DefaultBackgroundColor"/> property.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.Wpf.WebView2.DefaultBackgroundColor">
<summary>
The default background color for the WebView.
This property directly exposes <see cref="P:Microsoft.Web.WebView2.Core.CoreWebView2Controller.DefaultBackgroundColor"/>, see its documentation for more info.
Getting this property before the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Controller"/> has been initialized will retrieve the last value which was
set to it, or <c>Color.White</c> (the default) if none has been.
The most recent value set to this property before CoreWebView2Controller has been initialized will be set on it after initialization.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.DefaultBackgroundColorPropertyChanged(System.Windows.DependencyObject,System.Windows.DependencyPropertyChangedEventArgs)">
<summary>
This is a callback that WPF calls when our WPF DefaultBackgroundColor property's value changes.
Since CoreWebView2Controller does not update this property itself, this is only triggered by the
caller setting DefaultBackgroundColor.
</summary>
</member>
<member name="F:Microsoft.Web.WebView2.Wpf.WebView2.DesignModeForegroundColorProperty">
<summary>
The WPF <see cref="T:System.Windows.DependencyProperty"/> which backs the <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.DesignModeForegroundColor"/> property.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.Wpf.WebView2.DesignModeForegroundColor">
<summary>
The foreground color to be used in design mode.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.GoBack">
<summary>
Navigates the WebView to the previous page in the navigation history.
Equivalent to calling <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.GoBack"/>
If <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/> hasn't been initialized yet then does nothing.
</summary>
<exception cref="T:System.InvalidOperationException">
Thrown if the calling thread isn't the thread which created this object (usually the UI thread). See <see cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/> for more info.
May also be thrown if the browser process has crashed unexpectedly and left the control in an invalid state. We are considering throwing a different type of exception for this case in the future.
</exception>
<exception cref="T:System.ObjectDisposedException">Thrown if <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.Dispose(System.Boolean)"/> has already been called on the control.</exception>
<seealso cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/>
<seealso cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.CanGoBack"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.GoForward">
<summary>
Navigates the WebView to the next page in the navigation history.
Equivalent to calling <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.GoForward"/>.
If <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/> hasn't been initialized yet then does nothing.
</summary>
<exception cref="T:System.InvalidOperationException">
Thrown if the calling thread isn't the thread which created this object (usually the UI thread). See <see cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/> for more info.
May also be thrown if the browser process has crashed unexpectedly and left the control in an invalid state. We are considering throwing a different type of exception for this case in the future.
</exception>
<exception cref="T:System.ObjectDisposedException">Thrown if <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.Dispose(System.Boolean)"/> has already been called on the control.</exception>
<seealso cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.GoForward"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.Reload">
<summary>
Reloads the current page.
Equivalent to calling <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Reload"/>.
</summary>
<exception cref="T:System.InvalidOperationException">
Thrown if <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> hasn't been initialized yet, or if the calling thread isn't the thread which created this object (usually the UI thread). See <see cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/> for more info.
May also be thrown if the browser process has crashed unexpectedly and left the control in an invalid state. We are considering throwing a different type of exception for this case in the future.
</exception>
<exception cref="T:System.ObjectDisposedException">Thrown if <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.Dispose(System.Boolean)"/> has already been called on the control.</exception>
<seealso cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Reload"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.Stop">
<summary>
Stops all navigations and pending resource fetches.
Equivalent to calling <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Stop"/>.
</summary>
<exception cref="T:System.InvalidOperationException">
Thrown if <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> hasn't been initialized yet, or if the calling thread isn't the thread which created this object (usually the UI thread). See <see cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/> for more info.
May also be thrown if the browser process has crashed unexpectedly and left the control in an invalid state. We are considering throwing a different type of exception for this case in the future.
</exception>
<exception cref="T:System.ObjectDisposedException">Thrown if <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.Dispose(System.Boolean)"/> has already been called on the control.</exception>
<seealso cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Stop"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.NavigateToString(System.String)">
<summary>
Initiates a navigation to htmlContent as source HTML of a new document.
Equivalent to calling <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.NavigateToString(System.String)"/>.
</summary>
<exception cref="T:System.InvalidOperationException">
Thrown if <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> hasn't been initialized yet, or if the calling thread isn't the thread which created this object (usually the UI thread). See <see cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/> for more info.
May also be thrown if the browser process has crashed unexpectedly and left the control in an invalid state. We are considering throwing a different type of exception for this case in the future.
</exception>
<exception cref="T:System.ObjectDisposedException">Thrown if <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.Dispose(System.Boolean)"/> has already been called on the control.</exception>
<remarks>The <c>htmlContent</c> parameter may not be larger than 2 MB (2 * 1024 * 1024 bytes) in total size. The origin of the new page is <c>about:blank</c>.</remarks>
<seealso cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.NavigateToString(System.String)"/>
</member>
<member name="E:Microsoft.Web.WebView2.Wpf.WebView2.ContentLoading">
<summary>
A wrapper around the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.ContentLoading"/>.
The only difference between this event and <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.ContentLoading"/> is the first parameter that's passed to handlers.
Handlers of this event will receive the <see cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/> control, whereas handlers of <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.ContentLoading"/> will receive the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/> instance.
</summary>
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.ContentLoading"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2_ContentLoading(System.Object,Microsoft.Web.WebView2.Core.CoreWebView2ContentLoadingEventArgs)">
<summary>
This is an event handler for our CoreWebView2's ContentLoading event.
We just need to propagate the event to WPF.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.ExecuteScriptAsync(System.String)">
<summary>
Executes JavaScript code from the javaScript parameter in the current top level document rendered in the WebView.
Equivalent to calling <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.ExecuteScriptAsync(System.String)"/>.
</summary>
<exception cref="T:System.InvalidOperationException">
Thrown if <see cref="P:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2"/> hasn't been initialized yet, or if the calling thread isn't the thread which created this object (usually the UI thread). See <see cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/> for more info.
May also be thrown if the browser process has crashed unexpectedly and left the control in an invalid state. We are considering throwing a different type of exception for this case in the future.
</exception>
<exception cref="T:System.ObjectDisposedException">Thrown if <see cref="M:Microsoft.Web.WebView2.Wpf.WebView2.Dispose(System.Boolean)"/> has already been called on the control.</exception>
<seealso cref="M:System.Windows.Threading.DispatcherObject.VerifyAccess"/>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.ExecuteScriptAsync(System.String)"/>
</member>
<member name="E:Microsoft.Web.WebView2.Wpf.WebView2.WebMessageReceived">
<summary>
A wrapper around the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.WebMessageReceived"/>.
The only difference between this event and <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.WebMessageReceived"/> is the first parameter that's passed to handlers.
Handlers of this event will receive the <see cref="T:Microsoft.Web.WebView2.Wpf.WebView2"/> control, whereas handlers of <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.WebMessageReceived"/> will receive the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/> instance.
</summary>
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.WebMessageReceived"/>
</member>
<member name="M:Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2_WebMessageReceived(System.Object,Microsoft.Web.WebView2.Core.CoreWebView2WebMessageReceivedEventArgs)">
<summary>
This is an event handler for our CoreWebView2's WebMessageReceived event.
We just need to propagate the event to WPF.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.Wpf.WebView2.IsInDesignMode">
<summary>
True when we're in design mode and shouldn't create an underlying CoreWebView2.
</summary>
</member>
</members>
</doc>

View File

@ -0,0 +1,12 @@
<FileList>
<File Reference = "Microsoft.Web.WebView2.Wpf.dll" >
<ToolboxItems UIFramework="WPF" VSCategory="WebView2" BlendCategory="WebView2">
<Item Type="Microsoft.Web.WebView2.Wpf.WebView2" />
</ToolboxItems>
</File>
<File Reference = "Microsoft.Web.WebView2.WinForms.dll" >
<ToolboxItems UIFramework="WinForms" VSCategory="WebView2">
<Item Type="Microsoft.Web.WebView2.WinForms.WebView2" />
</ToolboxItems>
</File>
</FileList>

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) Microsoft Corporation. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE

View File

@ -0,0 +1,60 @@
THIRD PARTY SOFTWARE NOTICES AND INFORMATION
Do Not Translate or Localize
This software incorporates material from third parties. Microsoft makes certain open source code available at http://3rdpartysource.microsoft.com, or you may send a check or money order for US $5.00, including the product name, the open source component name, and version number, to:
Source Code Compliance Team
Microsoft Corporation
One Microsoft Way
Redmond, WA 98052
USA
Notwithstanding any other terms, you may reverse engineer this software to the extent required to debug changes to any libraries licensed under the GNU Lesser General Public License.
Libc++
Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Catch2
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,8 @@
<?xml version="1.0"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
</Project>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,748 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_COMMON_INCLUDED
#define __WIL_COMMON_INCLUDED
#if defined(_KERNEL_MODE ) && !defined(__WIL_MIN_KERNEL)
// This define indicates that the WIL usage is in a kernel mode context where
// a high degree of WIL functionality is desired.
//
// Use (sparingly) to change behavior based on whether WIL is being used in kernel
// mode or user mode.
#define WIL_KERNEL_MODE
#endif
// Defining WIL_HIDE_DEPRECATED will hide everything deprecated.
// Each wave of deprecation will add a new WIL_HIDE_DEPRECATED_YYMM number that can be used to lock deprecation at
// a particular point, allowing components to avoid backslide and catch up to the current independently.
#ifdef WIL_HIDE_DEPRECATED
#define WIL_HIDE_DEPRECATED_1809
#endif
#ifdef WIL_HIDE_DEPRECATED_1809
#define WIL_HIDE_DEPRECATED_1612
#endif
#ifdef WIL_HIDE_DEPRECATED_1612
#define WIL_HIDE_DEPRECATED_1611
#endif
// Implementation side note: ideally the deprecation would be done with the function-level declspec
// as it allows you to utter the error text when used. The declspec works, but doing it selectively with
// a macro makes intellisense deprecation comments not work. So we just use the #pragma deprecation.
#ifdef WIL_WARN_DEPRECATED
#define WIL_WARN_DEPRECATED_1809
#endif
#ifdef WIL_WARN_DEPRECATED_1809
#define WIL_WARN_DEPRECATED_1612
#endif
#ifdef WIL_WARN_DEPRECATED_1612
#define WIL_WARN_DEPRECATED_1611
#endif
#ifdef WIL_WARN_DEPRECATED_1809
#define WIL_WARN_DEPRECATED_1809_PRAGMA(...) __pragma(deprecated(__VA_ARGS__))
#else
#define WIL_WARN_DEPRECATED_1809_PRAGMA(...)
#endif
#ifdef WIL_WARN_DEPRECATED_1611
#define WIL_WARN_DEPRECATED_1611_PRAGMA(...) __pragma(deprecated(__VA_ARGS__))
#else
#define WIL_WARN_DEPRECATED_1611_PRAGMA(...)
#endif
#ifdef WIL_WARN_DEPRECATED_1612
#define WIL_WARN_DEPRECATED_1612_PRAGMA(...) __pragma(deprecated(__VA_ARGS__))
#else
#define WIL_WARN_DEPRECATED_1612_PRAGMA(...)
#endif
#if defined(_MSVC_LANG)
#define __WI_SUPPRESS_4127_S __pragma(warning(push)) __pragma(warning(disable:4127)) __pragma(warning(disable:26498)) __pragma(warning(disable:4245))
#define __WI_SUPPRESS_4127_E __pragma(warning(pop))
#define __WI_SUPPRESS_NULLPTR_ANALYSIS __pragma(warning(suppress:28285)) __pragma(warning(suppress:6504))
#define __WI_SUPPRESS_NONINIT_ANALYSIS __pragma(warning(suppress:26495))
#define __WI_SUPPRESS_NOEXCEPT_ANALYSIS __pragma(warning(suppress:26439))
#else
#define __WI_SUPPRESS_4127_S
#define __WI_SUPPRESS_4127_E
#define __WI_SUPPRESS_NULLPTR_ANALYSIS
#define __WI_SUPPRESS_NONINIT_ANALYSIS
#define __WI_SUPPRESS_NOEXCEPT_ANALYSIS
#endif
#include <sal.h>
// Some SAL remapping / decoration to better support Doxygen. Macros that look like function calls can
// confuse Doxygen when they are used to decorate a function or variable. We simplify some of these to
// basic macros without the function for common use cases.
/// @cond
#define _Success_return_ _Success_(return)
#define _Success_true_ _Success_(true)
#define __declspec_noinline_ __declspec(noinline)
#define __declspec_selectany_ __declspec(selectany)
/// @endcond
//! @defgroup macrobuilding Macro Composition
//! The following macros are building blocks primarily intended for authoring other macros.
//! @{
//! Re-state a macro value (indirection for composition)
#define WI_FLATTEN(...) __VA_ARGS__
/// @cond
#define __WI_PASTE_imp(a, b) a##b
/// @endcond
//! This macro is for use in other macros to paste two tokens together, such as a constant and the __LINE__ macro.
#define WI_PASTE(a, b) __WI_PASTE_imp(a, b)
/// @cond
#define __WI_HAS_VA_OPT_IMPL(F, T, ...) T
#define __WI_HAS_VA_OPT_(...) __WI_HAS_VA_OPT_IMPL(__VA_OPT__(0,) 1, 0)
/// @endcond
//! Evaluates to '1' when support for '__VA_OPT__' is available, else '0'
#define WI_HAS_VA_OPT __WI_HAS_VA_OPT_(unused)
/// @cond
#define __WI_ARGS_COUNT1(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26, A27, A28, A29, \
A30, A31, A32, A33, A34, A35, A36, A37, A38, A39, A40, A41, A42, A43, A44, A45, A46, A47, A48, A49, A50, A51, A52, A53, A54, A55, A56, A57, A58, A59, \
A60, A61, A62, A63, A64, A65, A66, A67, A68, A69, A70, A71, A72, A73, A74, A75, A76, A77, A78, A79, A80, A81, A82, A83, A84, A85, A86, A87, A88, A89, \
A90, A91, A92, A93, A94, A95, A96, A97, A98, A99, count, ...) count
#define __WI_ARGS_COUNT0(...) WI_FLATTEN(__WI_ARGS_COUNT1(__VA_ARGS__, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, \
79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, \
39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))
#define __WI_ARGS_COUNT_PREFIX(...) 0, __VA_ARGS__
/// @endcond
//! This variadic macro returns the number of arguments passed to it (up to 99).
#if WI_HAS_VA_OPT
#define WI_ARGS_COUNT(...) __WI_ARGS_COUNT0(0 __VA_OPT__(, __VA_ARGS__))
#else
#define WI_ARGS_COUNT(...) __WI_ARGS_COUNT0(__WI_ARGS_COUNT_PREFIX(__VA_ARGS__))
#endif
/// @cond
#define __WI_FOR_imp0( fn)
#define __WI_FOR_imp1( fn, arg) fn(arg)
#define __WI_FOR_imp2( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp1(fn, __VA_ARGS__))
#define __WI_FOR_imp3( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp2(fn, __VA_ARGS__))
#define __WI_FOR_imp4( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp3(fn, __VA_ARGS__))
#define __WI_FOR_imp5( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp4(fn, __VA_ARGS__))
#define __WI_FOR_imp6( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp5(fn, __VA_ARGS__))
#define __WI_FOR_imp7( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp6(fn, __VA_ARGS__))
#define __WI_FOR_imp8( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp7(fn, __VA_ARGS__))
#define __WI_FOR_imp9( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp8(fn, __VA_ARGS__))
#define __WI_FOR_imp10(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp9(fn, __VA_ARGS__))
#define __WI_FOR_imp11(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp10(fn, __VA_ARGS__))
#define __WI_FOR_imp12(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp11(fn, __VA_ARGS__))
#define __WI_FOR_imp13(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp12(fn, __VA_ARGS__))
#define __WI_FOR_imp14(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp13(fn, __VA_ARGS__))
#define __WI_FOR_imp15(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp14(fn, __VA_ARGS__))
#define __WI_FOR_imp16(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp15(fn, __VA_ARGS__))
#define __WI_FOR_imp17(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp16(fn, __VA_ARGS__))
#define __WI_FOR_imp18(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp17(fn, __VA_ARGS__))
#define __WI_FOR_imp19(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp18(fn, __VA_ARGS__))
#define __WI_FOR_imp20(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp19(fn, __VA_ARGS__))
#define __WI_FOR_imp21(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp20(fn, __VA_ARGS__))
#define __WI_FOR_imp22(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp21(fn, __VA_ARGS__))
#define __WI_FOR_imp23(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp22(fn, __VA_ARGS__))
#define __WI_FOR_imp24(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp23(fn, __VA_ARGS__))
#define __WI_FOR_imp25(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp24(fn, __VA_ARGS__))
#define __WI_FOR_imp26(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp25(fn, __VA_ARGS__))
#define __WI_FOR_imp27(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp26(fn, __VA_ARGS__))
#define __WI_FOR_imp28(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp27(fn, __VA_ARGS__))
#define __WI_FOR_imp29(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp28(fn, __VA_ARGS__))
#define __WI_FOR_imp30(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp29(fn, __VA_ARGS__))
#define __WI_FOR_imp31(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp30(fn, __VA_ARGS__))
#define __WI_FOR_imp32(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp31(fn, __VA_ARGS__))
#define __WI_FOR_imp33(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp32(fn, __VA_ARGS__))
#define __WI_FOR_imp34(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp33(fn, __VA_ARGS__))
#define __WI_FOR_imp35(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp34(fn, __VA_ARGS__))
#define __WI_FOR_imp36(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp35(fn, __VA_ARGS__))
#define __WI_FOR_imp37(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp36(fn, __VA_ARGS__))
#define __WI_FOR_imp38(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp37(fn, __VA_ARGS__))
#define __WI_FOR_imp39(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp38(fn, __VA_ARGS__))
#define __WI_FOR_imp40(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp39(fn, __VA_ARGS__))
#define __WI_FOR_imp41(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp40(fn, __VA_ARGS__))
#define __WI_FOR_imp42(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp41(fn, __VA_ARGS__))
#define __WI_FOR_imp43(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp42(fn, __VA_ARGS__))
#define __WI_FOR_imp44(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp43(fn, __VA_ARGS__))
#define __WI_FOR_imp45(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp44(fn, __VA_ARGS__))
#define __WI_FOR_imp46(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp45(fn, __VA_ARGS__))
#define __WI_FOR_imp47(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp46(fn, __VA_ARGS__))
#define __WI_FOR_imp48(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp47(fn, __VA_ARGS__))
#define __WI_FOR_imp49(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp48(fn, __VA_ARGS__))
#define __WI_FOR_imp50(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp49(fn, __VA_ARGS__))
#define __WI_FOR_imp51(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp50(fn, __VA_ARGS__))
#define __WI_FOR_imp52(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp51(fn, __VA_ARGS__))
#define __WI_FOR_imp53(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp52(fn, __VA_ARGS__))
#define __WI_FOR_imp54(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp53(fn, __VA_ARGS__))
#define __WI_FOR_imp55(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp54(fn, __VA_ARGS__))
#define __WI_FOR_imp56(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp55(fn, __VA_ARGS__))
#define __WI_FOR_imp57(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp56(fn, __VA_ARGS__))
#define __WI_FOR_imp58(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp57(fn, __VA_ARGS__))
#define __WI_FOR_imp59(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp58(fn, __VA_ARGS__))
#define __WI_FOR_imp60(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp59(fn, __VA_ARGS__))
#define __WI_FOR_imp61(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp60(fn, __VA_ARGS__))
#define __WI_FOR_imp62(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp61(fn, __VA_ARGS__))
#define __WI_FOR_imp63(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp62(fn, __VA_ARGS__))
#define __WI_FOR_imp64(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp63(fn, __VA_ARGS__))
#define __WI_FOR_imp65(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp64(fn, __VA_ARGS__))
#define __WI_FOR_imp66(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp65(fn, __VA_ARGS__))
#define __WI_FOR_imp67(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp66(fn, __VA_ARGS__))
#define __WI_FOR_imp68(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp67(fn, __VA_ARGS__))
#define __WI_FOR_imp69(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp68(fn, __VA_ARGS__))
#define __WI_FOR_imp70(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp69(fn, __VA_ARGS__))
#define __WI_FOR_imp71(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp70(fn, __VA_ARGS__))
#define __WI_FOR_imp72(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp71(fn, __VA_ARGS__))
#define __WI_FOR_imp73(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp72(fn, __VA_ARGS__))
#define __WI_FOR_imp74(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp73(fn, __VA_ARGS__))
#define __WI_FOR_imp75(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp74(fn, __VA_ARGS__))
#define __WI_FOR_imp76(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp75(fn, __VA_ARGS__))
#define __WI_FOR_imp77(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp76(fn, __VA_ARGS__))
#define __WI_FOR_imp78(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp77(fn, __VA_ARGS__))
#define __WI_FOR_imp79(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp78(fn, __VA_ARGS__))
#define __WI_FOR_imp80(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp79(fn, __VA_ARGS__))
#define __WI_FOR_imp81(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp80(fn, __VA_ARGS__))
#define __WI_FOR_imp82(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp81(fn, __VA_ARGS__))
#define __WI_FOR_imp83(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp82(fn, __VA_ARGS__))
#define __WI_FOR_imp84(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp83(fn, __VA_ARGS__))
#define __WI_FOR_imp85(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp84(fn, __VA_ARGS__))
#define __WI_FOR_imp86(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp85(fn, __VA_ARGS__))
#define __WI_FOR_imp87(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp86(fn, __VA_ARGS__))
#define __WI_FOR_imp88(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp87(fn, __VA_ARGS__))
#define __WI_FOR_imp89(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp88(fn, __VA_ARGS__))
#define __WI_FOR_imp90(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp89(fn, __VA_ARGS__))
#define __WI_FOR_imp91(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp90(fn, __VA_ARGS__))
#define __WI_FOR_imp92(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp91(fn, __VA_ARGS__))
#define __WI_FOR_imp93(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp92(fn, __VA_ARGS__))
#define __WI_FOR_imp94(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp93(fn, __VA_ARGS__))
#define __WI_FOR_imp95(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp94(fn, __VA_ARGS__))
#define __WI_FOR_imp96(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp95(fn, __VA_ARGS__))
#define __WI_FOR_imp97(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp96(fn, __VA_ARGS__))
#define __WI_FOR_imp98(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp97(fn, __VA_ARGS__))
#define __WI_FOR_imp99(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp98(fn, __VA_ARGS__))
#define __WI_FOR_imp(n, fnAndArgs) WI_PASTE(__WI_FOR_imp, n) fnAndArgs
/// @endcond
//! Iterates through each of the given arguments invoking the specified macro against each one.
#define WI_FOREACH(fn, ...) __WI_FOR_imp(WI_ARGS_COUNT(__VA_ARGS__), (fn, ##__VA_ARGS__))
//! Dispatches a single macro name to separate macros based on the number of arguments passed to it.
#define WI_MACRO_DISPATCH(name, ...) WI_PASTE(WI_PASTE(name, WI_ARGS_COUNT(__VA_ARGS__)), (__VA_ARGS__))
//! @} // Macro composition helpers
#if !defined(__cplusplus) || defined(__WIL_MIN_KERNEL)
#define WI_ODR_PRAGMA(NAME, TOKEN)
#define WI_NOEXCEPT
#else
#pragma warning(push)
#pragma warning(disable:4714) // __forceinline not honored
// DO NOT add *any* further includes to this file -- there should be no dependencies from its usage
#include "wistd_type_traits.h"
//! This macro inserts ODR violation protection; the macro allows it to be compatible with straight "C" code
#define WI_ODR_PRAGMA(NAME, TOKEN) __pragma(detect_mismatch("ODR_violation_" NAME "_mismatch", TOKEN))
#ifdef WIL_KERNEL_MODE
WI_ODR_PRAGMA("WIL_KERNEL_MODE", "1")
#else
WI_ODR_PRAGMA("WIL_KERNEL_MODE", "0")
#endif
#if defined(_CPPUNWIND) && !defined(WIL_SUPPRESS_EXCEPTIONS)
/** This define is automatically set when exceptions are enabled within wil.
It is automatically defined when your code is compiled with exceptions enabled (via checking for the built-in
_CPPUNWIND flag) unless you explicitly define WIL_SUPPRESS_EXCEPTIONS ahead of including your first wil
header. All exception-based WIL methods and classes are included behind:
~~~~
#ifdef WIL_ENABLE_EXCEPTIONS
// code
#endif
~~~~
This enables exception-free code to directly include WIL headers without worrying about exception-based
routines suddenly becoming available. */
#define WIL_ENABLE_EXCEPTIONS
#endif
/// @endcond
/// @cond
#if defined(WIL_EXCEPTION_MODE)
static_assert(WIL_EXCEPTION_MODE <= 2, "Invalid exception mode");
#elif !defined(WIL_LOCK_EXCEPTION_MODE)
#define WIL_EXCEPTION_MODE 0 // default, can link exception-based and non-exception based libraries together
#pragma detect_mismatch("ODR_violation_WIL_EXCEPTION_MODE_mismatch", "0")
#elif defined(WIL_ENABLE_EXCEPTIONS)
#define WIL_EXCEPTION_MODE 1 // new code optimization: ONLY support linking libraries together that have exceptions enabled
#pragma detect_mismatch("ODR_violation_WIL_EXCEPTION_MODE_mismatch", "1")
#else
#define WIL_EXCEPTION_MODE 2 // old code optimization: ONLY support linking libraries that are NOT using exceptions
#pragma detect_mismatch("ODR_violation_WIL_EXCEPTION_MODE_mismatch", "2")
#endif
#if WIL_EXCEPTION_MODE == 1 && !defined(WIL_ENABLE_EXCEPTIONS)
#error Must enable exceptions when WIL_EXCEPTION_MODE == 1
#endif
// block for documentation only
#if defined(WIL_DOXYGEN)
/** This define can be explicitly set to disable exception usage within wil.
Normally this define is never needed as the WIL_ENABLE_EXCEPTIONS macro is enabled automatically by looking
at _CPPUNWIND. If your code compiles with exceptions enabled, but does not want to enable the exception-based
classes and methods from WIL, define this macro ahead of including the first WIL header. */
#define WIL_SUPPRESS_EXCEPTIONS
/** This define can be explicitly set to lock the process exception mode to WIL_ENABLE_EXCEPTIONS.
Locking the exception mode provides optimizations to exception barriers, staging hooks and DLL load costs as it eliminates the need to
do copy-on-write initialization of various function pointers and the necessary indirection that's done within WIL to avoid ODR violations
when linking libraries together with different exception handling semantics. */
#define WIL_LOCK_EXCEPTION_MODE
/** This define explicit sets the exception mode for the process to control optimizations.
Three exception modes are available:
0) This is the default. This enables a binary to link both exception-based and non-exception based libraries together that
use WIL. This adds overhead to exception barriers, DLL copy on write pages and indirection through function pointers to avoid ODR
violations when linking libraries together with different exception handling semantics.
1) Prefer this setting when it can be used. This locks the binary to only supporting libraries which were built with exceptions enabled.
2) This locks the binary to libraries built without exceptions. */
#define WIL_EXCEPTION_MODE
#endif
#if (__cplusplus >= 201703) || (_MSVC_LANG >= 201703)
#define WIL_HAS_CXX_17 1
#else
#define WIL_HAS_CXX_17 0
#endif
// Until we'll have C++17 enabled in our code base, we're falling back to SAL
#define WI_NODISCARD __WI_LIBCPP_NODISCARD_ATTRIBUTE
#define __R_ENABLE_IF_IS_CLASS(ptrType) wistd::enable_if_t<wistd::is_class<ptrType>::value, void*> = (void*)0
#define __R_ENABLE_IF_IS_NOT_CLASS(ptrType) wistd::enable_if_t<!wistd::is_class<ptrType>::value, void*> = (void*)0
//! @defgroup bitwise Bitwise Inspection and Manipulation
//! Bitwise helpers to improve readability and reduce the error rate of bitwise operations.
//! Several macros have been constructed to assist with bitwise inspection and manipulation. These macros exist
//! for two primary purposes:
//!
//! 1. To improve the readability of bitwise comparisons and manipulation.
//!
//! The macro names are the more concise, readable form of what's being done and do not require that any flags
//! or variables be specified multiple times for the comparisons.
//!
//! 2. To reduce the error rate associated with bitwise operations.
//!
//! The readability improvements naturally lend themselves to this by cutting down the number of concepts.
//! Using `WI_IsFlagSet(var, MyEnum::Flag)` rather than `((var & MyEnum::Flag) == MyEnum::Flag)` removes the comparison
//! operator and repetition in the flag value.
//!
//! Additionally, these macros separate single flag operations (which tend to be the most common) from multi-flag
//! operations so that compile-time errors are generated for bitwise operations which are likely incorrect,
//! such as: `WI_IsFlagSet(var, MyEnum::None)` or `WI_IsFlagSet(var, MyEnum::ValidMask)`.
//!
//! Note that the single flag helpers should be used when a compile-time constant single flag is being manipulated. These
//! helpers provide compile-time errors on misuse and should be preferred over the multi-flag helpers. The multi-flag helpers
//! should be used when multiple flags are being used simultaneously or when the flag values are not compile-time constants.
//!
//! Common example usage (manipulation of flag variables):
//! ~~~~
//! WI_SetFlag(m_flags, MyFlags::Foo); // Set a single flag in the given variable
//! WI_SetAllFlags(m_flags, MyFlags::Foo | MyFlags::Bar); // Set one or more flags
//! WI_ClearFlagIf(m_flags, MyFlags::Bar, isBarClosed); // Conditionally clear a single flag based upon a bool
//! WI_ClearAllFlags(m_flags, MyFlags::Foo | MyFlags::Bar); // Clear one or more flags from the given variable
//! WI_ToggleFlag(m_flags, MyFlags::Foo); // Toggle (change to the opposite value) a single flag
//! WI_UpdateFlag(m_flags, MyFlags::Bar, isBarClosed); // Sets or Clears a single flag from the given variable based upon a bool value
//! WI_UpdateFlagsInMask(m_flags, flagsMask, newFlagValues); // Sets or Clears the flags in flagsMask to the masked values from newFlagValues
//! ~~~~
//! Common example usage (inspection of flag variables):
//! ~~~~
//! if (WI_IsFlagSet(m_flags, MyFlags::Foo)) // Is a single flag set in the given variable?
//! if (WI_IsAnyFlagSet(m_flags, MyFlags::Foo | MyFlags::Bar)) // Is at least one flag from the given mask set?
//! if (WI_AreAllFlagsClear(m_flags, MyFlags::Foo | MyFlags::Bar)) // Are all flags in the given list clear?
//! if (WI_IsSingleFlagSet(m_flags)) // Is *exactly* one flag set in the given variable?
//! ~~~~
//! @{
//! Returns the unsigned type of the same width and numeric value as the given enum
#define WI_EnumValue(val) static_cast<::wil::integral_from_enum<decltype(val)>>(val)
//! Validates that exactly ONE bit is set in compile-time constant `flag`
#define WI_StaticAssertSingleBitSet(flag) static_cast<decltype(flag)>(::wil::details::verify_single_flag_helper<static_cast<unsigned long long>(WI_EnumValue(flag))>::value)
//! @name Bitwise manipulation macros
//! @{
//! Set zero or more bitflags specified by `flags` in the variable `var`.
#define WI_SetAllFlags(var, flags) ((var) |= (flags))
//! Set a single compile-time constant `flag` in the variable `var`.
#define WI_SetFlag(var, flag) WI_SetAllFlags(var, WI_StaticAssertSingleBitSet(flag))
//! Conditionally sets a single compile-time constant `flag` in the variable `var` only if `condition` is true.
#define WI_SetFlagIf(var, flag, condition) do { if (wil::verify_bool(condition)) { WI_SetFlag(var, flag); } } while ((void)0, 0)
//! Clear zero or more bitflags specified by `flags` from the variable `var`.
#define WI_ClearAllFlags(var, flags) ((var) &= ~(flags))
//! Clear a single compile-time constant `flag` from the variable `var`.
#define WI_ClearFlag(var, flag) WI_ClearAllFlags(var, WI_StaticAssertSingleBitSet(flag))
//! Conditionally clear a single compile-time constant `flag` in the variable `var` only if `condition` is true.
#define WI_ClearFlagIf(var, flag, condition) do { if (wil::verify_bool(condition)) { WI_ClearFlag(var, flag); } } while ((void)0, 0)
//! Changes a single compile-time constant `flag` in the variable `var` to be set if `isFlagSet` is true or cleared if `isFlagSet` is false.
#define WI_UpdateFlag(var, flag, isFlagSet) (wil::verify_bool(isFlagSet) ? WI_SetFlag(var, flag) : WI_ClearFlag(var, flag))
//! Changes only the flags specified by `flagsMask` in the variable `var` to match the corresponding flags in `newFlags`.
#define WI_UpdateFlagsInMask(var, flagsMask, newFlags) wil::details::UpdateFlagsInMaskHelper(var, flagsMask, newFlags)
//! Toggles (XOR the value) of multiple bitflags specified by `flags` in the variable `var`.
#define WI_ToggleAllFlags(var, flags) ((var) ^= (flags))
//! Toggles (XOR the value) of a single compile-time constant `flag` in the variable `var`.
#define WI_ToggleFlag(var, flag) WI_ToggleAllFlags(var, WI_StaticAssertSingleBitSet(flag))
//! @} // bitwise manipulation macros
//! @name Bitwise inspection macros
//! @{
//! Evaluates as true if every bitflag specified in `flags` is set within `val`.
#define WI_AreAllFlagsSet(val, flags) wil::details::AreAllFlagsSetHelper(val, flags)
//! Evaluates as true if one or more bitflags specified in `flags` are set within `val`.
#define WI_IsAnyFlagSet(val, flags) (static_cast<decltype((val) & (flags))>(WI_EnumValue(val) & WI_EnumValue(flags)) != static_cast<decltype((val) & (flags))>(0))
//! Evaluates as true if a single compile-time constant `flag` is set within `val`.
#define WI_IsFlagSet(val, flag) WI_IsAnyFlagSet(val, WI_StaticAssertSingleBitSet(flag))
//! Evaluates as true if every bitflag specified in `flags` is clear within `val`.
#define WI_AreAllFlagsClear(val, flags) (static_cast<decltype((val) & (flags))>(WI_EnumValue(val) & WI_EnumValue(flags)) == static_cast<decltype((val) & (flags))>(0))
//! Evaluates as true if one or more bitflags specified in `flags` are clear within `val`.
#define WI_IsAnyFlagClear(val, flags) (!wil::details::AreAllFlagsSetHelper(val, flags))
//! Evaluates as true if a single compile-time constant `flag` is clear within `val`.
#define WI_IsFlagClear(val, flag) WI_AreAllFlagsClear(val, WI_StaticAssertSingleBitSet(flag))
//! Evaluates as true if exactly one bit (any bit) is set within `val`.
#define WI_IsSingleFlagSet(val) wil::details::IsSingleFlagSetHelper(val)
//! Evaluates as true if exactly one bit from within the specified `mask` is set within `val`.
#define WI_IsSingleFlagSetInMask(val, mask) wil::details::IsSingleFlagSetHelper((val) & (mask))
//! Evaluates as true if exactly one bit (any bit) is set within `val` or if there are no bits set within `val`.
#define WI_IsClearOrSingleFlagSet(val) wil::details::IsClearOrSingleFlagSetHelper(val)
//! Evaluates as true if exactly one bit from within the specified `mask` is set within `val` or if there are no bits from `mask` set within `val`.
#define WI_IsClearOrSingleFlagSetInMask(val, mask) wil::details::IsClearOrSingleFlagSetHelper((val) & (mask))
//! @}
#if defined(WIL_DOXYGEN)
/** This macro provides a C++ header with a guaranteed initialization function.
Normally, were a global object's constructor used for this purpose, the optimizer/linker might throw
the object away if it's unreferenced (which throws away the side-effects that the initialization function
was trying to achieve). Using this macro forces linker inclusion of a variable that's initialized by the
provided function to elide that optimization.
//!
This functionality is primarily provided as a building block for header-based libraries (such as WIL)
to be able to layer additional functionality into other libraries by their mere inclusion. Alternative models
of initialization should be used whenever they are available.
~~~~
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
WI_HEADER_INITITALIZATION_FUNCTION(InitializeDesktopFamilyApis, []
{
g_pfnGetModuleName = GetCurrentModuleName;
g_pfnFailFastInLoaderCallout = FailFastInLoaderCallout;
return 1;
});
#endif
~~~~
The above example is used within WIL to decide whether or not the library containing WIL is allowed to use
desktop APIs. Building this functionality as #IFDEFs within functions would create ODR violations, whereas
doing it with global function pointers and header initialization allows a runtime determination. */
#define WI_HEADER_INITITALIZATION_FUNCTION(name, fn)
#elif defined(_M_IX86)
#define WI_HEADER_INITITALIZATION_FUNCTION(name, fn) \
extern "C" { __declspec(selectany) unsigned char g_header_init_ ## name = static_cast<unsigned char>(fn()); } \
__pragma(comment(linker, "/INCLUDE:_g_header_init_" #name))
#elif defined(_M_IA64) || defined(_M_AMD64) || defined(_M_ARM) || defined(_M_ARM64)
#define WI_HEADER_INITITALIZATION_FUNCTION(name, fn) \
extern "C" { __declspec(selectany) unsigned char g_header_init_ ## name = static_cast<unsigned char>(fn()); } \
__pragma(comment(linker, "/INCLUDE:g_header_init_" #name))
#else
#error linker pragma must include g_header_init variation
#endif
/** All Windows Implementation Library classes and functions are located within the "wil" namespace.
The 'wil' namespace is an intentionally short name as the intent is for code to be able to reference
the namespace directly (example: `wil::srwlock lock;`) without a using statement. Resist adding a using
statement for wil to avoid introducing potential name collisions between wil and other namespaces. */
namespace wil
{
/// @cond
namespace details
{
template <typename T>
class pointer_range
{
public:
pointer_range(T begin_, T end_) : m_begin(begin_), m_end(end_) {}
T begin() const { return m_begin; }
T end() const { return m_end; }
private:
T m_begin;
T m_end;
};
}
/// @endcond
/** Enables using range-based for between a begin and end object pointer.
~~~~
for (auto& obj : make_range(objPointerBegin, objPointerEnd)) { }
~~~~ */
template <typename T>
details::pointer_range<T> make_range(T begin, T end)
{
return details::pointer_range<T>(begin, end);
}
/** Enables using range-based for on a range when given the base pointer and the number of objects in the range.
~~~~
for (auto& obj : make_range(objPointer, objCount)) { }
~~~~ */
template <typename T>
details::pointer_range<T> make_range(T begin, size_t count)
{
return details::pointer_range<T>(begin, begin + count);
}
//! @defgroup outparam Output Parameters
//! Improve the conciseness of assigning values to optional output parameters.
//! @{
/** Assign the given value to an optional output parameter.
Makes code more concise by removing trivial `if (outParam)` blocks. */
template <typename T>
inline void assign_to_opt_param(_Out_opt_ T *outParam, T val)
{
if (outParam != nullptr)
{
*outParam = val;
}
}
/** Assign NULL to an optional output pointer parameter.
Makes code more concise by removing trivial `if (outParam)` blocks. */
template <typename T>
inline void assign_null_to_opt_param(_Out_opt_ T *outParam)
{
if (outParam != nullptr)
{
*outParam = nullptr;
}
}
//! @} // end output parameter helpers
/** Performs a logical or of the given variadic template parameters allowing indirect compile-time boolean evaluation.
Example usage:
~~~~
template <unsigned int... Rest>
struct FeatureRequiredBy
{
static const bool enabled = wil::variadic_logical_or<WilFeature<Rest>::enabled...>::value;
};
~~~~ */
template <bool...> struct variadic_logical_or;
/// @cond
template <> struct variadic_logical_or<> : wistd::false_type { };
template <bool... Rest> struct variadic_logical_or<true, Rest...> : wistd::true_type { };
template <bool... Rest> struct variadic_logical_or<false, Rest...> : variadic_logical_or<Rest...>::type { };
/// @endcond
/// @cond
namespace details
{
template <unsigned long long flag>
struct verify_single_flag_helper
{
static_assert((flag != 0) && ((flag & (flag - 1)) == 0), "Single flag expected, zero or multiple flags found");
static const unsigned long long value = flag;
};
}
/// @endcond
//! @defgroup typesafety Type Validation
//! Helpers to validate variable types to prevent accidental, but allowed type conversions.
//! These helpers are most useful when building macros that accept a particular type. Putting these functions around the types accepted
//! prior to pushing that type through to a function (or using it within the macro) allows the macro to add an additional layer of type
//! safety that would ordinarily be stripped away by C++ implicit conversions. This system is extensively used in the error handling helper
//! macros to validate the types given to various macro parameters.
//! @{
/** Verify that `val` can be evaluated as a logical bool.
Other types will generate an intentional compilation error. Allowed types for a logical bool are bool, BOOL,
boolean, BOOLEAN, and classes with an explicit bool cast.
@param val The logical bool expression
@return A C++ bool representing the evaluation of `val`. */
template <typename T, __R_ENABLE_IF_IS_CLASS(T)>
_Post_satisfies_(return == static_cast<bool>(val))
__forceinline constexpr bool verify_bool(const T& val)
{
return static_cast<bool>(val);
}
template <typename T, __R_ENABLE_IF_IS_NOT_CLASS(T)>
__forceinline constexpr bool verify_bool(T /*val*/)
{
static_assert(!wistd::is_same<T, T>::value, "Wrong Type: bool/BOOL/BOOLEAN/boolean expected");
return false;
}
template <>
_Post_satisfies_(return == val)
__forceinline constexpr bool verify_bool<bool>(bool val)
{
return val;
}
template <>
_Post_satisfies_(return == (val != 0))
__forceinline constexpr bool verify_bool<int>(int val)
{
return (val != 0);
}
template <>
_Post_satisfies_(return == !!val)
__forceinline constexpr bool verify_bool<unsigned char>(unsigned char val)
{
return !!val;
}
/** Verify that `val` is a Win32 BOOL value.
Other types (including other logical bool expressions) will generate an intentional compilation error. Note that this will
accept any `int` value as long as that is the underlying typedef behind `BOOL`.
@param val The Win32 BOOL returning expression
@return A Win32 BOOL representing the evaluation of `val`. */
template <typename T>
_Post_satisfies_(return == val)
__forceinline constexpr int verify_BOOL(T val)
{
// Note: Written in terms of 'int' as BOOL is actually: typedef int BOOL;
static_assert((wistd::is_same<T, int>::value), "Wrong Type: BOOL expected");
return val;
}
/** Verify that `hr` is an HRESULT value.
Other types will generate an intentional compilation error. Note that this will accept any `long` value as that is the
underlying typedef behind HRESULT.
//!
Note that occasionally you might run into an HRESULT which is directly defined with a #define, such as:
~~~~
#define UIA_E_NOTSUPPORTED 0x80040204
~~~~
Though this looks like an `HRESULT`, this is actually an `unsigned long` (the hex specification forces this). When
these are encountered and they are NOT in the public SDK (have not yet shipped to the public), then you should change
their definition to match the manner in which `HRESULT` constants are defined in winerror.h:
~~~~
#define E_NOTIMPL _HRESULT_TYPEDEF_(0x80004001L)
~~~~
When these are encountered in the public SDK, their type should not be changed and you should use a static_cast
to use this value in a macro that utilizes `verify_hresult`, for example:
~~~~
RETURN_HR_IF(static_cast<HRESULT>(UIA_E_NOTSUPPORTED), (patternId != UIA_DragPatternId));
~~~~
@param val The HRESULT returning expression
@return An HRESULT representing the evaluation of `val`. */
template <typename T>
_Post_satisfies_(return == hr)
inline constexpr long verify_hresult(T hr)
{
// Note: Written in terms of 'int' as HRESULT is actually: typedef _Return_type_success_(return >= 0) long HRESULT
static_assert(wistd::is_same<T, long>::value, "Wrong Type: HRESULT expected");
return hr;
}
/// @} // end type validation routines
/// @cond
// Implementation details for macros and helper functions... do not use directly.
namespace details
{
// Use size-specific casts to avoid sign extending numbers -- avoid warning C4310: cast truncates constant value
#define __WI_MAKE_UNSIGNED(val) \
(__pragma(warning(push)) __pragma(warning(disable: 4310 4309)) (sizeof(val) == 1 ? static_cast<unsigned char>(val) : \
sizeof(val) == 2 ? static_cast<unsigned short>(val) : \
sizeof(val) == 4 ? static_cast<unsigned long>(val) : \
static_cast<unsigned long long>(val)) __pragma(warning(pop)))
#define __WI_IS_UNSIGNED_SINGLE_FLAG_SET(val) ((val) && !((val) & ((val) - 1)))
#define __WI_IS_SINGLE_FLAG_SET(val) __WI_IS_UNSIGNED_SINGLE_FLAG_SET(__WI_MAKE_UNSIGNED(val))
template <typename TVal, typename TFlags>
__forceinline constexpr bool AreAllFlagsSetHelper(TVal val, TFlags flags)
{
return ((val & flags) == static_cast<decltype(val & flags)>(flags));
}
template <typename TVal>
__forceinline constexpr bool IsSingleFlagSetHelper(TVal val)
{
return __WI_IS_SINGLE_FLAG_SET(val);
}
template <typename TVal>
__forceinline constexpr bool IsClearOrSingleFlagSetHelper(TVal val)
{
return ((val == static_cast<wistd::remove_reference_t<TVal>>(0)) || IsSingleFlagSetHelper(val));
}
template <typename TVal, typename TMask, typename TFlags>
__forceinline constexpr void UpdateFlagsInMaskHelper(_Inout_ TVal& val, TMask mask, TFlags flags)
{
val = static_cast<wistd::remove_reference_t<TVal>>((val & ~mask) | (flags & mask));
}
template <long>
struct variable_size;
template <>
struct variable_size<1>
{
typedef unsigned char type;
};
template <>
struct variable_size<2>
{
typedef unsigned short type;
};
template <>
struct variable_size<4>
{
typedef unsigned long type;
};
template <>
struct variable_size<8>
{
typedef unsigned long long type;
};
template <typename T>
struct variable_size_mapping
{
typedef typename variable_size<sizeof(T)>::type type;
};
} // details
/// @endcond
/** Defines the unsigned type of the same width (1, 2, 4, or 8 bytes) as the given type.
This allows code to generically convert any enum class to it's corresponding underlying type. */
template <typename T>
using integral_from_enum = typename details::variable_size_mapping<T>::type;
} // wil
#pragma warning(pop)
#endif // __cplusplus
#endif // __WIL_COMMON_INCLUDED

View File

@ -0,0 +1,251 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_CPPWINRT_INCLUDED
#define __WIL_CPPWINRT_INCLUDED
#include "common.h"
#include <windows.h>
#include <unknwn.h>
#include <hstring.h>
// WIL and C++/WinRT use two different exception types for communicating HRESULT failures. Thus, both libraries need to
// understand how to translate these exception types into the correct HRESULT values at the ABI boundary. Prior to
// C++/WinRT "2.0" this was accomplished by injecting the WINRT_EXTERNAL_CATCH_CLAUSE macro - that WIL defines below -
// into its exception handler (winrt::to_hresult). Starting with C++/WinRT "2.0" this mechanism has shifted to a global
// function pointer - winrt_to_hresult_handler - that WIL sets automatically when this header is included and
// 'CPPWINRT_SUPPRESS_STATIC_INITIALIZERS' is not defined.
/// @cond
namespace wil::details
{
// Since the C++/WinRT version macro is a string...
inline constexpr int major_version_from_string(const char* versionString)
{
int result = 0;
auto str = versionString;
while ((*str >= '0') && (*str <= '9'))
{
result = result * 10 + (*str - '0');
++str;
}
return result;
}
}
/// @endcond
#ifdef CPPWINRT_VERSION
// Prior to C++/WinRT "2.0" this header needed to be included before 'winrt/base.h' so that our definition of
// 'WINRT_EXTERNAL_CATCH_CLAUSE' would get picked up in the implementation of 'winrt::to_hresult'. This is no longer
// problematic, so only emit an error when using a version of C++/WinRT prior to 2.0
static_assert(::wil::details::major_version_from_string(CPPWINRT_VERSION) >= 2,
"Please include wil/cppwinrt.h before including any C++/WinRT headers");
#endif
// NOTE: Will eventually be removed once C++/WinRT 2.0 use can be assumed
#ifdef WINRT_EXTERNAL_CATCH_CLAUSE
#define __WI_CONFLICTING_WINRT_EXTERNAL_CATCH_CLAUSE 1
#else
#define WINRT_EXTERNAL_CATCH_CLAUSE \
catch (const wil::ResultException& e) \
{ \
return winrt::hresult_error(e.GetErrorCode(), winrt::to_hstring(e.what())).to_abi(); \
}
#endif
#include "result_macros.h"
#include <winrt/base.h>
#if __WI_CONFLICTING_WINRT_EXTERNAL_CATCH_CLAUSE
static_assert(::wil::details::major_version_from_string(CPPWINRT_VERSION) >= 2,
"C++/WinRT external catch clause already defined outside of WIL");
#endif
// In C++/WinRT 2.0 and beyond, this function pointer exists. In earlier versions it does not. It's much easier to avoid
// linker errors than it is to SFINAE on variable existence, so we declare the variable here, but are careful not to
// use it unless the version of C++/WinRT is high enough
extern std::int32_t(__stdcall* winrt_to_hresult_handler)(void*) noexcept;
/// @cond
namespace wil::details
{
inline void MaybeGetExceptionString(
const winrt::hresult_error& exception,
_Out_writes_opt_(debugStringChars) PWSTR debugString,
_When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars)
{
if (debugString)
{
StringCchPrintfW(debugString, debugStringChars, L"winrt::hresult_error: %ls", exception.message().c_str());
}
}
inline HRESULT __stdcall ResultFromCaughtException_CppWinRt(
_Inout_updates_opt_(debugStringChars) PWSTR debugString,
_When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars,
_Inout_ bool* isNormalized) noexcept
{
if (g_pfnResultFromCaughtException)
{
try
{
throw;
}
catch (const ResultException& exception)
{
*isNormalized = true;
MaybeGetExceptionString(exception, debugString, debugStringChars);
return exception.GetErrorCode();
}
catch (const winrt::hresult_error& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return exception.to_abi();
}
catch (const std::bad_alloc& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return E_OUTOFMEMORY;
}
catch (const std::out_of_range& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return E_BOUNDS;
}
catch (const std::invalid_argument& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return E_INVALIDARG;
}
catch (...)
{
auto hr = RecognizeCaughtExceptionFromCallback(debugString, debugStringChars);
if (FAILED(hr))
{
return hr;
}
}
}
else
{
try
{
throw;
}
catch (const ResultException& exception)
{
*isNormalized = true;
MaybeGetExceptionString(exception, debugString, debugStringChars);
return exception.GetErrorCode();
}
catch (const winrt::hresult_error& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return exception.to_abi();
}
catch (const std::bad_alloc& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return E_OUTOFMEMORY;
}
catch (const std::out_of_range& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return E_BOUNDS;
}
catch (const std::invalid_argument& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return E_INVALIDARG;
}
catch (const std::exception& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION);
}
catch (...)
{
// Fall through to returning 'S_OK' below
}
}
// Tell the caller that we were unable to map the exception by succeeding...
return S_OK;
}
}
/// @endcond
namespace wil
{
inline std::int32_t __stdcall winrt_to_hresult(void* returnAddress) noexcept
{
// C++/WinRT only gives us the return address (caller), so pass along an empty 'DiagnosticsInfo' since we don't
// have accurate file/line/etc. information
return static_cast<std::int32_t>(details::ReportFailure_CaughtException<FailureType::Return>(__R_DIAGNOSTICS_RA(DiagnosticsInfo{}, returnAddress)));
}
inline void WilInitialize_CppWinRT()
{
details::g_pfnResultFromCaughtException_CppWinRt = details::ResultFromCaughtException_CppWinRt;
if constexpr (details::major_version_from_string(CPPWINRT_VERSION) >= 2)
{
WI_ASSERT(winrt_to_hresult_handler == nullptr);
winrt_to_hresult_handler = winrt_to_hresult;
}
}
/// @cond
namespace details
{
#ifndef CPPWINRT_SUPPRESS_STATIC_INITIALIZERS
WI_ODR_PRAGMA("CPPWINRT_SUPPRESS_STATIC_INITIALIZERS", "0")
WI_HEADER_INITITALIZATION_FUNCTION(WilInitialize_CppWinRT, []
{
::wil::WilInitialize_CppWinRT();
return 1;
});
#else
WI_ODR_PRAGMA("CPPWINRT_SUPPRESS_STATIC_INITIALIZERS", "1")
#endif
}
/// @endcond
// Provides an overload of verify_hresult so that the WIL macros can recognize winrt::hresult as a valid "hresult" type.
inline long verify_hresult(winrt::hresult hr) noexcept
{
return hr;
}
// Provides versions of get_abi and put_abi for genericity that directly use HSTRING for convenience.
template <typename T>
auto get_abi(T const& object) noexcept
{
return winrt::get_abi(object);
}
inline auto get_abi(winrt::hstring const& object) noexcept
{
return static_cast<HSTRING>(winrt::get_abi(object));
}
template <typename T>
auto put_abi(T& object) noexcept
{
return winrt::put_abi(object);
}
inline auto put_abi(winrt::hstring& object) noexcept
{
return reinterpret_cast<HSTRING*>(winrt::put_abi(object));
}
}
#endif // __WIL_CPPWINRT_INCLUDED

View File

@ -0,0 +1,963 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_FILESYSTEM_INCLUDED
#define __WIL_FILESYSTEM_INCLUDED
#ifdef _KERNEL_MODE
#error This header is not supported in kernel-mode.
#endif
#include <new>
#include <combaseapi.h> // Needed for CoTaskMemFree() used in output of some helpers.
#include <winbase.h> // LocalAlloc
#include <PathCch.h>
#include "result.h"
#include "win32_helpers.h"
#include "resource.h"
namespace wil
{
//! Determines if a path is an extended length path that can be used to access paths longer than MAX_PATH.
inline bool is_extended_length_path(_In_ PCWSTR path)
{
return wcsncmp(path, L"\\\\?\\", 4) == 0;
}
//! Find the last segment of a path. Matches the behavior of shlwapi!PathFindFileNameW()
//! note, does not support streams being specified like PathFindFileNameW(), is that a bug or a feature?
inline PCWSTR find_last_path_segment(_In_ PCWSTR path)
{
auto const pathLength = wcslen(path);
// If there is a trailing slash ignore that in the search.
auto const limitedLength = ((pathLength > 0) && (path[pathLength - 1] == L'\\')) ? (pathLength - 1) : pathLength;
PCWSTR result;
auto const offset = FindStringOrdinal(FIND_FROMEND, path, static_cast<int>(limitedLength), L"\\", 1, TRUE);
if (offset == -1)
{
result = path + pathLength; // null terminator
}
else
{
result = path + offset + 1; // just past the slash
}
return result;
}
//! Determine if the file name is one of the special "." or ".." names.
inline bool path_is_dot_or_dotdot(_In_ PCWSTR fileName)
{
return ((fileName[0] == L'.') &&
((fileName[1] == L'\0') || ((fileName[1] == L'.') && (fileName[2] == L'\0'))));
}
//! Returns the drive number, if it has one. Returns true if there is a drive number, false otherwise. Supports regular and extended length paths.
inline bool try_get_drive_letter_number(_In_ PCWSTR path, _Out_ int* driveNumber)
{
if (path[0] == L'\\' && path[1] == L'\\' && path[2] == L'?' && path[3] == L'\\')
{
path += 4;
}
if (path[0] && (path[1] == L':'))
{
if ((path[0] >= L'a') && (path[0] <= L'z'))
{
*driveNumber = path[0] - L'a';
return true;
}
else if ((path[0] >= L'A') && (path[0] <= L'Z'))
{
*driveNumber = path[0] - L'A';
return true;
}
}
*driveNumber = -1;
return false;
}
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
// PathCch.h APIs are only in desktop API for now.
// Compute the substring in the input value that is the parent folder path.
// returns:
// true + parentPathLength - path has a parent starting at the beginning path and of parentPathLength length.
// false, no parent path, the input is a root path.
inline bool try_get_parent_path_range(_In_ PCWSTR path, _Out_ size_t* parentPathLength)
{
*parentPathLength = 0;
bool hasParent = false;
PCWSTR rootEnd;
if (SUCCEEDED(PathCchSkipRoot(path, &rootEnd)) && (*rootEnd != L'\0'))
{
auto const lastSegment = find_last_path_segment(path);
*parentPathLength = lastSegment - path;
hasParent = (*parentPathLength != 0);
}
return hasParent;
}
// Creates directories for the specified path, creating parent paths
// as needed.
inline HRESULT CreateDirectoryDeepNoThrow(PCWSTR path) WI_NOEXCEPT
{
if (::CreateDirectoryW(path, nullptr) == FALSE)
{
DWORD const lastError = ::GetLastError();
if (lastError == ERROR_PATH_NOT_FOUND)
{
size_t parentLength;
if (try_get_parent_path_range(path, &parentLength))
{
wistd::unique_ptr<wchar_t[]> parent(new (std::nothrow) wchar_t[parentLength + 1]);
RETURN_IF_NULL_ALLOC(parent.get());
RETURN_IF_FAILED(StringCchCopyNW(parent.get(), parentLength + 1, path, parentLength));
CreateDirectoryDeepNoThrow(parent.get()); // recurs
}
RETURN_IF_WIN32_BOOL_FALSE(::CreateDirectoryW(path, nullptr));
}
else if (lastError != ERROR_ALREADY_EXISTS)
{
RETURN_WIN32(lastError);
}
}
return S_OK;
}
#ifdef WIL_ENABLE_EXCEPTIONS
inline void CreateDirectoryDeep(PCWSTR path)
{
THROW_IF_FAILED(CreateDirectoryDeepNoThrow(path));
}
#endif // WIL_ENABLE_EXCEPTIONS
//! A strongly typed version of the Win32 API GetFullPathNameW.
//! Return a path in an allocated buffer for handling long paths.
//! Optionally return the pointer to the file name part.
template <typename string_type, size_t stackBufferLength = 256>
HRESULT GetFullPathNameW(PCWSTR file, string_type& path, _Outptr_opt_ PCWSTR* filePart = nullptr)
{
wil::assign_null_to_opt_param(filePart);
const auto hr = AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(path,
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNull) -> HRESULT
{
// Note that GetFullPathNameW() is not limited to MAX_PATH
// but it does take a fixed size buffer.
*valueLengthNeededWithNull = ::GetFullPathNameW(file, static_cast<DWORD>(valueLength), value, nullptr);
RETURN_LAST_ERROR_IF(*valueLengthNeededWithNull == 0);
WI_ASSERT((*value != L'\0') == (*valueLengthNeededWithNull < valueLength));
if (*valueLengthNeededWithNull < valueLength)
{
(*valueLengthNeededWithNull)++; // it fit, account for the null
}
return S_OK;
});
if (SUCCEEDED(hr) && filePart)
{
*filePart = wil::find_last_path_segment(details::string_maker<string_type>::get(path));
}
return hr;
}
#ifdef WIL_ENABLE_EXCEPTIONS
//! A strongly typed version of the Win32 API of GetFullPathNameW.
//! Return a path in an allocated buffer for handling long paths.
//! Optionally return the pointer to the file name part.
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
string_type GetFullPathNameW(PCWSTR file, _Outptr_opt_ PCWSTR* filePart = nullptr)
{
string_type result;
THROW_IF_FAILED((GetFullPathNameW<string_type, stackBufferLength>(file, result, filePart)));
return result;
}
#endif
enum class RemoveDirectoryOptions
{
None = 0,
KeepRootDirectory = 0x1
};
DEFINE_ENUM_FLAG_OPERATORS(RemoveDirectoryOptions);
namespace details
{
// Reparse points should not be traversed in most recursive walks of the file system,
// unless allowed through the appropriate reparse tag.
inline bool CanRecurseIntoDirectory(const FILE_ATTRIBUTE_TAG_INFO& info)
{
return (WI_IsFlagSet(info.FileAttributes, FILE_ATTRIBUTE_DIRECTORY) &&
(WI_IsFlagClear(info.FileAttributes, FILE_ATTRIBUTE_REPARSE_POINT) ||
(IsReparseTagDirectory(info.ReparseTag) || (info.ReparseTag == IO_REPARSE_TAG_WCI))));
}
// Retrieve a handle to a directory only if it is safe to recurse into.
inline wil::unique_hfile TryCreateFileCanRecurseIntoDirectory(PCWSTR path, PWIN32_FIND_DATAW fileFindData)
{
wil::unique_hfile result(CreateFileW(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE,
nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr));
if (result)
{
FILE_ATTRIBUTE_TAG_INFO fati;
if (GetFileInformationByHandleEx(result.get(), FileAttributeTagInfo, &fati, sizeof(fati)) &&
CanRecurseIntoDirectory(fati))
{
if (fileFindData)
{
// Refresh the found file's data now that we have secured the directory from external manipulation.
fileFindData->dwFileAttributes = fati.FileAttributes;
fileFindData->dwReserved0 = fati.ReparseTag;
}
}
else
{
result.reset();
}
}
return result;
}
}
// If inputPath is a non-normalized name be sure to pass an extended length form to ensure
// it can be addressed and deleted.
inline HRESULT RemoveDirectoryRecursiveNoThrow(PCWSTR inputPath, RemoveDirectoryOptions options = RemoveDirectoryOptions::None) WI_NOEXCEPT
{
wil::unique_hlocal_string path;
PATHCCH_OPTIONS combineOptions = PATHCCH_NONE;
if (is_extended_length_path(inputPath))
{
path = wil::make_hlocal_string_nothrow(inputPath);
RETURN_IF_NULL_ALLOC(path);
// PathAllocCombine will convert extended length paths to regular paths if shorter than
// MAX_PATH, avoid that behavior to provide access inputPath with non-normalized names.
combineOptions = PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH;
}
else
{
// For regular paths normalize here to get consistent results when searching and deleting.
RETURN_IF_FAILED(wil::GetFullPathNameW(inputPath, path));
combineOptions = PATHCCH_ALLOW_LONG_PATHS;
}
wil::unique_hlocal_string searchPath;
RETURN_IF_FAILED(::PathAllocCombine(path.get(), L"*", combineOptions, &searchPath));
WIN32_FIND_DATAW fd;
wil::unique_hfind findHandle(::FindFirstFileW(searchPath.get(), &fd));
RETURN_LAST_ERROR_IF(!findHandle);
for (;;)
{
// skip "." and ".."
if (!(WI_IsFlagSet(fd.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY) && path_is_dot_or_dotdot(fd.cFileName)))
{
// Need to form an extended length path to provide the ability to delete paths > MAX_PATH
// and files with non-normalized names (dots or spaces at the end).
wil::unique_hlocal_string pathToDelete;
RETURN_IF_FAILED(::PathAllocCombine(path.get(), fd.cFileName,
PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH | PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, &pathToDelete));
if (WI_IsFlagSet(fd.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY))
{
// Get a handle to the directory to delete, preventing it from being replaced to prevent writes which could be used
// to bypass permission checks, and verify that it is not a name surrogate (e.g. symlink, mount point, etc).
wil::unique_hfile recursivelyDeletableDirectoryHandle = details::TryCreateFileCanRecurseIntoDirectory(pathToDelete.get(), &fd);
if (recursivelyDeletableDirectoryHandle)
{
RemoveDirectoryOptions localOptions = options;
RETURN_IF_FAILED(RemoveDirectoryRecursiveNoThrow(pathToDelete.get(), WI_ClearFlag(localOptions, RemoveDirectoryOptions::KeepRootDirectory)));
}
else if (WI_IsFlagSet(fd.dwFileAttributes, FILE_ATTRIBUTE_REPARSE_POINT))
{
// This is a directory reparse point that should not be recursed. Delete it without traversing into it.
RETURN_IF_WIN32_BOOL_FALSE(::RemoveDirectoryW(pathToDelete.get()));
}
else
{
// Failed to grab a handle to the file or to read its attributes. This is not safe to recurse.
RETURN_WIN32(::GetLastError());
}
}
else
{
// note: if pathToDelete is read-only this will fail, consider adding
// RemoveDirectoryOptions::RemoveReadOnly to enable this behavior.
RETURN_IF_WIN32_BOOL_FALSE(::DeleteFileW(pathToDelete.get()));
}
}
if (!::FindNextFileW(findHandle.get(), &fd))
{
auto const err = ::GetLastError();
if (err == ERROR_NO_MORE_FILES)
{
break;
}
RETURN_WIN32(err);
}
}
if (WI_IsFlagClear(options, RemoveDirectoryOptions::KeepRootDirectory))
{
RETURN_IF_WIN32_BOOL_FALSE(::RemoveDirectoryW(path.get()));
}
return S_OK;
}
#ifdef WIL_ENABLE_EXCEPTIONS
inline void RemoveDirectoryRecursive(PCWSTR path, RemoveDirectoryOptions options = RemoveDirectoryOptions::None)
{
THROW_IF_FAILED(RemoveDirectoryRecursiveNoThrow(path, options));
}
#endif // WIL_ENABLE_EXCEPTIONS
// Range based for that supports Win32 structures that use NextEntryOffset as the basis of traversing
// a result buffer that contains data. This is used in the following FileIO calls:
// FileStreamInfo, FILE_STREAM_INFO
// FileIdBothDirectoryInfo, FILE_ID_BOTH_DIR_INFO
// FileFullDirectoryInfo, FILE_FULL_DIR_INFO
// FileIdExtdDirectoryInfo, FILE_ID_EXTD_DIR_INFO
// ReadDirectoryChangesW, FILE_NOTIFY_INFORMATION
template <typename T>
struct next_entry_offset_iterator
{
// Fulfill std::iterator_traits requirements
using difference_type = ptrdiff_t;
using value_type = T;
using pointer = const T*;
using reference = const T&;
#ifdef _XUTILITY_
using iterator_category = ::std::forward_iterator_tag;
#endif
next_entry_offset_iterator(T *iterable = __nullptr) : current_(iterable) {}
// range based for requires operator!=, operator++ and operator* to do its work
// on the type returned from begin() and end(), provide those here.
bool operator!=(const next_entry_offset_iterator& other) const { return current_ != other.current_; }
next_entry_offset_iterator& operator++()
{
current_ = (current_->NextEntryOffset != 0) ?
reinterpret_cast<T *>(reinterpret_cast<unsigned char*>(current_) + current_->NextEntryOffset) :
__nullptr;
return *this;
}
next_entry_offset_iterator operator++(int)
{
auto copy = *this;
++(*this);
return copy;
}
reference operator*() const WI_NOEXCEPT { return *current_; }
pointer operator->() const WI_NOEXCEPT { return current_; }
next_entry_offset_iterator<T> begin() { return *this; }
next_entry_offset_iterator<T> end() { return next_entry_offset_iterator<T>(); }
T* current_;
};
template <typename T>
next_entry_offset_iterator<T> create_next_entry_offset_iterator(T* p)
{
return next_entry_offset_iterator<T>(p);
}
#pragma region Folder Watcher
// Example use in exception based code:
// auto watcher = wil::make_folder_watcher(folder.Path().c_str(), true, wil::allChangeEvents, []()
// {
// // respond
// });
//
// Example use in result code based code:
// wil::unique_folder_watcher watcher;
// THROW_IF_FAILED(watcher.create(folder, true, wil::allChangeEvents, []()
// {
// // respond
// }));
enum class FolderChangeEvent : DWORD
{
ChangesLost = 0, // requies special handling, reset state as events were lost
Added = FILE_ACTION_ADDED,
Removed = FILE_ACTION_REMOVED,
Modified = FILE_ACTION_MODIFIED,
RenameOldName = FILE_ACTION_RENAMED_OLD_NAME,
RenameNewName = FILE_ACTION_RENAMED_NEW_NAME,
};
enum class FolderChangeEvents : DWORD
{
None = 0,
FileName = FILE_NOTIFY_CHANGE_FILE_NAME,
DirectoryName = FILE_NOTIFY_CHANGE_DIR_NAME,
Attributes = FILE_NOTIFY_CHANGE_ATTRIBUTES,
FileSize = FILE_NOTIFY_CHANGE_SIZE,
LastWriteTime = FILE_NOTIFY_CHANGE_LAST_WRITE,
Security = FILE_NOTIFY_CHANGE_SECURITY,
All = FILE_NOTIFY_CHANGE_FILE_NAME |
FILE_NOTIFY_CHANGE_DIR_NAME |
FILE_NOTIFY_CHANGE_ATTRIBUTES |
FILE_NOTIFY_CHANGE_SIZE |
FILE_NOTIFY_CHANGE_LAST_WRITE |
FILE_NOTIFY_CHANGE_SECURITY
};
DEFINE_ENUM_FLAG_OPERATORS(FolderChangeEvents);
/// @cond
namespace details
{
struct folder_watcher_state
{
folder_watcher_state(wistd::function<void()> &&callback) : m_callback(wistd::move(callback))
{
}
wistd::function<void()> m_callback;
// Order is important, need to close the thread pool wait before the change handle.
unique_hfind_change m_findChangeHandle;
unique_threadpool_wait m_threadPoolWait;
};
inline void delete_folder_watcher_state(_In_opt_ folder_watcher_state *storage) { delete storage; }
typedef resource_policy<folder_watcher_state *, decltype(&details::delete_folder_watcher_state),
details::delete_folder_watcher_state, details::pointer_access_none> folder_watcher_state_resource_policy;
}
/// @endcond
template <typename storage_t, typename err_policy = err_exception_policy>
class folder_watcher_t : public storage_t
{
public:
// forward all base class constructors...
template <typename... args_t>
explicit folder_watcher_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward<args_t>(args)...) {}
// HRESULT or void error handling...
typedef typename err_policy::result result;
// Exception-based constructors
folder_watcher_t(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void()> &&callback)
{
static_assert(wistd::is_same<void, result>::value, "this constructor requires exceptions; use the create method");
create(folderToWatch, isRecursive, filter, wistd::move(callback));
}
result create(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void()> &&callback)
{
return err_policy::HResult(create_common(folderToWatch, isRecursive, filter, wistd::move(callback)));
}
private:
// Factored into a standalone function to support Clang which does not support conversion of stateless lambdas
// to __stdcall
static void __stdcall callback(PTP_CALLBACK_INSTANCE /*Instance*/, void *context, TP_WAIT *pThreadPoolWait, TP_WAIT_RESULT /*result*/)
{
auto watcherState = static_cast<details::folder_watcher_state *>(context);
watcherState->m_callback();
// Rearm the wait. Should not fail with valid parameters.
FindNextChangeNotification(watcherState->m_findChangeHandle.get());
SetThreadpoolWait(pThreadPoolWait, watcherState->m_findChangeHandle.get(), __nullptr);
}
// This function exists to avoid template expansion of this code based on err_policy.
HRESULT create_common(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void()> &&callback)
{
wistd::unique_ptr<details::folder_watcher_state> watcherState(new(std::nothrow) details::folder_watcher_state(wistd::move(callback)));
RETURN_IF_NULL_ALLOC(watcherState);
watcherState->m_findChangeHandle.reset(FindFirstChangeNotificationW(folderToWatch, isRecursive, static_cast<DWORD>(filter)));
RETURN_LAST_ERROR_IF(!watcherState->m_findChangeHandle);
watcherState->m_threadPoolWait.reset(CreateThreadpoolWait(&folder_watcher_t::callback, watcherState.get(), __nullptr));
RETURN_LAST_ERROR_IF(!watcherState->m_threadPoolWait);
this->reset(watcherState.release()); // no more failures after this, pass ownership
SetThreadpoolWait(this->get()->m_threadPoolWait.get(), this->get()->m_findChangeHandle.get(), __nullptr);
return S_OK;
}
};
typedef unique_any_t<folder_watcher_t<details::unique_storage<details::folder_watcher_state_resource_policy>, err_returncode_policy>> unique_folder_watcher_nothrow;
inline unique_folder_watcher_nothrow make_folder_watcher_nothrow(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void()> &&callback) WI_NOEXCEPT
{
unique_folder_watcher_nothrow watcher;
watcher.create(folderToWatch, isRecursive, filter, wistd::move(callback));
return watcher; // caller must test for success using if (watcher)
}
#ifdef WIL_ENABLE_EXCEPTIONS
typedef unique_any_t<folder_watcher_t<details::unique_storage<details::folder_watcher_state_resource_policy>, err_exception_policy>> unique_folder_watcher;
inline unique_folder_watcher make_folder_watcher(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void()> &&callback)
{
return unique_folder_watcher(folderToWatch, isRecursive, filter, wistd::move(callback));
}
#endif // WIL_ENABLE_EXCEPTIONS
#pragma endregion
#pragma region Folder Reader
// Example use for throwing:
// auto reader = wil::make_folder_change_reader(folder.Path().c_str(), true, wil::FolderChangeEvents::All,
// [](wil::FolderChangeEvent event, PCWSTR fileName)
// {
// switch (event)
// {
// case wil::FolderChangeEvent::ChangesLost: break;
// case wil::FolderChangeEvent::Added: break;
// case wil::FolderChangeEvent::Removed: break;
// case wil::FolderChangeEvent::Modified: break;
// case wil::FolderChangeEvent::RenamedOldName: break;
// case wil::FolderChangeEvent::RenamedNewName: break;
// });
//
// Example use for non throwing:
// wil::unique_folder_change_reader_nothrow reader;
// THROW_IF_FAILED(reader.create(folder, true, wil::FolderChangeEvents::All,
// [](wil::FolderChangeEvent event, PCWSTR fileName)
// {
// // handle changes
// }));
//
// @cond
namespace details
{
struct folder_change_reader_state
{
folder_change_reader_state(bool isRecursive, FolderChangeEvents filter, wistd::function<void(FolderChangeEvent, PCWSTR)> &&callback)
: m_callback(wistd::move(callback)), m_isRecursive(isRecursive), m_filter(filter)
{
}
~folder_change_reader_state()
{
if (m_tpIo != __nullptr)
{
TP_IO *tpIo = m_tpIo;
// Indicate to the callback function that this object is being torn
// down.
{
auto autoLock = m_cancelLock.lock_exclusive();
m_tpIo = __nullptr;
}
// Cancel IO to terminate the file system monitoring operation.
if (m_folderHandle)
{
CancelIoEx(m_folderHandle.get(), &m_overlapped);
}
// Wait for callbacks to complete.
//
// N.B. This is a blocking call and must not be made within a
// callback or within a lock which is taken inside the
// callback.
WaitForThreadpoolIoCallbacks(tpIo, TRUE);
CloseThreadpoolIo(tpIo);
}
}
HRESULT StartIo()
{
// Unfortunately we have to handle ref-counting of IOs on behalf of the
// thread pool.
StartThreadpoolIo(m_tpIo);
HRESULT hr = ReadDirectoryChangesW(m_folderHandle.get(), m_readBuffer, sizeof(m_readBuffer),
m_isRecursive, static_cast<DWORD>(m_filter), __nullptr, &m_overlapped, __nullptr) ?
S_OK : HRESULT_FROM_WIN32(::GetLastError());
if (FAILED(hr))
{
// This operation does not have the usual semantic of returning
// ERROR_IO_PENDING.
// WI_ASSERT(hr != HRESULT_FROM_WIN32(ERROR_IO_PENDING));
// If the operation failed for whatever reason, ensure the TP
// ref counts are accurate.
CancelThreadpoolIo(m_tpIo);
}
return hr;
}
// void (wil::FolderChangeEvent event, PCWSTR fileName)
wistd::function<void(FolderChangeEvent, PCWSTR)> m_callback;
unique_handle m_folderHandle;
BOOL m_isRecursive = FALSE;
FolderChangeEvents m_filter = FolderChangeEvents::None;
OVERLAPPED m_overlapped{};
TP_IO *m_tpIo = __nullptr;
srwlock m_cancelLock;
char m_readBuffer[4096]; // Consider alternative buffer sizes. With 512 byte buffer i was not able to observe overflow.
};
inline void delete_folder_change_reader_state(_In_opt_ folder_change_reader_state *storage) { delete storage; }
typedef resource_policy<folder_change_reader_state *, decltype(&details::delete_folder_change_reader_state),
details::delete_folder_change_reader_state, details::pointer_access_none> folder_change_reader_state_resource_policy;
}
/// @endcond
template <typename storage_t, typename err_policy = err_exception_policy>
class folder_change_reader_t : public storage_t
{
public:
// forward all base class constructors...
template <typename... args_t>
explicit folder_change_reader_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward<args_t>(args)...) {}
// HRESULT or void error handling...
typedef typename err_policy::result result;
// Exception-based constructors
folder_change_reader_t(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void(FolderChangeEvent, PCWSTR)> &&callback)
{
static_assert(wistd::is_same<void, result>::value, "this constructor requires exceptions; use the create method");
create(folderToWatch, isRecursive, filter, wistd::move(callback));
}
result create(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void(FolderChangeEvent, PCWSTR)> &&callback)
{
return err_policy::HResult(create_common(folderToWatch, isRecursive, filter, wistd::move(callback)));
}
wil::unique_hfile& folder_handle() { return this->get()->m_folderHandle; }
private:
// Factored into a standalone function to support Clang which does not support conversion of stateless lambdas
// to __stdcall
static void __stdcall callback(PTP_CALLBACK_INSTANCE /* Instance */, void *context, void * /*overlapped*/,
ULONG result, ULONG_PTR /* BytesTransferred */, TP_IO * /* Io */)
{
auto readerState = static_cast<details::folder_change_reader_state *>(context);
// WI_ASSERT(overlapped == &readerState->m_overlapped);
bool requeue = true;
if (result == ERROR_SUCCESS)
{
for (auto const& info : create_next_entry_offset_iterator(reinterpret_cast<FILE_NOTIFY_INFORMATION *>(readerState->m_readBuffer)))
{
wchar_t realtiveFileName[MAX_PATH];
StringCchCopyNW(realtiveFileName, ARRAYSIZE(realtiveFileName), info.FileName, info.FileNameLength / sizeof(info.FileName[0]));
readerState->m_callback(static_cast<FolderChangeEvent>(info.Action), realtiveFileName);
}
}
else if (result == ERROR_NOTIFY_ENUM_DIR)
{
readerState->m_callback(FolderChangeEvent::ChangesLost, __nullptr);
}
else
{
requeue = false;
}
if (requeue)
{
// If the lock is held non-shared or the TP IO is nullptr, this
// structure is being torn down. Otherwise, monitor for further
// changes.
auto autoLock = readerState->m_cancelLock.try_lock_shared();
if (autoLock && readerState->m_tpIo)
{
readerState->StartIo(); // ignoring failure here
}
}
}
// This function exists to avoid template expansion of this code based on err_policy.
HRESULT create_common(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void(FolderChangeEvent, PCWSTR)> &&callback)
{
wistd::unique_ptr<details::folder_change_reader_state> readerState(new(std::nothrow) details::folder_change_reader_state(
isRecursive, filter, wistd::move(callback)));
RETURN_IF_NULL_ALLOC(readerState);
readerState->m_folderHandle.reset(CreateFileW(folderToWatch,
FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
__nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, __nullptr));
RETURN_LAST_ERROR_IF(!readerState->m_folderHandle);
readerState->m_tpIo = CreateThreadpoolIo(readerState->m_folderHandle.get(), &folder_change_reader_t::callback, readerState.get(), __nullptr);
RETURN_LAST_ERROR_IF_NULL(readerState->m_tpIo);
RETURN_IF_FAILED(readerState->StartIo());
this->reset(readerState.release());
return S_OK;
}
};
typedef unique_any_t<folder_change_reader_t<details::unique_storage<details::folder_change_reader_state_resource_policy>, err_returncode_policy>> unique_folder_change_reader_nothrow;
inline unique_folder_change_reader_nothrow make_folder_change_reader_nothrow(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter,
wistd::function<void(FolderChangeEvent, PCWSTR)> &&callback) WI_NOEXCEPT
{
unique_folder_change_reader_nothrow watcher;
watcher.create(folderToWatch, isRecursive, filter, wistd::move(callback));
return watcher; // caller must test for success using if (watcher)
}
#ifdef WIL_ENABLE_EXCEPTIONS
typedef unique_any_t<folder_change_reader_t<details::unique_storage<details::folder_change_reader_state_resource_policy>, err_exception_policy>> unique_folder_change_reader;
inline unique_folder_change_reader make_folder_change_reader(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter,
wistd::function<void(FolderChangeEvent, PCWSTR)> &&callback)
{
return unique_folder_change_reader(folderToWatch, isRecursive, filter, wistd::move(callback));
}
#endif // WIL_ENABLE_EXCEPTIONS
#pragma endregion
//! Dos and VolumeGuid paths are always extended length paths with the \\?\ prefix.
enum class VolumePrefix
{
Dos = VOLUME_NAME_DOS, // Extended Dos Device path form, e.g. \\?\C:\Users\Chris\AppData\Local\Temp\wil8C31.tmp
VolumeGuid = VOLUME_NAME_GUID, // \\?\Volume{588fb606-b95b-4eae-b3cb-1e49861aaf18}\Users\Chris\AppData\Local\Temp\wil8C31.tmp
// The following are special paths which can't be used with Win32 APIs, but are useful in other scenarios.
None = VOLUME_NAME_NONE, // Path without the volume root, e.g. \Users\Chris\AppData\Local\Temp\wil8C31.tmp
NtObjectName = VOLUME_NAME_NT, // Unique name used by Object Manager, e.g. \Device\HarddiskVolume4\Users\Chris\AppData\Local\Temp\wil8C31.tmp
};
enum class PathOptions
{
Normalized = FILE_NAME_NORMALIZED,
Opened = FILE_NAME_OPENED,
};
DEFINE_ENUM_FLAG_OPERATORS(PathOptions);
/** A strongly typed version of the Win32 API GetFinalPathNameByHandleW.
Get the full path name in different forms
Use this instead + VolumePrefix::None instead of GetFileInformationByHandleEx(FileNameInfo) to
get that path form. */
template <typename string_type, size_t stackBufferLength = 256>
HRESULT GetFinalPathNameByHandleW(HANDLE fileHandle, string_type& path,
wil::VolumePrefix volumePrefix = wil::VolumePrefix::Dos, wil::PathOptions options = wil::PathOptions::Normalized)
{
return AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(path,
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNull) -> HRESULT
{
*valueLengthNeededWithNull = ::GetFinalPathNameByHandleW(fileHandle, value, static_cast<DWORD>(valueLength),
static_cast<DWORD>(volumePrefix) | static_cast<DWORD>(options));
RETURN_LAST_ERROR_IF(*valueLengthNeededWithNull == 0);
WI_ASSERT((*value != L'\0') == (*valueLengthNeededWithNull < valueLength));
if (*valueLengthNeededWithNull < valueLength)
{
(*valueLengthNeededWithNull)++; // it fit, account for the null
}
return S_OK;
});
}
#ifdef WIL_ENABLE_EXCEPTIONS
/** A strongly typed version of the Win32 API GetFinalPathNameByHandleW.
Get the full path name in different forms. Use this + VolumePrefix::None
instead of GetFileInformationByHandleEx(FileNameInfo) to get that path form. */
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
string_type GetFinalPathNameByHandleW(HANDLE fileHandle,
wil::VolumePrefix volumePrefix = wil::VolumePrefix::Dos, wil::PathOptions options = wil::PathOptions::Normalized)
{
string_type result;
THROW_IF_FAILED((GetFinalPathNameByHandleW<string_type, stackBufferLength>(fileHandle, result, volumePrefix, options)));
return result;
}
#endif
//! A strongly typed version of the Win32 API of GetCurrentDirectoryW.
//! Return a path in an allocated buffer for handling long paths.
template <typename string_type, size_t stackBufferLength = 256>
HRESULT GetCurrentDirectoryW(string_type& path)
{
return AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(path,
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNull) -> HRESULT
{
*valueLengthNeededWithNull = ::GetCurrentDirectoryW(static_cast<DWORD>(valueLength), value);
RETURN_LAST_ERROR_IF(*valueLengthNeededWithNull == 0);
WI_ASSERT((*value != L'\0') == (*valueLengthNeededWithNull < valueLength));
if (*valueLengthNeededWithNull < valueLength)
{
(*valueLengthNeededWithNull)++; // it fit, account for the null
}
return S_OK;
});
}
#ifdef WIL_ENABLE_EXCEPTIONS
//! A strongly typed version of the Win32 API of GetCurrentDirectoryW.
//! Return a path in an allocated buffer for handling long paths.
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
string_type GetCurrentDirectoryW()
{
string_type result;
THROW_IF_FAILED((GetCurrentDirectoryW<string_type, stackBufferLength>(result)));
return result;
}
#endif
// TODO: add support for these and other similar APIs.
// GetShortPathNameW()
// GetLongPathNameW()
// GetWindowsDirectory()
// GetTempDirectory()
/// @cond
namespace details
{
template <FILE_INFO_BY_HANDLE_CLASS infoClass> struct MapInfoClassToInfoStruct; // failure to map is a usage error caught by the compiler
#define MAP_INFOCLASS_TO_STRUCT(InfoClass, InfoStruct, IsFixed, Extra) \
template <> struct MapInfoClassToInfoStruct<InfoClass> \
{ \
typedef InfoStruct type; \
static bool const isFixed = IsFixed; \
static size_t const extraSize = Extra; \
};
MAP_INFOCLASS_TO_STRUCT(FileBasicInfo, FILE_BASIC_INFO, true, 0);
MAP_INFOCLASS_TO_STRUCT(FileStandardInfo, FILE_STANDARD_INFO, true, 0);
MAP_INFOCLASS_TO_STRUCT(FileNameInfo, FILE_NAME_INFO, false, 32);
MAP_INFOCLASS_TO_STRUCT(FileRenameInfo, FILE_RENAME_INFO, false, 32);
MAP_INFOCLASS_TO_STRUCT(FileDispositionInfo, FILE_DISPOSITION_INFO, true, 0);
MAP_INFOCLASS_TO_STRUCT(FileAllocationInfo, FILE_ALLOCATION_INFO, true, 0);
MAP_INFOCLASS_TO_STRUCT(FileEndOfFileInfo, FILE_END_OF_FILE_INFO, true, 0);
MAP_INFOCLASS_TO_STRUCT(FileStreamInfo, FILE_STREAM_INFO, false, 32);
MAP_INFOCLASS_TO_STRUCT(FileCompressionInfo, FILE_COMPRESSION_INFO, true, 0);
MAP_INFOCLASS_TO_STRUCT(FileAttributeTagInfo, FILE_ATTRIBUTE_TAG_INFO, true, 0);
MAP_INFOCLASS_TO_STRUCT(FileIdBothDirectoryInfo, FILE_ID_BOTH_DIR_INFO, false, 4096);
MAP_INFOCLASS_TO_STRUCT(FileIdBothDirectoryRestartInfo, FILE_ID_BOTH_DIR_INFO, true, 0);
MAP_INFOCLASS_TO_STRUCT(FileIoPriorityHintInfo, FILE_IO_PRIORITY_HINT_INFO, true, 0);
MAP_INFOCLASS_TO_STRUCT(FileRemoteProtocolInfo, FILE_REMOTE_PROTOCOL_INFO, true, 0);
MAP_INFOCLASS_TO_STRUCT(FileFullDirectoryInfo, FILE_FULL_DIR_INFO, false, 4096);
MAP_INFOCLASS_TO_STRUCT(FileFullDirectoryRestartInfo, FILE_FULL_DIR_INFO, true, 0);
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
MAP_INFOCLASS_TO_STRUCT(FileStorageInfo, FILE_STORAGE_INFO, true, 0);
MAP_INFOCLASS_TO_STRUCT(FileAlignmentInfo, FILE_ALIGNMENT_INFO, true, 0);
MAP_INFOCLASS_TO_STRUCT(FileIdInfo, FILE_ID_INFO, true, 0);
MAP_INFOCLASS_TO_STRUCT(FileIdExtdDirectoryInfo, FILE_ID_EXTD_DIR_INFO, false, 4096);
MAP_INFOCLASS_TO_STRUCT(FileIdExtdDirectoryRestartInfo, FILE_ID_EXTD_DIR_INFO, true, 0);
#endif
// Type unsafe version used in the implementation to avoid template bloat.
inline HRESULT GetFileInfo(HANDLE fileHandle, FILE_INFO_BY_HANDLE_CLASS infoClass, size_t allocationSize,
_Outptr_result_nullonfailure_ void **result)
{
*result = nullptr;
wistd::unique_ptr<char[]> resultHolder(new(std::nothrow) char[allocationSize]);
RETURN_IF_NULL_ALLOC(resultHolder);
for (;;)
{
if (GetFileInformationByHandleEx(fileHandle, infoClass, resultHolder.get(), static_cast<DWORD>(allocationSize)))
{
*result = resultHolder.release();
break;
}
else
{
DWORD const lastError = ::GetLastError();
if (lastError == ERROR_MORE_DATA)
{
allocationSize *= 2;
resultHolder.reset(new(std::nothrow) char[allocationSize]);
RETURN_IF_NULL_ALLOC(resultHolder);
}
else if (lastError == ERROR_NO_MORE_FILES) // for folder enumeration cases
{
break;
}
else if (lastError == ERROR_INVALID_PARAMETER) // operation not supported by file system
{
return HRESULT_FROM_WIN32(lastError);
}
else
{
RETURN_WIN32(lastError);
}
}
}
return S_OK;
}
}
/// @endcond
/** Get file information for a variable sized structure, returns an HRESULT.
~~~
wistd::unique_ptr<FILE_NAME_INFO> fileNameInfo;
RETURN_IF_FAILED(GetFileInfoNoThrow<FileNameInfo>(fileHandle, fileNameInfo));
~~~
*/
template <FILE_INFO_BY_HANDLE_CLASS infoClass, typename wistd::enable_if<!details::MapInfoClassToInfoStruct<infoClass>::isFixed, int>::type = 0>
HRESULT GetFileInfoNoThrow(HANDLE fileHandle, wistd::unique_ptr<typename details::MapInfoClassToInfoStruct<infoClass>::type> &result) WI_NOEXCEPT
{
void *rawResult;
HRESULT hr = details::GetFileInfo(fileHandle, infoClass,
sizeof(typename details::MapInfoClassToInfoStruct<infoClass>::type) + details::MapInfoClassToInfoStruct<infoClass>::extraSize,
&rawResult);
result.reset(static_cast<typename details::MapInfoClassToInfoStruct<infoClass>::type*>(rawResult));
RETURN_HR_IF_EXPECTED(hr, hr == E_INVALIDARG); // operation not supported by file system
RETURN_IF_FAILED(hr);
return S_OK;
}
/** Get file information for a fixed sized structure, returns an HRESULT.
~~~
FILE_BASIC_INFO fileBasicInfo;
RETURN_IF_FAILED(GetFileInfoNoThrow<FileBasicInfo>(fileHandle, &fileBasicInfo));
~~~
*/
template <FILE_INFO_BY_HANDLE_CLASS infoClass, typename wistd::enable_if<details::MapInfoClassToInfoStruct<infoClass>::isFixed, int>::type = 0>
HRESULT GetFileInfoNoThrow(HANDLE fileHandle, _Out_ typename details::MapInfoClassToInfoStruct<infoClass>::type *result) WI_NOEXCEPT
{
const HRESULT hr = GetFileInformationByHandleEx(fileHandle, infoClass, result, sizeof(*result)) ?
S_OK : HRESULT_FROM_WIN32(::GetLastError());
RETURN_HR_IF_EXPECTED(hr, hr == E_INVALIDARG); // operation not supported by file system
RETURN_IF_FAILED(hr);
return S_OK;
}
#ifdef _CPPUNWIND
/** Get file information for a fixed sized structure, throws on failure.
~~~
auto fileBasicInfo = GetFileInfo<FileBasicInfo>(fileHandle);
~~~
*/
template <FILE_INFO_BY_HANDLE_CLASS infoClass, typename wistd::enable_if<details::MapInfoClassToInfoStruct<infoClass>::isFixed, int>::type = 0>
typename details::MapInfoClassToInfoStruct<infoClass>::type GetFileInfo(HANDLE fileHandle)
{
typename details::MapInfoClassToInfoStruct<infoClass>::type result;
THROW_IF_FAILED(GetFileInfoNoThrow<infoClass>(fileHandle, &result));
return result;
}
/** Get file information for a variable sized structure, throws on failure.
~~~
auto fileBasicInfo = GetFileInfo<FileNameInfo>(fileHandle);
~~~
*/
template <FILE_INFO_BY_HANDLE_CLASS infoClass, typename wistd::enable_if<!details::MapInfoClassToInfoStruct<infoClass>::isFixed, int>::type = 0>
wistd::unique_ptr<typename details::MapInfoClassToInfoStruct<infoClass>::type> GetFileInfo(HANDLE fileHandle)
{
wistd::unique_ptr<typename details::MapInfoClassToInfoStruct<infoClass>::type> result;
THROW_IF_FAILED(GetFileInfoNoThrow<infoClass>(fileHandle, result));
return result;
}
#endif // _CPPUNWIND
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
}
#endif // __WIL_FILESYSTEM_INCLUDED

View File

@ -0,0 +1,277 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_REGISTRY_INCLUDED
#define __WIL_REGISTRY_INCLUDED
#ifdef _KERNEL_MODE
#error This header is not supported in kernel-mode.
#endif
#include <winreg.h>
#include <new.h> // new(std::nothrow)
#include "resource.h" // unique_hkey
namespace wil
{
//! The key name includes the absolute path of the key in the registry, always starting at a
//! base key, for example, HKEY_LOCAL_MACHINE.
size_t const max_registry_key_name_length = 255;
//! The maximum number of characters allowed in a registry value's name.
size_t const max_registry_value_name_length = 16383;
// unique_registry_watcher/unique_registry_watcher_nothrow/unique_registry_watcher_failfast
// These classes make it easy to execute a provided function when a
// registry key changes (optionally recursively). Specify the key
// either as a root key + path, or an open registry handle as wil::unique_hkey
// or a raw HKEY value (that will be duplicated).
//
// Example use with exceptions base error handling:
// auto watcher = wil::make_registry_watcher(HKEY_CURRENT_USER, L"Software\\MyApp", true, wil::RegistryChangeKind changeKind[]
// {
// if (changeKind == RegistryChangeKind::Delete)
// {
// watcher.reset();
// }
// // invalidate cached registry data here
// });
//
// Example use with error code base error handling:
// auto watcher = wil::make_registry_watcher_nothrow(HKEY_CURRENT_USER, L"Software\\MyApp", true, wil::RegistryChangeKind[]
// {
// // invalidate cached registry data here
// });
// RETURN_IF_NULL_ALLOC(watcher);
enum class RegistryChangeKind
{
Modify = 0,
Delete = 1,
};
/// @cond
namespace details
{
struct registry_watcher_state
{
registry_watcher_state(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
: m_callback(wistd::move(callback)), m_keyToWatch(wistd::move(keyToWatch)), m_isRecursive(isRecursive)
{
}
wistd::function<void(RegistryChangeKind)> m_callback;
unique_hkey m_keyToWatch;
unique_event_nothrow m_eventHandle;
// While not strictly needed since this is ref counted the thread pool wait
// should be last to ensure that the other members are valid
// when it is destructed as it will reference them.
unique_threadpool_wait m_threadPoolWait;
bool m_isRecursive;
volatile long m_refCount = 1;
srwlock m_lock;
// Returns true if the refcount can be increased from a non zero value,
// false it was zero impling that the object is in or on the way to the destructor.
// In this case ReleaseFromCallback() should not be called.
bool TryAddRef()
{
return ::InterlockedIncrement(&m_refCount) > 1;
}
void Release()
{
auto lock = m_lock.lock_exclusive();
if (0 == ::InterlockedDecrement(&m_refCount))
{
lock.reset(); // leave the lock before deleting it.
delete this;
}
}
void ReleaseFromCallback(bool rearm)
{
auto lock = m_lock.lock_exclusive();
if (0 == ::InterlockedDecrement(&m_refCount))
{
// Destroy the thread pool wait now to avoid the wait that would occur in the
// destructor. That wait would cause a deadlock since we are doing this from the callback.
::CloseThreadpoolWait(m_threadPoolWait.release());
lock.reset(); // leave the lock before deleting it.
delete this;
// Sleep(1); // Enable for testing to find use after free bugs.
}
else if (rearm)
{
::SetThreadpoolWait(m_threadPoolWait.get(), m_eventHandle.get(), nullptr);
}
}
};
inline void delete_registry_watcher_state(_In_opt_ registry_watcher_state *watcherStorage) { watcherStorage->Release(); }
typedef resource_policy<registry_watcher_state *, decltype(&details::delete_registry_watcher_state),
details::delete_registry_watcher_state, details::pointer_access_none> registry_watcher_state_resource_policy;
}
/// @endcond
template <typename storage_t, typename err_policy = err_exception_policy>
class registry_watcher_t : public storage_t
{
public:
// forward all base class constructors...
template <typename... args_t>
explicit registry_watcher_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward<args_t>(args)...) {}
// HRESULT or void error handling...
typedef typename err_policy::result result;
// Exception-based constructors
registry_watcher_t(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
{
static_assert(wistd::is_same<void, result>::value, "this constructor requires exceptions; use the create method");
create(rootKey, subKey, isRecursive, wistd::move(callback));
}
registry_watcher_t(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
{
static_assert(wistd::is_same<void, result>::value, "this constructor requires exceptions; use the create method");
create(wistd::move(keyToWatch), isRecursive, wistd::move(callback));
}
// Pass a root key, sub key pair or use an empty string to use rootKey as the key to watch.
result create(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
{
// Most use will want to create the key, consider adding an option for open as a future design change.
unique_hkey keyToWatch;
HRESULT hr = HRESULT_FROM_WIN32(RegCreateKeyExW(rootKey, subKey, 0, nullptr, 0, KEY_NOTIFY, nullptr, &keyToWatch, nullptr));
if (FAILED(hr))
{
return err_policy::HResult(hr);
}
return err_policy::HResult(create_common(wistd::move(keyToWatch), isRecursive, wistd::move(callback)));
}
result create(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
{
return err_policy::HResult(create_common(wistd::move(keyToWatch), isRecursive, wistd::move(callback)));
}
private:
// Factored into a standalone function to support Clang which does not support conversion of stateless lambdas
// to __stdcall
static void __stdcall callback(PTP_CALLBACK_INSTANCE, void *context, TP_WAIT *, TP_WAIT_RESULT)
{
#ifndef __WIL_REGISTRY_CHANGE_CALLBACK_TEST
#define __WIL_REGISTRY_CHANGE_CALLBACK_TEST
#endif
__WIL_REGISTRY_CHANGE_CALLBACK_TEST
auto watcherState = static_cast<details::registry_watcher_state *>(context);
if (watcherState->TryAddRef())
{
// using auto reset event so don't need to manually reset.
// failure here is a programming error.
const LSTATUS error = RegNotifyChangeKeyValue(watcherState->m_keyToWatch.get(), watcherState->m_isRecursive,
REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_THREAD_AGNOSTIC,
watcherState->m_eventHandle.get(), TRUE);
// Call the client before re-arming to ensure that multiple callbacks don't
// run concurrently.
switch (error)
{
case ERROR_SUCCESS:
case ERROR_ACCESS_DENIED:
// Normal modification: send RegistryChangeKind::Modify and re-arm.
watcherState->m_callback(RegistryChangeKind::Modify);
watcherState->ReleaseFromCallback(true);
break;
case ERROR_KEY_DELETED:
// Key deleted, send RegistryChangeKind::Delete, do not re-arm.
watcherState->m_callback(RegistryChangeKind::Delete);
watcherState->ReleaseFromCallback(false);
break;
case ERROR_HANDLE_REVOKED:
// Handle revoked. This can occur if the user session ends before
// the watcher shuts-down. Disarm silently since there is generally no way to respond.
watcherState->ReleaseFromCallback(false);
break;
default:
FAIL_FAST_HR(HRESULT_FROM_WIN32(error));
}
}
}
// This function exists to avoid template expansion of this code based on err_policy.
HRESULT create_common(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
{
wistd::unique_ptr<details::registry_watcher_state> watcherState(new(std::nothrow) details::registry_watcher_state(
wistd::move(keyToWatch), isRecursive, wistd::move(callback)));
RETURN_IF_NULL_ALLOC(watcherState);
RETURN_IF_FAILED(watcherState->m_eventHandle.create());
RETURN_IF_WIN32_ERROR(RegNotifyChangeKeyValue(watcherState->m_keyToWatch.get(),
watcherState->m_isRecursive, REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_THREAD_AGNOSTIC,
watcherState->m_eventHandle.get(), TRUE));
watcherState->m_threadPoolWait.reset(CreateThreadpoolWait(&registry_watcher_t::callback, watcherState.get(), nullptr));
RETURN_LAST_ERROR_IF(!watcherState->m_threadPoolWait);
storage_t::reset(watcherState.release()); // no more failures after this, pass ownership
SetThreadpoolWait(storage_t::get()->m_threadPoolWait.get(), storage_t::get()->m_eventHandle.get(), nullptr);
return S_OK;
}
};
typedef unique_any_t<registry_watcher_t<details::unique_storage<details::registry_watcher_state_resource_policy>, err_returncode_policy>> unique_registry_watcher_nothrow;
typedef unique_any_t<registry_watcher_t<details::unique_storage<details::registry_watcher_state_resource_policy>, err_failfast_policy>> unique_registry_watcher_failfast;
inline unique_registry_watcher_nothrow make_registry_watcher_nothrow(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback) WI_NOEXCEPT
{
unique_registry_watcher_nothrow watcher;
watcher.create(rootKey, subKey, isRecursive, wistd::move(callback));
return watcher; // caller must test for success using if (watcher)
}
inline unique_registry_watcher_nothrow make_registry_watcher_nothrow(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback) WI_NOEXCEPT
{
unique_registry_watcher_nothrow watcher;
watcher.create(wistd::move(keyToWatch), isRecursive, wistd::move(callback));
return watcher; // caller must test for success using if (watcher)
}
inline unique_registry_watcher_failfast make_registry_watcher_failfast(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
{
return unique_registry_watcher_failfast(rootKey, subKey, isRecursive, wistd::move(callback));
}
inline unique_registry_watcher_failfast make_registry_watcher_failfast(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
{
return unique_registry_watcher_failfast(wistd::move(keyToWatch), isRecursive, wistd::move(callback));
}
#ifdef WIL_ENABLE_EXCEPTIONS
typedef unique_any_t<registry_watcher_t<details::unique_storage<details::registry_watcher_state_resource_policy>, err_exception_policy >> unique_registry_watcher;
inline unique_registry_watcher make_registry_watcher(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
{
return unique_registry_watcher(rootKey, subKey, isRecursive, wistd::move(callback));
}
inline unique_registry_watcher make_registry_watcher(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
{
return unique_registry_watcher(wistd::move(keyToWatch), isRecursive, wistd::move(callback));
}
#endif // WIL_ENABLE_EXCEPTIONS
} // namespace wil
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,127 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
// Note: When origination is enabled by including this file, origination is done as part of the RETURN_* and THROW_* macros. Before originating
// a new error we will observe whether there is already an error payload associated with the current thread. If there is, and the HRESULTs match,
// then a new error will not be originated. Otherwise we will overwrite it with a new origination. The ABI boundary for WinRT APIs will check the
// per-thread error information. The act of checking the error clears it, so there should be minimal risk of failing to originate distinct errors
// simply because the HRESULTs match.
//
// For THROW_ macros we will examine the thread-local error storage once per throw. So typically once, with additional calls if the exception is
// caught and re-thrown.
//
// For RETURN_ macros we will have to examine the thread-local error storage once per frame as the call stack unwinds. Because error conditions
// -should- be uncommon the performance impact of checking TLS should be minimal. The more expensive part is originating the error because it must
// capture the entire stack and some additional data.
#ifndef __WIL_RESULT_ORIGINATE_INCLUDED
#define __WIL_RESULT_ORIGINATE_INCLUDED
#include "result.h"
#include <OleAuto.h> // RestrictedErrorInfo uses BSTRs :(
#include "resource.h"
#include "com.h"
#include <roerrorapi.h>
#ifndef __cplusplus_winrt // The CX runtime likes to originate errors already so we would conflict with them.
namespace wil
{
namespace details
{
// Note: The name must begin with "Raise" so that the !analyze auto-bucketing will ignore this stack frame. Otherwise this line of code gets all the blame.
inline void __stdcall RaiseRoOriginateOnWilExceptions(wil::FailureInfo const& failure) WI_NOEXCEPT
{
if ((failure.type == FailureType::Return) || (failure.type == FailureType::Exception))
{
bool shouldOriginate = true;
wil::com_ptr_nothrow<IRestrictedErrorInfo> restrictedErrorInformation;
if (GetRestrictedErrorInfo(&restrictedErrorInformation) == S_OK)
{
// This thread already has an error origination payload. Don't originate again if it has the same HRESULT that we are
// observing right now.
wil::unique_bstr descriptionUnused;
HRESULT existingHr = failure.hr;
wil::unique_bstr restrictedDescriptionUnused;
wil::unique_bstr capabilitySidUnused;
if (SUCCEEDED(restrictedErrorInformation->GetErrorDetails(&descriptionUnused, &existingHr, &restrictedDescriptionUnused, &capabilitySidUnused)))
{
shouldOriginate = (failure.hr != existingHr);
}
}
if (shouldOriginate)
{
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
wil::unique_hmodule errorModule;
if (GetModuleHandleExW(0, L"api-ms-win-core-winrt-error-l1-1-1.dll", &errorModule))
{
auto pfn = reinterpret_cast<decltype(&::RoOriginateError)>(GetProcAddress(errorModule.get(), "RoOriginateError"));
if (pfn != nullptr)
{
pfn(failure.hr, nullptr);
}
}
#else // DESKTOP | SYSTEM
::RoOriginateError(failure.hr, nullptr);
#endif // DESKTOP | SYSTEM
}
else if (restrictedErrorInformation)
{
// GetRestrictedErrorInfo returns ownership of the error information. If we aren't originating, and an error was already present,
// then we need to restore the error information for later observation.
SetRestrictedErrorInfo(restrictedErrorInformation.get());
}
}
}
// This method will check for the presence of stowed exception data on the current thread. If such data exists, and the HRESULT
// matches the current failure, then we will call RoFailFastWithErrorContext. RoFailFastWithErrorContext in this situation will
// result in -VASTLY- improved crash bucketing. It is hard to express just how much better. In other cases we just return and
// the calling method fails fast the same way it always has.
inline void __stdcall FailfastWithContextCallback(wil::FailureInfo const& failure) WI_NOEXCEPT
{
wil::com_ptr_nothrow<IRestrictedErrorInfo> restrictedErrorInformation;
if (GetRestrictedErrorInfo(&restrictedErrorInformation) == S_OK)
{
wil::unique_bstr descriptionUnused;
HRESULT existingHr = failure.hr;
wil::unique_bstr restrictedDescriptionUnused;
wil::unique_bstr capabilitySidUnused;
if (SUCCEEDED(restrictedErrorInformation->GetErrorDetails(&descriptionUnused, &existingHr, &restrictedDescriptionUnused, &capabilitySidUnused)) &&
(existingHr == failure.hr))
{
// GetRestrictedErrorInfo returns ownership of the error information. We want it to be available for RoFailFastWithErrorContext
// so we must restore it via SetRestrictedErrorInfo first.
SetRestrictedErrorInfo(restrictedErrorInformation.get());
RoFailFastWithErrorContext(existingHr);
}
else
{
// The error didn't match the current failure. Put it back in thread-local storage even though we aren't failing fast
// in this method, so it is available in the debugger just-in-case.
SetRestrictedErrorInfo(restrictedErrorInformation.get());
}
}
}
} // namespace details
} // namespace wil
// Automatically call RoOriginateError upon error origination by including this file
WI_HEADER_INITITALIZATION_FUNCTION(ResultStowedExceptionInitialize, []
{
::wil::SetOriginateErrorCallback(::wil::details::RaiseRoOriginateOnWilExceptions);
::wil::SetFailfastWithContextCallback(::wil::details::FailfastWithContextCallback);
return 1;
});
#endif // __cplusplus_winrt
#endif // __WIL_RESULT_ORIGINATE_INCLUDED

View File

@ -0,0 +1,206 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_RPC_HELPERS_INCLUDED
#define __WIL_RPC_HELPERS_INCLUDED
#include "result.h"
#include "resource.h"
#include "wistd_functional.h"
#include "wistd_type_traits.h"
namespace wil
{
/// @cond
namespace details
{
// This call-adapter template converts a void-returning 'wistd::invoke' into
// an HRESULT-returning 'wistd::invoke' that emits S_OK. It can be eliminated
// with 'if constexpr' when C++17 is in wide use.
template<typename TReturnType> struct call_adapter
{
template<typename... TArgs> static HRESULT call(TArgs&& ... args)
{
return wistd::invoke(wistd::forward<TArgs>(args)...);
}
};
template<> struct call_adapter<void>
{
template<typename... TArgs> static HRESULT call(TArgs&& ... args)
{
wistd::invoke(wistd::forward<TArgs>(args)...);
return S_OK;
}
};
// Some RPC exceptions are already HRESULTs. Others are in the regular Win32
// error space. If the incoming exception code isn't an HRESULT, wrap it.
constexpr HRESULT map_rpc_exception(DWORD code)
{
return IS_ERROR(code) ? code : __HRESULT_FROM_WIN32(code);
}
}
/// @endcond
/** Invokes an RPC method, mapping structured exceptions to HRESULTs
Failures encountered by the RPC infrastructure (such as server crashes, authentication
errors, client parameter issues, etc.) are emitted by raising a structured exception from
within the RPC machinery. This method wraps the requested call in the usual RpcTryExcept,
RpcTryCatch, and RpcEndExcept sequence then maps the exceptions to HRESULTs for the usual
flow control machinery to use.
Many RPC methods are defined as returning HRESULT themselves, where the HRESULT indicates
the result of the _work_. HRESULTs returned by a successful completion of the _call_ are
returned as-is.
RPC methods that have a return type of 'void' are mapped to returning S_OK when the _call_
completes successfully.
For example, consider an RPC interface method defined in idl as:
~~~
HRESULT GetKittenState([in, ref, string] const wchar_t* name, [out, retval] KittenState** state);
~~~
To call this method, use:
~~~
wil::unique_rpc_binding binding = // typically gotten elsewhere;
wil::unique_midl_ptr<KittenState> state;
HRESULT hr = wil::invoke_rpc_nothrow(GetKittenState, binding.get(), L"fluffy", state.put());
RETURN_IF_FAILED(hr);
~~~
*/
template<typename... TCall> HRESULT invoke_rpc_nothrow(TCall&&... args) WI_NOEXCEPT
{
RpcTryExcept
{
// Note: this helper type can be removed with C++17 enabled via
// 'if constexpr(wistd::is_same_v<void, result_t>)'
using result_t = typename wistd::__invoke_of<TCall...>::type;
RETURN_IF_FAILED(details::call_adapter<result_t>::call(wistd::forward<TCall>(args)...));
return S_OK;
}
RpcExcept(RpcExceptionFilter(RpcExceptionCode()))
{
RETURN_HR(details::map_rpc_exception(RpcExceptionCode()));
}
RpcEndExcept
}
/** Invokes an RPC method, mapping structured exceptions to HRESULTs
Failures encountered by the RPC infrastructure (such as server crashes, authentication
errors, client parameter issues, etc.) are emitted by raising a structured exception from
within the RPC machinery. This method wraps the requested call in the usual RpcTryExcept,
RpcTryCatch, and RpcEndExcept sequence then maps the exceptions to HRESULTs for the usual
flow control machinery to use.
Some RPC methods return results (such as a state enumeration or other value) directly in
their signature. This adapter writes that result into a caller-provided object then
returns S_OK.
For example, consider an RPC interface method defined in idl as:
~~~
GUID GetKittenId([in, ref, string] const wchar_t* name);
~~~
To call this method, use:
~~~
wil::unique_rpc_binding binding = // typically gotten elsewhere;
GUID id;
HRESULT hr = wil::invoke_rpc_result_nothrow(id, GetKittenId, binding.get(), L"fluffy");
RETURN_IF_FAILED(hr);
~~~
*/
template<typename TResult, typename... TCall> HRESULT invoke_rpc_result_nothrow(TResult& result, TCall&&... args) WI_NOEXCEPT
{
RpcTryExcept
{
result = wistd::invoke(wistd::forward<TCall>(args)...);
return S_OK;
}
RpcExcept(RpcExceptionFilter(RpcExceptionCode()))
{
RETURN_HR(details::map_rpc_exception(RpcExceptionCode()));
}
RpcEndExcept
}
namespace details
{
// Provides an adapter around calling the context-handle-close method on an
// RPC interface, which itself is an RPC call.
template<typename TStorage, typename close_fn_t, close_fn_t close_fn>
struct rpc_closer_t
{
static void Close(TStorage arg) WI_NOEXCEPT
{
LOG_IF_FAILED(invoke_rpc_nothrow(close_fn, &arg));
}
};
}
/** Manages explicit RPC context handles
Explicit RPC context handles are used in many RPC interfaces. Most interfaces with
context handles have an explicit `FooClose([in, out] CONTEXT*)` method that lets
the server close out the context handle. As the close method itself is an RPC call,
it can fail and raise a structured exception.
This type routes the context-handle-specific `Close` call through the `invoke_rpc_nothrow`
helper, ensuring correct cleanup and lifecycle management.
~~~
// Assume the interface has two methods:
// HRESULT OpenFoo([in] handle_t binding, [out] FOO_CONTEXT*);
// HRESULT UseFoo([in] FOO_CONTEXT context;
// void CloseFoo([in, out] PFOO_CONTEXT);
using unique_foo_context = wil::unique_rpc_context_handle<FOO_CONTEXT, decltype(&CloseFoo), CloseFoo>;
unique_foo_context context;
RETURN_IF_FAILED(wil::invoke_rpc_nothrow(OpenFoo, m_binding.get(), context.put()));
RETURN_IF_FAILED(wil::invoke_rpc_nothrow(UseFoo, context.get()));
context.reset();
~~~
*/
template<typename TContext, typename close_fn_t, close_fn_t close_fn>
using unique_rpc_context_handle = unique_any<TContext, decltype(&details::rpc_closer_t<TContext, close_fn_t, close_fn>::Close), details::rpc_closer_t<TContext, close_fn_t, close_fn>::Close>;
#ifdef WIL_ENABLE_EXCEPTIONS
/** Invokes an RPC method, mapping structured exceptions to C++ exceptions
See `wil::invoke_rpc_nothrow` for additional information. Failures during the _call_
and those returned by the _method_ are mapped to HRESULTs and thrown inside a
wil::ResultException. Using the example RPC method provided above:
~~~
wil::unique_midl_ptr<KittenState> state;
wil::invoke_rpc(GetKittenState, binding.get(), L"fluffy", state.put());
// use 'state'
~~~
*/
template<typename... TCall> void invoke_rpc(TCall&& ... args)
{
THROW_IF_FAILED(invoke_rpc_nothrow(wistd::forward<TCall>(args)...));
}
/** Invokes an RPC method, mapping structured exceptions to C++ exceptions
See `wil::invoke_rpc_result_nothrow` for additional information. Failures during the
_call_ are mapped to HRESULTs and thrown inside a `wil::ResultException`. Using the
example RPC method provided above:
~~~
GUID id = wil::invoke_rpc_result(GetKittenId, binding.get());
// use 'id'
~~~
*/
template<typename... TCall> auto invoke_rpc_result(TCall&& ... args)
{
using result_t = typename wistd::__invoke_of<TCall...>::type;
result_t result{};
THROW_IF_FAILED(invoke_rpc_result_nothrow(result, wistd::forward<TCall>(args)...));
return result;
}
#endif
}
#endif

View File

@ -0,0 +1,369 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_SAFECAST_INCLUDED
#define __WIL_SAFECAST_INCLUDED
#include "result_macros.h"
#include <intsafe.h>
#include "wistd_config.h"
#include "wistd_type_traits.h"
namespace wil
{
namespace details
{
// Default error case for undefined conversions in intsafe.h
template<typename OldT, typename NewT> constexpr wistd::nullptr_t intsafe_conversion = nullptr;
// is_known_safe_static_cast_v determines if a conversion is known to be safe or not. Known
// safe conversions can be handled by static_cast, this includes conversions between the same
// type, when the new type is larger than the old type but is not a signed to unsigned
// conversion, and when the two types are the same size and signed/unsigned. All other
// conversions will be assumed to be potentially unsafe, and the conversion must be handled
// by intsafe and checked.
template <typename NewT, typename OldT>
constexpr bool is_known_safe_static_cast_v =
(sizeof(NewT) > sizeof(OldT) && !(wistd::is_signed_v<OldT> && wistd::is_unsigned_v<NewT>)) ||
(sizeof(NewT) == sizeof(OldT) && ((wistd::is_signed_v<NewT> && wistd::is_signed_v<OldT>) || (wistd::is_unsigned_v<NewT> && wistd::is_unsigned_v<OldT>)));
// Helper template to determine that NewT and OldT are both integral types. The safe_cast
// operation only supports conversions between integral types.
template <typename NewT, typename OldT>
constexpr bool both_integral_v = wistd::is_integral<NewT>::value && wistd::is_integral<OldT>::value;
// Note on native wchar_t (__wchar_t):
// Intsafe.h does not currently handle native wchar_t. When compiling with /Zc:wchar_t-, this is fine as wchar_t is
// typedef'd to unsigned short. However, when compiling with /Zc:wchar_t or wchar_t as a native type, the lack of
// support for native wchar_t in intsafe.h becomes an issue. To work around this, we treat native wchar_t as an
// unsigned short when passing it to intsafe.h, because the two on the Windows platform are the same size and
// share the same range according to MSDN. If the cast is to a native wchar_t, the result from intsafe.h is cast
// to a native wchar_t.
// Intsafe does not have a defined conversion for native wchar_t
template <typename NewT, typename OldT>
constexpr bool neither_native_wchar_v = !wistd::is_same<NewT, __wchar_t>::value && !wistd::is_same<OldT, __wchar_t>::value;
// Check to see if the cast is a conversion to native wchar_t
template <typename NewT, typename OldT>
constexpr bool is_cast_to_wchar_v = wistd::is_same<NewT, __wchar_t>::value && !wistd::is_same<OldT, __wchar_t>::value;
// Check to see if the cast is a conversion from native wchar_t
template <typename NewT, typename OldT>
constexpr bool is_cast_from_wchar_v = !wistd::is_same<NewT, __wchar_t>::value && wistd::is_same<OldT, __wchar_t>::value;
// Validate the conversion to be performed has a defined mapping to an intsafe conversion
template <typename NewT, typename OldT>
constexpr bool is_supported_intsafe_cast_v = intsafe_conversion<OldT, NewT> != nullptr;
// True when the conversion is between integral types and can be handled by static_cast
template <typename NewT, typename OldT>
constexpr bool is_supported_safe_static_cast_v = both_integral_v<NewT, OldT> && is_known_safe_static_cast_v<NewT, OldT>;
// True when the conversion is between integral types, does not involve native wchar, has
// a mapped intsafe conversion, and is unsafe.
template <typename NewT, typename OldT>
constexpr bool is_supported_unsafe_cast_no_wchar_v =
both_integral_v<NewT, OldT> &&
!is_known_safe_static_cast_v<NewT, OldT> &&
neither_native_wchar_v<NewT, OldT> &&
is_supported_intsafe_cast_v<NewT, OldT>;
// True when the conversion is between integral types, is a cast to native wchar_t, has
// a mapped intsafe conversion, and is unsafe.
template <typename NewT, typename OldT>
constexpr bool is_supported_unsafe_cast_to_wchar_v =
both_integral_v<NewT, OldT> &&
!is_known_safe_static_cast_v<NewT, OldT> &&
is_cast_to_wchar_v<NewT, OldT> &&
is_supported_intsafe_cast_v<unsigned short, OldT>;
// True when the conversion is between integral types, is a cast from native wchar_t, has
// a mapped intsafe conversion, and is unsafe.
template <typename NewT, typename OldT>
constexpr bool is_supported_unsafe_cast_from_wchar_v =
both_integral_v<NewT, OldT> &&
!is_known_safe_static_cast_v<NewT, OldT> &&
is_cast_from_wchar_v<NewT, OldT> &&
is_supported_intsafe_cast_v<NewT, unsigned short>;
// True when the conversion is supported and unsafe, and may or may not involve
// native wchar_t.
template <typename NewT, typename OldT>
constexpr bool is_supported_unsafe_cast_v =
is_supported_unsafe_cast_no_wchar_v<NewT, OldT> ||
is_supported_unsafe_cast_to_wchar_v<NewT, OldT> ||
is_supported_unsafe_cast_from_wchar_v<NewT, OldT>;
// True when T is any one of the primitive types that the variably sized types are defined as.
template <typename T>
constexpr bool is_potentially_variably_sized_type_v =
wistd::is_same<T, int>::value ||
wistd::is_same<T, unsigned int>::value ||
wistd::is_same<T, long>::value ||
wistd::is_same<T, unsigned long>::value ||
wistd::is_same<T, __int64>::value ||
wistd::is_same<T, unsigned __int64>::value;
// True when either type is potentialy variably sized (e.g. size_t, ptrdiff_t)
template <typename OldT, typename NewT>
constexpr bool is_potentially_variably_sized_cast_v =
is_potentially_variably_sized_type_v<OldT> ||
is_potentially_variably_sized_type_v<NewT>;
// Mappings of all conversions defined in intsafe.h to intsafe_conversion
// Note: Uppercase types (UINT, DWORD, SIZE_T, etc) and architecture dependent types resolve
// to the base types. The base types are used since they do not vary based on architecture.
template<> constexpr auto intsafe_conversion<__int64, char> = LongLongToChar;
template<> constexpr auto intsafe_conversion<__int64, int> = LongLongToInt;
template<> constexpr auto intsafe_conversion<__int64, long> = LongLongToLong;
template<> constexpr auto intsafe_conversion<__int64, short> = LongLongToShort;
template<> constexpr auto intsafe_conversion<__int64, signed char> = LongLongToInt8;
template<> constexpr auto intsafe_conversion<__int64, unsigned __int64> = LongLongToULongLong;
template<> constexpr auto intsafe_conversion<__int64, unsigned char> = LongLongToUChar;
template<> constexpr auto intsafe_conversion<__int64, unsigned int> = LongLongToUInt;
template<> constexpr auto intsafe_conversion<__int64, unsigned long> = LongLongToULong;
template<> constexpr auto intsafe_conversion<__int64, unsigned short> = LongLongToUShort;
template<> constexpr auto intsafe_conversion<int, char> = IntToChar;
template<> constexpr auto intsafe_conversion<int, short> = IntToShort;
template<> constexpr auto intsafe_conversion<int, signed char> = IntToInt8;
template<> constexpr auto intsafe_conversion<int, unsigned __int64> = IntToULongLong;
template<> constexpr auto intsafe_conversion<int, unsigned char> = IntToUChar;
template<> constexpr auto intsafe_conversion<int, unsigned int> = IntToUInt;
template<> constexpr auto intsafe_conversion<int, unsigned long> = IntToULong;
template<> constexpr auto intsafe_conversion<int, unsigned short> = IntToUShort;
template<> constexpr auto intsafe_conversion<long, char> = LongToChar;
template<> constexpr auto intsafe_conversion<long, int> = LongToInt;
template<> constexpr auto intsafe_conversion<long, short> = LongToShort;
template<> constexpr auto intsafe_conversion<long, signed char> = LongToInt8;
template<> constexpr auto intsafe_conversion<long, unsigned __int64> = LongToULongLong;
template<> constexpr auto intsafe_conversion<long, unsigned char> = LongToUChar;
template<> constexpr auto intsafe_conversion<long, unsigned int> = LongToUInt;
template<> constexpr auto intsafe_conversion<long, unsigned long> = LongToULong;
template<> constexpr auto intsafe_conversion<long, unsigned short> = LongToUShort;
template<> constexpr auto intsafe_conversion<short, char> = ShortToChar;
template<> constexpr auto intsafe_conversion<short, signed char> = ShortToInt8;
template<> constexpr auto intsafe_conversion<short, unsigned __int64> = ShortToULongLong;
template<> constexpr auto intsafe_conversion<short, unsigned char> = ShortToUChar;
template<> constexpr auto intsafe_conversion<short, unsigned int> = ShortToUInt;
template<> constexpr auto intsafe_conversion<short, unsigned long> = ShortToULong;
template<> constexpr auto intsafe_conversion<short, unsigned short> = ShortToUShort;
template<> constexpr auto intsafe_conversion<signed char, unsigned __int64> = Int8ToULongLong;
template<> constexpr auto intsafe_conversion<signed char, unsigned char> = Int8ToUChar;
template<> constexpr auto intsafe_conversion<signed char, unsigned int> = Int8ToUInt;
template<> constexpr auto intsafe_conversion<signed char, unsigned long> = Int8ToULong;
template<> constexpr auto intsafe_conversion<signed char, unsigned short> = Int8ToUShort;
template<> constexpr auto intsafe_conversion<unsigned __int64, __int64> = ULongLongToLongLong;
template<> constexpr auto intsafe_conversion<unsigned __int64, char> = ULongLongToChar;
template<> constexpr auto intsafe_conversion<unsigned __int64, int> = ULongLongToInt;
template<> constexpr auto intsafe_conversion<unsigned __int64, long> = ULongLongToLong;
template<> constexpr auto intsafe_conversion<unsigned __int64, short> = ULongLongToShort;
template<> constexpr auto intsafe_conversion<unsigned __int64, signed char> = ULongLongToInt8;
template<> constexpr auto intsafe_conversion<unsigned __int64, unsigned char> = ULongLongToUChar;
template<> constexpr auto intsafe_conversion<unsigned __int64, unsigned int> = ULongLongToUInt;
template<> constexpr auto intsafe_conversion<unsigned __int64, unsigned long> = ULongLongToULong;
template<> constexpr auto intsafe_conversion<unsigned __int64, unsigned short> = ULongLongToUShort;
template<> constexpr auto intsafe_conversion<unsigned char, char> = UInt8ToChar;
template<> constexpr auto intsafe_conversion<unsigned char, signed char> = UIntToInt8;
template<> constexpr auto intsafe_conversion<unsigned int, char> = UIntToChar;
template<> constexpr auto intsafe_conversion<unsigned int, int> = UIntToInt;
template<> constexpr auto intsafe_conversion<unsigned int, long> = UIntToLong;
template<> constexpr auto intsafe_conversion<unsigned int, short> = UIntToShort;
template<> constexpr auto intsafe_conversion<unsigned int, signed char> = UIntToInt8;
template<> constexpr auto intsafe_conversion<unsigned int, unsigned char> = UIntToUChar;
template<> constexpr auto intsafe_conversion<unsigned int, unsigned short> = UIntToUShort;
template<> constexpr auto intsafe_conversion<unsigned long, char> = ULongToChar;
template<> constexpr auto intsafe_conversion<unsigned long, int> = ULongToInt;
template<> constexpr auto intsafe_conversion<unsigned long, long> = ULongToLong;
template<> constexpr auto intsafe_conversion<unsigned long, short> = ULongToShort;
template<> constexpr auto intsafe_conversion<unsigned long, signed char> = ULongToInt8;
template<> constexpr auto intsafe_conversion<unsigned long, unsigned char> = ULongToUChar;
template<> constexpr auto intsafe_conversion<unsigned long, unsigned int> = ULongToUInt;
template<> constexpr auto intsafe_conversion<unsigned long, unsigned short> = ULongToUShort;
template<> constexpr auto intsafe_conversion<unsigned short, char> = UShortToChar;
template<> constexpr auto intsafe_conversion<unsigned short, short> = UShortToShort;
template<> constexpr auto intsafe_conversion<unsigned short, signed char> = UShortToInt8;
template<> constexpr auto intsafe_conversion<unsigned short, unsigned char> = UShortToUChar;
}
// Unsafe conversion where failure results in fail fast.
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_no_wchar_v<NewT, OldT>, int> = 0
>
NewT safe_cast_failfast(const OldT var)
{
NewT newVar;
FAIL_FAST_IF_FAILED((details::intsafe_conversion<OldT, NewT>(var, &newVar)));
return newVar;
}
// Unsafe conversion where failure results in fail fast.
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_from_wchar_v<NewT, OldT>, int> = 0
>
NewT safe_cast_failfast(const OldT var)
{
NewT newVar;
FAIL_FAST_IF_FAILED((details::intsafe_conversion<unsigned short, NewT>(static_cast<unsigned short>(var), &newVar)));
return newVar;
}
// Unsafe conversion where failure results in fail fast.
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_to_wchar_v<NewT, OldT>, int> = 0
>
NewT safe_cast_failfast(const OldT var)
{
unsigned short newVar;
FAIL_FAST_IF_FAILED((details::intsafe_conversion<OldT, unsigned short>(var, &newVar)));
return static_cast<__wchar_t>(newVar);
}
// This conversion is always safe, therefore a static_cast is fine.
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_safe_static_cast_v<NewT, OldT>, int> = 0
>
NewT safe_cast_failfast(const OldT var)
{
return static_cast<NewT>(var);
}
#ifdef WIL_ENABLE_EXCEPTIONS
// Unsafe conversion where failure results in a thrown exception.
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_no_wchar_v<NewT, OldT>, int> = 0
>
NewT safe_cast(const OldT var)
{
NewT newVar;
THROW_IF_FAILED((details::intsafe_conversion<OldT, NewT>(var, &newVar)));
return newVar;
}
// Unsafe conversion where failure results in a thrown exception.
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_from_wchar_v<NewT, OldT>, int> = 0
>
NewT safe_cast(const OldT var)
{
NewT newVar;
THROW_IF_FAILED((details::intsafe_conversion<unsigned short, NewT>(static_cast<unsigned short>(var), &newVar)));
return newVar;
}
// Unsafe conversion where failure results in a thrown exception.
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_to_wchar_v<NewT, OldT>, int> = 0
>
NewT safe_cast(const OldT var)
{
unsigned short newVar;
THROW_IF_FAILED((details::intsafe_conversion<OldT, unsigned short>(var, &newVar)));
return static_cast<__wchar_t>(newVar);
}
// This conversion is always safe, therefore a static_cast is fine.
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_safe_static_cast_v<NewT, OldT>, int> = 0
>
NewT safe_cast(const OldT var)
{
return static_cast<NewT>(var);
}
#endif
// This conversion is unsafe, therefore the two parameter version of safe_cast_nothrow must be used
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_v<NewT, OldT>, int> = 0
>
NewT safe_cast_nothrow(const OldT /*var*/)
{
static_assert(!wistd::is_same_v<NewT, NewT>, "This cast has the potential to fail, use the two parameter safe_cast_nothrow instead");
}
// This conversion is always safe, therefore a static_cast is fine.
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_safe_static_cast_v<NewT, OldT>, int> = 0
>
NewT safe_cast_nothrow(const OldT var)
{
return static_cast<NewT>(var);
}
// Unsafe conversion where an HRESULT is returned. It is up to the callee to check and handle the HRESULT
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_no_wchar_v<NewT, OldT>, int> = 0
>
HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult)
{
return details::intsafe_conversion<OldT, NewT>(var, newTResult);
}
// Unsafe conversion where an HRESULT is returned. It is up to the callee to check and handle the HRESULT
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_from_wchar_v<NewT, OldT>, int> = 0
>
HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult)
{
return details::intsafe_conversion<unsigned short, NewT>(static_cast<unsigned short>(var), newTResult);
}
// Unsafe conversion where an HRESULT is returned. It is up to the callee to check and handle the HRESULT
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_to_wchar_v<NewT, OldT>, int> = 0
>
HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult)
{
return details::intsafe_conversion<OldT, unsigned short>(var, reinterpret_cast<unsigned short *>(newTResult));
}
// This conversion is always safe, therefore a static_cast is fine. If it can be determined the conversion
// does not involve a variably sized type, then the compilation will fail and say the single parameter version
// of safe_cast_nothrow should be used instead.
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_safe_static_cast_v<NewT, OldT>, int> = 0
>
HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult)
{
static_assert(details::is_potentially_variably_sized_cast_v<OldT, NewT>, "This cast is always safe; use safe_cast_nothrow<T>(value) to avoid unnecessary error handling.");
*newTResult = static_cast<NewT>(var);
return S_OK;
}
}
#endif // __WIL_SAFECAST_INCLUDED

View File

@ -0,0 +1,114 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_STL_INCLUDED
#define __WIL_STL_INCLUDED
#include "common.h"
#include "resource.h"
#include <memory>
#include <string>
#include <vector>
#if defined(WIL_ENABLE_EXCEPTIONS)
namespace wil
{
/** Secure allocator for STL containers.
The `wil::secure_allocator` allocator calls `SecureZeroMemory` before deallocating
memory. This provides a mechanism for secure STL containers such as `wil::secure_vector`,
`wil::secure_string`, and `wil::secure_wstring`. */
template <typename T>
struct secure_allocator
: public std::allocator<T>
{
template<typename Other>
struct rebind
{
typedef secure_allocator<Other> other;
};
secure_allocator()
: std::allocator<T>()
{
}
~secure_allocator() = default;
secure_allocator(const secure_allocator& a)
: std::allocator<T>(a)
{
}
template <class U>
secure_allocator(const secure_allocator<U>& a)
: std::allocator<T>(a)
{
}
T* allocate(size_t n)
{
return std::allocator<T>::allocate(n);
}
void deallocate(T* p, size_t n)
{
SecureZeroMemory(p, sizeof(T) * n);
std::allocator<T>::deallocate(p, n);
}
};
//! `wil::secure_vector` will be securely zeroed before deallocation.
template <typename Type>
using secure_vector = std::vector<Type, secure_allocator<Type>>;
//! `wil::secure_wstring` will be securely zeroed before deallocation.
using secure_wstring = std::basic_string<wchar_t, std::char_traits<wchar_t>, wil::secure_allocator<wchar_t>>;
//! `wil::secure_string` will be securely zeroed before deallocation.
using secure_string = std::basic_string<char, std::char_traits<char>, wil::secure_allocator<char>>;
/// @cond
namespace details
{
template<> struct string_maker<std::wstring>
{
HRESULT make(_In_reads_opt_(length) PCWSTR source, size_t length) WI_NOEXCEPT try
{
m_value = source ? std::wstring(source, length) : std::wstring(length, L'\0');
return S_OK;
}
catch (...)
{
return E_OUTOFMEMORY;
}
wchar_t* buffer() { return &m_value[0]; }
std::wstring release() { return std::wstring(std::move(m_value)); }
static PCWSTR get(const std::wstring& value) { return value.c_str(); }
private:
std::wstring m_value;
};
}
/// @endcond
// str_raw_ptr is an overloaded function that retrieves a const pointer to the first character in a string's buffer.
// This is the overload for std::wstring. Other overloads available in resource.h.
inline PCWSTR str_raw_ptr(const std::wstring& str)
{
return str.c_str();
}
} // namespace wil
#endif // WIL_ENABLE_EXCEPTIONS
#endif // __WIL_STL_INCLUDED

View File

@ -0,0 +1,597 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_TOKEN_HELPERS_INCLUDED
#define __WIL_TOKEN_HELPERS_INCLUDED
#ifdef _KERNEL_MODE
#error This header is not supported in kernel-mode.
#endif
#include "resource.h"
#include <new>
#include <lmcons.h> // for UNLEN and DNLEN
#include <processthreadsapi.h>
// for GetUserNameEx()
#define SECURITY_WIN32
#include <Security.h>
namespace wil
{
/// @cond
namespace details
{
// Template specialization for TOKEN_INFORMATION_CLASS, add more mappings here as needed
// TODO: The mapping should be reversed to be MapTokenInfoClassToStruct since there may
// be an info class value that uses the same structure. That is the case for the file
// system information.
template<typename T> struct MapTokenStructToInfoClass;
template<> struct MapTokenStructToInfoClass<TOKEN_ACCESS_INFORMATION> { static const TOKEN_INFORMATION_CLASS infoClass = TokenAccessInformation; static const bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_APPCONTAINER_INFORMATION> { static const TOKEN_INFORMATION_CLASS infoClass = TokenAppContainerSid; static const bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_DEFAULT_DACL> { static const TOKEN_INFORMATION_CLASS infoClass = TokenDefaultDacl; static const bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_GROUPS_AND_PRIVILEGES> { static const TOKEN_INFORMATION_CLASS infoClass = TokenGroupsAndPrivileges; static const bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_MANDATORY_LABEL> { static const TOKEN_INFORMATION_CLASS infoClass = TokenIntegrityLevel; static const bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_OWNER> { static const TOKEN_INFORMATION_CLASS infoClass = TokenOwner; static const bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_PRIMARY_GROUP> { static const TOKEN_INFORMATION_CLASS infoClass = TokenPrimaryGroup; static const bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_PRIVILEGES> { static const TOKEN_INFORMATION_CLASS infoClass = TokenPrivileges; static const bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_USER> { static const TOKEN_INFORMATION_CLASS infoClass = TokenUser; static const bool FixedSize = false; };
// fixed size cases
template<> struct MapTokenStructToInfoClass<TOKEN_ELEVATION_TYPE> { static const TOKEN_INFORMATION_CLASS infoClass = TokenElevationType; static const bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_MANDATORY_POLICY> { static const TOKEN_INFORMATION_CLASS infoClass = TokenMandatoryPolicy; static const bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_ORIGIN> { static const TOKEN_INFORMATION_CLASS infoClass = TokenOrigin; static const bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_SOURCE> { static const TOKEN_INFORMATION_CLASS infoClass = TokenSource; static const bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_STATISTICS> { static const TOKEN_INFORMATION_CLASS infoClass = TokenStatistics; static const bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_TYPE> { static const TOKEN_INFORMATION_CLASS infoClass = TokenType; static const bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<SECURITY_IMPERSONATION_LEVEL> { static const TOKEN_INFORMATION_CLASS infoClass = TokenImpersonationLevel; static const bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_ELEVATION> { static const TOKEN_INFORMATION_CLASS infoClass = TokenElevation; static const bool FixedSize = true; };
}
/// @endcond
enum class OpenThreadTokenAs
{
Current,
Self
};
/** Open the active token.
Opens either the current thread token (if impersonating) or the current process token. Returns a token the caller
can use with methods like get_token_information<> below. By default, the token is opened for TOKEN_QUERY and as the
effective user.
Consider using GetCurrentThreadEffectiveToken() instead of this method when eventually calling get_token_information.
This method returns a real handle to the effective token, but GetCurrentThreadEffectiveToken() is a Pseudo-handle
and much easier to manage.
~~~~
wil::unique_handle theToken;
RETURN_IF_FAILED(wil::open_current_access_token_nothrow(&theToken));
~~~~
Callers who want more access to the token (such as to duplicate or modify the token) can pass
any mask of the token rights.
~~~~
wil::unique_handle theToken;
RETURN_IF_FAILED(wil::open_current_access_token_nothrow(&theToken, TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES));
~~~~
Services impersonating their clients may need to request that the active token is opened on the
behalf of the service process to perform certain operations. Opening a token for impersonation access
or privilege-adjustment are examples of uses.
~~~~
wil::unique_handle callerToken;
RETURN_IF_FAILED(wil::open_current_access_token_nothrow(&theToken, TOKEN_QUERY | TOKEN_IMPERSONATE, true));
~~~~
@param tokenHandle Receives the token opened during the operation. Must be CloseHandle'd by the caller, or
(preferably) stored in a wil::unique_handle
@param access Bits from the TOKEN_* access mask which are passed to OpenThreadToken/OpenProcessToken
@param asSelf When true, and if the thread is impersonating, the thread token is opened using the
process token's rights.
*/
inline HRESULT open_current_access_token_nothrow(_Out_ HANDLE* tokenHandle, unsigned long access = TOKEN_QUERY, OpenThreadTokenAs openAs = OpenThreadTokenAs::Current)
{
HRESULT hr = (OpenThreadToken(GetCurrentThread(), access, (openAs == OpenThreadTokenAs::Self), tokenHandle) ? S_OK : HRESULT_FROM_WIN32(::GetLastError()));
if (hr == HRESULT_FROM_WIN32(ERROR_NO_TOKEN))
{
hr = (OpenProcessToken(GetCurrentProcess(), access, tokenHandle) ? S_OK : HRESULT_FROM_WIN32(::GetLastError()));
}
return hr;
}
//! Current thread or process token, consider using GetCurrentThreadEffectiveToken() instead.
inline wil::unique_handle open_current_access_token_failfast(unsigned long access = TOKEN_QUERY, OpenThreadTokenAs openAs = OpenThreadTokenAs::Current)
{
HANDLE rawTokenHandle;
FAIL_FAST_IF_FAILED(open_current_access_token_nothrow(&rawTokenHandle, access, openAs));
return wil::unique_handle(rawTokenHandle);
}
// Exception based function to open current thread/process access token and acquire pointer to it
#ifdef WIL_ENABLE_EXCEPTIONS
//! Current thread or process token, consider using GetCurrentThreadEffectiveToken() instead.
inline wil::unique_handle open_current_access_token(unsigned long access = TOKEN_QUERY, OpenThreadTokenAs openAs = OpenThreadTokenAs::Current)
{
HANDLE rawTokenHandle;
THROW_IF_FAILED(open_current_access_token_nothrow(&rawTokenHandle, access, openAs));
return wil::unique_handle(rawTokenHandle);
}
#endif // WIL_ENABLE_EXCEPTIONS
// Returns tokenHandle or the effective thread token if tokenHandle is null.
// Note, this returns an token handle who's lifetime is managed independently
// and it may be a pseudo token, don't free it!
inline HANDLE GetCurrentThreadEffectiveTokenWithOverride(HANDLE tokenHandle)
{
return tokenHandle ? tokenHandle : GetCurrentThreadEffectiveToken();
}
/** Fetches information about a token.
See GetTokenInformation on MSDN for what this method can return. For variable sized structs the information
is returned to the caller as a wistd::unique_ptr<T> (like TOKEN_ORIGIN, TOKEN_USER, TOKEN_ELEVATION, etc.). For
fixed sized, the struct is returned directly.
The caller must have access to read the information from the provided token. This method works with both real
(e.g. OpenCurrentAccessToken) and pseudo (e.g. GetCurrentThreadToken) token handles.
~~~~
// Retrieve the TOKEN_USER structure for the current process
wistd::unique_ptr<TOKEN_USER> user;
RETURN_IF_FAILED(wil::get_token_information_nothrow(user, GetCurrentProcessToken()));
RETURN_IF_FAILED(ConsumeSid(user->User.Sid));
~~~~
Not specifying the token handle is the same as specifying 'nullptr' and retrieves information about the effective token.
~~~~
wistd::unique_ptr<TOKEN_PRIVILEGES> privileges;
RETURN_IF_FAILED(wil::get_token_information_nothrow(privileges));
for (auto const& privilege : wil::GetRange(privileges->Privileges, privileges->PrivilegeCount))
{
RETURN_IF_FAILED(ConsumePrivilege(privilege));
}
~~~~
@param tokenInfo Receives a pointer to a structure containing the results of GetTokenInformation for the requested
type. The type of <T> selects which TOKEN_INFORMATION_CLASS will be used.
@param tokenHandle Specifies which token will be queried. When nullptr, the thread's effective current token is used.
@return S_OK on success, a FAILED hresult containing the win32 error from querying the token otherwise.
*/
template <typename T, wistd::enable_if_t<!details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
inline HRESULT get_token_information_nothrow(wistd::unique_ptr<T>& tokenInfo, HANDLE tokenHandle = nullptr)
{
tokenInfo.reset();
tokenHandle = GetCurrentThreadEffectiveTokenWithOverride(tokenHandle);
DWORD tokenInfoSize = 0;
const auto infoClass = details::MapTokenStructToInfoClass<T>::infoClass;
RETURN_LAST_ERROR_IF(!((!GetTokenInformation(tokenHandle, infoClass, nullptr, 0, &tokenInfoSize)) &&
(::GetLastError() == ERROR_INSUFFICIENT_BUFFER)));
wistd::unique_ptr<char> tokenInfoClose(
static_cast<char*>(operator new(tokenInfoSize, std::nothrow)));
RETURN_IF_NULL_ALLOC(tokenInfoClose.get());
RETURN_IF_WIN32_BOOL_FALSE(GetTokenInformation(tokenHandle, infoClass, tokenInfoClose.get(), tokenInfoSize, &tokenInfoSize));
tokenInfo.reset(reinterpret_cast<T *>(tokenInfoClose.release()));
return S_OK;
}
template <typename T, wistd::enable_if_t<details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
inline HRESULT get_token_information_nothrow(_Out_ T* tokenInfo, HANDLE tokenHandle = nullptr)
{
*tokenInfo = {};
tokenHandle = GetCurrentThreadEffectiveTokenWithOverride(tokenHandle);
DWORD tokenInfoSize = sizeof(T);
const auto infoClass = details::MapTokenStructToInfoClass<T>::infoClass;
RETURN_IF_WIN32_BOOL_FALSE(GetTokenInformation(tokenHandle, infoClass, tokenInfo, tokenInfoSize, &tokenInfoSize));
return S_OK;
}
namespace details
{
template<typename T, typename policy, wistd::enable_if_t<!details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
wistd::unique_ptr<T> GetTokenInfoWrap(HANDLE token = nullptr)
{
wistd::unique_ptr<T> temp;
policy::HResult(get_token_information_nothrow(temp, token));
return temp;
}
template<typename T, typename policy, wistd::enable_if_t<details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
T GetTokenInfoWrap(HANDLE token = nullptr)
{
T temp{};
policy::HResult(get_token_information_nothrow(&temp, token));
return temp;
}
}
//! A variant of get_token_information<T> that fails-fast on errors retrieving the token
template <typename T>
inline auto get_token_information_failfast(HANDLE token = nullptr)
{
return details::GetTokenInfoWrap<T, err_failfast_policy>(token);
}
//! Overload of GetTokenInformationNoThrow that retrieves a token linked from the provided token
inline HRESULT get_token_information_nothrow(unique_token_linked_token& tokenInfo, HANDLE tokenHandle = nullptr)
{
static_assert(sizeof(tokenInfo) == sizeof(TOKEN_LINKED_TOKEN), "confusing size mismatch");
tokenHandle = GetCurrentThreadEffectiveTokenWithOverride(tokenHandle);
DWORD tokenInfoSize = 0;
RETURN_IF_WIN32_BOOL_FALSE(::GetTokenInformation(tokenHandle, TokenLinkedToken,
tokenInfo.reset_and_addressof(), sizeof(tokenInfo), &tokenInfoSize));
return S_OK;
}
/** Retrieves the linked-token information for a token.
Fails-fast if the link information cannot be retrieved.
~~~~
auto link = get_linked_token_information_failfast(GetCurrentThreadToken());
auto tokenUser = get_token_information<TOKEN_USER>(link.LinkedToken);
~~~~
@param token Specifies the token to query. Pass nullptr to use the current effective thread token
@return unique_token_linked_token containing a handle to the linked token
*/
inline unique_token_linked_token get_linked_token_information_failfast(HANDLE token = nullptr)
{
unique_token_linked_token tokenInfo;
FAIL_FAST_IF_FAILED(get_token_information_nothrow(tokenInfo, token));
return tokenInfo;
}
#ifdef WIL_ENABLE_EXCEPTIONS
/** Fetches information about a token.
See get_token_information_nothrow for full details.
~~~~
auto user = wil::get_token_information<TOKEN_USER>(GetCurrentProcessToken());
ConsumeSid(user->User.Sid);
~~~~
Pass 'nullptr' (or omit the parameter) as tokenHandle to retrieve information about the effective token.
~~~~
auto privs = wil::get_token_information<TOKEN_PRIVILEGES>(privileges);
for (auto& priv : wil::make_range(privs->Privileges, privs->Privilieges + privs->PrivilegeCount))
{
if (priv.Attributes & SE_PRIVILEGE_ENABLED)
{
// ...
}
}
~~~~
@return A pointer to a structure containing the results of GetTokenInformation for the requested type. The type of
<T> selects which TOKEN_INFORMATION_CLASS will be used.
@param token Specifies which token will be queried. When nullptr or not set, the thread's effective current token is used.
*/
template <typename T>
inline auto get_token_information(HANDLE token = nullptr)
{
return details::GetTokenInfoWrap<T, err_exception_policy>(token);
}
/** Retrieves the linked-token information for a token.
Throws an exception if the link information cannot be retrieved.
~~~~
auto link = get_linked_token_information(GetCurrentThreadToken());
auto tokenUser = get_token_information<TOKEN_USER>(link.LinkedToken);
~~~~
@param token Specifies the token to query. Pass nullptr to use the current effective thread token
@return unique_token_linked_token containing a handle to the linked token
*/
inline unique_token_linked_token get_linked_token_information(HANDLE token = nullptr)
{
unique_token_linked_token tokenInfo;
THROW_IF_FAILED(get_token_information_nothrow(tokenInfo, token));
return tokenInfo;
}
#endif
/// @cond
namespace details
{
inline void RevertImpersonateToken(_Pre_opt_valid_ _Frees_ptr_opt_ HANDLE oldToken)
{
FAIL_FAST_IMMEDIATE_IF(!::SetThreadToken(nullptr, oldToken));
if (oldToken)
{
::CloseHandle(oldToken);
}
}
}
/// @endcond
using unique_token_reverter = wil::unique_any<
HANDLE,
decltype(&details::RevertImpersonateToken),
details::RevertImpersonateToken,
details::pointer_access_none,
HANDLE,
INT_PTR,
-1,
HANDLE>;
/** Temporarily impersonates a token on this thread.
This method sets a new token on a thread, restoring the current token when the returned object
is destroyed. Useful for impersonating other tokens or running as 'self,' especially in services.
~~~~
HRESULT OpenFileAsSessionuser(PCWSTR filePath, DWORD session, _Out_ HANDLE* opened)
{
wil::unique_handle userToken;
RETURN_IF_WIN32_BOOL_FALSE(QueryUserToken(session, &userToken));
wil::unique_token_reverter reverter;
RETURN_IF_FAILED(wil::impersonate_token_nothrow(userToken.get(), reverter));
wil::unique_hfile userFile(::CreateFile(filePath, ...));
RETURN_LAST_ERROR_IF(!userFile && (::GetLastError() != ERROR_FILE_NOT_FOUND));
*opened = userFile.release();
return S_OK;
}
~~~~
@param token A token to impersonate, or 'nullptr' to run as the process identity.
*/
inline HRESULT impersonate_token_nothrow(HANDLE token, unique_token_reverter& reverter)
{
wil::unique_handle currentToken;
// Get the token for the current thread. If there wasn't one, the reset will clear it as well
if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &currentToken))
{
RETURN_LAST_ERROR_IF(::GetLastError() != ERROR_NO_TOKEN);
}
// Update the current token
RETURN_IF_WIN32_BOOL_FALSE(::SetThreadToken(nullptr, token));
reverter.reset(currentToken.release()); // Ownership passed
return S_OK;
}
/** Temporarily clears any impersonation on this thread.
This method resets the current thread's token to nullptr, indicating that it is not impersonating
any user. Useful for elevating to whatever identity a service or higher-privilege process might
be capable of running under.
~~~~
HRESULT DeleteFileRetryAsSelf(PCWSTR filePath)
{
if (!::DeleteFile(filePath))
{
RETURN_LAST_ERROR_IF(::GetLastError() != ERROR_ACCESS_DENIED);
wil::unique_token_reverter reverter;
RETURN_IF_FAILED(wil::run_as_self_nothrow(reverter));
RETURN_IF_FAILED(TakeOwnershipOfFile(filePath));
RETURN_IF_FAILED(GrantDeleteAccess(filePath));
RETURN_IF_WIN32_BOOL_FALSE(::DeleteFile(filePath));
}
return S_OK;
}
~~~~
*/
inline HRESULT run_as_self_nothrow(unique_token_reverter& reverter)
{
return impersonate_token_nothrow(nullptr, reverter);
}
inline unique_token_reverter impersonate_token_failfast(HANDLE token)
{
unique_token_reverter oldToken;
FAIL_FAST_IF_FAILED(impersonate_token_nothrow(token, oldToken));
return oldToken;
}
inline unique_token_reverter run_as_self_failfast()
{
return impersonate_token_failfast(nullptr);
}
#ifdef WIL_ENABLE_EXCEPTIONS
/** Temporarily impersonates a token on this thread.
This method sets a new token on a thread, restoring the current token when the returned object
is destroyed. Useful for impersonating other tokens or running as 'self,' especially in services.
~~~~
wil::unique_hfile OpenFileAsSessionuser(_In_z_ const wchar_t* filePath, DWORD session)
{
wil::unique_handle userToken;
THROW_IF_WIN32_BOOL_FALSE(QueryUserToken(session, &userToken));
auto priorToken = wil::impersonate_token(userToken.get());
wil::unique_hfile userFile(::CreateFile(filePath, ...));
THROW_LAST_ERROR_IF(::GetLastError() != ERROR_FILE_NOT_FOUND);
return userFile;
}
~~~~
@param token A token to impersonate, or 'nullptr' to run as the process identity.
*/
inline unique_token_reverter impersonate_token(HANDLE token = nullptr)
{
unique_token_reverter oldToken;
THROW_IF_FAILED(impersonate_token_nothrow(token, oldToken));
return oldToken;
}
/** Temporarily clears any impersonation on this thread.
This method resets the current thread's token to nullptr, indicating that it is not impersonating
any user. Useful for elevating to whatever identity a service or higher-privilege process might
be capable of running under.
~~~~
void DeleteFileRetryAsSelf(_In_z_ const wchar_t* filePath)
{
if (!::DeleteFile(filePath) && (::GetLastError() == ERROR_ACCESS_DENIED))
{
auto priorToken = wil::run_as_self();
TakeOwnershipOfFile(filePath);
GrantDeleteAccess(filePath);
::DeleteFile(filePath);
}
}
~~~~
*/
inline unique_token_reverter run_as_self()
{
return impersonate_token(nullptr);
}
#endif // WIL_ENABLE_EXCEPTIONS
namespace details
{
template<size_t AuthorityCount> struct static_sid_t
{
BYTE Revision;
BYTE SubAuthorityCount;
SID_IDENTIFIER_AUTHORITY IdentifierAuthority;
DWORD SubAuthority[AuthorityCount];
PSID get()
{
return reinterpret_cast<PSID>(this);
}
template<size_t other> static_sid_t& operator=(const static_sid_t<other>& source)
{
static_assert(other <= AuthorityCount, "Cannot assign from a larger static sid to a smaller one");
if (&this->Revision != &source.Revision)
{
memcpy(this, &source, sizeof(source));
}
return *this;
}
};
}
/** Returns a structure containing a Revision 1 SID initialized with the authorities provided
Replaces AllocateAndInitializeSid by constructing a structure laid out like a PSID, but
returned like a value. The resulting object is suitable for use with any method taking PSID,
passed by "&the_sid" or via "the_sid.get()"
~~~~
// Change the owner of the key to administrators
auto systemSid = wil::make_static_sid(SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS);
RETURN_IF_WIN32_ERROR(SetNamedSecurityInfo(keyPath, SE_REGISTRY_KEY, OWNER_SECURITY_INFORMATION, &systemSid, nullptr, nullptr, nullptr));
~~~~
*/
template<typename... Ts> constexpr auto make_static_sid(const SID_IDENTIFIER_AUTHORITY& authority, Ts&&... subAuthorities)
{
using sid_t = details::static_sid_t<sizeof...(subAuthorities)>;
static_assert(sizeof...(subAuthorities) <= SID_MAX_SUB_AUTHORITIES, "too many sub authorities");
static_assert(offsetof(sid_t, Revision) == offsetof(_SID, Revision), "layout mismatch");
static_assert(offsetof(sid_t, SubAuthorityCount) == offsetof(_SID, SubAuthorityCount), "layout mismatch");
static_assert(offsetof(sid_t, IdentifierAuthority) == offsetof(_SID, IdentifierAuthority), "layout mismatch");
static_assert(offsetof(sid_t, SubAuthority) == offsetof(_SID, SubAuthority), "layout mismatch");
return sid_t { SID_REVISION, sizeof...(subAuthorities), authority, { static_cast<DWORD>(subAuthorities)... } };
}
//! Variant of static_sid that defaults to the NT authority
template<typename... Ts> constexpr auto make_static_nt_sid(Ts&& ... subAuthorities)
{
return make_static_sid(SECURITY_NT_AUTHORITY, wistd::forward<Ts>(subAuthorities)...);
}
/** Determines whether a specified security identifier (SID) is enabled in an access token.
This function determines whether a security identifier, described by a given set of subauthorities, is enabled
in the given access token. Note that only up to eight subauthorities can be passed to this function.
~~~~
bool IsGuest()
{
return wil::test_token_membership(nullptr, SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS));
}
~~~~
@param result This will be set to true if and only if a security identifier described by the given set of subauthorities is enabled in the given access token.
@param token A handle to an access token. The handle must have TOKEN_QUERY access to the token, and must be an impersonation token. If token is nullptr, test_token_membership
uses the impersonation token of the calling thread. If the thread is not impersonating, the function duplicates the thread's primary token to create an impersonation token.
@param sidAuthority A reference to a SID_IDENTIFIER_AUTHORITY structure. This structure provides the top-level identifier authority value to set in the SID.
@param subAuthorities Up to 15 subauthority values to place in the SID (this is a systemwide limit)
@return S_OK on success, a FAILED hresult containing the win32 error from creating the SID or querying the token otherwise.
*/
template<typename... Ts> HRESULT test_token_membership_nothrow(_Out_ bool* result, _In_opt_ HANDLE token,
const SID_IDENTIFIER_AUTHORITY& sidAuthority, Ts&&... subAuthorities)
{
*result = false;
auto tempSid = make_static_sid(sidAuthority, wistd::forward<Ts>(subAuthorities)...);
BOOL isMember;
RETURN_IF_WIN32_BOOL_FALSE(CheckTokenMembership(token, &tempSid, &isMember));
*result = (isMember != FALSE);
return S_OK;
}
/** Determine whether a token represents an app container
This method uses the passed in token and emits a boolean indicating that
whether TokenIsAppContainer is true.
~~~~
HRESULT OnlyIfAppContainer()
{
bool isAppContainer;
RETURN_IF_FAILED(wil::get_token_is_app_container_nothrow(nullptr, isAppContainer));
RETURN_HR_IF(E_ACCESSDENIED, !isAppContainer);
RETURN_HR(...);
}
~~~~
@param token A token to get info about, or 'nullptr' to run as the current thread.
*/
inline HRESULT get_token_is_app_container_nothrow(_In_opt_ HANDLE token, bool& value)
{
DWORD isAppContainer = 0;
DWORD returnLength = 0;
RETURN_IF_WIN32_BOOL_FALSE(::GetTokenInformation(
token ? token : GetCurrentThreadEffectiveToken(),
TokenIsAppContainer,
&isAppContainer,
sizeof(isAppContainer),
&returnLength));
value = (isAppContainer != 0);
return S_OK;
}
//! A variant of get_token_is_app_container_nothrow that fails-fast on errors retrieving the token information
inline bool get_token_is_app_container_failfast(HANDLE token = nullptr)
{
bool value = false;
FAIL_FAST_IF_FAILED(get_token_is_app_container_nothrow(token, value));
return value;
}
#ifdef WIL_ENABLE_EXCEPTIONS
//! A variant of get_token_is_app_container_nothrow that throws on errors retrieving the token information
inline bool get_token_is_app_container(HANDLE token = nullptr)
{
bool value = false;
THROW_IF_FAILED(get_token_is_app_container_nothrow(token, value));
return value;
}
#endif // WIL_ENABLE_EXCEPTIONS
template<typename... Ts> bool test_token_membership_failfast(_In_opt_ HANDLE token,
const SID_IDENTIFIER_AUTHORITY& sidAuthority, Ts&&... subAuthorities)
{
bool result;
FAIL_FAST_IF_FAILED(test_token_membership_nothrow(&result, token, sidAuthority, wistd::forward<Ts>(subAuthorities)...));
return result;
}
#ifdef WIL_ENABLE_EXCEPTIONS
template<typename... Ts> bool test_token_membership(_In_opt_ HANDLE token, const SID_IDENTIFIER_AUTHORITY& sidAuthority,
Ts&&... subAuthorities)
{
bool result;
THROW_IF_FAILED(test_token_membership_nothrow(&result, token, sidAuthority, wistd::forward<Ts>(subAuthorities)...));
return result;
}
#endif
} //namespace wil
#endif // __WIL_TOKEN_HELPERS_INCLUDED

View File

@ -0,0 +1,563 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_WIN32_HELPERS_INCLUDED
#define __WIL_WIN32_HELPERS_INCLUDED
#include <minwindef.h> // FILETIME, HINSTANCE
#include <sysinfoapi.h> // GetSystemTimeAsFileTime
#include <libloaderapi.h> // GetProcAddress
#include <Psapi.h> // GetModuleFileNameExW (macro), K32GetModuleFileNameExW
#include <PathCch.h>
#include <objbase.h>
#include "result.h"
#include "resource.h"
#include "wistd_functional.h"
#include "wistd_type_traits.h"
namespace wil
{
//! Strictly a function of the file system but this is the value for all known file system, NTFS, FAT.
//! CDFs has a limit of 254.
size_t const max_path_segment_length = 255;
//! Character length not including the null, MAX_PATH (260) includes the null.
size_t const max_path_length = 259;
//! 32743 Character length not including the null. This is a system defined limit.
//! The 24 is for the expansion of the roots from "C:" to "\Device\HarddiskVolume4"
//! It will be 25 when there are more than 9 disks.
size_t const max_extended_path_length = 0x7FFF - 24;
//! For {guid} string form. Includes space for the null terminator.
size_t const guid_string_buffer_length = 39;
//! For {guid} string form. Not including the null terminator.
size_t const guid_string_length = 38;
#pragma region FILETIME helpers
// FILETIME duration values. FILETIME is in 100 nanosecond units.
namespace filetime_duration
{
long long const one_millisecond = 10000LL;
long long const one_second = 10000000LL;
long long const one_minute = 10000000LL * 60; // 600000000 or 600000000LL
long long const one_hour = 10000000LL * 60 * 60; // 36000000000 or 36000000000LL
long long const one_day = 10000000LL * 60 * 60 * 24; // 864000000000 or 864000000000LL
};
namespace filetime
{
inline unsigned long long to_int64(const FILETIME &ft)
{
// Cannot reinterpret_cast FILETIME* to unsigned long long*
// due to alignment differences.
return (static_cast<unsigned long long>(ft.dwHighDateTime) << 32) + ft.dwLowDateTime;
}
inline FILETIME from_int64(unsigned long long i64)
{
static_assert(sizeof(i64) == sizeof(FILETIME), "sizes don't match");
static_assert(__alignof(unsigned long long) >= __alignof(FILETIME), "alignment not compatible with type pun");
return *reinterpret_cast<FILETIME *>(&i64);
}
inline FILETIME add(_In_ FILETIME const &ft, long long delta)
{
return from_int64(to_int64(ft) + delta);
}
inline bool is_empty(const FILETIME &ft)
{
return (ft.dwHighDateTime == 0) && (ft.dwLowDateTime == 0);
}
inline FILETIME get_system_time()
{
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
return ft;
}
}
#pragma endregion
// Use to adapt Win32 APIs that take a fixed size buffer into forms that return
// an allocated buffer. Supports many types of string representation.
// See comments below on the expected behavior of the callback.
// Adjust stackBufferLength based on typical result sizes to optimize use and
// to test the boundary cases.
template <typename string_type, size_t stackBufferLength = 256>
HRESULT AdaptFixedSizeToAllocatedResult(string_type& result, wistd::function<HRESULT(PWSTR, size_t, size_t*)> callback)
{
details::string_maker<string_type> maker;
wchar_t value[stackBufferLength];
value[0] = L'\0';
size_t valueLengthNeededWithNull{}; // callback returns the number of characters needed including the null terminator.
RETURN_IF_FAILED_EXPECTED(callback(value, ARRAYSIZE(value), &valueLengthNeededWithNull));
WI_ASSERT(valueLengthNeededWithNull > 0);
if (valueLengthNeededWithNull <= ARRAYSIZE(value))
{
// Success case as described above, make() adds the space for the null.
RETURN_IF_FAILED(maker.make(value, valueLengthNeededWithNull - 1));
}
else
{
// Did not fit in the stack allocated buffer, need to do 2 phase construction.
// valueLengthNeededWithNull includes the null so subtract that as make() will add space for it.
RETURN_IF_FAILED(maker.make(nullptr, valueLengthNeededWithNull - 1));
size_t secondLength{};
RETURN_IF_FAILED(callback(maker.buffer(), valueLengthNeededWithNull, &secondLength));
// Ensure callback produces consistent result.
FAIL_FAST_IF(valueLengthNeededWithNull != secondLength);
}
result = maker.release();
return S_OK;
}
/** Expands the '%' quoted environment variables in 'input' using ExpandEnvironmentStringsW(); */
template <typename string_type, size_t stackBufferLength = 256>
HRESULT ExpandEnvironmentStringsW(_In_ PCWSTR input, string_type& result) WI_NOEXCEPT
{
return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(result,
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
{
*valueLengthNeededWithNul = ::ExpandEnvironmentStringsW(input, value, static_cast<DWORD>(valueLength));
RETURN_LAST_ERROR_IF(*valueLengthNeededWithNul == 0);
return S_OK;
});
}
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
/** Searches for a specified file in a specified path using ExpandEnvironmentStringsW(); */
template <typename string_type, size_t stackBufferLength = 256>
HRESULT SearchPathW(_In_opt_ PCWSTR path, _In_ PCWSTR fileName, _In_opt_ PCWSTR extension, string_type& result) WI_NOEXCEPT
{
return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(result,
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
{
*valueLengthNeededWithNul = ::SearchPathW(path, fileName, extension, static_cast<DWORD>(valueLength), value, nullptr);
if (*valueLengthNeededWithNul == 0)
{
// ERROR_FILE_NOT_FOUND is an expected return value for SearchPathW
const HRESULT searchResult = HRESULT_FROM_WIN32(::GetLastError());
RETURN_HR_IF_EXPECTED(searchResult, searchResult == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
RETURN_IF_FAILED(searchResult);
}
// AdaptFixedSizeToAllocatedResult expects that the length will always include the NUL.
// If the result is copied to the buffer, SearchPathW returns the length of copied string, WITHOUT the NUL.
// If the buffer is too small to hold the result, SearchPathW returns the length of the required buffer WITH the nul.
if (*valueLengthNeededWithNul < valueLength)
{
(*valueLengthNeededWithNul)++; // It fit, account for the null.
}
return S_OK;
});
}
// This function does not work beyond the default stack buffer size (255).
// Needs to to retry in a loop similar to wil::GetModuleFileNameExW
// These updates and unit tests are tracked by https://github.com/Microsoft/wil/issues/3
template <typename string_type, size_t stackBufferLength = 256>
HRESULT QueryFullProcessImageNameW(HANDLE processHandle, _In_ DWORD flags, string_type& result) WI_NOEXCEPT
{
return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(result,
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
{
DWORD lengthToUse = static_cast<DWORD>(valueLength);
BOOL const success = ::QueryFullProcessImageNameW(processHandle, flags, value, &lengthToUse);
RETURN_LAST_ERROR_IF((success == FALSE) && (::GetLastError() != ERROR_INSUFFICIENT_BUFFER));
// On both success or insufficient buffer case, add +1 for the null-terminating character
*valueLengthNeededWithNul = lengthToUse + 1;
return S_OK;
});
}
/** Expands environment strings and checks path existence with SearchPathW */
template <typename string_type, size_t stackBufferLength = 256>
HRESULT ExpandEnvAndSearchPath(_In_ PCWSTR input, string_type& result) WI_NOEXCEPT
{
wil::unique_cotaskmem_string expandedName;
RETURN_IF_FAILED((wil::ExpandEnvironmentStringsW<string_type, stackBufferLength>(input, expandedName)));
// ERROR_FILE_NOT_FOUND is an expected return value for SearchPathW
const HRESULT searchResult = (wil::SearchPathW<string_type, stackBufferLength>(nullptr, expandedName.get(), nullptr, result));
RETURN_HR_IF_EXPECTED(searchResult, searchResult == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
RETURN_IF_FAILED(searchResult);
return S_OK;
}
#endif
/** Looks up the environment variable 'key' and fails if it is not found.
'key' should not have '%' prefix and suffix.
Dangerous since environment variable generally are optional. */
template <typename string_type>
inline HRESULT GetEnvironmentVariableW(_In_ PCWSTR key, string_type& result) WI_NOEXCEPT
{
return wil::AdaptFixedSizeToAllocatedResult(result,
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
{
// If the function succeeds, the return value is the number of characters stored in the buffer
// pointed to by lpBuffer, not including the terminating null character.
//
// If lpBuffer is not large enough to hold the data, the return value is the buffer size, in
// characters, required to hold the string and its terminating null character and the contents of
// lpBuffer are undefined.
//
// If the function fails, the return value is zero. If the specified environment variable was not
// found in the environment block, GetLastError returns ERROR_ENVVAR_NOT_FOUND.
::SetLastError(ERROR_SUCCESS);
*valueLengthNeededWithNul = ::GetEnvironmentVariableW(key, value, static_cast<DWORD>(valueLength));
RETURN_LAST_ERROR_IF_EXPECTED((*valueLengthNeededWithNul == 0) && (::GetLastError() != ERROR_SUCCESS));
if (*valueLengthNeededWithNul < valueLength)
{
(*valueLengthNeededWithNul)++; // It fit, account for the null.
}
return S_OK;
});
}
/** Looks up the environment variable 'key' and returns null if it is not found.
'key' should not have '%' prefix and suffix. */
template <typename string_type>
HRESULT TryGetEnvironmentVariableW(_In_ PCWSTR key, string_type& result) WI_NOEXCEPT
{
const auto hr = wil::GetEnvironmentVariableW<string_type>(key, result);
RETURN_HR_IF(hr, FAILED(hr) && (hr != HRESULT_FROM_WIN32(ERROR_ENVVAR_NOT_FOUND)));
return S_OK;
}
/** Retrieves the fully qualified path for the file containing the specified module loaded
by a given process. Note GetModuleFileNameExW is a macro.*/
template <typename string_type, size_t initialBufferLength = 128>
HRESULT GetModuleFileNameExW(_In_opt_ HANDLE process, _In_opt_ HMODULE module, string_type& path)
{
// initialBufferLength is a template parameter to allow for testing. It creates some waste for
// shorter paths, but avoids iteration through the loop in common cases where paths are less
// than 128 characters.
// wil::max_extended_path_length + 1 (for the null char)
// + 1 (to be certain GetModuleFileNameExW didn't truncate)
size_t const ensureNoTrucation = (process != nullptr) ? 1 : 0;
size_t const maxExtendedPathLengthWithNull = wil::max_extended_path_length + 1 + ensureNoTrucation;
details::string_maker<string_type> maker;
for (size_t lengthWithNull = initialBufferLength;
lengthWithNull <= maxExtendedPathLengthWithNull;
lengthWithNull = (wistd::min)(lengthWithNull * 2, maxExtendedPathLengthWithNull))
{
// make() adds space for the trailing null
RETURN_IF_FAILED(maker.make(nullptr, lengthWithNull - 1));
DWORD copiedCount;
bool copyFailed;
bool copySucceededWithNoTruncation;
if (process != nullptr)
{
// GetModuleFileNameExW truncates and provides no error or other indication it has done so.
// The only way to be sure it didn't truncate is if it didn't need the whole buffer.
copiedCount = ::GetModuleFileNameExW(process, module, maker.buffer(), static_cast<DWORD>(lengthWithNull));
copyFailed = (0 == copiedCount);
copySucceededWithNoTruncation = !copyFailed && (copiedCount < lengthWithNull - 1);
}
else
{
// In cases of insufficient buffer, GetModuleFileNameW will return a value equal to lengthWithNull
// and set the last error to ERROR_INSUFFICIENT_BUFFER.
copiedCount = ::GetModuleFileNameW(module, maker.buffer(), static_cast<DWORD>(lengthWithNull));
copyFailed = (0 == copiedCount);
copySucceededWithNoTruncation = !copyFailed && (copiedCount < lengthWithNull);
}
if (copyFailed)
{
RETURN_LAST_ERROR();
}
else if (copySucceededWithNoTruncation)
{
path = maker.release();
return S_OK;
}
WI_ASSERT((process != nullptr) || (::GetLastError() == ERROR_INSUFFICIENT_BUFFER));
if (lengthWithNull == maxExtendedPathLengthWithNull)
{
// If we've reached this point, there's no point in trying a larger buffer size.
break;
}
}
// Any path should fit into the maximum max_extended_path_length. If we reached here, something went
// terribly wrong.
FAIL_FAST();
}
/** Retrieves the fully qualified path for the file that contains the specified module.
The module must have been loaded by the current process. The path returned will use the
same format that was specified when the module was loaded. Therefore, the path can be a
long or short file name, and can have the prefix '\\?\'. */
template <typename string_type, size_t initialBufferLength = 128>
HRESULT GetModuleFileNameW(HMODULE module, string_type& path)
{
return wil::GetModuleFileNameExW<string_type, initialBufferLength>(nullptr, module, path);
}
template <typename string_type, size_t stackBufferLength = 256>
HRESULT GetSystemDirectoryW(string_type& result) WI_NOEXCEPT
{
return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(result,
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
{
*valueLengthNeededWithNul = ::GetSystemDirectoryW(value, static_cast<DWORD>(valueLength));
RETURN_LAST_ERROR_IF(*valueLengthNeededWithNul == 0);
if (*valueLengthNeededWithNul < valueLength)
{
(*valueLengthNeededWithNul)++; // it fit, account for the null
}
return S_OK;
});
}
#ifdef WIL_ENABLE_EXCEPTIONS
/** Expands the '%' quoted environment variables in 'input' using ExpandEnvironmentStringsW(); */
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
string_type ExpandEnvironmentStringsW(_In_ PCWSTR input)
{
string_type result;
THROW_IF_FAILED((wil::ExpandEnvironmentStringsW<string_type, stackBufferLength>(input, result)));
return result;
}
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
/** Searches for a specified file in a specified path using SearchPathW*/
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
string_type TrySearchPathW(_In_opt_ PCWSTR path, _In_ PCWSTR fileName, PCWSTR _In_opt_ extension)
{
string_type result;
HRESULT searchHR = wil::SearchPathW<string_type, stackBufferLength>(path, fileName, extension, result);
THROW_HR_IF(searchHR, FAILED(searchHR) && (searchHR != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)));
return result;
}
#endif
/** Looks up the environment variable 'key' and fails if it is not found.
'key' should not have '%' prefix and suffix.
Dangerous since environment variable generally are optional. */
template <typename string_type = wil::unique_cotaskmem_string>
string_type GetEnvironmentVariableW(_In_ PCWSTR key)
{
string_type result;
THROW_IF_FAILED(wil::GetEnvironmentVariableW<string_type>(key, result));
return result;
}
/** Looks up the environment variable 'key' and returns null if it is not found.
'key' should not have '%' prefix and suffix. */
template <typename string_type = wil::unique_cotaskmem_string>
string_type TryGetEnvironmentVariableW(_In_ PCWSTR key)
{
string_type result;
THROW_IF_FAILED(wil::TryGetEnvironmentVariableW<string_type>(key, result));
return result;
}
template <typename string_type = wil::unique_cotaskmem_string>
string_type GetModuleFileNameW(HMODULE module)
{
string_type result;
THROW_IF_FAILED(wil::GetModuleFileNameW(module, result));
return result;
}
template <typename string_type = wil::unique_cotaskmem_string>
string_type GetModuleFileNameExW(HANDLE process, HMODULE module)
{
string_type result;
THROW_IF_FAILED(wil::GetModuleFileNameExW(process, module, result));
return result;
}
#endif
/** Retrieve the HINSTANCE for the current DLL or EXE using this symbol that
the linker provides for every module. This avoids the need for a global HINSTANCE variable
and provides access to this value for static libraries. */
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
inline HINSTANCE GetModuleInstanceHandle() { return reinterpret_cast<HINSTANCE>(&__ImageBase); }
/// @cond
namespace details
{
class init_once_completer
{
INIT_ONCE& m_once;
unsigned long m_flags = INIT_ONCE_INIT_FAILED;
public:
init_once_completer(_In_ INIT_ONCE& once) : m_once(once)
{
}
#pragma warning(push)
#pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2
void success()
{
m_flags = 0;
}
#pragma warning(pop)
~init_once_completer()
{
::InitOnceComplete(&m_once, m_flags, nullptr);
}
};
}
/// @endcond
/** Performs one-time initialization
Simplifies using the Win32 INIT_ONCE structure to perform one-time initialization. The provided `func` is invoked
at most once.
~~~~
INIT_ONCE g_init{};
ComPtr<IFoo> g_foo;
HRESULT MyMethod()
{
bool winner = false;
RETURN_IF_FAILED(wil::init_once_nothrow(g_init, []
{
ComPtr<IFoo> foo;
RETURN_IF_FAILED(::CoCreateInstance(..., IID_PPV_ARGS(&foo));
RETURN_IF_FAILED(foo->Startup());
g_foo = foo;
}, &winner);
if (winner)
{
RETURN_IF_FAILED(g_foo->Another());
}
return S_OK;
}
~~~~
See MSDN for more information on `InitOnceExecuteOnce`.
@param initOnce The INIT_ONCE structure to use as context for initialization.
@param func A function that will be invoked to perform initialization. If this fails, the init call
fails and the once-init is not marked as initialized. A later caller could attempt to
initialize it a second time.
@param callerCompleted Set to 'true' if this was the call that caused initialization, false otherwise.
*/
template<typename T> HRESULT init_once_nothrow(_Inout_ INIT_ONCE& initOnce, T func, _Out_opt_ bool* callerCompleted = nullptr) WI_NOEXCEPT
{
BOOL pending = FALSE;
wil::assign_to_opt_param(callerCompleted, false);
__WIL_PRIVATE_RETURN_IF_WIN32_BOOL_FALSE(InitOnceBeginInitialize(&initOnce, 0, &pending, nullptr));
if (pending)
{
details::init_once_completer completion(initOnce);
__WIL_PRIVATE_RETURN_IF_FAILED(func());
completion.success();
wil::assign_to_opt_param(callerCompleted, true);
}
return S_OK;
}
//! Similar to init_once_nothrow, but fails-fast if the initialization step failed. The 'callerComplete' value is
//! returned to the caller instead of being an out-parameter.
template<typename T> bool init_once_failfast(_Inout_ INIT_ONCE& initOnce, T&& func) WI_NOEXCEPT
{
bool callerCompleted;
FAIL_FAST_IF_FAILED(init_once_nothrow(initOnce, wistd::forward<T>(func), &callerCompleted));
return callerCompleted;
};
//! Returns 'true' if this `init_once` structure has finished initialization, false otherwise.
inline bool init_once_initialized(_Inout_ INIT_ONCE& initOnce) WI_NOEXCEPT
{
BOOL pending = FALSE;
return ::InitOnceBeginInitialize(&initOnce, INIT_ONCE_CHECK_ONLY, &pending, nullptr) && !pending;
}
#ifdef WIL_ENABLE_EXCEPTIONS
/** Performs one-time initialization
Simplifies using the Win32 INIT_ONCE structure to perform one-time initialization. The provided `func` is invoked
at most once.
~~~~
INIT_ONCE g_init{};
ComPtr<IFoo> g_foo;
void MyMethod()
{
bool winner = wil::init_once(g_init, []
{
ComPtr<IFoo> foo;
THROW_IF_FAILED(::CoCreateInstance(..., IID_PPV_ARGS(&foo));
THROW_IF_FAILED(foo->Startup());
g_foo = foo;
});
if (winner)
{
THROW_IF_FAILED(g_foo->Another());
}
}
~~~~
See MSDN for more information on `InitOnceExecuteOnce`.
@param initOnce The INIT_ONCE structure to use as context for initialization.
@param func A function that will be invoked to perform initialization. If this fails, the init call
fails and the once-init is not marked as initialized. A later caller could attempt to
initialize it a second time.
@returns 'true' if this was the call that caused initialization, false otherwise.
*/
template<typename T> bool init_once(_Inout_ INIT_ONCE& initOnce, T func)
{
BOOL pending = FALSE;
THROW_IF_WIN32_BOOL_FALSE(::InitOnceBeginInitialize(&initOnce, 0, &pending, nullptr));
if (pending)
{
details::init_once_completer completion(initOnce);
func();
completion.success();
return true;
}
else
{
return false;
}
}
#endif // WIL_ENABLE_EXCEPTIONS
}
// Macro for calling GetProcAddress(), with type safety for C++ clients
// using the type information from the specified function.
// The return value is automatically cast to match the function prototype of the input function.
//
// Sample usage:
//
// auto sendMail = GetProcAddressByFunctionDeclaration(hinstMAPI, MAPISendMailW);
// if (sendMail)
// {
// sendMail(0, 0, pmm, MAPI_USE_DEFAULT, 0);
// }
// Declaration
#define GetProcAddressByFunctionDeclaration(hinst, fn) reinterpret_cast<decltype(::fn)*>(GetProcAddress(hinst, #fn))
#endif // __WIL_WIN32_HELPERS_INCLUDED

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,548 @@
// -*- C++ -*-
//===--------------------------- __config ---------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// STL common functionality
//
// Some aspects of STL are core language concepts that should be used from all C++ code, regardless
// of whether exceptions are enabled in the component. Common library code that expects to be used
// from exception-free components want these concepts, but including STL headers directly introduces
// friction as it requires components not using STL to declare their STL version. Doing so creates
// ambiguity around whether STL use is safe in a particular component and implicitly brings in
// a long list of headers (including <new>) which can create further ambiguity around throwing new
// support (some routines pulled in may expect it). Secondarily, pulling in these headers also has
// the potential to create naming conflicts or other implied dependencies.
//
// To promote the use of these core language concepts outside of STL-based binaries, this file is
// selectively pulling those concepts *directly* from corresponding STL headers. The corresponding
// "std::" namespace STL functions and types should be preferred over these in code that is bound to
// STL. The implementation and naming of all functions are taken directly from STL, instead using
// "wistd" (Windows Implementation std) as the namespace.
//
// Routines in this namespace should always be considered a reflection of the *current* STL implementation
// of those routines. Updates from STL should be taken, but no "bugs" should be fixed here.
//
// New, exception-based code should not use this namespace, but instead should prefer the std:: implementation.
// Only code that is not exception-based and libraries that expect to be utilized across both exception
// and non-exception based code should utilize this functionality.
// This header mimics libc++'s '__config' header to the extent necessary to get the wistd::* definitions compiling. Note
// that this has a few key differences since libc++'s MSVC compatability is currently not functional and a bit behind
#ifndef _WISTD_CONFIG_H_
#define _WISTD_CONFIG_H_
// DO NOT add *any* additional includes to this file -- there should be no dependencies from its usage
#include <stddef.h> // For size_t and other necessary types
/// @cond
#if defined(_MSC_VER) && !defined(__clang__)
# if !defined(__WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# define __WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER
# endif
#endif
#ifndef __WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER
#pragma GCC system_header
#endif
#ifdef __GNUC__
# define __WI_GNUC_VER (__GNUC__ * 100 + __GNUC_MINOR__)
// The __WI_GNUC_VER_NEW macro better represents the new GCC versioning scheme
// introduced in GCC 5.0.
# define __WI_GNUC_VER_NEW (__WI_GNUC_VER * 10 + __GNUC_PATCHLEVEL__)
#else
# define __WI_GNUC_VER 0
# define __WI_GNUC_VER_NEW 0
#endif
// _MSVC_LANG is the more accurate way to get the C++ version in MSVC
#if defined(_MSVC_LANG) && (_MSVC_LANG > __cplusplus)
#define __WI_CPLUSPLUS _MSVC_LANG
#else
#define __WI_CPLUSPLUS __cplusplus
#endif
#ifndef __WI_LIBCPP_STD_VER
# if __WI_CPLUSPLUS <= 201103L
# define __WI_LIBCPP_STD_VER 11
# elif __WI_CPLUSPLUS <= 201402L
# define __WI_LIBCPP_STD_VER 14
# elif __WI_CPLUSPLUS <= 201703L
# define __WI_LIBCPP_STD_VER 17
# else
# define __WI_LIBCPP_STD_VER 18 // current year, or date of c++2a ratification
# endif
#endif // __WI_LIBCPP_STD_VER
#if __WI_CPLUSPLUS < 201103L
#define __WI_LIBCPP_CXX03_LANG
#endif
#if defined(__ELF__)
# define __WI_LIBCPP_OBJECT_FORMAT_ELF 1
#elif defined(__MACH__)
# define __WI_LIBCPP_OBJECT_FORMAT_MACHO 1
#elif defined(_WIN32)
# define __WI_LIBCPP_OBJECT_FORMAT_COFF 1
#elif defined(__wasm__)
# define __WI_LIBCPP_OBJECT_FORMAT_WASM 1
#else
# error Unknown object file format
#endif
#if defined(__clang__)
# define __WI_LIBCPP_COMPILER_CLANG
#elif defined(__GNUC__)
# define __WI_LIBCPP_COMPILER_GCC
#elif defined(_MSC_VER)
# define __WI_LIBCPP_COMPILER_MSVC
#elif defined(__IBMCPP__)
# define __WI_LIBCPP_COMPILER_IBM
#endif
// NOTE: MSVC, which is what we primarily target, is severly underrepresented in libc++ and checks such as
// __has_feature(...) are always false for MSVC, even when the feature being tested _is_ present in MSVC. Therefore, we
// instead modify all checks to be __WI_HAS_FEATURE_IS_UNION, etc., which provides the correct value for MSVC and falls
// back to the __has_feature(...), etc. value otherwise. We intentionally leave '__has_feature', etc. undefined for MSVC
// so that we don't accidentally use the incorrect behavior
#ifndef __WI_LIBCPP_COMPILER_MSVC
#ifndef __has_feature
#define __has_feature(__x) 0
#endif
// '__is_identifier' returns '0' if '__x' is a reserved identifier provided by
// the compiler and '1' otherwise.
#ifndef __is_identifier
#define __is_identifier(__x) 1
#endif
#ifndef __has_cpp_attribute
#define __has_cpp_attribute(__x) 0
#endif
#ifndef __has_attribute
#define __has_attribute(__x) 0
#endif
#ifndef __has_builtin
#define __has_builtin(__x) 0
#endif
#if __has_feature(cxx_alignas)
# define __WI_ALIGNAS_TYPE(x) alignas(x)
# define __WI_ALIGNAS(x) alignas(x)
#else
# define __WI_ALIGNAS_TYPE(x) __attribute__((__aligned__(__alignof(x))))
# define __WI_ALIGNAS(x) __attribute__((__aligned__(x)))
#endif
#if __has_feature(cxx_explicit_conversions) || defined(__IBMCPP__) || \
(!defined(__WI_LIBCPP_CXX03_LANG) && defined(__GNUC__)) // All supported GCC versions
# define __WI_LIBCPP_EXPLICIT explicit
#else
# define __WI_LIBCPP_EXPLICIT
#endif
#if __has_feature(cxx_attributes)
# define __WI_LIBCPP_NORETURN [[noreturn]]
#else
# define __WI_LIBCPP_NORETURN __attribute__ ((noreturn))
#endif
#define __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
#define __WI_LIBCPP_SUPPRESS_NOEXCEPT_ANALYSIS
// The __WI_LIBCPP_NODISCARD_ATTRIBUTE should only be used to define other
// NODISCARD macros to the correct attribute.
#if __has_cpp_attribute(nodiscard)
# define __WI_LIBCPP_NODISCARD_ATTRIBUTE [[nodiscard]]
#elif defined(__WI_LIBCPP_COMPILER_CLANG) && !defined(__WI_LIBCPP_CXX03_LANG)
# define __WI_LIBCPP_NODISCARD_ATTRIBUTE [[clang::warn_unused_result]]
#else
// We can't use GCC's [[gnu::warn_unused_result]] and
// __attribute__((warn_unused_result)), because GCC does not silence them via
// (void) cast.
# define __WI_LIBCPP_NODISCARD_ATTRIBUTE
#endif
#define __WI_HAS_FEATURE_IS_UNION __has_feature(is_union)
#define __WI_HAS_FEATURE_IS_CLASS __has_feature(is_class)
#define __WI_HAS_FEATURE_IS_ENUM __has_feature(is_enum)
#define __WI_HAS_FEATURE_IS_CONVERTIBLE_TO __has_feature(is_convertible_to)
#define __WI_HAS_FEATURE_IS_EMPTY __has_feature(is_empty)
#define __WI_HAS_FEATURE_IS_POLYMORPHIC __has_feature(is_polymorphic)
#define __WI_HAS_FEATURE_HAS_VIRTUAL_DESTRUCTOR __has_feature(has_virtual_destructor)
#define __WI_HAS_FEATURE_REFERENCE_QUALIFIED_FUNCTIONS __has_feature(cxx_reference_qualified_functions)
#define __WI_HAS_FEATURE_IS_CONSTRUCTIBLE __has_feature(is_constructible)
#define __WI_HAS_FEATURE_IS_TRIVIALLY_CONSTRUCTIBLE __has_feature(is_trivially_constructible)
#define __WI_HAS_FEATURE_IS_TRIVIALLY_ASSIGNABLE __has_feature(is_trivially_assignable)
#define __WI_HAS_FEATURE_HAS_TRIVIAL_DESTRUCTOR __has_feature(has_trivial_destructor)
#define __WI_HAS_FEATURE_NOEXCEPT __has_feature(cxx_noexcept)
#define __WI_HAS_FEATURE_IS_POD __has_feature(is_pod)
#define __WI_HAS_FEATURE_IS_STANDARD_LAYOUT __has_feature(is_standard_layout)
#define __WI_HAS_FEATURE_IS_TRIVIALLY_COPYABLE __has_feature(is_trivially_copyable)
#define __WI_HAS_FEATURE_IS_TRIVIAL __has_feature(is_trivial)
#define __WI_HAS_FEATURE_HAS_TRIVIAL_CONSTRUCTOR __has_feature(has_trivial_constructor) || (__WI_GNUC_VER >= 403)
#define __WI_HAS_FEATURE_HAS_NOTHROW_CONSTRUCTOR __has_feature(has_nothrow_constructor) || (__WI_GNUC_VER >= 403)
#define __WI_HAS_FEATURE_HAS_NOTHROW_COPY __has_feature(has_nothrow_copy) || (__WI_GNUC_VER >= 403)
#define __WI_HAS_FEATURE_HAS_NOTHROW_ASSIGN __has_feature(has_nothrow_assign) || (__WI_GNUC_VER >= 403)
#if !(__has_feature(cxx_noexcept))
#define __WI_LIBCPP_HAS_NO_NOEXCEPT
#endif
#if !__is_identifier(__has_unique_object_representations) || __WI_GNUC_VER >= 700
#define __WI_LIBCPP_HAS_UNIQUE_OBJECT_REPRESENTATIONS
#endif
#if !(__has_feature(cxx_variadic_templates))
#define __WI_LIBCPP_HAS_NO_VARIADICS
#endif
#if __has_feature(is_literal) || __WI_GNUC_VER >= 407
#define __WI_LIBCPP_IS_LITERAL(T) __is_literal(T)
#endif
#if __has_feature(underlying_type) || __WI_GNUC_VER >= 407
#define __WI_LIBCPP_UNDERLYING_TYPE(T) __underlying_type(T)
#endif
#if __has_feature(is_final) || __WI_GNUC_VER >= 407
#define __WI_LIBCPP_HAS_IS_FINAL
#endif
#if __has_feature(is_base_of) || defined(__GNUC__) && __WI_GNUC_VER >= 403
#define __WI_LIBCPP_HAS_IS_BASE_OF
#endif
#if __is_identifier(__is_aggregate) && (__WI_GNUC_VER_NEW < 7001)
#define __WI_LIBCPP_HAS_NO_IS_AGGREGATE
#endif
#if !(__has_feature(cxx_rtti)) && !defined(__WI_LIBCPP_NO_RTTI)
#define __WI_LIBCPP_NO_RTTI
#endif
#if !(__has_feature(cxx_variable_templates))
#define __WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES
#endif
#if !(__has_feature(cxx_relaxed_constexpr))
#define __WI_LIBCPP_HAS_NO_CXX14_CONSTEXPR
#endif
#if !__has_builtin(__builtin_addressof) && _GNUC_VER < 700
#define __WI_LIBCPP_HAS_NO_BUILTIN_ADDRESSOF
#endif
#if __has_attribute(__no_sanitize__) && !defined(__WI_LIBCPP_COMPILER_GCC)
# define __WI_LIBCPP_NO_CFI __attribute__((__no_sanitize__("cfi")))
#else
# define __WI_LIBCPP_NO_CFI
#endif
#define __WI_LIBCPP_ALWAYS_INLINE __attribute__ ((__always_inline__))
#if __has_attribute(internal_linkage)
# define __WI_LIBCPP_INTERNAL_LINKAGE __attribute__ ((internal_linkage))
#else
# define __WI_LIBCPP_INTERNAL_LINKAGE __WI_LIBCPP_ALWAYS_INLINE
#endif
#else
// NOTE: Much of the following assumes a decently recent version of MSVC. Past versions can be supported, but will need
// to be updated to contain the proper _MSC_VER check
#define __WI_ALIGNAS_TYPE(x) alignas(x)
#define __WI_ALIGNAS(x) alignas(x)
#define __alignof__ __alignof
#define __WI_LIBCPP_EXPLICIT explicit
#define __WI_LIBCPP_NORETURN [[noreturn]]
#define __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS __pragma(warning(suppress:26495))
#define __WI_LIBCPP_SUPPRESS_NOEXCEPT_ANALYSIS __pragma(warning(suppress:26439))
#if __WI_LIBCPP_STD_VER > 14
#define __WI_LIBCPP_NODISCARD_ATTRIBUTE [[nodiscard]]
#else
#define __WI_LIBCPP_NODISCARD_ATTRIBUTE _Check_return_
#endif
#define __WI_HAS_FEATURE_IS_UNION 1
#define __WI_HAS_FEATURE_IS_CLASS 1
#define __WI_HAS_FEATURE_IS_ENUM 1
#define __WI_HAS_FEATURE_IS_CONVERTIBLE_TO 1
#define __WI_HAS_FEATURE_IS_EMPTY 1
#define __WI_HAS_FEATURE_IS_POLYMORPHIC 1
#define __WI_HAS_FEATURE_HAS_VIRTUAL_DESTRUCTOR 1
#define __WI_LIBCPP_HAS_UNIQUE_OBJECT_REPRESENTATIONS 1
#define __WI_HAS_FEATURE_REFERENCE_QUALIFIED_FUNCTIONS 1
#define __WI_HAS_FEATURE_IS_CONSTRUCTIBLE 1
#define __WI_HAS_FEATURE_IS_TRIVIALLY_CONSTRUCTIBLE 1
#define __WI_HAS_FEATURE_IS_TRIVIALLY_ASSIGNABLE 1
#define __WI_HAS_FEATURE_HAS_TRIVIAL_DESTRUCTOR 1
#define __WI_HAS_FEATURE_NOEXCEPT 1
#define __WI_HAS_FEATURE_IS_POD 1
#define __WI_HAS_FEATURE_IS_STANDARD_LAYOUT 1
#define __WI_HAS_FEATURE_IS_TRIVIALLY_COPYABLE 1
#define __WI_HAS_FEATURE_IS_TRIVIAL 1
#define __WI_HAS_FEATURE_HAS_TRIVIAL_CONSTRUCTOR 1
#define __WI_HAS_FEATURE_HAS_NOTHROW_CONSTRUCTOR 1
#define __WI_HAS_FEATURE_HAS_NOTHROW_COPY 1
#define __WI_HAS_FEATURE_HAS_NOTHROW_ASSIGN 1
#define __WI_HAS_FEATURE_IS_DESTRUCTIBLE 1
#if !defined(_CPPRTTI) && !defined(__WI_LIBCPP_NO_RTTI)
#define __WI_LIBCPP_NO_RTTI
#endif
#define __WI_LIBCPP_IS_LITERAL(T) __is_literal_type(T)
#define __WI_LIBCPP_UNDERLYING_TYPE(T) __underlying_type(T)
#define __WI_LIBCPP_HAS_IS_FINAL
#define __WI_LIBCPP_HAS_IS_BASE_OF
#if __WI_LIBCPP_STD_VER < 14
#define __WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES
#endif
#define __WI_LIBCPP_HAS_NO_BUILTIN_ADDRESSOF
#define __WI_LIBCPP_NO_CFI
#define __WI_LIBCPP_ALWAYS_INLINE __forceinline
#define __WI_LIBCPP_INTERNAL_LINKAGE
#endif
#ifndef _WIN32
#ifdef __LITTLE_ENDIAN__
# if __LITTLE_ENDIAN__
# define __WI_LIBCPP_LITTLE_ENDIAN
# endif // __LITTLE_ENDIAN__
#endif // __LITTLE_ENDIAN__
#ifdef __BIG_ENDIAN__
# if __BIG_ENDIAN__
# define __WI_LIBCPP_BIG_ENDIAN
# endif // __BIG_ENDIAN__
#endif // __BIG_ENDIAN__
#ifdef __BYTE_ORDER__
# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
# define __WI_LIBCPP_LITTLE_ENDIAN
# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
# define __WI_LIBCPP_BIG_ENDIAN
# endif // __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#endif // __BYTE_ORDER__
#if !defined(__WI_LIBCPP_LITTLE_ENDIAN) && !defined(__WI_LIBCPP_BIG_ENDIAN)
# include <endian.h>
# if __BYTE_ORDER == __LITTLE_ENDIAN
# define __WI_LIBCPP_LITTLE_ENDIAN
# elif __BYTE_ORDER == __BIG_ENDIAN
# define __WI_LIBCPP_BIG_ENDIAN
# else // __BYTE_ORDER == __BIG_ENDIAN
# error unable to determine endian
# endif
#endif // !defined(__WI_LIBCPP_LITTLE_ENDIAN) && !defined(__WI_LIBCPP_BIG_ENDIAN)
#else // _WIN32
#define __WI_LIBCPP_LITTLE_ENDIAN
#endif // _WIN32
#ifdef __WI_LIBCPP_HAS_NO_CONSTEXPR
# define __WI_LIBCPP_CONSTEXPR
#else
# define __WI_LIBCPP_CONSTEXPR constexpr
#endif
#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_CXX14_CONSTEXPR)
# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 constexpr
#else
# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
#endif
#if __WI_LIBCPP_STD_VER > 14 && !defined(__WI_LIBCPP_HAS_NO_CXX14_CONSTEXPR)
# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX14 constexpr
#else
# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX14
#endif
#if __WI_LIBCPP_STD_VER > 17 && !defined(__WI_LIBCPP_HAS_NO_CXX14_CONSTEXPR)
# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX17 constexpr
#else
# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX17
#endif
#if !defined(__WI_LIBCPP_DISABLE_NODISCARD_AFTER_CXX17) && \
(__WI_LIBCPP_STD_VER > 17 || defined(__WI_LIBCPP_ENABLE_NODISCARD))
# define __WI_LIBCPP_NODISCARD_AFTER_CXX17 __WI_LIBCPP_NODISCARD_ATTRIBUTE
#else
# define __WI_LIBCPP_NODISCARD_AFTER_CXX17
#endif
#if __WI_LIBCPP_STD_VER > 14 && defined(__cpp_inline_variables) && (__cpp_inline_variables >= 201606L)
# define __WI_LIBCPP_INLINE_VAR inline
#else
# define __WI_LIBCPP_INLINE_VAR
#endif
#ifdef __WI_LIBCPP_CXX03_LANG
#define __WI_LIBCPP_HAS_NO_UNICODE_CHARS
#define __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES
#endif
#ifndef __SIZEOF_INT128__
#define __WI_LIBCPP_HAS_NO_INT128
#endif
#if !__WI_HAS_FEATURE_NOEXCEPT && !defined(__WI_LIBCPP_HAS_NO_NOEXCEPT)
#define __WI_LIBCPP_HAS_NO_NOEXCEPT
#endif
#ifndef __WI_LIBCPP_HAS_NO_NOEXCEPT
# define WI_NOEXCEPT noexcept
# define __WI_NOEXCEPT_(x) noexcept(x)
#else
# define WI_NOEXCEPT throw()
# define __WI_NOEXCEPT_(x)
#endif
#if defined(__WI_LIBCPP_OBJECT_FORMAT_COFF)
#define __WI_LIBCPP_HIDDEN
#define __WI_LIBCPP_TEMPLATE_VIS
#endif // defined(__WI_LIBCPP_OBJECT_FORMAT_COFF)
#ifndef __WI_LIBCPP_HIDDEN
# if !defined(__WI_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS)
# define __WI_LIBCPP_HIDDEN __attribute__ ((__visibility__("hidden")))
# else
# define __WI_LIBCPP_HIDDEN
# endif
#endif
#ifndef __WI_LIBCPP_TEMPLATE_VIS
# if !defined(__WI_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS) && !defined(__WI_LIBCPP_COMPILER_MSVC)
# if __has_attribute(__type_visibility__)
# define __WI_LIBCPP_TEMPLATE_VIS __attribute__ ((__type_visibility__("default")))
# else
# define __WI_LIBCPP_TEMPLATE_VIS __attribute__ ((__visibility__("default")))
# endif
# else
# define __WI_LIBCPP_TEMPLATE_VIS
# endif
#endif
#define __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_HIDDEN __WI_LIBCPP_INTERNAL_LINKAGE
namespace wistd // ("Windows Implementation" std)
{
typedef decltype(__nullptr) nullptr_t;
template <class _T1, class _T2 = _T1>
struct __less
{
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
bool operator()(const _T1& __x, const _T1& __y) const {return __x < __y;}
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
bool operator()(const _T1& __x, const _T2& __y) const {return __x < __y;}
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
bool operator()(const _T2& __x, const _T1& __y) const {return __x < __y;}
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
bool operator()(const _T2& __x, const _T2& __y) const {return __x < __y;}
};
template <class _T1>
struct __less<_T1, _T1>
{
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
bool operator()(const _T1& __x, const _T1& __y) const {return __x < __y;}
};
template <class _T1>
struct __less<const _T1, _T1>
{
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
bool operator()(const _T1& __x, const _T1& __y) const {return __x < __y;}
};
template <class _T1>
struct __less<_T1, const _T1>
{
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
bool operator()(const _T1& __x, const _T1& __y) const {return __x < __y;}
};
// These are added to wistd to enable use of min/max without having to use the windows.h min/max
// macros that some clients might not have access to. Note: the STL versions of these have debug
// checking for the less than operator and support for iterators that these implementations lack.
// Use the STL versions when you require use of those features.
// min
template <class _Tp, class _Compare>
inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
const _Tp&
(min)(const _Tp& __a, const _Tp& __b, _Compare __comp)
{
return __comp(__b, __a) ? __b : __a;
}
template <class _Tp>
inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
const _Tp&
(min)(const _Tp& __a, const _Tp& __b)
{
return (min)(__a, __b, __less<_Tp>());
}
// max
template <class _Tp, class _Compare>
inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
const _Tp&
(max)(const _Tp& __a, const _Tp& __b, _Compare __comp)
{
return __comp(__a, __b) ? __b : __a;
}
template <class _Tp>
inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
const _Tp&
(max)(const _Tp& __a, const _Tp& __b)
{
return (max)(__a, __b, __less<_Tp>());
}
template <class _Arg, class _Result>
struct __WI_LIBCPP_TEMPLATE_VIS unary_function
{
typedef _Arg argument_type;
typedef _Result result_type;
};
template <class _Arg1, class _Arg2, class _Result>
struct __WI_LIBCPP_TEMPLATE_VIS binary_function
{
typedef _Arg1 first_argument_type;
typedef _Arg2 second_argument_type;
typedef _Result result_type;
};
}
/// @endcond
#endif _WISTD_CONFIG_H_

View File

@ -0,0 +1,544 @@
// -*- C++ -*-
//===------------------------ functional ----------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// STL common functionality
//
// Some aspects of STL are core language concepts that should be used from all C++ code, regardless
// of whether exceptions are enabled in the component. Common library code that expects to be used
// from exception-free components want these concepts, but including STL headers directly introduces
// friction as it requires components not using STL to declare their STL version. Doing so creates
// ambiguity around whether STL use is safe in a particular component and implicitly brings in
// a long list of headers (including <new>) which can create further ambiguity around throwing new
// support (some routines pulled in may expect it). Secondarily, pulling in these headers also has
// the potential to create naming conflicts or other implied dependencies.
//
// To promote the use of these core language concepts outside of STL-based binaries, this file is
// selectively pulling those concepts *directly* from corresponding STL headers. The corresponding
// "std::" namespace STL functions and types should be preferred over these in code that is bound to
// STL. The implementation and naming of all functions are taken directly from STL, instead using
// "wistd" (Windows Implementation std) as the namespace.
//
// Routines in this namespace should always be considered a reflection of the *current* STL implementation
// of those routines. Updates from STL should be taken, but no "bugs" should be fixed here.
//
// New, exception-based code should not use this namespace, but instead should prefer the std:: implementation.
// Only code that is not exception-based and libraries that expect to be utilized across both exception
// and non-exception based code should utilize this functionality.
#ifndef _WISTD_FUNCTIONAL_H_
#define _WISTD_FUNCTIONAL_H_
// DO NOT add *any* additional includes to this file -- there should be no dependencies from its usage
#include "wistd_memory.h"
#include <intrin.h> // For __fastfail
#include <new.h> // For placement new
#if !defined(__WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
#pragma GCC system_header
#endif
#pragma warning(push)
#pragma warning(disable: 4324)
#pragma warning(disable: 4800)
/// @cond
namespace wistd // ("Windows Implementation" std)
{
// wistd::function
//
// All of the code below is in direct support of wistd::function. This class is identical to std::function
// with the following exceptions:
//
// 1) It never allocates and is safe to use from exception-free code (custom allocators are not supported)
// 2) It's slightly bigger on the stack (64 bytes, rather than 24 for 32bit)
// 3) There is an explicit static-assert if a lambda becomes too large to hold in the internal buffer (rather than an allocation)
template <class _Ret>
struct __invoke_void_return_wrapper
{
#ifndef __WI_LIBCPP_CXX03_LANG
template <class ..._Args>
static _Ret __call(_Args&&... __args) {
return __invoke(wistd::forward<_Args>(__args)...);
}
#else
template <class _Fn>
static _Ret __call(_Fn __f) {
return __invoke(__f);
}
template <class _Fn, class _A0>
static _Ret __call(_Fn __f, _A0& __a0) {
return __invoke(__f, __a0);
}
template <class _Fn, class _A0, class _A1>
static _Ret __call(_Fn __f, _A0& __a0, _A1& __a1) {
return __invoke(__f, __a0, __a1);
}
template <class _Fn, class _A0, class _A1, class _A2>
static _Ret __call(_Fn __f, _A0& __a0, _A1& __a1, _A2& __a2){
return __invoke(__f, __a0, __a1, __a2);
}
#endif
};
template <>
struct __invoke_void_return_wrapper<void>
{
#ifndef __WI_LIBCPP_CXX03_LANG
template <class ..._Args>
static void __call(_Args&&... __args) {
(void)__invoke(wistd::forward<_Args>(__args)...);
}
#else
template <class _Fn>
static void __call(_Fn __f) {
__invoke(__f);
}
template <class _Fn, class _A0>
static void __call(_Fn __f, _A0& __a0) {
__invoke(__f, __a0);
}
template <class _Fn, class _A0, class _A1>
static void __call(_Fn __f, _A0& __a0, _A1& __a1) {
__invoke(__f, __a0, __a1);
}
template <class _Fn, class _A0, class _A1, class _A2>
static void __call(_Fn __f, _A0& __a0, _A1& __a1, _A2& __a2) {
__invoke(__f, __a0, __a1, __a2);
}
#endif
};
////////////////////////////////////////////////////////////////////////////////
// FUNCTION
//==============================================================================
// bad_function_call
__WI_LIBCPP_NORETURN inline __WI_LIBCPP_INLINE_VISIBILITY
void __throw_bad_function_call()
{
__fastfail(7); // FAST_FAIL_FATAL_APP_EXIT
}
template<class _Fp> class __WI_LIBCPP_TEMPLATE_VIS function; // undefined
namespace __function
{
template<class _Rp>
struct __maybe_derive_from_unary_function
{
};
template<class _Rp, class _A1>
struct __maybe_derive_from_unary_function<_Rp(_A1)>
: public unary_function<_A1, _Rp>
{
};
template<class _Rp>
struct __maybe_derive_from_binary_function
{
};
template<class _Rp, class _A1, class _A2>
struct __maybe_derive_from_binary_function<_Rp(_A1, _A2)>
: public binary_function<_A1, _A2, _Rp>
{
};
template <class _Fp>
__WI_LIBCPP_INLINE_VISIBILITY
bool __not_null(_Fp const&) { return true; }
template <class _Fp>
__WI_LIBCPP_INLINE_VISIBILITY
bool __not_null(_Fp* __ptr) { return __ptr; }
template <class _Ret, class _Class>
__WI_LIBCPP_INLINE_VISIBILITY
bool __not_null(_Ret _Class::*__ptr) { return __ptr; }
template <class _Fp>
__WI_LIBCPP_INLINE_VISIBILITY
bool __not_null(function<_Fp> const& __f) { return !!__f; }
} // namespace __function
#ifndef __WI_LIBCPP_CXX03_LANG
namespace __function {
template<class _Fp> class __base;
template<class _Rp, class ..._ArgTypes>
class __base<_Rp(_ArgTypes...)>
{
__base(const __base&);
__base& operator=(const __base&);
public:
__WI_LIBCPP_INLINE_VISIBILITY __base() {}
__WI_LIBCPP_INLINE_VISIBILITY virtual ~__base() {}
virtual void __clone(__base*) const = 0;
virtual void __move(__base*) = 0;
virtual void destroy() WI_NOEXCEPT = 0;
virtual _Rp operator()(_ArgTypes&& ...) = 0;
};
template<class _FD, class _FB> class __func;
template<class _Fp, class _Rp, class ..._ArgTypes>
class __func<_Fp, _Rp(_ArgTypes...)>
: public __base<_Rp(_ArgTypes...)>
{
_Fp __f_;
public:
__WI_LIBCPP_INLINE_VISIBILITY
explicit __func(_Fp&& __f)
: __f_(wistd::move(__f)) {}
__WI_LIBCPP_INLINE_VISIBILITY
explicit __func(const _Fp& __f)
: __f_(__f) {}
virtual void __clone(__base<_Rp(_ArgTypes...)>*) const;
virtual void __move(__base<_Rp(_ArgTypes...)>*);
virtual void destroy() WI_NOEXCEPT;
virtual _Rp operator()(_ArgTypes&& ... __arg);
};
template<class _Fp, class _Rp, class ..._ArgTypes>
void
__func<_Fp, _Rp(_ArgTypes...)>::__clone(__base<_Rp(_ArgTypes...)>* __p) const
{
::new (__p) __func(__f_);
}
template<class _Fp, class _Rp, class ..._ArgTypes>
void
__func<_Fp, _Rp(_ArgTypes...)>::__move(__base<_Rp(_ArgTypes...)>* __p)
{
::new (__p) __func(wistd::move(__f_));
}
template<class _Fp, class _Rp, class ..._ArgTypes>
void
__func<_Fp, _Rp(_ArgTypes...)>::destroy() WI_NOEXCEPT
{
__f_.~_Fp();
}
template<class _Fp, class _Rp, class ..._ArgTypes>
_Rp
__func<_Fp, _Rp(_ArgTypes...)>::operator()(_ArgTypes&& ... __arg)
{
typedef __invoke_void_return_wrapper<_Rp> _Invoker;
return _Invoker::__call(__f_, wistd::forward<_ArgTypes>(__arg)...);
}
} // __function
template<class _Rp, class ..._ArgTypes>
class __WI_LIBCPP_TEMPLATE_VIS function<_Rp(_ArgTypes...)>
: public __function::__maybe_derive_from_unary_function<_Rp(_ArgTypes...)>,
public __function::__maybe_derive_from_binary_function<_Rp(_ArgTypes...)>
{
// 'wistd::function' is most similar to 'inplace_function' in that it _only_ permits holding function objects
// that can fit within its internal buffer. Therefore, we expand this size to accommodate space for at least 12
// pointers (__base vtable takes an additional one).
static constexpr size_t __buffer_size = 13 * sizeof(void*);
typedef __function::__base<_Rp(_ArgTypes...)> __base;
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
typename aligned_storage<__buffer_size>::type __buf_;
__base* __f_;
__WI_LIBCPP_NO_CFI static __base *__as_base(void *p) {
return reinterpret_cast<__base*>(p);
}
template <class _Fp, bool>
struct __callable_imp
{
static const bool value = is_same<void, _Rp>::value ||
is_convertible<typename __invoke_of<_Fp&, _ArgTypes...>::type,
_Rp>::value;
};
template <class _Fp>
struct __callable_imp<_Fp, false>
{
static const bool value = false;
};
template <class _Fp>
struct __callable
{
static const bool value = __callable_imp<_Fp, __lazy_and<
integral_constant<bool, !is_same<__uncvref_t<_Fp>, function>::value>,
__invokable<_Fp&, _ArgTypes...>
>::value>::value;
};
template <class _Fp>
using _EnableIfCallable = typename enable_if<__callable<_Fp>::value>::type;
public:
typedef _Rp result_type;
// construct/copy/destroy:
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
function() WI_NOEXCEPT : __f_(0) {}
__WI_LIBCPP_INLINE_VISIBILITY
function(nullptr_t) WI_NOEXCEPT : __f_(0) {}
function(const function&);
function(function&&);
template<class _Fp, class = _EnableIfCallable<_Fp>>
function(_Fp);
function& operator=(const function&);
function& operator=(function&&);
function& operator=(nullptr_t) WI_NOEXCEPT;
template<class _Fp, class = _EnableIfCallable<_Fp>>
function& operator=(_Fp&&);
~function();
// function modifiers:
void swap(function&);
// function capacity:
__WI_LIBCPP_INLINE_VISIBILITY
__WI_LIBCPP_EXPLICIT operator bool() const WI_NOEXCEPT {return __f_;}
// deleted overloads close possible hole in the type system
template<class _R2, class... _ArgTypes2>
bool operator==(const function<_R2(_ArgTypes2...)>&) const = delete;
template<class _R2, class... _ArgTypes2>
bool operator!=(const function<_R2(_ArgTypes2...)>&) const = delete;
public:
// function invocation:
_Rp operator()(_ArgTypes...) const;
// NOTE: type_info is very compiler specific, and on top of that, we're operating in a namespace other than
// 'std' so all functions requiring RTTI have been removed
};
template<class _Rp, class ..._ArgTypes>
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
function<_Rp(_ArgTypes...)>::function(const function& __f)
{
if (__f.__f_ == 0)
__f_ = 0;
else
{
__f_ = __as_base(&__buf_);
__f.__f_->__clone(__f_);
}
}
template<class _Rp, class ..._ArgTypes>
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS __WI_LIBCPP_SUPPRESS_NOEXCEPT_ANALYSIS
function<_Rp(_ArgTypes...)>::function(function&& __f)
{
if (__f.__f_ == 0)
__f_ = 0;
else
{
__f_ = __as_base(&__buf_);
__f.__f_->__move(__f_);
__f.__f_->destroy();
__f.__f_ = 0;
}
}
template<class _Rp, class ..._ArgTypes>
template <class _Fp, class>
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
function<_Rp(_ArgTypes...)>::function(_Fp __f)
: __f_(0)
{
if (__function::__not_null(__f))
{
typedef __function::__func<_Fp, _Rp(_ArgTypes...)> _FF;
static_assert(sizeof(_FF) <= sizeof(__buf_),
"The sizeof(wistd::function) has grown too large for the reserved buffer (12 pointers). Refactor to reduce size of the capture.");
__f_ = ::new((void*)&__buf_) _FF(wistd::move(__f));
}
}
template<class _Rp, class ..._ArgTypes>
function<_Rp(_ArgTypes...)>&
function<_Rp(_ArgTypes...)>::operator=(const function& __f)
{
*this = nullptr;
if (__f.__f_)
{
__f_ = __as_base(&__buf_);
__f.__f_->__clone(__f_);
}
return *this;
}
template<class _Rp, class ..._ArgTypes>
function<_Rp(_ArgTypes...)>&
function<_Rp(_ArgTypes...)>::operator=(function&& __f)
{
*this = nullptr;
if (__f.__f_)
{
__f_ = __as_base(&__buf_);
__f.__f_->__move(__f_);
__f.__f_->destroy();
__f.__f_ = 0;
}
return *this;
}
template<class _Rp, class ..._ArgTypes>
function<_Rp(_ArgTypes...)>&
function<_Rp(_ArgTypes...)>::operator=(nullptr_t) WI_NOEXCEPT
{
__base* __t = __f_;
__f_ = 0;
if (__t)
__t->destroy();
return *this;
}
template<class _Rp, class ..._ArgTypes>
template <class _Fp, class>
function<_Rp(_ArgTypes...)>&
function<_Rp(_ArgTypes...)>::operator=(_Fp&& __f)
{
*this = nullptr;
if (__function::__not_null(__f))
{
typedef __function::__func<typename decay<_Fp>::type, _Rp(_ArgTypes...)> _FF;
static_assert(sizeof(_FF) <= sizeof(__buf_),
"The sizeof(wistd::function) has grown too large for the reserved buffer (12 pointers). Refactor to reduce size of the capture.");
__f_ = ::new((void*)&__buf_) _FF(wistd::move(__f));
}
return *this;
}
template<class _Rp, class ..._ArgTypes>
function<_Rp(_ArgTypes...)>::~function()
{
if (__f_)
__f_->destroy();
}
template<class _Rp, class ..._ArgTypes>
void
function<_Rp(_ArgTypes...)>::swap(function& __f)
{
if (wistd::addressof(__f) == this)
return;
if (__f_ && __f.__f_)
{
typename aligned_storage<sizeof(__buf_)>::type __tempbuf;
__base* __t = __as_base(&__tempbuf);
__f_->__move(__t);
__f_->destroy();
__f_ = 0;
__f.__f_->__move(__as_base(&__buf_));
__f.__f_->destroy();
__f.__f_ = 0;
__f_ = __as_base(&__buf_);
__t->__move(__as_base(&__f.__buf_));
__t->destroy();
__f.__f_ = __as_base(&__f.__buf_);
}
else if (__f_)
{
__f_->__move(__as_base(&__f.__buf_));
__f_->destroy();
__f_ = 0;
__f.__f_ = __as_base(&__f.__buf_);
}
else if (__f.__f_)
{
__f.__f_->__move(__as_base(&__buf_));
__f.__f_->destroy();
__f.__f_ = 0;
__f_ = __as_base(&__buf_);
}
}
template<class _Rp, class ..._ArgTypes>
_Rp
function<_Rp(_ArgTypes...)>::operator()(_ArgTypes... __arg) const
{
if (__f_ == 0)
__throw_bad_function_call();
return (*__f_)(wistd::forward<_ArgTypes>(__arg)...);
}
template <class _Rp, class... _ArgTypes>
inline __WI_LIBCPP_INLINE_VISIBILITY
bool
operator==(const function<_Rp(_ArgTypes...)>& __f, nullptr_t) WI_NOEXCEPT {return !__f;}
template <class _Rp, class... _ArgTypes>
inline __WI_LIBCPP_INLINE_VISIBILITY
bool
operator==(nullptr_t, const function<_Rp(_ArgTypes...)>& __f) WI_NOEXCEPT {return !__f;}
template <class _Rp, class... _ArgTypes>
inline __WI_LIBCPP_INLINE_VISIBILITY
bool
operator!=(const function<_Rp(_ArgTypes...)>& __f, nullptr_t) WI_NOEXCEPT {return (bool)__f;}
template <class _Rp, class... _ArgTypes>
inline __WI_LIBCPP_INLINE_VISIBILITY
bool
operator!=(nullptr_t, const function<_Rp(_ArgTypes...)>& __f) WI_NOEXCEPT {return (bool)__f;}
// Provide both 'swap_wil' and 'swap' since we now have two ADL scenarios that we need to work
template <class _Rp, class... _ArgTypes>
inline __WI_LIBCPP_INLINE_VISIBILITY
void
swap(function<_Rp(_ArgTypes...)>& __x, function<_Rp(_ArgTypes...)>& __y)
{return __x.swap(__y);}
template <class _Rp, class... _ArgTypes>
inline __WI_LIBCPP_INLINE_VISIBILITY
void
swap_wil(function<_Rp(_ArgTypes...)>& __x, function<_Rp(_ArgTypes...)>& __y)
{return __x.swap(__y);}
// std::invoke
template <class _Fn, class ..._Args>
typename __invoke_of<_Fn, _Args...>::type
invoke(_Fn&& __f, _Args&&... __args)
__WI_NOEXCEPT_((__nothrow_invokable<_Fn, _Args...>::value))
{
return wistd::__invoke(wistd::forward<_Fn>(__f), wistd::forward<_Args>(__args)...);
}
#else // __WI_LIBCPP_CXX03_LANG
#error wistd::function and wistd::invoke not implemented for pre-C++11
#endif
}
/// @endcond
#pragma warning(pop)
#endif // _WISTD_FUNCTIONAL_H_

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,84 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_WRL_INCLUDED
#define __WIL_WRL_INCLUDED
#include <wrl.h>
#include "result.h"
#include "common.h" // wistd type_traits helpers
namespace wil
{
#ifdef WIL_ENABLE_EXCEPTIONS
#pragma region Object construction helpers that throw exceptions
/** Used to construct a RuntimeClass based object that uses 2 phase construction.
Construct a RuntimeClass based object that uses 2 phase construction (by implementing
RuntimeClassInitialize() and returning error codes for failures.
~~~~
// SomeClass uses 2 phase initialization by implementing RuntimeClassInitialize()
auto someClass = MakeAndInitializeOrThrow<SomeClass>(L"input", true);
~~~~ */
template <typename T, typename... TArgs>
Microsoft::WRL::ComPtr<T> MakeAndInitializeOrThrow(TArgs&&... args)
{
Microsoft::WRL::ComPtr<T> obj;
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<T>(&obj, Microsoft::WRL::Details::Forward<TArgs>(args)...));
return obj;
}
/** Used to construct an RuntimeClass based object that uses exceptions in its constructor (and does
not require 2 phase construction).
~~~~
// SomeClass uses exceptions for error handling in its constructor.
auto someClass = MakeOrThrow<SomeClass>(L"input", true);
~~~~ */
template <typename T, typename... TArgs>
Microsoft::WRL::ComPtr<T> MakeOrThrow(TArgs&&... args)
{
// This is how you can detect the presence of RuntimeClassInitialize() and find dangerous use.
// Unfortunately this produces false positives as all RuntimeClass derived classes have
// a RuntimeClassInitialize() method from their base class.
// static_assert(!std::is_member_function_pointer<decltype(&T::RuntimeClassInitialize)>::value,
// "class has a RuntimeClassInitialize member, use MakeAndInitializeOrThrow instead");
auto obj = Microsoft::WRL::Make<T>(Microsoft::WRL::Details::Forward<TArgs>(args)...);
THROW_IF_NULL_ALLOC(obj.Get());
return obj;
}
#pragma endregion
#endif // WIL_ENABLE_EXCEPTIONS
/** By default WRL Callback objects are not agile, use this to make an agile one. Replace use of Callback<> with MakeAgileCallback<>.
Will return null on failure, translate that into E_OUTOFMEMORY using XXX_IF_NULL_ALLOC()
from wil\result.h to test the result. */
template<typename TDelegateInterface, typename ...Args>
::Microsoft::WRL::ComPtr<TDelegateInterface> MakeAgileCallbackNoThrow(Args&&... args) WI_NOEXCEPT
{
using namespace Microsoft::WRL;
return Callback<Implements<RuntimeClassFlags<ClassicCom>, TDelegateInterface, FtmBase>>(wistd::forward<Args>(args)...);
}
#ifdef WIL_ENABLE_EXCEPTIONS
template<typename TDelegateInterface, typename ...Args>
::Microsoft::WRL::ComPtr<TDelegateInterface> MakeAgileCallback(Args&&... args)
{
auto result = MakeAgileCallbackNoThrow<TDelegateInterface, Args...>(wistd::forward<Args>(args)...);
THROW_IF_NULL_ALLOC(result);
return result;
}
#endif // WIL_ENABLE_EXCEPTIONS
} // namespace wil
#endif // __WIL_WRL_INCLUDED

Binary file not shown.

View File

@ -0,0 +1,47 @@
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(Force-Enable-cpprestsdk)' == '' And ('$(PlatformToolset)' != 'v141' And '$(PlatformToolset)' != 'v142')">
<Disable-cpprestsdk>true</Disable-cpprestsdk>
</PropertyGroup>
<PropertyGroup Condition="'$(Force-Disable-cpprestsdk)' != ''">
<Disable-cpprestsdk>true</Disable-cpprestsdk>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Disable-cpprestsdk)' == ''">
<ClCompile>
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<!--
<Link>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
-->
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Disable-cpprestsdk)' == ''">
<Link>
<AdditionalDependencies Condition="'$(Configuration)' == 'Debug' And ('$(Platform)' == 'Win32' Or '$(Platform)' == 'x86')">$(MSBuildThisFileDirectory)x86\lib\cpprest141_2_10d.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies Condition="'$(Configuration)' == 'Release' And ('$(Platform)' == 'Win32' Or '$(Platform)' == 'x86')">$(MSBuildThisFileDirectory)x86\lib\cpprest141_2_10.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies Condition="'$(Configuration)' == 'Debug' And '$(Platform)' == 'x64'">$(MSBuildThisFileDirectory)x64\lib\cpprest141_2_10d.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies Condition="'$(Configuration)' == 'Release' And '$(Platform)' == 'x64'">$(MSBuildThisFileDirectory)x64\lib\cpprest141_2_10.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup Condition="'$(Configuration)' == 'Debug' And ('$(Platform)' == 'Win32' Or '$(Platform)' == 'x86') And '$(Disable-cpprestsdk)' == '' ">
<ReferenceCopyLocalPaths Include="$(MSBuildThisFileDirectory)x86\bin\cpprest141_2_10d.dll" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Release' And ('$(Platform)' == 'Win32' Or '$(Platform)' == 'x86') And '$(Disable-cpprestsdk)' == '' ">
<ReferenceCopyLocalPaths Include="$(MSBuildThisFileDirectory)x86\bin\cpprest141_2_10.dll" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Debug' And '$(Platform)' == 'x64' And '$(Disable-cpprestsdk)' == '' ">
<ReferenceCopyLocalPaths Include="$(MSBuildThisFileDirectory)x64\bin\cpprest141_2_10d.dll" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Release' And '$(Platform)' == 'x64' And '$(Disable-cpprestsdk)' == '' ">
<ReferenceCopyLocalPaths Include="$(MSBuildThisFileDirectory)x64\bin\cpprest141_2_10.dll" />
</ItemGroup>
</Project>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,697 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Various common utilities.
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include "cpprest/details/basic_types.h"
#include "pplx/pplxtasks.h"
#include <chrono>
#include <cstdint>
#include <limits.h>
#include <locale.h>
#include <random>
#include <string>
#include <system_error>
#include <vector>
#ifndef _WIN32
#include <sys/time.h>
#if !defined(ANDROID) && !defined(__ANDROID__) && defined(HAVE_XLOCALE_H) // CodePlex 269
/* Systems using glibc: xlocale.h has been removed from glibc 2.26
The above include of locale.h is sufficient
Further details: https://sourceware.org/git/?p=glibc.git;a=commit;h=f0be25b6336db7492e47d2e8e72eb8af53b5506d */
#include <xlocale.h>
#endif
#endif
/// Various utilities for string conversions and date and time manipulation.
namespace utility
{
// Left over from VS2010 support, remains to avoid breaking.
typedef std::chrono::seconds seconds;
/// Functions for converting to/from std::chrono::seconds to xml string.
namespace timespan
{
/// <summary>
/// Converts a timespan/interval in seconds to xml duration string as specified by
/// http://www.w3.org/TR/xmlschema-2/#duration
/// </summary>
_ASYNCRTIMP utility::string_t __cdecl seconds_to_xml_duration(utility::seconds numSecs);
/// <summary>
/// Converts an xml duration to timespan/interval in seconds
/// http://www.w3.org/TR/xmlschema-2/#duration
/// </summary>
_ASYNCRTIMP utility::seconds __cdecl xml_duration_to_seconds(const utility::string_t& timespanString);
} // namespace timespan
/// Functions for Unicode string conversions.
namespace conversions
{
/// <summary>
/// Converts a UTF-16 string to a UTF-8 string.
/// </summary>
/// <param name="w">A two byte character UTF-16 string.</param>
/// <returns>A single byte character UTF-8 string.</returns>
_ASYNCRTIMP std::string __cdecl utf16_to_utf8(const utf16string& w);
/// <summary>
/// Converts a UTF-8 string to a UTF-16
/// </summary>
/// <param name="s">A single byte character UTF-8 string.</param>
/// <returns>A two byte character UTF-16 string.</returns>
_ASYNCRTIMP utf16string __cdecl utf8_to_utf16(const std::string& s);
/// <summary>
/// Converts a ASCII (us-ascii) string to a UTF-16 string.
/// </summary>
/// <param name="s">A single byte character us-ascii string.</param>
/// <returns>A two byte character UTF-16 string.</returns>
_ASYNCRTIMP utf16string __cdecl usascii_to_utf16(const std::string& s);
/// <summary>
/// Converts a Latin1 (iso-8859-1) string to a UTF-16 string.
/// </summary>
/// <param name="s">A single byte character UTF-8 string.</param>
/// <returns>A two byte character UTF-16 string.</returns>
_ASYNCRTIMP utf16string __cdecl latin1_to_utf16(const std::string& s);
/// <summary>
/// Converts a Latin1 (iso-8859-1) string to a UTF-8 string.
/// </summary>
/// <param name="s">A single byte character UTF-8 string.</param>
/// <returns>A single byte character UTF-8 string.</returns>
_ASYNCRTIMP utf8string __cdecl latin1_to_utf8(const std::string& s);
/// <summary>
/// Converts to a platform dependent Unicode string type.
/// </summary>
/// <param name="s">A single byte character UTF-8 string.</param>
/// <returns>A platform dependent string type.</returns>
#ifdef _UTF16_STRINGS
_ASYNCRTIMP utility::string_t __cdecl to_string_t(std::string&& s);
#else
inline utility::string_t&& to_string_t(std::string&& s) { return std::move(s); }
#endif
/// <summary>
/// Converts to a platform dependent Unicode string type.
/// </summary>
/// <param name="s">A two byte character UTF-16 string.</param>
/// <returns>A platform dependent string type.</returns>
#ifdef _UTF16_STRINGS
inline utility::string_t&& to_string_t(utf16string&& s) { return std::move(s); }
#else
_ASYNCRTIMP utility::string_t __cdecl to_string_t(utf16string&& s);
#endif
/// <summary>
/// Converts to a platform dependent Unicode string type.
/// </summary>
/// <param name="s">A single byte character UTF-8 string.</param>
/// <returns>A platform dependent string type.</returns>
#ifdef _UTF16_STRINGS
_ASYNCRTIMP utility::string_t __cdecl to_string_t(const std::string& s);
#else
inline const utility::string_t& to_string_t(const std::string& s) { return s; }
#endif
/// <summary>
/// Converts to a platform dependent Unicode string type.
/// </summary>
/// <param name="s">A two byte character UTF-16 string.</param>
/// <returns>A platform dependent string type.</returns>
#ifdef _UTF16_STRINGS
inline const utility::string_t& to_string_t(const utf16string& s) { return s; }
#else
_ASYNCRTIMP utility::string_t __cdecl to_string_t(const utf16string& s);
#endif
/// <summary>
/// Converts to a UTF-16 from string.
/// </summary>
/// <param name="value">A single byte character UTF-8 string.</param>
/// <returns>A two byte character UTF-16 string.</returns>
_ASYNCRTIMP utf16string __cdecl to_utf16string(const std::string& value);
/// <summary>
/// Converts to a UTF-16 from string.
/// </summary>
/// <param name="value">A two byte character UTF-16 string.</param>
/// <returns>A two byte character UTF-16 string.</returns>
inline const utf16string& to_utf16string(const utf16string& value) { return value; }
/// <summary>
/// Converts to a UTF-16 from string.
/// </summary>
/// <param name="value">A two byte character UTF-16 string.</param>
/// <returns>A two byte character UTF-16 string.</returns>
inline utf16string&& to_utf16string(utf16string&& value) { return std::move(value); }
/// <summary>
/// Converts to a UTF-8 string.
/// </summary>
/// <param name="value">A single byte character UTF-8 string.</param>
/// <returns>A single byte character UTF-8 string.</returns>
inline std::string&& to_utf8string(std::string&& value) { return std::move(value); }
/// <summary>
/// Converts to a UTF-8 string.
/// </summary>
/// <param name="value">A single byte character UTF-8 string.</param>
/// <returns>A single byte character UTF-8 string.</returns>
inline const std::string& to_utf8string(const std::string& value) { return value; }
/// <summary>
/// Converts to a UTF-8 string.
/// </summary>
/// <param name="value">A two byte character UTF-16 string.</param>
/// <returns>A single byte character UTF-8 string.</returns>
_ASYNCRTIMP std::string __cdecl to_utf8string(const utf16string& value);
/// <summary>
/// Encode the given byte array into a base64 string
/// </summary>
_ASYNCRTIMP utility::string_t __cdecl to_base64(const std::vector<unsigned char>& data);
/// <summary>
/// Encode the given 8-byte integer into a base64 string
/// </summary>
_ASYNCRTIMP utility::string_t __cdecl to_base64(uint64_t data);
/// <summary>
/// Decode the given base64 string to a byte array
/// </summary>
_ASYNCRTIMP std::vector<unsigned char> __cdecl from_base64(const utility::string_t& str);
template<typename Source>
CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if "
"locale support is required.")
utility::string_t print_string(const Source& val, const std::locale& loc = std::locale())
{
utility::ostringstream_t oss;
oss.imbue(loc);
oss << val;
if (oss.bad())
{
throw std::bad_cast();
}
return oss.str();
}
CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if "
"locale support is required.")
inline utility::string_t print_string(const utility::string_t& val) { return val; }
namespace details
{
#if defined(__ANDROID__)
template<class T>
inline std::string to_string(const T t)
{
std::ostringstream os;
os.imbue(std::locale::classic());
os << t;
return os.str();
}
#endif
template<class T>
inline utility::string_t to_string_t(const T t)
{
#ifdef _UTF16_STRINGS
using std::to_wstring;
return to_wstring(t);
#else
#if !defined(__ANDROID__)
using std::to_string;
#endif
return to_string(t);
#endif
}
template<typename Source>
utility::string_t print_string(const Source& val)
{
utility::ostringstream_t oss;
oss.imbue(std::locale::classic());
oss << val;
if (oss.bad())
{
throw std::bad_cast();
}
return oss.str();
}
inline const utility::string_t& print_string(const utility::string_t& val) { return val; }
template<typename Source>
utf8string print_utf8string(const Source& val)
{
return conversions::to_utf8string(print_string(val));
}
inline const utf8string& print_utf8string(const utf8string& val) { return val; }
template<typename Target>
Target scan_string(const utility::string_t& str)
{
Target t;
utility::istringstream_t iss(str);
iss.imbue(std::locale::classic());
iss >> t;
if (iss.bad())
{
throw std::bad_cast();
}
return t;
}
inline const utility::string_t& scan_string(const utility::string_t& str) { return str; }
} // namespace details
template<typename Target>
CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if "
"locale support is required.")
Target scan_string(const utility::string_t& str, const std::locale& loc = std::locale())
{
Target t;
utility::istringstream_t iss(str);
iss.imbue(loc);
iss >> t;
if (iss.bad())
{
throw std::bad_cast();
}
return t;
}
CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if "
"locale support is required.")
inline utility::string_t scan_string(const utility::string_t& str) { return str; }
} // namespace conversions
namespace details
{
/// <summary>
/// Cross platform RAII container for setting thread local locale.
/// </summary>
class scoped_c_thread_locale
{
public:
_ASYNCRTIMP scoped_c_thread_locale();
_ASYNCRTIMP ~scoped_c_thread_locale();
#if !defined(ANDROID) && !defined(__ANDROID__) // CodePlex 269
#ifdef _WIN32
typedef _locale_t xplat_locale;
#else
typedef locale_t xplat_locale;
#endif
static _ASYNCRTIMP xplat_locale __cdecl c_locale();
#endif
private:
#ifdef _WIN32
std::string m_prevLocale;
int m_prevThreadSetting;
#elif !(defined(ANDROID) || defined(__ANDROID__))
locale_t m_prevLocale;
#endif
scoped_c_thread_locale(const scoped_c_thread_locale&);
scoped_c_thread_locale& operator=(const scoped_c_thread_locale&);
};
/// <summary>
/// Our own implementation of alpha numeric instead of std::isalnum to avoid
/// taking global lock for performance reasons.
/// </summary>
inline bool __cdecl is_alnum(const unsigned char uch) CPPREST_NOEXCEPT
{ // test if uch is an alnum character
// special casing char to avoid branches
// clang-format off
static CPPREST_CONSTEXPR bool is_alnum_table[UCHAR_MAX + 1] = {
/* X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 XA XB XC XD XE XF */
/* 0X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 1X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 2X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 3X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 0-9 */
/* 4X */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* A-Z */
/* 5X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
/* 6X */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* a-z */
/* 7X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0
/* non-ASCII values initialized to 0 */
};
// clang-format on
return (is_alnum_table[uch]);
}
/// <summary>
/// Our own implementation of alpha numeric instead of std::isalnum to avoid
/// taking global lock for performance reasons.
/// </summary>
inline bool __cdecl is_alnum(const char ch) CPPREST_NOEXCEPT { return (is_alnum(static_cast<unsigned char>(ch))); }
/// <summary>
/// Our own implementation of alpha numeric instead of std::isalnum to avoid
/// taking global lock for performance reasons.
/// </summary>
template<class Elem>
inline bool __cdecl is_alnum(Elem ch) CPPREST_NOEXCEPT
{
// assumes 'x' == L'x' for the ASCII range
typedef typename std::make_unsigned<Elem>::type UElem;
const auto uch = static_cast<UElem>(ch);
return (uch <= static_cast<UElem>('z') && is_alnum(static_cast<unsigned char>(uch)));
}
/// <summary>
/// Simplistic implementation of make_unique. A better implementation would be based on variadic templates
/// and therefore not be compatible with Dev10.
/// </summary>
template<typename _Type>
std::unique_ptr<_Type> make_unique()
{
return std::unique_ptr<_Type>(new _Type());
}
template<typename _Type, typename _Arg1>
std::unique_ptr<_Type> make_unique(_Arg1&& arg1)
{
return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1)));
}
template<typename _Type, typename _Arg1, typename _Arg2>
std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2)
{
return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2)));
}
template<typename _Type, typename _Arg1, typename _Arg2, typename _Arg3>
std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3)
{
return std::unique_ptr<_Type>(
new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2), std::forward<_Arg3>(arg3)));
}
template<typename _Type, typename _Arg1, typename _Arg2, typename _Arg3, typename _Arg4>
std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3, _Arg4&& arg4)
{
return std::unique_ptr<_Type>(new _Type(
std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2), std::forward<_Arg3>(arg3), std::forward<_Arg4>(arg4)));
}
template<typename _Type, typename _Arg1, typename _Arg2, typename _Arg3, typename _Arg4, typename _Arg5>
std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3, _Arg4&& arg4, _Arg5&& arg5)
{
return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1),
std::forward<_Arg2>(arg2),
std::forward<_Arg3>(arg3),
std::forward<_Arg4>(arg4),
std::forward<_Arg5>(arg5)));
}
template<typename _Type, typename _Arg1, typename _Arg2, typename _Arg3, typename _Arg4, typename _Arg5, typename _Arg6>
std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3, _Arg4&& arg4, _Arg5&& arg5, _Arg6&& arg6)
{
return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1),
std::forward<_Arg2>(arg2),
std::forward<_Arg3>(arg3),
std::forward<_Arg4>(arg4),
std::forward<_Arg5>(arg5),
std::forward<_Arg6>(arg6)));
}
/// <summary>
/// Cross platform utility function for performing case insensitive string equality comparison.
/// </summary>
/// <param name="left">First string to compare.</param>
/// <param name="right">Second strong to compare.</param>
/// <returns>true if the strings are equivalent, false otherwise</returns>
_ASYNCRTIMP bool __cdecl str_iequal(const std::string& left, const std::string& right) CPPREST_NOEXCEPT;
/// <summary>
/// Cross platform utility function for performing case insensitive string equality comparison.
/// </summary>
/// <param name="left">First string to compare.</param>
/// <param name="right">Second strong to compare.</param>
/// <returns>true if the strings are equivalent, false otherwise</returns>
_ASYNCRTIMP bool __cdecl str_iequal(const std::wstring& left, const std::wstring& right) CPPREST_NOEXCEPT;
/// <summary>
/// Cross platform utility function for performing case insensitive string less-than comparison.
/// </summary>
/// <param name="left">First string to compare.</param>
/// <param name="right">Second strong to compare.</param>
/// <returns>true if a lowercase view of left is lexicographically less than a lowercase view of right; otherwise,
/// false.</returns>
_ASYNCRTIMP bool __cdecl str_iless(const std::string& left, const std::string& right) CPPREST_NOEXCEPT;
/// <summary>
/// Cross platform utility function for performing case insensitive string less-than comparison.
/// </summary>
/// <param name="left">First string to compare.</param>
/// <param name="right">Second strong to compare.</param>
/// <returns>true if a lowercase view of left is lexicographically less than a lowercase view of right; otherwise,
/// false.</returns>
_ASYNCRTIMP bool __cdecl str_iless(const std::wstring& left, const std::wstring& right) CPPREST_NOEXCEPT;
/// <summary>
/// Convert a string to lowercase in place.
/// </summary>
/// <param name="target">The string to convert to lowercase.</param>
_ASYNCRTIMP void __cdecl inplace_tolower(std::string& target) CPPREST_NOEXCEPT;
/// <summary>
/// Convert a string to lowercase in place.
/// </summary>
/// <param name="target">The string to convert to lowercase.</param>
_ASYNCRTIMP void __cdecl inplace_tolower(std::wstring& target) CPPREST_NOEXCEPT;
#ifdef _WIN32
/// <summary>
/// Category error type for Windows OS errors.
/// </summary>
class windows_category_impl : public std::error_category
{
public:
virtual const char* name() const CPPREST_NOEXCEPT { return "windows"; }
virtual std::string message(int errorCode) const CPPREST_NOEXCEPT;
virtual std::error_condition default_error_condition(int errorCode) const CPPREST_NOEXCEPT;
};
/// <summary>
/// Gets the one global instance of the windows error category.
/// </summary>
/// </returns>An error category instance.</returns>
_ASYNCRTIMP const std::error_category& __cdecl windows_category();
#else
/// <summary>
/// Gets the one global instance of the linux error category.
/// </summary>
/// </returns>An error category instance.</returns>
_ASYNCRTIMP const std::error_category& __cdecl linux_category();
#endif
/// <summary>
/// Gets the one global instance of the current platform's error category.
/// </summary>
_ASYNCRTIMP const std::error_category& __cdecl platform_category();
/// <summary>
/// Creates an instance of std::system_error from a OS error code.
/// </summary>
inline std::system_error __cdecl create_system_error(unsigned long errorCode)
{
std::error_code code((int)errorCode, platform_category());
return std::system_error(code, code.message());
}
/// <summary>
/// Creates a std::error_code from a OS error code.
/// </summary>
inline std::error_code __cdecl create_error_code(unsigned long errorCode)
{
return std::error_code((int)errorCode, platform_category());
}
/// <summary>
/// Creates the corresponding error message from a OS error code.
/// </summary>
inline utility::string_t __cdecl create_error_message(unsigned long errorCode)
{
return utility::conversions::to_string_t(create_error_code(errorCode).message());
}
} // namespace details
class datetime
{
public:
typedef uint64_t interval_type;
/// <summary>
/// Defines the supported date and time string formats.
/// </summary>
enum date_format
{
RFC_1123,
ISO_8601
};
/// <summary>
/// Returns the current UTC time.
/// </summary>
static _ASYNCRTIMP datetime __cdecl utc_now();
/// <summary>
/// An invalid UTC timestamp value.
/// </summary>
enum : interval_type
{
utc_timestamp_invalid = static_cast<interval_type>(-1)
};
/// <summary>
/// Returns seconds since Unix/POSIX time epoch at 01-01-1970 00:00:00.
/// If time is before epoch, utc_timestamp_invalid is returned.
/// </summary>
static interval_type utc_timestamp()
{
const auto seconds = utc_now().to_interval() / _secondTicks;
if (seconds >= 11644473600LL)
{
return seconds - 11644473600LL;
}
else
{
return utc_timestamp_invalid;
}
}
datetime() : m_interval(0) {}
/// <summary>
/// Creates <c>datetime</c> from a string representing time in UTC in RFC 1123 format.
/// </summary>
/// <returns>Returns a <c>datetime</c> of zero if not successful.</returns>
static _ASYNCRTIMP datetime __cdecl from_string(const utility::string_t& timestring, date_format format = RFC_1123);
/// <summary>
/// Returns a string representation of the <c>datetime</c>.
/// </summary>
_ASYNCRTIMP utility::string_t to_string(date_format format = RFC_1123) const;
/// <summary>
/// Returns the integral time value.
/// </summary>
interval_type to_interval() const { return m_interval; }
datetime operator-(interval_type value) const { return datetime(m_interval - value); }
datetime operator+(interval_type value) const { return datetime(m_interval + value); }
bool operator==(datetime dt) const { return m_interval == dt.m_interval; }
bool operator!=(const datetime& dt) const { return !(*this == dt); }
static interval_type from_milliseconds(unsigned int milliseconds) { return milliseconds * _msTicks; }
static interval_type from_seconds(unsigned int seconds) { return seconds * _secondTicks; }
static interval_type from_minutes(unsigned int minutes) { return minutes * _minuteTicks; }
static interval_type from_hours(unsigned int hours) { return hours * _hourTicks; }
static interval_type from_days(unsigned int days) { return days * _dayTicks; }
bool is_initialized() const { return m_interval != 0; }
private:
friend int operator-(datetime t1, datetime t2);
static const interval_type _msTicks = static_cast<interval_type>(10000);
static const interval_type _secondTicks = 1000 * _msTicks;
static const interval_type _minuteTicks = 60 * _secondTicks;
static const interval_type _hourTicks = 60 * 60 * _secondTicks;
static const interval_type _dayTicks = 24 * 60 * 60 * _secondTicks;
// Private constructor. Use static methods to create an instance.
datetime(interval_type interval) : m_interval(interval) {}
// Storing as hundreds of nanoseconds 10e-7, i.e. 1 here equals 100ns.
interval_type m_interval;
};
inline int operator-(datetime t1, datetime t2)
{
auto diff = (t1.m_interval - t2.m_interval);
// Round it down to seconds
diff /= 10 * 1000 * 1000;
return static_cast<int>(diff);
}
/// <summary>
/// Nonce string generator class.
/// </summary>
class nonce_generator
{
public:
/// <summary>
/// Define default nonce length.
/// </summary>
enum
{
default_length = 32
};
/// <summary>
/// Nonce generator constructor.
/// </summary>
/// <param name="length">Length of the generated nonce string.</param>
nonce_generator(int length = default_length)
: m_random(static_cast<unsigned int>(utility::datetime::utc_timestamp())), m_length(length)
{
}
/// <summary>
/// Generate a nonce string containing random alphanumeric characters (A-Za-z0-9).
/// Length of the generated string is set by length().
/// </summary>
/// <returns>The generated nonce string.</returns>
_ASYNCRTIMP utility::string_t generate();
/// <summary>
/// Get length of generated nonce string.
/// </summary>
/// <returns>Nonce string length.</returns>
int length() const { return m_length; }
/// <summary>
/// Set length of the generated nonce string.
/// </summary>
/// <param name="length">Lenght of nonce string.</param>
void set_length(int length) { m_length = length; }
private:
std::mt19937 m_random;
int m_length;
};
} // namespace utility

View File

@ -0,0 +1,391 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Protocol independent support for URIs.
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include "cpprest/asyncrt_utils.h"
#include "cpprest/details/basic_types.h"
#include <map>
#include <string>
#include <utility>
#include <vector>
namespace web
{
namespace details
{
struct uri_components
{
uri_components() : m_path(_XPLATSTR("/")), m_port(-1) {}
uri_components(const uri_components&) = default;
uri_components& operator=(const uri_components&) = default;
// This is for VS2013 compatibility -- replace with '= default' when VS2013 is completely dropped.
uri_components(uri_components&& other) CPPREST_NOEXCEPT : m_scheme(std::move(other.m_scheme)),
m_host(std::move(other.m_host)),
m_user_info(std::move(other.m_user_info)),
m_path(std::move(other.m_path)),
m_query(std::move(other.m_query)),
m_fragment(std::move(other.m_fragment)),
m_port(other.m_port)
{
}
// This is for VS2013 compatibility -- replace with '= default' when VS2013 is completely dropped.
uri_components& operator=(uri_components&& other) CPPREST_NOEXCEPT
{
if (this != &other)
{
m_scheme = std::move(other.m_scheme);
m_host = std::move(other.m_host);
m_user_info = std::move(other.m_user_info);
m_path = std::move(other.m_path);
m_query = std::move(other.m_query);
m_fragment = std::move(other.m_fragment);
m_port = other.m_port;
}
return *this;
}
_ASYNCRTIMP utility::string_t join();
utility::string_t m_scheme;
utility::string_t m_host;
utility::string_t m_user_info;
utility::string_t m_path;
utility::string_t m_query;
utility::string_t m_fragment;
int m_port;
};
} // namespace details
/// <summary>
/// A single exception type to represent errors in parsing, encoding, and decoding URIs.
/// </summary>
class uri_exception : public std::exception
{
public:
uri_exception(std::string msg) : m_msg(std::move(msg)) {}
~uri_exception() CPPREST_NOEXCEPT {}
const char* what() const CPPREST_NOEXCEPT { return m_msg.c_str(); }
private:
std::string m_msg;
};
/// <summary>
/// A flexible, protocol independent URI implementation.
///
/// URI instances are immutable. Querying the various fields on an empty URI will return empty strings. Querying
/// various diagnostic members on an empty URI will return false.
/// </summary>
/// <remarks>
/// This implementation accepts both URIs ('http://msn.com/path') and URI relative-references
/// ('/path?query#frag').
///
/// This implementation does not provide any scheme-specific handling -- an example of this
/// would be the following: 'http://path1/path'. This is a valid URI, but it's not a valid
/// http-uri -- that is, it's syntactically correct but does not conform to the requirements
/// of the http scheme (http requires a host).
/// We could provide this by allowing a pluggable 'scheme' policy-class, which would provide
/// extra capability for validating and canonicalizing a URI according to scheme, and would
/// introduce a layer of type-safety for URIs of differing schemes, and thus differing semantics.
///
/// One issue with implementing a scheme-independent URI facility is that of comparing for equality.
/// For instance, these URIs are considered equal 'http://msn.com', 'http://msn.com:80'. That is --
/// the 'default' port can be either omitted or explicit. Since we don't have a way to map a scheme
/// to it's default port, we don't have a way to know these are equal. This is just one of a class of
/// issues with regard to scheme-specific behavior.
/// </remarks>
class uri
{
public:
/// <summary>
/// The various components of a URI. This enum is used to indicate which
/// URI component is being encoded to the encode_uri_component. This allows
/// specific encoding to be performed.
///
/// Scheme and port don't allow '%' so they don't need to be encoded.
/// </summary>
class components
{
public:
enum component
{
user_info,
host,
path,
query,
fragment,
full_uri
};
};
/// <summary>
/// Encodes a URI component according to RFC 3986.
/// Note if a full URI is specified instead of an individual URI component all
/// characters not in the unreserved set are escaped.
/// </summary>
/// <param name="raw">The URI as a string.</param>
/// <returns>The encoded string.</returns>
_ASYNCRTIMP static utility::string_t __cdecl encode_uri(const utility::string_t& raw,
uri::components::component = components::full_uri);
/// <summary>
/// Encodes a string by converting all characters except for RFC 3986 unreserved characters to their
/// hexadecimal representation.
/// </summary>
/// <returns>The encoded string.</returns>
_ASYNCRTIMP static utility::string_t __cdecl encode_data_string(const utility::string_t& data);
/// <summary>
/// Decodes an encoded string.
/// </summary>
/// <param name="encoded">The URI as a string.</param>
/// <returns>The decoded string.</returns>
_ASYNCRTIMP static utility::string_t __cdecl decode(const utility::string_t& encoded);
/// <summary>
/// Splits a path into its hierarchical components.
/// </summary>
/// <param name="path">The path as a string</param>
/// <returns>A <c>std::vector&lt;utility::string_t&gt;</c> containing the segments in the path.</returns>
_ASYNCRTIMP static std::vector<utility::string_t> __cdecl split_path(const utility::string_t& path);
/// <summary>
/// Splits a query into its key-value components.
/// </summary>
/// <param name="query">The query string</param>
/// <returns>A <c>std::map&lt;utility::string_t, utility::string_t&gt;</c> containing the key-value components of
/// the query.</returns>
_ASYNCRTIMP static std::map<utility::string_t, utility::string_t> __cdecl split_query(
const utility::string_t& query);
/// <summary>
/// Validates a string as a URI.
/// </summary>
/// <remarks>
/// This function accepts both uris ('http://msn.com') and uri relative-references ('path1/path2?query').
/// </remarks>
/// <param name="uri_string">The URI string to be validated.</param>
/// <returns><c>true</c> if the given string represents a valid URI, <c>false</c> otherwise.</returns>
_ASYNCRTIMP static bool __cdecl validate(const utility::string_t& uri_string);
/// <summary>
/// Creates an empty uri
/// </summary>
uri() : m_uri(_XPLATSTR("/")) {}
/// <summary>
/// Creates a URI from the given encoded string. This will throw an exception if the string
/// does not contain a valid URI. Use uri::validate if processing user-input.
/// </summary>
/// <param name="uri_string">A pointer to an encoded string to create the URI instance.</param>
_ASYNCRTIMP uri(const utility::char_t* uri_string);
/// <summary>
/// Creates a URI from the given encoded string. This will throw an exception if the string
/// does not contain a valid URI. Use uri::validate if processing user-input.
/// </summary>
/// <param name="uri_string">An encoded URI string to create the URI instance.</param>
_ASYNCRTIMP uri(const utility::string_t& uri_string);
/// <summary>
/// Copy constructor.
/// </summary>
uri(const uri&) = default;
/// <summary>
/// Copy assignment operator.
/// </summary>
uri& operator=(const uri&) = default;
/// <summary>
/// Move constructor.
/// </summary>
// This is for VS2013 compatibility -- replace with '= default' when VS2013 is completely dropped.
uri(uri&& other) CPPREST_NOEXCEPT : m_uri(std::move(other.m_uri)), m_components(std::move(other.m_components)) {}
/// <summary>
/// Move assignment operator
/// </summary>
// This is for VS2013 compatibility -- replace with '= default' when VS2013 is completely dropped.
uri& operator=(uri&& other) CPPREST_NOEXCEPT
{
if (this != &other)
{
m_uri = std::move(other.m_uri);
m_components = std::move(other.m_components);
}
return *this;
}
/// <summary>
/// Get the scheme component of the URI as an encoded string.
/// </summary>
/// <returns>The URI scheme as a string.</returns>
const utility::string_t& scheme() const { return m_components.m_scheme; }
/// <summary>
/// Get the user information component of the URI as an encoded string.
/// </summary>
/// <returns>The URI user information as a string.</returns>
const utility::string_t& user_info() const { return m_components.m_user_info; }
/// <summary>
/// Get the host component of the URI as an encoded string.
/// </summary>
/// <returns>The URI host as a string.</returns>
const utility::string_t& host() const { return m_components.m_host; }
/// <summary>
/// Get the port component of the URI. Returns -1 if no port is specified.
/// </summary>
/// <returns>The URI port as an integer.</returns>
int port() const { return m_components.m_port; }
/// <summary>
/// Get the path component of the URI as an encoded string.
/// </summary>
/// <returns>The URI path as a string.</returns>
const utility::string_t& path() const { return m_components.m_path; }
/// <summary>
/// Get the query component of the URI as an encoded string.
/// </summary>
/// <returns>The URI query as a string.</returns>
const utility::string_t& query() const { return m_components.m_query; }
/// <summary>
/// Get the fragment component of the URI as an encoded string.
/// </summary>
/// <returns>The URI fragment as a string.</returns>
const utility::string_t& fragment() const { return m_components.m_fragment; }
/// <summary>
/// Creates a new uri object with the same authority portion as this one, omitting the resource and query portions.
/// </summary>
/// <returns>The new uri object with the same authority.</returns>
_ASYNCRTIMP uri authority() const;
/// <summary>
/// Gets the path, query, and fragment portion of this uri, which may be empty.
/// </summary>
/// <returns>The new URI object with the path, query and fragment portion of this URI.</returns>
_ASYNCRTIMP uri resource() const;
/// <summary>
/// An empty URI specifies no components, and serves as a default value
/// </summary>
bool is_empty() const { return this->m_uri.empty() || this->m_uri == _XPLATSTR("/"); }
/// <summary>
/// A loopback URI is one which refers to a hostname or ip address with meaning only on the local machine.
/// </summary>
/// <remarks>
/// Examples include "localhost", or ip addresses in the loopback range (127.0.0.0/24).
/// </remarks>
/// <returns><c>true</c> if this URI references the local host, <c>false</c> otherwise.</returns>
bool is_host_loopback() const
{
return !is_empty() &&
((host() == _XPLATSTR("localhost")) || (host().size() > 4 && host().substr(0, 4) == _XPLATSTR("127.")));
}
/// <summary>
/// A wildcard URI is one which refers to all hostnames that resolve to the local machine (using the * or +)
/// </summary>
/// <example>
/// http://*:80
/// </example>
bool is_host_wildcard() const
{
return !is_empty() && (this->host() == _XPLATSTR("*") || this->host() == _XPLATSTR("+"));
}
/// <summary>
/// A portable URI is one with a hostname that can be resolved globally (used from another machine).
/// </summary>
/// <returns><c>true</c> if this URI can be resolved globally (used from another machine), <c>false</c>
/// otherwise.</returns> <remarks> The hostname "localhost" is a reserved name that is guaranteed to resolve to the
/// local machine, and cannot be used for inter-machine communication. Likewise the hostnames "*" and "+" on Windows
/// represent wildcards, and do not map to a resolvable address.
/// </remarks>
bool is_host_portable() const { return !(is_empty() || is_host_loopback() || is_host_wildcard()); }
/// <summary>
/// A default port is one where the port is unspecified, and will be determined by the operating system.
/// The choice of default port may be dictated by the scheme (http -> 80) or not.
/// </summary>
/// <returns><c>true</c> if this URI instance has a default port, <c>false</c> otherwise.</returns>
bool is_port_default() const { return !is_empty() && this->port() == 0; }
/// <summary>
/// An "authority" URI is one with only a scheme, optional userinfo, hostname, and (optional) port.
/// </summary>
/// <returns><c>true</c> if this is an "authority" URI, <c>false</c> otherwise.</returns>
bool is_authority() const { return !is_empty() && is_path_empty() && query().empty() && fragment().empty(); }
/// <summary>
/// Returns whether the other URI has the same authority as this one
/// </summary>
/// <param name="other">The URI to compare the authority with.</param>
/// <returns><c>true</c> if both the URI's have the same authority, <c>false</c> otherwise.</returns>
bool has_same_authority(const uri& other) const { return !is_empty() && this->authority() == other.authority(); }
/// <summary>
/// Returns whether the path portion of this URI is empty
/// </summary>
/// <returns><c>true</c> if the path portion of this URI is empty, <c>false</c> otherwise.</returns>
bool is_path_empty() const { return path().empty() || path() == _XPLATSTR("/"); }
/// <summary>
/// Returns the full (encoded) URI as a string.
/// </summary>
/// <returns>The full encoded URI string.</returns>
utility::string_t to_string() const { return m_uri; }
/// <summary>
/// Returns an URI resolved against <c>this</c> as the base URI
/// according to RFC3986, Section 5 (https://tools.ietf.org/html/rfc3986#section-5).
/// </summary>
/// <param name="relativeUri">The relative URI to be resolved against <c>this</c> as base.</param>
/// <returns>The new resolved URI string.</returns>
_ASYNCRTIMP utility::string_t resolve_uri(const utility::string_t& relativeUri) const;
_ASYNCRTIMP bool operator==(const uri& other) const;
bool operator<(const uri& other) const { return m_uri < other.m_uri; }
bool operator!=(const uri& other) const { return !(this->operator==(other)); }
private:
friend class uri_builder;
/// <summary>
/// Creates a URI from the given URI components.
/// </summary>
/// <param name="components">A URI components object to create the URI instance.</param>
_ASYNCRTIMP uri(const details::uri_components& components);
// Used by uri_builder
static utility::string_t __cdecl encode_query_impl(const utf8string& raw);
utility::string_t m_uri;
details::uri_components m_components;
};
} // namespace web

View File

@ -0,0 +1,590 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* This file defines a basic STL-container-based stream buffer. Reading from the buffer will not remove any data
* from it and seeking is thus supported.
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include "cpprest/astreambuf.h"
#include "cpprest/streams.h"
#include "pplx/pplxtasks.h"
#include <algorithm>
#include <iterator>
#include <queue>
#include <vector>
namespace Concurrency
{
namespace streams
{
// Forward declarations
template<typename _CollectionType>
class container_buffer;
namespace details
{
/// <summary>
/// The basic_container_buffer class serves as a memory-based steam buffer that supports writing or reading
/// sequences of characters.
/// The class itself should not be used in application code, it is used by the stream definitions farther down in the
/// header file.
/// </summary>
/// <remarks> When closed, neither writing nor reading is supported any longer. <c>basic_container_buffer</c> does not
/// support simultaneous use of the buffer for reading and writing.</remarks>
template<typename _CollectionType>
class basic_container_buffer : public streams::details::streambuf_state_manager<typename _CollectionType::value_type>
{
public:
typedef typename _CollectionType::value_type _CharType;
typedef typename basic_streambuf<_CharType>::traits traits;
typedef typename basic_streambuf<_CharType>::int_type int_type;
typedef typename basic_streambuf<_CharType>::pos_type pos_type;
typedef typename basic_streambuf<_CharType>::off_type off_type;
/// <summary>
/// Returns the underlying data container
/// </summary>
_CollectionType& collection() { return m_data; }
/// <summary>
/// Destructor
/// </summary>
virtual ~basic_container_buffer()
{
// Invoke the synchronous versions since we need to
// purge the request queue before deleting the buffer
this->_close_read();
this->_close_write();
}
protected:
/// <summary>
/// can_seek is used to determine whether a stream buffer supports seeking.
/// </summary>
virtual bool can_seek() const { return this->is_open(); }
/// <summary>
/// <c>has_size<c/> is used to determine whether a stream buffer supports size().
/// </summary>
virtual bool has_size() const { return this->is_open(); }
/// <summary>
/// Gets the size of the stream, if known. Calls to <c>has_size</c> will determine whether
/// the result of <c>size</c> can be relied on.
/// </summary>
virtual utility::size64_t size() const { return utility::size64_t(m_data.size()); }
/// <summary>
/// Get the stream buffer size, if one has been set.
/// </summary>
/// <param name="direction">The direction of buffering (in or out)</param>
/// <remarks>An implementation that does not support buffering will always return '0'.</remarks>
virtual size_t buffer_size(std::ios_base::openmode = std::ios_base::in) const { return 0; }
/// <summary>
/// Sets the stream buffer implementation to buffer or not buffer.
/// </summary>
/// <param name="size">The size to use for internal buffering, 0 if no buffering should be done.</param>
/// <param name="direction">The direction of buffering (in or out)</param>
/// <remarks>An implementation that does not support buffering will silently ignore calls to this function and it
/// will not have any effect on what is returned by subsequent calls to <see cref="::buffer_size method"
/// />.</remarks>
virtual void set_buffer_size(size_t, std::ios_base::openmode = std::ios_base::in) { return; }
/// <summary>
/// For any input stream, <c>in_avail</c> returns the number of characters that are immediately available
/// to be consumed without blocking. May be used in conjunction with <cref="::sbumpc method"/> to read data without
/// incurring the overhead of using tasks.
/// </summary>
virtual size_t in_avail() const
{
// See the comment in seek around the restriction that we do not allow read head to
// seek beyond the current write_end.
_ASSERTE(m_current_position <= m_data.size());
msl::safeint3::SafeInt<size_t> readhead(m_current_position);
msl::safeint3::SafeInt<size_t> writeend(m_data.size());
return (size_t)(writeend - readhead);
}
virtual pplx::task<bool> _sync() { return pplx::task_from_result(true); }
virtual pplx::task<int_type> _putc(_CharType ch)
{
int_type retVal = (this->write(&ch, 1) == 1) ? static_cast<int_type>(ch) : traits::eof();
return pplx::task_from_result<int_type>(retVal);
}
virtual pplx::task<size_t> _putn(const _CharType* ptr, size_t count)
{
return pplx::task_from_result<size_t>(this->write(ptr, count));
}
/// <summary>
/// Allocates a contiguous memory block and returns it.
/// </summary>
/// <param name="count">The number of characters to allocate.</param>
/// <returns>A pointer to a block to write to, null if the stream buffer implementation does not support
/// alloc/commit.</returns>
_CharType* _alloc(size_t count)
{
if (!this->can_write()) return nullptr;
// Allocate space
resize_for_write(m_current_position + count);
// Let the caller copy the data
return (_CharType*)&m_data[m_current_position];
}
/// <summary>
/// Submits a block already allocated by the stream buffer.
/// </summary>
/// <param name="count">The number of characters to be committed.</param>
void _commit(size_t actual)
{
// Update the write position and satisfy any pending reads
update_current_position(m_current_position + actual);
}
/// <summary>
/// Gets a pointer to the next already allocated contiguous block of data.
/// </summary>
/// <param name="ptr">A reference to a pointer variable that will hold the address of the block on success.</param>
/// <param name="count">The number of contiguous characters available at the address in 'ptr'.</param>
/// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
/// <remarks>
/// A return of false does not necessarily indicate that a subsequent read operation would fail, only that
/// there is no block to return immediately or that the stream buffer does not support the operation.
/// The stream buffer may not de-allocate the block until <see cref="::release method" /> is called.
/// If the end of the stream is reached, the function will return <c>true</c>, a null pointer, and a count of zero;
/// a subsequent read will not succeed.
/// </remarks>
virtual bool acquire(_Out_ _CharType*& ptr, _Out_ size_t& count)
{
ptr = nullptr;
count = 0;
if (!this->can_read()) return false;
count = in_avail();
if (count > 0)
{
ptr = (_CharType*)&m_data[m_current_position];
return true;
}
else
{
// Can only be open for read OR write, not both. If there is no data then
// we have reached the end of the stream so indicate such with true.
return true;
}
}
/// <summary>
/// Releases a block of data acquired using <see cref="::acquire method"/>. This frees the stream buffer to
/// de-allocate the memory, if it so desires. Move the read position ahead by the count.
/// </summary>
/// <param name="ptr">A pointer to the block of data to be released.</param>
/// <param name="count">The number of characters that were read.</param>
virtual void release(_Out_writes_opt_(count) _CharType* ptr, _In_ size_t count)
{
if (ptr != nullptr) update_current_position(m_current_position + count);
}
virtual pplx::task<size_t> _getn(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
{
return pplx::task_from_result(this->read(ptr, count));
}
size_t _sgetn(_Out_writes_(count) _CharType* ptr, _In_ size_t count) { return this->read(ptr, count); }
virtual size_t _scopy(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
{
return this->read(ptr, count, false);
}
virtual pplx::task<int_type> _bumpc() { return pplx::task_from_result(this->read_byte(true)); }
virtual int_type _sbumpc() { return this->read_byte(true); }
virtual pplx::task<int_type> _getc() { return pplx::task_from_result(this->read_byte(false)); }
int_type _sgetc() { return this->read_byte(false); }
virtual pplx::task<int_type> _nextc()
{
this->read_byte(true);
return pplx::task_from_result(this->read_byte(false));
}
virtual pplx::task<int_type> _ungetc()
{
auto pos = seekoff(-1, std::ios_base::cur, std::ios_base::in);
if (pos == (pos_type)traits::eof()) return pplx::task_from_result(traits::eof());
return this->getc();
}
/// <summary>
/// Gets the current read or write position in the stream.
/// </summary>
/// <param name="direction">The I/O direction to seek (see remarks)</param>
/// <returns>The current position. EOF if the operation fails.</returns>
/// <remarks>Some streams may have separate write and read cursors.
/// For such streams, the direction parameter defines whether to move the read or the write
/// cursor.</remarks>
virtual pos_type getpos(std::ios_base::openmode mode) const
{
if (((mode & std::ios_base::in) && !this->can_read()) || ((mode & std::ios_base::out) && !this->can_write()))
return static_cast<pos_type>(traits::eof());
return static_cast<pos_type>(m_current_position);
}
/// <summary>
/// Seeks to the given position.
/// </summary>
/// <param name="pos">The offset from the beginning of the stream.</param>
/// <param name="direction">The I/O direction to seek (see remarks).</param>
/// <returns>The position. EOF if the operation fails.</returns>
/// <remarks>Some streams may have separate write and read cursors. For such streams, the direction parameter
/// defines whether to move the read or the write cursor.</remarks>
virtual pos_type seekpos(pos_type position, std::ios_base::openmode mode)
{
pos_type beg(0);
// In order to support relative seeking from the end position we need to fix an end position.
// Technically, there is no end for the stream buffer as new writes would just expand the buffer.
// For now, we assume that the current write_end is the end of the buffer. We use this artificial
// end to restrict the read head from seeking beyond what is available.
pos_type end(m_data.size());
if (position >= beg)
{
auto pos = static_cast<size_t>(position);
// Read head
if ((mode & std::ios_base::in) && this->can_read())
{
if (position <= end)
{
// We do not allow reads to seek beyond the end or before the start position.
update_current_position(pos);
return static_cast<pos_type>(m_current_position);
}
}
// Write head
if ((mode & std::ios_base::out) && this->can_write())
{
// Allocate space
resize_for_write(pos);
// Nothing to really copy
// Update write head and satisfy read requests if any
update_current_position(pos);
return static_cast<pos_type>(m_current_position);
}
}
return static_cast<pos_type>(traits::eof());
}
/// <summary>
/// Seeks to a position given by a relative offset.
/// </summary>
/// <param name="offset">The relative position to seek to</param>
/// <param name="way">The starting point (beginning, end, current) for the seek.</param>
/// <param name="mode">The I/O direction to seek (see remarks)</param>
/// <returns>The position. EOF if the operation fails.</returns>
/// <remarks>Some streams may have separate write and read cursors.
/// For such streams, the mode parameter defines whether to move the read or the write cursor.</remarks>
virtual pos_type seekoff(off_type offset, std::ios_base::seekdir way, std::ios_base::openmode mode)
{
pos_type beg = 0;
pos_type cur = static_cast<pos_type>(m_current_position);
pos_type end = static_cast<pos_type>(m_data.size());
switch (way)
{
case std::ios_base::beg: return seekpos(beg + offset, mode);
case std::ios_base::cur: return seekpos(cur + offset, mode);
case std::ios_base::end: return seekpos(end + offset, mode);
default: return static_cast<pos_type>(traits::eof());
}
}
private:
template<typename _CollectionType1>
friend class streams::container_buffer;
/// <summary>
/// Constructor
/// </summary>
basic_container_buffer(std::ios_base::openmode mode)
: streambuf_state_manager<typename _CollectionType::value_type>(mode), m_current_position(0)
{
validate_mode(mode);
}
/// <summary>
/// Constructor
/// </summary>
basic_container_buffer(_CollectionType data, std::ios_base::openmode mode)
: streambuf_state_manager<typename _CollectionType::value_type>(mode)
, m_data(std::move(data))
, m_current_position((mode & std::ios_base::in) ? 0 : m_data.size())
{
validate_mode(mode);
}
static void validate_mode(std::ios_base::openmode mode)
{
// Disallow simultaneous use of the stream buffer for writing and reading.
if ((mode & std::ios_base::in) && (mode & std::ios_base::out))
throw std::invalid_argument("this combination of modes on container stream not supported");
}
/// <summary>
/// Determine if the request can be satisfied.
/// </summary>
bool can_satisfy(size_t)
{
// We can always satisfy a read, at least partially, unless the
// read position is at the very end of the buffer.
return (in_avail() > 0);
}
/// <summary>
/// Reads a byte from the stream and returns it as int_type.
/// Note: This routine shall only be called if can_satisfy() returned true.
/// </summary>
int_type read_byte(bool advance = true)
{
_CharType value;
auto read_size = this->read(&value, 1, advance);
return read_size == 1 ? static_cast<int_type>(value) : traits::eof();
}
/// <summary>
/// Reads up to count characters into ptr and returns the count of characters copied.
/// The return value (actual characters copied) could be <= count.
/// Note: This routine shall only be called if can_satisfy() returned true.
/// </summary>
size_t read(_Out_writes_(count) _CharType* ptr, _In_ size_t count, bool advance = true)
{
if (!can_satisfy(count)) return 0;
msl::safeint3::SafeInt<size_t> request_size(count);
msl::safeint3::SafeInt<size_t> read_size = request_size.Min(in_avail());
size_t newPos = m_current_position + read_size;
auto readBegin = std::begin(m_data) + m_current_position;
auto readEnd = std::begin(m_data) + newPos;
#if defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL != 0
// Avoid warning C4996: Use checked iterators under SECURE_SCL
std::copy(readBegin, readEnd, stdext::checked_array_iterator<_CharType*>(ptr, count));
#else
std::copy(readBegin, readEnd, ptr);
#endif // _WIN32
if (advance)
{
update_current_position(newPos);
}
return (size_t)read_size;
}
/// <summary>
/// Write count characters from the ptr into the stream buffer
/// </summary>
size_t write(const _CharType* ptr, size_t count)
{
if (!this->can_write() || (count == 0)) return 0;
auto newSize = m_current_position + count;
// Allocate space
resize_for_write(newSize);
// Copy the data
std::copy(ptr, ptr + count, std::begin(m_data) + m_current_position);
// Update write head and satisfy pending reads if any
update_current_position(newSize);
return count;
}
/// <summary>
/// Resize the underlying container to match the new write head
/// </summary>
void resize_for_write(size_t newPos)
{
// Resize the container if required
if (newPos > m_data.size())
{
m_data.resize(newPos);
}
}
/// <summary>
/// Updates the write head to the new position
/// </summary>
void update_current_position(size_t newPos)
{
// The new write head
m_current_position = newPos;
_ASSERTE(m_current_position <= m_data.size());
}
// The actual data store
_CollectionType m_data;
// Read/write head
size_t m_current_position;
};
} // namespace details
/// <summary>
/// The basic_container_buffer class serves as a memory-based steam buffer that supports writing or reading
/// sequences of characters. Note that it cannot be used as a consumer producer buffer.
/// </summary>
/// <typeparam name="_CollectionType">
/// The type of the container.
/// </typeparam>
/// <remarks>
/// This is a reference-counted version of <c>basic_container_buffer</c>.
/// </remarks>
template<typename _CollectionType>
class container_buffer : public streambuf<typename _CollectionType::value_type>
{
public:
typedef typename _CollectionType::value_type char_type;
/// <summary>
/// Creates a container_buffer given a collection, copying its data into the buffer.
/// </summary>
/// <param name="data">The collection that is the starting point for the buffer</param>
/// <param name="mode">The I/O mode that the buffer should use (in / out)</param>
container_buffer(_CollectionType data, std::ios_base::openmode mode = std::ios_base::in)
: streambuf<typename _CollectionType::value_type>(
std::shared_ptr<details::basic_container_buffer<_CollectionType>>(
new streams::details::basic_container_buffer<_CollectionType>(std::move(data), mode)))
{
}
/// <summary>
/// Creates a container_buffer starting from an empty collection.
/// </summary>
/// <param name="mode">The I/O mode that the buffer should use (in / out)</param>
container_buffer(std::ios_base::openmode mode = std::ios_base::out)
: streambuf<typename _CollectionType::value_type>(
std::shared_ptr<details::basic_container_buffer<_CollectionType>>(
new details::basic_container_buffer<_CollectionType>(mode)))
{
}
_CollectionType& collection() const
{
auto listBuf = static_cast<details::basic_container_buffer<_CollectionType>*>(this->get_base().get());
return listBuf->collection();
}
};
/// <summary>
/// A static class to allow users to create input and out streams based off STL
/// collections. The sole purpose of this class to avoid users from having to know
/// anything about stream buffers.
/// </summary>
/// <typeparam name="_CollectionType">The type of the STL collection.</typeparam>
template<typename _CollectionType>
class container_stream
{
public:
typedef typename _CollectionType::value_type char_type;
typedef container_buffer<_CollectionType> buffer_type;
/// <summary>
/// Creates an input stream given an STL container.
/// </summary>
/// </param name="data">STL container to back the input stream.</param>
/// <returns>An input stream.</returns>
static concurrency::streams::basic_istream<char_type> open_istream(_CollectionType data)
{
return concurrency::streams::basic_istream<char_type>(buffer_type(std::move(data), std::ios_base::in));
}
/// <summary>
/// Creates an output stream using an STL container as the storage.
/// </summary>
/// <returns>An output stream.</returns>
static concurrency::streams::basic_ostream<char_type> open_ostream()
{
return concurrency::streams::basic_ostream<char_type>(buffer_type(std::ios_base::out));
}
};
/// <summary>
/// The stringstream allows an input stream to be constructed from std::string or std::wstring
/// For output streams the underlying string container could be retrieved using <c>buf-&gt;collection().</c>
/// </summary>
typedef container_stream<std::basic_string<char>> stringstream;
typedef stringstream::buffer_type stringstreambuf;
typedef container_stream<utility::string_t> wstringstream;
typedef wstringstream::buffer_type wstringstreambuf;
/// <summary>
/// The <c>bytestream</c> is a static class that allows an input stream to be constructed from any STL container.
/// </summary>
class bytestream
{
public:
/// <summary>
/// Creates a single byte character input stream given an STL container.
/// </summary>
/// <typeparam name="_CollectionType">The type of the STL collection.</typeparam>
/// <param name="data">STL container to back the input stream.</param>
/// <returns>An single byte character input stream.</returns>
template<typename _CollectionType>
static concurrency::streams::istream open_istream(_CollectionType data)
{
return concurrency::streams::istream(
streams::container_buffer<_CollectionType>(std::move(data), std::ios_base::in));
}
/// <summary>
/// Creates a single byte character output stream using an STL container as storage.
/// </summary>
/// <typeparam name="_CollectionType">The type of the STL collection.</typeparam>
/// <returns>A single byte character output stream.</returns>
template<typename _CollectionType>
static concurrency::streams::ostream open_ostream()
{
return concurrency::streams::ostream(streams::container_buffer<_CollectionType>());
}
};
} // namespace streams
} // namespace Concurrency

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,131 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Platform-dependent type definitions
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include "cpprest/details/cpprest_compat.h"
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#ifndef _WIN32
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif
#include <stdint.h>
#else
#include <cstdint>
#endif
#include "cpprest/details/SafeInt3.hpp"
namespace utility
{
#ifdef _WIN32
#define _UTF16_STRINGS
#endif
// We should be using a 64-bit size type for most situations that do
// not involve specifying the size of a memory allocation or buffer.
typedef uint64_t size64_t;
#ifndef _WIN32
typedef uint32_t HRESULT; // Needed for PPLX
#endif
#ifdef _UTF16_STRINGS
//
// On Windows, all strings are wide
//
typedef wchar_t char_t;
typedef std::wstring string_t;
#define _XPLATSTR(x) L##x
typedef std::wostringstream ostringstream_t;
typedef std::wofstream ofstream_t;
typedef std::wostream ostream_t;
typedef std::wistream istream_t;
typedef std::wifstream ifstream_t;
typedef std::wistringstream istringstream_t;
typedef std::wstringstream stringstream_t;
#define ucout std::wcout
#define ucin std::wcin
#define ucerr std::wcerr
#else
//
// On POSIX platforms, all strings are narrow
//
typedef char char_t;
typedef std::string string_t;
#define _XPLATSTR(x) x
typedef std::ostringstream ostringstream_t;
typedef std::ofstream ofstream_t;
typedef std::ostream ostream_t;
typedef std::istream istream_t;
typedef std::ifstream ifstream_t;
typedef std::istringstream istringstream_t;
typedef std::stringstream stringstream_t;
#define ucout std::cout
#define ucin std::cin
#define ucerr std::cerr
#endif // endif _UTF16_STRINGS
#ifndef _TURN_OFF_PLATFORM_STRING
// The 'U' macro can be used to create a string or character literal of the platform type, i.e. utility::char_t.
// If you are using a library causing conflicts with 'U' macro, it can be turned off by defining the macro
// '_TURN_OFF_PLATFORM_STRING' before including the C++ REST SDK header files, and e.g. use '_XPLATSTR' instead.
#define U(x) _XPLATSTR(x)
#endif // !_TURN_OFF_PLATFORM_STRING
} // namespace utility
typedef char utf8char;
typedef std::string utf8string;
typedef std::stringstream utf8stringstream;
typedef std::ostringstream utf8ostringstream;
typedef std::ostream utf8ostream;
typedef std::istream utf8istream;
typedef std::istringstream utf8istringstream;
#ifdef _UTF16_STRINGS
typedef wchar_t utf16char;
typedef std::wstring utf16string;
typedef std::wstringstream utf16stringstream;
typedef std::wostringstream utf16ostringstream;
typedef std::wostream utf16ostream;
typedef std::wistream utf16istream;
typedef std::wistringstream utf16istringstream;
#else
typedef char16_t utf16char;
typedef std::u16string utf16string;
typedef std::basic_stringstream<utf16char> utf16stringstream;
typedef std::basic_ostringstream<utf16char> utf16ostringstream;
typedef std::basic_ostream<utf16char> utf16ostream;
typedef std::basic_istream<utf16char> utf16istream;
typedef std::basic_istringstream<utf16char> utf16istringstream;
#endif
#if defined(_WIN32)
// Include on everything except Windows Desktop ARM, unless explicitly excluded.
#if !defined(CPPREST_EXCLUDE_WEBSOCKETS)
#if defined(WINAPI_FAMILY)
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && defined(_M_ARM)
#define CPPREST_EXCLUDE_WEBSOCKETS
#endif
#else
#if defined(_M_ARM)
#define CPPREST_EXCLUDE_WEBSOCKETS
#endif
#endif
#endif
#endif

View File

@ -0,0 +1,89 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Standard macros and definitions.
* This header has minimal dependency on windows headers and is safe for use in the public API
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#if defined(_WIN32)
#if _MSC_VER >= 1900
#define CPPREST_NOEXCEPT noexcept
#define CPPREST_CONSTEXPR constexpr
#else
#define CPPREST_NOEXCEPT
#define CPPREST_CONSTEXPR const
#endif // _MSC_VER >= 1900
#define CASABLANCA_UNREFERENCED_PARAMETER(x) (x)
#include <sal.h>
#else // ^^^ _WIN32 ^^^ // vvv !_WIN32 vvv
#define __declspec(x) __attribute__((x))
#define dllimport
#define novtable /* no novtable equivalent */
#define __assume(x) \
do \
{ \
if (!(x)) __builtin_unreachable(); \
} while (false)
#define CASABLANCA_UNREFERENCED_PARAMETER(x) (void)x
#define CPPREST_NOEXCEPT noexcept
#define CPPREST_CONSTEXPR constexpr
#include <assert.h>
#define _ASSERTE(x) assert(x)
// No SAL on non Windows platforms
#include "cpprest/details/nosal.h"
#if !defined(__cdecl)
#if defined(cdecl)
#define __cdecl __attribute__((cdecl))
#else // ^^^ defined cdecl ^^^ // vvv !defined cdecl vvv
#define __cdecl
#endif // defined cdecl
#endif // not defined __cdecl
#if defined(__ANDROID__)
// This is needed to disable the use of __thread inside the boost library.
// Android does not support thread local storage -- if boost is included
// without this macro defined, it will create references to __tls_get_addr
// which (while able to link) will not be available at runtime and prevent
// the .so from loading.
#if not defined BOOST_ASIO_DISABLE_THREAD_KEYWORD_EXTENSION
#define BOOST_ASIO_DISABLE_THREAD_KEYWORD_EXTENSION
#endif // not defined BOOST_ASIO_DISABLE_THREAD_KEYWORD_EXTENSION
#endif // defined(__ANDROID__)
#ifdef __clang__
#include <cstdio>
#endif // __clang__
#endif // _WIN32
#ifdef _NO_ASYNCRTIMP
#define _ASYNCRTIMP
#else // ^^^ _NO_ASYNCRTIMP ^^^ // vvv !_NO_ASYNCRTIMP vvv
#ifdef _ASYNCRT_EXPORT
#define _ASYNCRTIMP __declspec(dllexport)
#else // ^^^ _ASYNCRT_EXPORT ^^^ // vvv !_ASYNCRT_EXPORT vvv
#define _ASYNCRTIMP __declspec(dllimport)
#endif // _ASYNCRT_EXPORT
#endif // _NO_ASYNCRTIMP
#ifdef CASABLANCA_DEPRECATION_NO_WARNINGS
#define CASABLANCA_DEPRECATED(x)
#else
#define CASABLANCA_DEPRECATED(x) __declspec(deprecated(x))
#endif // CASABLANCA_DEPRECATION_NO_WARNINGS

View File

@ -0,0 +1,220 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* fileio.h
*
* Asynchronous I/O: stream buffer implementation details
*
* We're going to some lengths to avoid exporting C++ class member functions and implementation details across
* module boundaries, and the factoring requires that we keep the implementation details away from the main header
* files. The supporting functions, which are in this file, use C-like signatures to avoid as many issues as
* possible.
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#ifdef _WIN32
#include <cstdint>
#endif
#include "cpprest/details/basic_types.h"
#include "pplx/pplxtasks.h"
namespace Concurrency
{
namespace streams
{
namespace details
{
/// <summary>
/// A record containing the essential private data members of a file stream,
/// in particular the parts that need to be shared between the public header
/// file and the implementation in the implementation file.
/// </summary>
struct _file_info
{
_ASYNCRTIMP _file_info(std::ios_base::openmode mode, size_t buffer_size)
: m_rdpos(0)
, m_wrpos(0)
, m_atend(false)
, m_buffer_size(buffer_size)
, m_buffer(nullptr)
, m_bufoff(0)
, m_bufsize(0)
, m_buffill(0)
, m_mode(mode)
{
}
// Positional data
size_t m_rdpos;
size_t m_wrpos;
bool m_atend;
// Input buffer
size_t m_buffer_size; // The intended size of the buffer to read into.
char* m_buffer;
size_t m_bufoff; // File position that the start of the buffer represents.
msl::safeint3::SafeInt<size_t> m_bufsize; // Buffer allocated size, as actually allocated.
size_t m_buffill; // Amount of file data actually in the buffer
std::ios_base::openmode m_mode;
pplx::extensibility::recursive_lock_t m_lock;
};
/// <summary>
/// This interface provides the necessary callbacks for completion events.
/// </summary>
class _filestream_callback
{
public:
virtual void on_opened(_In_ details::_file_info*) {}
virtual void on_closed() {}
virtual void on_error(const std::exception_ptr&) {}
virtual void on_completed(size_t) {}
protected:
virtual ~_filestream_callback() {}
};
} // namespace details
} // namespace streams
} // namespace Concurrency
extern "C"
{
/// <summary>
/// Open a file and create a streambuf instance to represent it.
/// </summary>
/// <param name="callback">A pointer to the callback interface to invoke when the file has been opened.</param>
/// <param name="filename">The name of the file to open</param>
/// <param name="mode">A creation mode for the stream buffer</param>
/// <param name="prot">A file protection mode to use for the file stream (not supported on Linux)</param>
/// <returns><c>true</c> if the opening operation could be initiated, <c>false</c> otherwise.</returns>
/// <remarks>
/// True does not signal that the file will eventually be successfully opened, just that the process was started.
/// </remarks>
#if !defined(__cplusplus_winrt)
_ASYNCRTIMP bool __cdecl _open_fsb_str(_In_ concurrency::streams::details::_filestream_callback* callback,
const utility::char_t* filename,
std::ios_base::openmode mode,
int prot);
#endif
/// <summary>
/// Create a streambuf instance to represent a WinRT file.
/// </summary>
/// <param name="callback">A pointer to the callback interface to invoke when the file has been opened.</param>
/// <param name="file">The file object</param>
/// <param name="mode">A creation mode for the stream buffer</param>
/// <returns><c>true</c> if the opening operation could be initiated, <c>false</c> otherwise.</returns>
/// <remarks>
/// True does not signal that the file will eventually be successfully opened, just that the process was started.
/// This is only available for WinRT.
/// </remarks>
#if defined(__cplusplus_winrt)
_ASYNCRTIMP bool __cdecl _open_fsb_stf_str(_In_ concurrency::streams::details::_filestream_callback* callback,
::Windows::Storage::StorageFile ^ file,
std::ios_base::openmode mode,
int prot);
#endif
/// <summary>
/// Close a file stream buffer.
/// </summary>
/// <param name="info">The file info record of the file</param>
/// <param name="callback">A pointer to the callback interface to invoke when the file has been opened.</param>
/// <returns><c>true</c> if the closing operation could be initiated, <c>false</c> otherwise.</returns>
/// <remarks>
/// True does not signal that the file will eventually be successfully closed, just that the process was started.
/// </remarks>
_ASYNCRTIMP bool __cdecl _close_fsb_nolock(_In_ concurrency::streams::details::_file_info** info,
_In_ concurrency::streams::details::_filestream_callback* callback);
_ASYNCRTIMP bool __cdecl _close_fsb(_In_ concurrency::streams::details::_file_info** info,
_In_ concurrency::streams::details::_filestream_callback* callback);
/// <summary>
/// Write data from a buffer into the file stream.
/// </summary>
/// <param name="info">The file info record of the file</param>
/// <param name="callback">A pointer to the callback interface to invoke when the write request is
/// completed.</param> <param name="ptr">A pointer to a buffer where the data should be placed</param> <param
/// name="count">The size (in characters) of the buffer</param> <returns>0 if the read request is still outstanding,
/// -1 if the request failed, otherwise the size of the data read into the buffer</returns>
_ASYNCRTIMP size_t __cdecl _putn_fsb(_In_ concurrency::streams::details::_file_info* info,
_In_ concurrency::streams::details::_filestream_callback* callback,
const void* ptr,
size_t count,
size_t char_size);
/// <summary>
/// Read data from a file stream into a buffer
/// </summary>
/// <param name="info">The file info record of the file</param>
/// <param name="callback">A pointer to the callback interface to invoke when the write request is
/// completed.</param> <param name="ptr">A pointer to a buffer where the data should be placed</param> <param
/// name="count">The size (in characters) of the buffer</param> <returns>0 if the read request is still outstanding,
/// -1 if the request failed, otherwise the size of the data read into the buffer</returns>
_ASYNCRTIMP size_t __cdecl _getn_fsb(_In_ concurrency::streams::details::_file_info* info,
_In_ concurrency::streams::details::_filestream_callback* callback,
_Out_writes_(count) void* ptr,
_In_ size_t count,
size_t char_size);
/// <summary>
/// Flush all buffered data to the underlying file.
/// </summary>
/// <param name="info">The file info record of the file</param>
/// <param name="callback">A pointer to the callback interface to invoke when the write request is
/// completed.</param> <returns><c>true</c> if the request was initiated</returns>
_ASYNCRTIMP bool __cdecl _sync_fsb(_In_ concurrency::streams::details::_file_info* info,
_In_ concurrency::streams::details::_filestream_callback* callback);
/// <summary>
/// Get the size of the underlying file.
/// </summary>
/// <param name="info">The file info record of the file</param>
/// <returns>The file size</returns>
_ASYNCRTIMP utility::size64_t __cdecl _get_size(_In_ concurrency::streams::details::_file_info* info,
size_t char_size);
/// <summary>
/// Adjust the internal buffers and pointers when the application seeks to a new read location in the stream.
/// </summary>
/// <param name="info">The file info record of the file</param>
/// <param name="pos">The new position (offset from the start) in the file stream</param>
/// <returns><c>true</c> if the request was initiated</returns>
_ASYNCRTIMP size_t __cdecl _seekrdpos_fsb(_In_ concurrency::streams::details::_file_info* info,
size_t pos,
size_t char_size);
/// <summary>
/// Adjust the internal buffers and pointers when the application seeks to a new read location in the stream.
/// </summary>
/// <param name="info">The file info record of the file</param>
/// <param name="pos">The new position (offset from the start) in the file stream</param>
/// <returns><c>true</c> if the request was initiated</returns>
_ASYNCRTIMP size_t __cdecl _seekrdtoend_fsb(_In_ concurrency::streams::details::_file_info* info,
int64_t offset,
size_t char_size);
/// <summary>
/// Adjust the internal buffers and pointers when the application seeks to a new write location in the stream.
/// </summary>
/// <param name="info">The file info record of the file</param>
/// <param name="pos">The new position (offset from the start) in the file stream</param>
/// <returns><c>true</c> if the request was initiated</returns>
_ASYNCRTIMP size_t __cdecl _seekwrpos_fsb(_In_ concurrency::streams::details::_file_info* info,
size_t pos,
size_t char_size);
}

View File

@ -0,0 +1,198 @@
#ifdef _METHODS
DAT(GET, _XPLATSTR("GET"))
DAT(POST, _XPLATSTR("POST"))
DAT(PUT, _XPLATSTR("PUT"))
DAT(DEL, _XPLATSTR("DELETE"))
DAT(HEAD, _XPLATSTR("HEAD"))
DAT(OPTIONS, _XPLATSTR("OPTIONS"))
DAT(TRCE, _XPLATSTR("TRACE"))
DAT(CONNECT, _XPLATSTR("CONNECT"))
DAT(MERGE, _XPLATSTR("MERGE"))
DAT(PATCH, _XPLATSTR("PATCH"))
#endif
#ifdef _PHRASES
DAT(Continue, 100, _XPLATSTR("Continue"))
DAT(SwitchingProtocols, 101, _XPLATSTR("Switching Protocols"))
DAT(OK, 200, _XPLATSTR("OK"))
DAT(Created, 201, _XPLATSTR("Created"))
DAT(Accepted, 202, _XPLATSTR("Accepted"))
DAT(NonAuthInfo, 203, _XPLATSTR("Non-Authoritative Information"))
DAT(NoContent, 204, _XPLATSTR("No Content"))
DAT(ResetContent, 205, _XPLATSTR("Reset Content"))
DAT(PartialContent, 206, _XPLATSTR("Partial Content"))
DAT(MultiStatus, 207, _XPLATSTR("Multi-Status"))
DAT(AlreadyReported, 208, _XPLATSTR("Already Reported"))
DAT(IMUsed, 226, _XPLATSTR("IM Used"))
DAT(MultipleChoices, 300, _XPLATSTR("Multiple Choices"))
DAT(MovedPermanently, 301, _XPLATSTR("Moved Permanently"))
DAT(Found, 302, _XPLATSTR("Found"))
DAT(SeeOther, 303, _XPLATSTR("See Other"))
DAT(NotModified, 304, _XPLATSTR("Not Modified"))
DAT(UseProxy, 305, _XPLATSTR("Use Proxy"))
DAT(TemporaryRedirect, 307, _XPLATSTR("Temporary Redirect"))
DAT(PermanentRedirect, 308, _XPLATSTR("Permanent Redirect"))
DAT(BadRequest, 400, _XPLATSTR("Bad Request"))
DAT(Unauthorized, 401, _XPLATSTR("Unauthorized"))
DAT(PaymentRequired, 402, _XPLATSTR("Payment Required"))
DAT(Forbidden, 403, _XPLATSTR("Forbidden"))
DAT(NotFound, 404, _XPLATSTR("Not Found"))
DAT(MethodNotAllowed, 405, _XPLATSTR("Method Not Allowed"))
DAT(NotAcceptable, 406, _XPLATSTR("Not Acceptable"))
DAT(ProxyAuthRequired, 407, _XPLATSTR("Proxy Authentication Required"))
DAT(RequestTimeout, 408, _XPLATSTR("Request Time-out"))
DAT(Conflict, 409, _XPLATSTR("Conflict"))
DAT(Gone, 410, _XPLATSTR("Gone"))
DAT(LengthRequired, 411, _XPLATSTR("Length Required"))
DAT(PreconditionFailed, 412, _XPLATSTR("Precondition Failed"))
DAT(RequestEntityTooLarge, 413, _XPLATSTR("Request Entity Too Large"))
DAT(RequestUriTooLarge, 414, _XPLATSTR("Request Uri Too Large"))
DAT(UnsupportedMediaType, 415, _XPLATSTR("Unsupported Media Type"))
DAT(RangeNotSatisfiable, 416, _XPLATSTR("Requested range not satisfiable"))
DAT(ExpectationFailed, 417, _XPLATSTR("Expectation Failed"))
DAT(MisdirectedRequest, 421, _XPLATSTR("Misdirected Request"))
DAT(UnprocessableEntity, 422, _XPLATSTR("Unprocessable Entity"))
DAT(Locked, 423, _XPLATSTR("Locked"))
DAT(FailedDependency, 424, _XPLATSTR("Failed Dependency"))
DAT(UpgradeRequired, 426, _XPLATSTR("Upgrade Required"))
DAT(PreconditionRequired, 428, _XPLATSTR("Precondition Required"))
DAT(TooManyRequests, 429, _XPLATSTR("Too Many Requests"))
DAT(RequestHeaderFieldsTooLarge, 431, _XPLATSTR("Request Header Fields Too Large"))
DAT(UnavailableForLegalReasons, 451, _XPLATSTR("Unavailable For Legal Reasons"))
DAT(InternalError, 500, _XPLATSTR("Internal Error"))
DAT(NotImplemented, 501, _XPLATSTR("Not Implemented"))
DAT(BadGateway, 502, _XPLATSTR("Bad Gateway"))
DAT(ServiceUnavailable, 503, _XPLATSTR("Service Unavailable"))
DAT(GatewayTimeout, 504, _XPLATSTR("Gateway Time-out"))
DAT(HttpVersionNotSupported, 505, _XPLATSTR("HTTP Version not supported"))
DAT(VariantAlsoNegotiates, 506, _XPLATSTR("Variant Also Negotiates"))
DAT(InsufficientStorage, 507, _XPLATSTR("Insufficient Storage"))
DAT(LoopDetected, 508, _XPLATSTR("Loop Detected"))
DAT(NotExtended, 510, _XPLATSTR("Not Extended"))
DAT(NetworkAuthenticationRequired, 511, _XPLATSTR("Network Authentication Required"))
#endif // _PHRASES
#ifdef _HEADER_NAMES
DAT(accept, "Accept")
DAT(accept_charset, "Accept-Charset")
DAT(accept_encoding, "Accept-Encoding")
DAT(accept_language, "Accept-Language")
DAT(accept_ranges, "Accept-Ranges")
DAT(access_control_allow_origin, "Access-Control-Allow-Origin")
DAT(age, "Age")
DAT(allow, "Allow")
DAT(authorization, "Authorization")
DAT(cache_control, "Cache-Control")
DAT(connection, "Connection")
DAT(content_encoding, "Content-Encoding")
DAT(content_language, "Content-Language")
DAT(content_length, "Content-Length")
DAT(content_location, "Content-Location")
DAT(content_md5, "Content-MD5")
DAT(content_range, "Content-Range")
DAT(content_type, "Content-Type")
DAT(content_disposition, "Content-Disposition")
DAT(date, "Date")
DAT(etag, "ETag")
DAT(expect, "Expect")
DAT(expires, "Expires")
DAT(from, "From")
DAT(host, "Host")
DAT(if_match, "If-Match")
DAT(if_modified_since, "If-Modified-Since")
DAT(if_none_match, "If-None-Match")
DAT(if_range, "If-Range")
DAT(if_unmodified_since, "If-Unmodified-Since")
DAT(last_modified, "Last-Modified")
DAT(location, "Location")
DAT(max_forwards, "Max-Forwards")
DAT(pragma, "Pragma")
DAT(proxy_authenticate, "Proxy-Authenticate")
DAT(proxy_authorization, "Proxy-Authorization")
DAT(range, "Range")
DAT(referer, "Referer")
DAT(retry_after, "Retry-After")
DAT(server, "Server")
DAT(te, "TE")
DAT(trailer, "Trailer")
DAT(transfer_encoding, "Transfer-Encoding")
DAT(upgrade, "Upgrade")
DAT(user_agent, "User-Agent")
DAT(vary, "Vary")
DAT(via, "Via")
DAT(warning, "Warning")
DAT(www_authenticate, "WWW-Authenticate")
#endif // _HEADER_NAMES
#ifdef _MIME_TYPES
DAT(application_atom_xml, "application/atom+xml")
DAT(application_http, "application/http")
DAT(application_javascript, "application/javascript")
DAT(application_json, "application/json")
DAT(application_xjson, "application/x-json")
DAT(application_octetstream, "application/octet-stream")
DAT(application_x_www_form_urlencoded, "application/x-www-form-urlencoded")
DAT(multipart_form_data, "multipart/form-data")
DAT(boundary, "boundary")
DAT(form_data, "form-data")
DAT(application_xjavascript, "application/x-javascript")
DAT(application_xml, "application/xml")
DAT(message_http, "message/http")
DAT(text, "text")
DAT(text_javascript, "text/javascript")
DAT(text_json, "text/json")
DAT(text_plain, "text/plain")
DAT(text_plain_utf16, "text/plain; charset=utf-16")
DAT(text_plain_utf16le, "text/plain; charset=utf-16le")
DAT(text_plain_utf8, "text/plain; charset=utf-8")
DAT(text_xjavascript, "text/x-javascript")
DAT(text_xjson, "text/x-json")
#endif // _MIME_TYPES
#ifdef _CHARSET_TYPES
DAT(ascii, "ascii")
DAT(usascii, "us-ascii")
DAT(latin1, "iso-8859-1")
DAT(utf8, "utf-8")
DAT(utf16, "utf-16")
DAT(utf16le, "utf-16le")
DAT(utf16be, "utf-16be")
#endif // _CHARSET_TYPES
#ifdef _OAUTH1_METHODS
DAT(hmac_sha1, _XPLATSTR("HMAC-SHA1"))
DAT(plaintext, _XPLATSTR("PLAINTEXT"))
#endif // _OAUTH1_METHODS
#ifdef _OAUTH1_STRINGS
DAT(callback, "oauth_callback")
DAT(callback_confirmed, "oauth_callback_confirmed")
DAT(consumer_key, "oauth_consumer_key")
DAT(nonce, "oauth_nonce")
DAT(realm, "realm") // NOTE: No "oauth_" prefix.
DAT(signature, "oauth_signature")
DAT(signature_method, "oauth_signature_method")
DAT(timestamp, "oauth_timestamp")
DAT(token, "oauth_token")
DAT(token_secret, "oauth_token_secret")
DAT(verifier, "oauth_verifier")
DAT(version, "oauth_version")
#endif // _OAUTH1_STRINGS
#ifdef _OAUTH2_STRINGS
DAT(access_token, "access_token")
DAT(authorization_code, "authorization_code")
DAT(bearer, "bearer")
DAT(client_id, "client_id")
DAT(client_secret, "client_secret")
DAT(code, "code")
DAT(expires_in, "expires_in")
DAT(grant_type, "grant_type")
DAT(redirect_uri, "redirect_uri")
DAT(refresh_token, "refresh_token")
DAT(response_type, "response_type")
DAT(scope, "scope")
DAT(state, "state")
DAT(token, "token")
DAT(token_type, "token_type")
#endif // _OAUTH2_STRINGS

View File

@ -0,0 +1,48 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Implementation Details of the http.h layer of messaging
*
* Functions and types for interoperating with http.h from modern C++
* This file includes windows definitions and should not be included in a public header
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include "cpprest/details/basic_types.h"
#include "cpprest/http_msg.h"
namespace web
{
namespace http
{
namespace details
{
namespace chunked_encoding
{
// Transfer-Encoding: chunked support
static const size_t additional_encoding_space = 12;
static const size_t data_offset = additional_encoding_space - 2;
// Add the data necessary for properly sending data with transfer-encoding: chunked.
//
// There are up to 12 additional bytes needed for each chunk:
//
// The last chunk requires 5 bytes, and is fixed.
// All other chunks require up to 8 bytes for the length, and four for the two CRLF
// delimiters.
//
_ASYNCRTIMP size_t __cdecl add_chunked_delimiters(_Out_writes_(buffer_size) uint8_t* data,
_In_ size_t buffer_size,
size_t bytes_read);
} // namespace chunked_encoding
} // namespace details
} // namespace http
} // namespace web

View File

@ -0,0 +1,72 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* HTTP Library: interface to implement HTTP server to service http_listeners.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#if _WIN32_WINNT < _WIN32_WINNT_VISTA
#error "Error: http server APIs are not supported in XP"
#endif //_WIN32_WINNT < _WIN32_WINNT_VISTA
#include "cpprest/http_listener.h"
namespace web
{
namespace http
{
namespace experimental
{
namespace details
{
/// <summary>
/// Interface http listeners interact with for receiving and responding to http requests.
/// </summary>
class http_server
{
public:
/// <summary>
/// Release any held resources.
/// </summary>
virtual ~http_server() {};
/// <summary>
/// Start listening for incoming requests.
/// </summary>
virtual pplx::task<void> start() = 0;
/// <summary>
/// Registers an http listener.
/// </summary>
virtual pplx::task<void> register_listener(
_In_ web::http::experimental::listener::details::http_listener_impl* pListener) = 0;
/// <summary>
/// Unregisters an http listener.
/// </summary>
virtual pplx::task<void> unregister_listener(
_In_ web::http::experimental::listener::details::http_listener_impl* pListener) = 0;
/// <summary>
/// Stop processing and listening for incoming requests.
/// </summary>
virtual pplx::task<void> stop() = 0;
/// <summary>
/// Asynchronously sends the specified http response.
/// </summary>
/// <param name="response">The http_response to send.</param>
/// <returns>A operation which is completed once the response has been sent.</returns>
virtual pplx::task<void> respond(http::http_response response) = 0;
};
} // namespace details
} // namespace experimental
} // namespace http
} // namespace web

View File

@ -0,0 +1,93 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* HTTP Library: exposes the entry points to the http server transport apis.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#if _WIN32_WINNT < _WIN32_WINNT_VISTA
#error "Error: http server APIs are not supported in XP"
#endif //_WIN32_WINNT < _WIN32_WINNT_VISTA
#include "cpprest/http_listener.h"
#include <memory>
namespace web
{
namespace http
{
namespace experimental
{
namespace details
{
class http_server;
/// <summary>
/// Singleton class used to register for http requests and send responses.
///
/// The lifetime is tied to http listener registration. When the first listener registers an instance is created
/// and when the last one unregisters the receiver stops and is destroyed. It can be started back up again if
/// listeners are again registered.
/// </summary>
class http_server_api
{
public:
/// <summary>
/// Returns whether or not any listeners are registered.
/// </summary>
static bool __cdecl has_listener();
/// <summary>
/// Registers a HTTP server API.
/// </summary>
static void __cdecl register_server_api(std::unique_ptr<http_server> server_api);
/// <summary>
/// Clears the http server API.
/// </summary>
static void __cdecl unregister_server_api();
/// <summary>
/// Registers a listener for HTTP requests and starts receiving.
/// </summary>
static pplx::task<void> __cdecl register_listener(
_In_ web::http::experimental::listener::details::http_listener_impl* pListener);
/// <summary>
/// Unregisters the given listener and stops listening for HTTP requests.
/// </summary>
static pplx::task<void> __cdecl unregister_listener(
_In_ web::http::experimental::listener::details::http_listener_impl* pListener);
/// <summary>
/// Gets static HTTP server API. Could be null if no registered listeners.
/// </summary>
static http_server* __cdecl server_api();
private:
/// Used to lock access to the server api registration
static pplx::extensibility::critical_section_t s_lock;
/// Registers a server API set -- this assumes the lock has already been taken
static void unsafe_register_server_api(std::unique_ptr<http_server> server_api);
// Static instance of the HTTP server API.
static std::unique_ptr<http_server> s_server_api;
/// Number of registered listeners;
static pplx::details::atomic_long s_registrations;
// Static only class. No creation.
http_server_api();
};
} // namespace details
} // namespace experimental
} // namespace http
} // namespace web

View File

@ -0,0 +1,77 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
***/
#pragma once
// selected MS SAL annotations
#ifdef _In_
#undef _In_
#endif
#define _In_
#ifdef _Inout_
#undef _Inout_
#endif
#define _Inout_
#ifdef _Out_
#undef _Out_
#endif
#define _Out_
#ifdef _In_z_
#undef _In_z_
#endif
#define _In_z_
#ifdef _Out_z_
#undef _Out_z_
#endif
#define _Out_z_
#ifdef _Inout_z_
#undef _Inout_z_
#endif
#define _Inout_z_
#ifdef _In_opt_
#undef _In_opt_
#endif
#define _In_opt_
#ifdef _Out_opt_
#undef _Out_opt_
#endif
#define _Out_opt_
#ifdef _Inout_opt_
#undef _Inout_opt_
#endif
#define _Inout_opt_
#ifdef _Out_writes_
#undef _Out_writes_
#endif
#define _Out_writes_(x)
#ifdef _Out_writes_opt_
#undef _Out_writes_opt_
#endif
#define _Out_writes_opt_(x)
#ifdef _In_reads_
#undef _In_reads_
#endif
#define _In_reads_(x)
#ifdef _Inout_updates_bytes_
#undef _Inout_updates_bytes_
#endif
#define _Inout_updates_bytes_(x)

View File

@ -0,0 +1,14 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by Resource.rc
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 101
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View File

@ -0,0 +1,223 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* utility classes used by the different web:: clients
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include "cpprest/asyncrt_utils.h"
#include "cpprest/uri.h"
namespace web
{
namespace details
{
class zero_memory_deleter
{
public:
_ASYNCRTIMP void operator()(::utility::string_t* data) const;
};
typedef std::unique_ptr<::utility::string_t, zero_memory_deleter> plaintext_string;
#if defined(_WIN32) && !defined(CPPREST_TARGET_XP)
#if defined(__cplusplus_winrt)
class winrt_encryption
{
public:
winrt_encryption() {}
_ASYNCRTIMP winrt_encryption(const std::wstring& data);
_ASYNCRTIMP plaintext_string decrypt() const;
private:
::pplx::task<Windows::Storage::Streams::IBuffer ^> m_buffer;
};
#else
class win32_encryption
{
public:
win32_encryption() {}
_ASYNCRTIMP win32_encryption(const std::wstring& data);
_ASYNCRTIMP ~win32_encryption();
_ASYNCRTIMP plaintext_string decrypt() const;
private:
std::vector<char> m_buffer;
size_t m_numCharacters;
};
#endif
#endif
} // namespace details
/// <summary>
/// Represents a set of user credentials (user name and password) to be used
/// for authentication.
/// </summary>
class credentials
{
public:
/// <summary>
/// Constructs an empty set of credentials without a user name or password.
/// </summary>
credentials() {}
/// <summary>
/// Constructs credentials from given user name and password.
/// </summary>
/// <param name="username">User name as a string.</param>
/// <param name="password">Password as a string.</param>
credentials(utility::string_t username, const utility::string_t& password)
: m_username(std::move(username)), m_password(password)
{
}
/// <summary>
/// The user name associated with the credentials.
/// </summary>
/// <returns>A string containing the user name.</returns>
const utility::string_t& username() const { return m_username; }
/// <summary>
/// The password for the user name associated with the credentials.
/// </summary>
/// <returns>A string containing the password.</returns>
CASABLANCA_DEPRECATED(
"This API is deprecated for security reasons to avoid unnecessary password copies stored in plaintext.")
utility::string_t password() const
{
#if defined(_WIN32) && !defined(CPPREST_TARGET_XP)
return utility::string_t(*m_password.decrypt());
#else
return m_password;
#endif
}
/// <summary>
/// Checks if credentials have been set
/// </summary>
/// <returns><c>true</c> if user name and password is set, <c>false</c> otherwise.</returns>
bool is_set() const { return !m_username.empty(); }
details::plaintext_string _internal_decrypt() const
{
// Encryption APIs not supported on XP
#if defined(_WIN32) && !defined(CPPREST_TARGET_XP)
return m_password.decrypt();
#else
return details::plaintext_string(new ::utility::string_t(m_password));
#endif
}
private:
::utility::string_t m_username;
#if defined(_WIN32) && !defined(CPPREST_TARGET_XP)
#if defined(__cplusplus_winrt)
details::winrt_encryption m_password;
#else
details::win32_encryption m_password;
#endif
#else
::utility::string_t m_password;
#endif
};
/// <summary>
/// web_proxy represents the concept of the web proxy, which can be auto-discovered,
/// disabled, or specified explicitly by the user.
/// </summary>
class web_proxy
{
enum web_proxy_mode_internal
{
use_default_,
use_auto_discovery_,
disabled_,
user_provided_
};
public:
enum web_proxy_mode
{
use_default = use_default_,
use_auto_discovery = use_auto_discovery_,
disabled = disabled_
};
/// <summary>
/// Constructs a proxy with the default settings.
/// </summary>
web_proxy() : m_address(_XPLATSTR("")), m_mode(use_default_) {}
/// <summary>
/// Creates a proxy with specified mode.
/// </summary>
/// <param name="mode">Mode to use.</param>
web_proxy(web_proxy_mode mode) : m_address(_XPLATSTR("")), m_mode(static_cast<web_proxy_mode_internal>(mode)) {}
/// <summary>
/// Creates a proxy explicitly with provided address.
/// </summary>
/// <param name="address">Proxy URI to use.</param>
web_proxy(uri address) : m_address(address), m_mode(user_provided_) {}
/// <summary>
/// Gets this proxy's URI address. Returns an empty URI if not explicitly set by user.
/// </summary>
/// <returns>A reference to this proxy's URI.</returns>
const uri& address() const { return m_address; }
/// <summary>
/// Gets the credentials used for authentication with this proxy.
/// </summary>
/// <returns>Credentials to for this proxy.</returns>
const web::credentials& credentials() const { return m_credentials; }
/// <summary>
/// Sets the credentials to use for authentication with this proxy.
/// </summary>
/// <param name="cred">Credentials to use for this proxy.</param>
void set_credentials(web::credentials cred)
{
if (m_mode == disabled_)
{
throw std::invalid_argument("Cannot attach credentials to a disabled proxy");
}
m_credentials = std::move(cred);
}
/// <summary>
/// Checks if this proxy was constructed with default settings.
/// </summary>
/// <returns>True if default, false otherwise.</param>
bool is_default() const { return m_mode == use_default_; }
/// <summary>
/// Checks if using a proxy is disabled.
/// </summary>
/// <returns>True if disabled, false otherwise.</returns>
bool is_disabled() const { return m_mode == disabled_; }
/// <summary>
/// Checks if the auto discovery protocol, WPAD, is to be used.
/// </summary>
/// <returns>True if auto discovery enabled, false otherwise.</returns>
bool is_auto_discovery() const { return m_mode == use_auto_discovery_; }
/// <summary>
/// Checks if a proxy address is explicitly specified by the user.
/// </summary>
/// <returns>True if a proxy address was explicitly specified, false otherwise.</returns>
bool is_specified() const { return m_mode == user_provided_; }
private:
web::uri m_address;
web_proxy_mode_internal m_mode;
web::credentials m_credentials;
};
} // namespace web

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,729 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* HTTP Library: Client-side APIs.
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#ifndef CASA_HTTP_CLIENT_H
#define CASA_HTTP_CLIENT_H
#if defined(__cplusplus_winrt)
#if !defined(__WRL_NO_DEFAULT_LIB__)
#define __WRL_NO_DEFAULT_LIB__
#endif
#include <msxml6.h>
#include <wrl.h>
namespace web
{
namespace http
{
namespace client
{
typedef IXMLHTTPRequest2* native_handle;
}
} // namespace http
} // namespace web
#else
namespace web
{
namespace http
{
namespace client
{
typedef void* native_handle;
}
} // namespace http
} // namespace web
#endif // __cplusplus_winrt
#include "cpprest/asyncrt_utils.h"
#include "cpprest/details/basic_types.h"
#include "cpprest/details/web_utilities.h"
#include "cpprest/http_msg.h"
#include "cpprest/json.h"
#include "cpprest/uri.h"
#include "pplx/pplxtasks.h"
#include <limits>
#include <memory>
#if !defined(CPPREST_TARGET_XP)
#include "cpprest/oauth1.h"
#endif
#include "cpprest/oauth2.h"
#if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO)
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
#endif
#include "boost/asio/ssl.hpp"
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
#endif
/// The web namespace contains functionality common to multiple protocols like HTTP and WebSockets.
namespace web
{
/// Declarations and functionality for the HTTP protocol.
namespace http
{
/// HTTP client side library.
namespace client
{
// credentials and web_proxy class has been moved from web::http::client namespace to web namespace.
// The below using declarations ensure we don't break existing code.
// Please use the web::credentials and web::web_proxy class going forward.
using web::credentials;
using web::web_proxy;
/// <summary>
/// HTTP client configuration class, used to set the possible configuration options
/// used to create an http_client instance.
/// </summary>
class http_client_config
{
public:
http_client_config()
: m_guarantee_order(false)
, m_timeout(std::chrono::seconds(30))
, m_chunksize(0)
, m_request_compressed(false)
#if !defined(__cplusplus_winrt)
, m_validate_certificates(true)
#endif
#if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO)
, m_tlsext_sni_enabled(true)
#endif
#if defined(_WIN32) && !defined(__cplusplus_winrt)
, m_buffer_request(false)
#endif
{
}
#if !defined(CPPREST_TARGET_XP)
/// <summary>
/// Get OAuth 1.0 configuration.
/// </summary>
/// <returns>Shared pointer to OAuth 1.0 configuration.</returns>
const std::shared_ptr<oauth1::experimental::oauth1_config> oauth1() const { return m_oauth1; }
/// <summary>
/// Set OAuth 1.0 configuration.
/// </summary>
/// <param name="config">OAuth 1.0 configuration to set.</param>
void set_oauth1(oauth1::experimental::oauth1_config config)
{
m_oauth1 = std::make_shared<oauth1::experimental::oauth1_config>(std::move(config));
}
#endif
/// <summary>
/// Get OAuth 2.0 configuration.
/// </summary>
/// <returns>Shared pointer to OAuth 2.0 configuration.</returns>
const std::shared_ptr<oauth2::experimental::oauth2_config> oauth2() const { return m_oauth2; }
/// <summary>
/// Set OAuth 2.0 configuration.
/// </summary>
/// <param name="config">OAuth 2.0 configuration to set.</param>
void set_oauth2(oauth2::experimental::oauth2_config config)
{
m_oauth2 = std::make_shared<oauth2::experimental::oauth2_config>(std::move(config));
}
/// <summary>
/// Get the web proxy object
/// </summary>
/// <returns>A reference to the web proxy object.</returns>
const web_proxy& proxy() const { return m_proxy; }
/// <summary>
/// Set the web proxy object
/// </summary>
/// <param name="proxy">A reference to the web proxy object.</param>
void set_proxy(web_proxy proxy) { m_proxy = std::move(proxy); }
/// <summary>
/// Get the client credentials
/// </summary>
/// <returns>A reference to the client credentials.</returns>
const http::client::credentials& credentials() const { return m_credentials; }
/// <summary>
/// Set the client credentials
/// </summary>
/// <param name="cred">A reference to the client credentials.</param>
void set_credentials(const http::client::credentials& cred) { m_credentials = cred; }
/// <summary>
/// Get the 'guarantee order' property
/// </summary>
/// <returns>The value of the property.</returns>
bool guarantee_order() const { return m_guarantee_order; }
/// <summary>
/// Set the 'guarantee order' property
/// </summary>
/// <param name="guarantee_order">The value of the property.</param>
CASABLANCA_DEPRECATED(
"Confusing API will be removed in future releases. If you need to order HTTP requests use task continuations.")
void set_guarantee_order(bool guarantee_order) { m_guarantee_order = guarantee_order; }
/// <summary>
/// Get the timeout
/// </summary>
/// <returns>The timeout (in seconds) used for each send and receive operation on the client.</returns>
utility::seconds timeout() const { return std::chrono::duration_cast<utility::seconds>(m_timeout); }
/// <summary>
/// Get the timeout
/// </summary>
/// <returns>The timeout (in whatever duration) used for each send and receive operation on the client.</returns>
template<class T>
T timeout() const
{
return std::chrono::duration_cast<T>(m_timeout);
}
/// <summary>
/// Set the timeout
/// </summary>
/// <param name="timeout">The timeout (duration from microseconds range and up) used for each send and receive
/// operation on the client.</param>
template<class T>
void set_timeout(const T& timeout)
{
m_timeout = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
}
/// <summary>
/// Get the client chunk size.
/// </summary>
/// <returns>The internal buffer size used by the http client when sending and receiving data from the
/// network.</returns>
size_t chunksize() const { return m_chunksize == 0 ? 64 * 1024 : m_chunksize; }
/// <summary>
/// Sets the client chunk size.
/// </summary>
/// <param name="size">The internal buffer size used by the http client when sending and receiving data from the
/// network.</param> <remarks>This is a hint -- an implementation may disregard the setting and use some other chunk
/// size.</remarks>
void set_chunksize(size_t size) { m_chunksize = size; }
/// <summary>
/// Returns true if the default chunk size is in use.
/// <remarks>If true, implementations are allowed to choose whatever size is best.</remarks>
/// </summary>
/// <returns>True if default, false if set by user.</returns>
bool is_default_chunksize() const { return m_chunksize == 0; }
/// <summary>
/// Checks if requesting a compressed response using Content-Encoding is turned on, the default is off.
/// </summary>
/// <returns>True if a content-encoded compressed response is allowed, false otherwise</returns>
bool request_compressed_response() const { return m_request_compressed; }
/// <summary>
/// Request that the server respond with a compressed body using Content-Encoding; to use Transfer-Encoding, do not
/// set this, and specify a vector of <see cref="web::http::details::compression::decompress_factory" /> pointers
/// to the set_decompress_factories method of the <see cref="web::http::http_request" /> object for the request.
/// If true and the server does not support compression, this will have no effect.
/// The response body is internally decompressed before the consumer receives the data.
/// </summary>
/// <param name="request_compressed">True to turn on content-encoded response body compression, false
/// otherwise.</param> <remarks>Please note there is a performance cost due to copying the request data. Currently
/// only supported on Windows and OSX.</remarks>
void set_request_compressed_response(bool request_compressed) { m_request_compressed = request_compressed; }
#if !defined(__cplusplus_winrt)
/// <summary>
/// Gets the server certificate validation property.
/// </summary>
/// <returns>True if certificates are to be verified, false otherwise.</returns>
bool validate_certificates() const { return m_validate_certificates; }
/// <summary>
/// Sets the server certificate validation property.
/// </summary>
/// <param name="validate_certs">False to turn ignore all server certificate validation errors, true
/// otherwise.</param> <remarks>Note ignoring certificate errors can be dangerous and should be done with
/// caution.</remarks>
void set_validate_certificates(bool validate_certs) { m_validate_certificates = validate_certs; }
#endif
#if defined(_WIN32) && !defined(__cplusplus_winrt)
/// <summary>
/// Checks if request data buffering is turned on, the default is off.
/// </summary>
/// <returns>True if buffering is enabled, false otherwise</returns>
bool buffer_request() const { return m_buffer_request; }
/// <summary>
/// Sets the request buffering property.
/// If true, in cases where the request body/stream doesn't support seeking the request data will be buffered.
/// This can help in situations where an authentication challenge might be expected.
/// </summary>
/// <param name="buffer_request">True to turn on buffer, false otherwise.</param>
/// <remarks>Please note there is a performance cost due to copying the request data.</remarks>
void set_buffer_request(bool buffer_request) { m_buffer_request = buffer_request; }
#endif
/// <summary>
/// Sets a callback to enable custom setting of platform specific options.
/// </summary>
/// <remarks>
/// The native_handle is the following type depending on the underlying platform:
/// Windows Desktop, WinHTTP - HINTERNET (session)
/// </remarks>
/// <param name="callback">A user callback allowing for customization of the session</param>
void set_nativesessionhandle_options(const std::function<void(native_handle)>& callback)
{
m_set_user_nativesessionhandle_options = callback;
}
/// <summary>
/// Invokes a user's callback to allow for customization of the session.
/// </summary>
/// <remarks>Internal Use Only</remarks>
/// <param name="handle">A internal implementation handle.</param>
void _invoke_nativesessionhandle_options(native_handle handle) const
{
if (m_set_user_nativesessionhandle_options) m_set_user_nativesessionhandle_options(handle);
}
/// <summary>
/// Sets a callback to enable custom setting of platform specific options.
/// </summary>
/// <remarks>
/// The native_handle is the following type depending on the underlying platform:
/// Windows Desktop, WinHTTP - HINTERNET
/// Windows Runtime, WinRT - IXMLHTTPRequest2 *
/// All other platforms, Boost.Asio:
/// https - boost::asio::ssl::stream<boost::asio::ip::tcp::socket &> *
/// http - boost::asio::ip::tcp::socket *
/// </remarks>
/// <param name="callback">A user callback allowing for customization of the request</param>
void set_nativehandle_options(const std::function<void(native_handle)>& callback)
{
m_set_user_nativehandle_options = callback;
}
/// <summary>
/// Invokes a user's callback to allow for customization of the request.
/// </summary>
/// <param name="handle">A internal implementation handle.</param>
void invoke_nativehandle_options(native_handle handle) const
{
if (m_set_user_nativehandle_options) m_set_user_nativehandle_options(handle);
}
#if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO)
/// <summary>
/// Sets a callback to enable custom setting of the ssl context, at construction time.
/// </summary>
/// <param name="callback">A user callback allowing for customization of the ssl context at construction
/// time.</param>
void set_ssl_context_callback(const std::function<void(boost::asio::ssl::context&)>& callback)
{
m_ssl_context_callback = callback;
}
/// <summary>
/// Gets the user's callback to allow for customization of the ssl context.
/// </summary>
const std::function<void(boost::asio::ssl::context&)>& get_ssl_context_callback() const
{
return m_ssl_context_callback;
}
/// <summary>
/// Gets the TLS extension server name indication (SNI) status.
/// </summary>
/// <returns>True if TLS server name indication is enabled, false otherwise.</returns>
bool is_tlsext_sni_enabled() const { return m_tlsext_sni_enabled; }
/// <summary>
/// Sets the TLS extension server name indication (SNI) status.
/// </summary>
/// <param name="tlsext_sni_enabled">False to disable the TLS (ClientHello) extension for server name indication,
/// true otherwise.</param> <remarks>Note: This setting is enabled by default as it is required in most virtual
/// hosting scenarios.</remarks>
void set_tlsext_sni_enabled(bool tlsext_sni_enabled) { m_tlsext_sni_enabled = tlsext_sni_enabled; }
#endif
private:
#if !defined(CPPREST_TARGET_XP)
std::shared_ptr<oauth1::experimental::oauth1_config> m_oauth1;
#endif
std::shared_ptr<oauth2::experimental::oauth2_config> m_oauth2;
web_proxy m_proxy;
http::client::credentials m_credentials;
// Whether or not to guarantee ordering, i.e. only using one underlying TCP connection.
bool m_guarantee_order;
std::chrono::microseconds m_timeout;
size_t m_chunksize;
bool m_request_compressed;
#if !defined(__cplusplus_winrt)
// IXmlHttpRequest2 doesn't allow configuration of certificate verification.
bool m_validate_certificates;
#endif
std::function<void(native_handle)> m_set_user_nativehandle_options;
std::function<void(native_handle)> m_set_user_nativesessionhandle_options;
#if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO)
std::function<void(boost::asio::ssl::context&)> m_ssl_context_callback;
bool m_tlsext_sni_enabled;
#endif
#if defined(_WIN32) && !defined(__cplusplus_winrt)
bool m_buffer_request;
#endif
};
class http_pipeline;
/// <summary>
/// HTTP client class, used to maintain a connection to an HTTP service for an extended session.
/// </summary>
class http_client
{
public:
/// <summary>
/// Creates a new http_client connected to specified uri.
/// </summary>
/// <param name="base_uri">A string representation of the base uri to be used for all requests. Must start with
/// either "http://" or "https://"</param>
_ASYNCRTIMP http_client(const uri& base_uri);
/// <summary>
/// Creates a new http_client connected to specified uri.
/// </summary>
/// <param name="base_uri">A string representation of the base uri to be used for all requests. Must start with
/// either "http://" or "https://"</param> <param name="client_config">The http client configuration object
/// containing the possible configuration options to initialize the <c>http_client</c>. </param>
_ASYNCRTIMP http_client(const uri& base_uri, const http_client_config& client_config);
/// <summary>
/// Note the destructor doesn't necessarily close the connection and release resources.
/// The connection is reference counted with the http_responses.
/// </summary>
_ASYNCRTIMP ~http_client() CPPREST_NOEXCEPT;
/// <summary>
/// Gets the base URI.
/// </summary>
/// <returns>
/// A base URI initialized in constructor
/// </returns>
_ASYNCRTIMP const uri& base_uri() const;
/// <summary>
/// Get client configuration object
/// </summary>
/// <returns>A reference to the client configuration object.</returns>
_ASYNCRTIMP const http_client_config& client_config() const;
/// <summary>
/// Adds an HTTP pipeline stage to the client.
/// </summary>
/// <param name="handler">A function object representing the pipeline stage.</param>
_ASYNCRTIMP void add_handler(const std::function<pplx::task<http_response> __cdecl(
http_request, std::shared_ptr<http::http_pipeline_stage>)>& handler);
/// <summary>
/// Adds an HTTP pipeline stage to the client.
/// </summary>
/// <param name="stage">A shared pointer to a pipeline stage.</param>
_ASYNCRTIMP void add_handler(const std::shared_ptr<http::http_pipeline_stage>& stage);
/// <summary>
/// Asynchronously sends an HTTP request.
/// </summary>
/// <param name="request">Request to send.</param>
/// <param name="token">Cancellation token for cancellation of this request operation.</param>
/// <returns>An asynchronous operation that is completed once a response from the request is received.</returns>
_ASYNCRTIMP pplx::task<http_response> request(
http_request request, const pplx::cancellation_token& token = pplx::cancellation_token::none());
/// <summary>
/// Asynchronously sends an HTTP request.
/// </summary>
/// <param name="mtd">HTTP request method.</param>
/// <param name="token">Cancellation token for cancellation of this request operation.</param>
/// <returns>An asynchronous operation that is completed once a response from the request is received.</returns>
pplx::task<http_response> request(const method& mtd,
const pplx::cancellation_token& token = pplx::cancellation_token::none())
{
http_request msg(mtd);
return request(msg, token);
}
/// <summary>
/// Asynchronously sends an HTTP request.
/// </summary>
/// <param name="mtd">HTTP request method.</param>
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
/// base URI.</param> <param name="token">Cancellation token for cancellation of this request operation.</param>
/// <returns>An asynchronous operation that is completed once a response from the request is received.</returns>
pplx::task<http_response> request(const method& mtd,
const utility::string_t& path_query_fragment,
const pplx::cancellation_token& token = pplx::cancellation_token::none())
{
http_request msg(mtd);
msg.set_request_uri(path_query_fragment);
return request(msg, token);
}
/// <summary>
/// Asynchronously sends an HTTP request.
/// </summary>
/// <param name="mtd">HTTP request method.</param>
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
/// base URI.</param> <param name="body_data">The data to be used as the message body, represented using the json
/// object library.</param> <param name="token">Cancellation token for cancellation of this request
/// operation.</param> <returns>An asynchronous operation that is completed once a response from the request is
/// received.</returns>
pplx::task<http_response> request(const method& mtd,
const utility::string_t& path_query_fragment,
const json::value& body_data,
const pplx::cancellation_token& token = pplx::cancellation_token::none())
{
http_request msg(mtd);
msg.set_request_uri(path_query_fragment);
msg.set_body(body_data);
return request(msg, token);
}
/// <summary>
/// Asynchronously sends an HTTP request with a string body. Assumes the
/// character encoding of the string is UTF-8.
/// </summary>
/// <param name="mtd">HTTP request method.</param>
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
/// base URI.</param> <param name="content_type">A string holding the MIME type of the message body.</param> <param
/// name="body_data">String containing the text to use in the message body.</param> <param name="token">Cancellation
/// token for cancellation of this request operation.</param> <returns>An asynchronous operation that is completed
/// once a response from the request is received.</returns>
pplx::task<http_response> request(const method& mtd,
const utf8string& path_query_fragment,
const utf8string& body_data,
const utf8string& content_type = "text/plain; charset=utf-8",
const pplx::cancellation_token& token = pplx::cancellation_token::none())
{
http_request msg(mtd);
msg.set_request_uri(::utility::conversions::to_string_t(path_query_fragment));
msg.set_body(body_data, content_type);
return request(msg, token);
}
/// <summary>
/// Asynchronously sends an HTTP request with a string body. Assumes the
/// character encoding of the string is UTF-8.
/// </summary>
/// <param name="mtd">HTTP request method.</param>
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
/// base URI.</param> <param name="content_type">A string holding the MIME type of the message body.</param> <param
/// name="body_data">String containing the text to use in the message body.</param> <param name="token">Cancellation
/// token for cancellation of this request operation.</param> <returns>An asynchronous operation that is completed
/// once a response from the request is received.</returns>
pplx::task<http_response> request(const method& mtd,
const utf8string& path_query_fragment,
utf8string&& body_data,
const utf8string& content_type = "text/plain; charset=utf-8",
const pplx::cancellation_token& token = pplx::cancellation_token::none())
{
http_request msg(mtd);
msg.set_request_uri(::utility::conversions::to_string_t(path_query_fragment));
msg.set_body(std::move(body_data), content_type);
return request(msg, token);
}
/// <summary>
/// Asynchronously sends an HTTP request with a string body. Assumes the
/// character encoding of the string is UTF-16 will perform conversion to UTF-8.
/// </summary>
/// <param name="mtd">HTTP request method.</param>
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
/// base URI.</param> <param name="content_type">A string holding the MIME type of the message body.</param> <param
/// name="body_data">String containing the text to use in the message body.</param> <param name="token">Cancellation
/// token for cancellation of this request operation.</param> <returns>An asynchronous operation that is completed
/// once a response from the request is received.</returns>
pplx::task<http_response> request(
const method& mtd,
const utf16string& path_query_fragment,
const utf16string& body_data,
const utf16string& content_type = utility::conversions::to_utf16string("text/plain"),
const pplx::cancellation_token& token = pplx::cancellation_token::none())
{
http_request msg(mtd);
msg.set_request_uri(::utility::conversions::to_string_t(path_query_fragment));
msg.set_body(body_data, content_type);
return request(msg, token);
}
/// <summary>
/// Asynchronously sends an HTTP request with a string body. Assumes the
/// character encoding of the string is UTF-8.
/// </summary>
/// <param name="mtd">HTTP request method.</param>
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
/// base URI.</param> <param name="body_data">String containing the text to use in the message body.</param> <param
/// name="token">Cancellation token for cancellation of this request operation.</param> <returns>An asynchronous
/// operation that is completed once a response from the request is received.</returns>
pplx::task<http_response> request(const method& mtd,
const utf8string& path_query_fragment,
const utf8string& body_data,
const pplx::cancellation_token& token)
{
return request(mtd, path_query_fragment, body_data, "text/plain; charset=utf-8", token);
}
/// <summary>
/// Asynchronously sends an HTTP request with a string body. Assumes the
/// character encoding of the string is UTF-8.
/// </summary>
/// <param name="mtd">HTTP request method.</param>
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
/// base URI.</param> <param name="body_data">String containing the text to use in the message body.</param> <param
/// name="token">Cancellation token for cancellation of this request operation.</param> <returns>An asynchronous
/// operation that is completed once a response from the request is received.</returns>
pplx::task<http_response> request(const method& mtd,
const utf8string& path_query_fragment,
utf8string&& body_data,
const pplx::cancellation_token& token)
{
http_request msg(mtd);
msg.set_request_uri(::utility::conversions::to_string_t(path_query_fragment));
msg.set_body(std::move(body_data), "text/plain; charset=utf-8");
return request(msg, token);
}
/// <summary>
/// Asynchronously sends an HTTP request with a string body. Assumes
/// the character encoding of the string is UTF-16 will perform conversion to UTF-8.
/// </summary>
/// <param name="mtd">HTTP request method.</param>
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
/// base URI.</param> <param name="body_data">String containing the text to use in the message body.</param> <param
/// name="token">Cancellation token for cancellation of this request operation.</param> <returns>An asynchronous
/// operation that is completed once a response from the request is received.</returns>
pplx::task<http_response> request(const method& mtd,
const utf16string& path_query_fragment,
const utf16string& body_data,
const pplx::cancellation_token& token)
{
return request(
mtd, path_query_fragment, body_data, ::utility::conversions::to_utf16string("text/plain"), token);
}
#if !defined(__cplusplus_winrt)
/// <summary>
/// Asynchronously sends an HTTP request.
/// </summary>
/// <param name="mtd">HTTP request method.</param>
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
/// base URI.</param> <param name="body">An asynchronous stream representing the body data.</param> <param
/// name="content_type">A string holding the MIME type of the message body.</param> <param name="token">Cancellation
/// token for cancellation of this request operation.</param> <returns>A task that is completed once a response from
/// the request is received.</returns>
pplx::task<http_response> request(const method& mtd,
const utility::string_t& path_query_fragment,
const concurrency::streams::istream& body,
const utility::string_t& content_type = _XPLATSTR("application/octet-stream"),
const pplx::cancellation_token& token = pplx::cancellation_token::none())
{
http_request msg(mtd);
msg.set_request_uri(path_query_fragment);
msg.set_body(body, content_type);
return request(msg, token);
}
/// <summary>
/// Asynchronously sends an HTTP request.
/// </summary>
/// <param name="mtd">HTTP request method.</param>
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
/// base URI.</param> <param name="body">An asynchronous stream representing the body data.</param> <param
/// name="token">Cancellation token for cancellation of this request operation.</param> <returns>A task that is
/// completed once a response from the request is received.</returns>
pplx::task<http_response> request(const method& mtd,
const utility::string_t& path_query_fragment,
const concurrency::streams::istream& body,
const pplx::cancellation_token& token)
{
return request(mtd, path_query_fragment, body, _XPLATSTR("application/octet-stream"), token);
}
#endif // __cplusplus_winrt
/// <summary>
/// Asynchronously sends an HTTP request.
/// </summary>
/// <param name="mtd">HTTP request method.</param>
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
/// base URI.</param> <param name="body">An asynchronous stream representing the body data.</param> <param
/// name="content_length">Size of the message body.</param> <param name="content_type">A string holding the MIME
/// type of the message body.</param> <param name="token">Cancellation token for cancellation of this request
/// operation.</param> <returns>A task that is completed once a response from the request is received.</returns>
/// <remarks>Winrt requires to provide content_length.</remarks>
pplx::task<http_response> request(const method& mtd,
const utility::string_t& path_query_fragment,
const concurrency::streams::istream& body,
size_t content_length,
const utility::string_t& content_type = _XPLATSTR("application/octet-stream"),
const pplx::cancellation_token& token = pplx::cancellation_token::none())
{
http_request msg(mtd);
msg.set_request_uri(path_query_fragment);
msg.set_body(body, content_length, content_type);
return request(msg, token);
}
/// <summary>
/// Asynchronously sends an HTTP request.
/// </summary>
/// <param name="mtd">HTTP request method.</param>
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
/// base URI.</param> <param name="body">An asynchronous stream representing the body data.</param> <param
/// name="content_length">Size of the message body.</param> <param name="token">Cancellation token for cancellation
/// of this request operation.</param> <returns>A task that is completed once a response from the request is
/// received.</returns> <remarks>Winrt requires to provide content_length.</remarks>
pplx::task<http_response> request(const method& mtd,
const utility::string_t& path_query_fragment,
const concurrency::streams::istream& body,
size_t content_length,
const pplx::cancellation_token& token)
{
return request(mtd, path_query_fragment, body, content_length, _XPLATSTR("application/octet-stream"), token);
}
private:
std::shared_ptr<::web::http::client::http_pipeline> m_pipeline;
};
namespace details
{
#if defined(_WIN32)
extern const utility::char_t* get_with_body_err_msg;
#endif
} // namespace details
} // namespace client
} // namespace http
} // namespace web
#endif

View File

@ -0,0 +1,326 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* HTTP Library: Compression and decompression interfaces
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
namespace web
{
namespace http
{
namespace compression
{
/// <summary>
/// Hint as to whether a compress or decompress call is meant to be the last for a particular HTTP request or reply
/// </summary>
enum operation_hint
{
is_last, // Used for the expected last compress() call, or for an expected single decompress() call
has_more // Used when further compress() calls will be made, or when multiple decompress() calls may be required
};
/// <summary>
/// Result structure for asynchronous compression and decompression operations
/// </summary>
struct operation_result
{
size_t input_bytes_processed; // From the input buffer
size_t output_bytes_produced; // To the output buffer
bool done; // For compress, set when 'last' is true and there was enough space to complete compression;
// for decompress, set if the end of the decompression stream has been reached
};
/// <summary>
/// Compression interface for use with HTTP requests
/// </summary>
class compress_provider
{
public:
virtual const utility::string_t& algorithm() const = 0;
virtual size_t compress(const uint8_t* input,
size_t input_size,
uint8_t* output,
size_t output_size,
operation_hint hint,
size_t& input_bytes_processed,
bool& done) = 0;
virtual pplx::task<operation_result> compress(
const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size, operation_hint hint) = 0;
virtual void reset() = 0;
virtual ~compress_provider() = default;
};
/// <summary>
/// Decompression interface for use with HTTP requests
/// </summary>
class decompress_provider
{
public:
virtual const utility::string_t& algorithm() const = 0;
virtual size_t decompress(const uint8_t* input,
size_t input_size,
uint8_t* output,
size_t output_size,
operation_hint hint,
size_t& input_bytes_processed,
bool& done) = 0;
virtual pplx::task<operation_result> decompress(
const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size, operation_hint hint) = 0;
virtual void reset() = 0;
virtual ~decompress_provider() = default;
};
/// <summary>
/// Factory interface for compressors for use with received HTTP requests
/// </summary>
class compress_factory
{
public:
virtual const utility::string_t& algorithm() const = 0;
virtual std::unique_ptr<compress_provider> make_compressor() const = 0;
virtual ~compress_factory() = default;
};
/// <summary>
/// Factory interface for decompressors for use with HTTP requests
/// </summary>
class decompress_factory
{
public:
virtual const utility::string_t& algorithm() const = 0;
virtual uint16_t weight() const = 0;
virtual std::unique_ptr<decompress_provider> make_decompressor() const = 0;
virtual ~decompress_factory() = default;
};
/// <summary>
/// Built-in compression support
/// </summary>
namespace builtin
{
/// <summary>
/// Test whether cpprestsdk was built with built-in compression support
/// <returns>True if cpprestsdk was built with built-in compression support, and false if not.</returns>
/// </summary>
_ASYNCRTIMP bool supported();
/// <summary>
// String constants for each built-in compression algorithm, for convenient use with the factory functions
/// </summary>
namespace algorithm
{
#if defined(_MSC_VER) && _MSC_VER < 1900
const utility::char_t* const GZIP = _XPLATSTR("gzip");
const utility::char_t* const DEFLATE = _XPLATSTR("deflate");
const utility::char_t* const BROTLI = _XPLATSTR("br");
#else // ^^^ VS2013 and before ^^^ // vvv VS2015+, and everything else vvv
constexpr const utility::char_t* const GZIP = _XPLATSTR("gzip");
constexpr const utility::char_t* const DEFLATE = _XPLATSTR("deflate");
constexpr const utility::char_t* const BROTLI = _XPLATSTR("br");
#endif
/// <summary>
/// Test whether cpprestsdk was built with built-in compression support and
/// the supplied string matches a supported built-in algorithm
/// <param name="algorithm">The name of the algorithm to test for built-in support.</param>
/// <returns>True if cpprestsdk was built with built-in compression support and
/// the supplied string matches a supported built-in algorithm, and false if not.</returns>
/// <summary>
_ASYNCRTIMP bool supported(const utility::string_t& algorithm);
} // namespace algorithm
/// <summary>
/// Factory function to instantiate a built-in compression provider with default parameters by compression algorithm
/// name.
/// </summary>
/// <param name="algorithm">The name of the algorithm for which to instantiate a provider.</param>
/// <returns>
/// A caller-owned pointer to a provider of the requested-type, or to nullptr if no such built-in type exists.
/// </returns>
_ASYNCRTIMP std::unique_ptr<compress_provider> make_compressor(const utility::string_t& algorithm);
/// <summary>
/// Factory function to instantiate a built-in decompression provider with default parameters by compression algorithm
/// name.
/// </summary>
/// <param name="algorithm">The name of the algorithm for which to instantiate a provider.</param>
/// <returns>
/// A caller-owned pointer to a provider of the requested-type, or to nullptr if no such built-in type exists.
/// </returns>
_ASYNCRTIMP std::unique_ptr<decompress_provider> make_decompressor(const utility::string_t& algorithm);
/// <summary>
/// Factory function to obtain a pointer to a built-in compression provider factory by compression algorithm name.
/// </summary>
/// <param name="algorithm">The name of the algorithm for which to find a factory.</param>
/// <returns>
/// A caller-owned pointer to a provider of the requested-type, or to nullptr if no such built-in type exists.
/// </returns>
_ASYNCRTIMP std::shared_ptr<compress_factory> get_compress_factory(const utility::string_t& algorithm);
/// <summary>
/// Factory function to obtain a pointer to a built-in decompression provider factory by compression algorithm name.
/// </summary>
/// <param name="algorithm">The name of the algorithm for which to find a factory.</param>
/// <returns>
/// A caller-owned pointer to a provider of the requested-type, or to nullptr if no such built-in type exists.
/// </returns>
_ASYNCRTIMP std::shared_ptr<decompress_factory> get_decompress_factory(const utility::string_t& algorithm);
/// <summary>
// Factory function to instantiate a built-in gzip compression provider with caller-selected parameters.
/// </summary>
/// <returns>
/// A caller-owned pointer to a gzip compression provider, or to nullptr if the library was built without built-in
/// compression support.
/// </returns>
_ASYNCRTIMP std::unique_ptr<compress_provider> make_gzip_compressor(int compressionLevel,
int method,
int strategy,
int memLevel);
/// <summary>
// Factory function to instantiate a built-in deflate compression provider with caller-selected parameters.
/// </summary>
/// <returns>
/// A caller-owned pointer to a deflate compression provider, or to nullptr if the library was built without built-in
/// compression support..
/// </returns>
_ASYNCRTIMP std::unique_ptr<compress_provider> make_deflate_compressor(int compressionLevel,
int method,
int strategy,
int memLevel);
/// <summary>
// Factory function to instantiate a built-in Brotli compression provider with caller-selected parameters.
/// </summary>
/// <returns>
/// A caller-owned pointer to a Brotli compression provider, or to nullptr if the library was built without built-in
/// compression support.
/// </returns>
_ASYNCRTIMP std::unique_ptr<compress_provider> make_brotli_compressor(
uint32_t window, uint32_t quality, uint32_t mode, uint32_t block, uint32_t nomodel, uint32_t hint);
} // namespace builtin
/// <summary>
/// Factory function to instantiate a compression provider factory by compression algorithm name.
/// </summary>
/// <param name="algorithm">The name of the algorithm supported by the factory. Must match that returned by the
/// <c>web::http::compression::compress_provider</c> type instantiated by the factory's make_compressor function.
/// The supplied string is copied, and thus need not remain valid once the call returns.</param>
/// <param name="make_compressor">A factory function to be used to instantiate a compressor matching the factory's
/// reported algorithm.</param>
/// <returns>
/// A pointer to a generic provider factory implementation configured with the supplied parameters.
/// </returns>
/// <remarks>
/// This method may be used to conveniently instantiate a factory object for a caller-selected <c>compress_provider</c>.
/// That provider may be of the caller's own design, or it may be one of the built-in types. As such, this method may
/// be helpful when a caller wishes to build vectors containing a mix of custom and built-in providers.
/// </remarks>
_ASYNCRTIMP std::shared_ptr<compress_factory> make_compress_factory(
const utility::string_t& algorithm, std::function<std::unique_ptr<compress_provider>()> make_compressor);
/// <summary>
/// Factory function to instantiate a decompression provider factory by compression algorithm name.
/// </summary>
/// <param name="algorithm">The name of the algorithm supported by the factory. Must match that returned by the
/// <c>web::http::compression::decompress_provider</c> type instantiated by the factory's make_decompressor function.
/// The supplied string is copied, and thus need not remain valid once the call returns.</param>
/// <param name="weight">A numeric weight for the compression algorithm, times 1000, for use as a "quality value" when
/// requesting that the server send a compressed response. Valid values are between 0 and 1000, inclusive, where higher
/// values indicate more preferred algorithms, and 0 indicates that the algorithm is not allowed; values greater than
/// 1000 are treated as 1000.</param>
/// <param name="make_decompressor">A factory function to be used to instantiate a decompressor matching the factory's
/// reported algorithm.</param>
/// <returns>
/// A pointer to a generic provider factory implementation configured with the supplied parameters.
/// </returns>
/// <remarks>
/// This method may be used to conveniently instantiate a factory object for a caller-selected
/// <c>decompress_provider</c>. That provider may be of the caller's own design, or it may be one of the built-in
/// types. As such, this method may be helpful when a caller wishes to change the weights of built-in provider types,
/// to use custom providers without explicitly implementing a <c>decompress_factory</c>, or to build vectors containing
/// a mix of custom and built-in providers.
/// </remarks>
_ASYNCRTIMP std::shared_ptr<decompress_factory> make_decompress_factory(
const utility::string_t& algorithm,
uint16_t weight,
std::function<std::unique_ptr<decompress_provider>()> make_decompressor);
namespace details
{
/// <summary>
/// Header type enum for use with compressor and decompressor header parsing and building functions
/// </summary>
enum header_types
{
transfer_encoding,
content_encoding,
te,
accept_encoding
};
/// <summary>
/// Factory function to instantiate an appropriate compression provider, if any.
/// </summary>
/// <param name="encoding">A TE or Accept-Encoding header to interpret.</param>
/// <param name="type">Specifies the type of header whose contents are in the encoding parameter; valid values are
/// <c>header_type::te</c> and <c>header_type::accept_encoding</c>.</param>
/// <param name="preferred">A compressor object of the caller's preferred (possibly custom) type, which is used if
/// possible.</param>
/// <param name="factories">A collection of factory objects for use in construction of an appropriate compressor, if
/// any. If empty or not supplied, the set of supported built-in compressors is used.</param>
/// <returns>
/// A pointer to a compressor object that is acceptable per the supplied header, or to nullptr if no matching
/// algorithm is found.
/// </returns>
_ASYNCRTIMP std::unique_ptr<compress_provider> get_compressor_from_header(
const utility::string_t& encoding,
header_types type,
const std::vector<std::shared_ptr<compress_factory>>& factories = std::vector<std::shared_ptr<compress_factory>>());
/// <summary>
/// Factory function to instantiate an appropriate decompression provider, if any.
/// </summary>
/// <param name="encoding">A Transfer-Encoding or Content-Encoding header to interpret.</param>
/// <param name="type">Specifies the type of header whose contents are in the encoding parameter; valid values are
/// <c>header_type::transfer_encoding</c> and <c>header_type::content_encoding</c>.</param>
/// <param name="factories">A collection of factory objects for use in construction of an appropriate decompressor,
/// if any. If empty or not supplied, the set of supported built-in compressors is used.</param>
/// <returns>
/// A pointer to a decompressor object that is acceptable per the supplied header, or to nullptr if no matching
/// algorithm is found.
/// </returns>
_ASYNCRTIMP std::unique_ptr<decompress_provider> get_decompressor_from_header(
const utility::string_t& encoding,
header_types type,
const std::vector<std::shared_ptr<decompress_factory>>& factories =
std::vector<std::shared_ptr<decompress_factory>>());
/// <summary>
/// Helper function to compose a TE or Accept-Encoding header with supported, and possibly ranked, compression
/// algorithms.
/// </summary>
/// <param name="type">Specifies the type of header to be built; valid values are <c>header_type::te</c> and
/// <c>header_type::accept_encoding</c>.</param>
/// <param name="factories">A collection of factory objects for use in header construction. If empty or not
/// supplied, the set of supported built-in compressors is used.</param>
/// <returns>
/// A well-formed header, without the header name, specifying the acceptable ranked compression types.
/// </returns>
_ASYNCRTIMP utility::string_t build_supported_header(header_types type,
const std::vector<std::shared_ptr<decompress_factory>>& factories =
std::vector<std::shared_ptr<decompress_factory>>());
} // namespace details
} // namespace compression
} // namespace http
} // namespace web

View File

@ -0,0 +1,322 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include "cpprest/asyncrt_utils.h"
#include <map>
#include <memory>
#include <string>
#include <system_error>
#include <vector>
namespace web
{
namespace http
{
/// <summary>
/// Binds an individual reference to a string value.
/// </summary>
/// <typeparam name="key_type">The type of string value.</typeparam>
/// <typeparam name="_t">The type of the value to bind to.</typeparam>
/// <param name="text">The string value.</param>
/// <param name="ref">The value to bind to.</param>
/// <returns><c>true</c> if the binding succeeds, <c>false</c> otherwise.</returns>
template<typename key_type, typename _t>
CASABLANCA_DEPRECATED("This API is deprecated and will be removed in a future release, std::istringstream instead.")
bool bind(const key_type& text, _t& ref) // const
{
utility::istringstream_t iss(text);
iss >> ref;
if (iss.fail() || !iss.eof())
{
return false;
}
return true;
}
/// <summary>
/// Binds an individual reference to a string value.
/// This specialization is need because <c>istringstream::&gt;&gt;</c> delimits on whitespace.
/// </summary>
/// <typeparam name="key_type">The type of the string value.</typeparam>
/// <param name="text">The string value.</param>
/// <param name="ref">The value to bind to.</param>
/// <returns><c>true</c> if the binding succeeds, <c>false</c> otherwise.</returns>
template<typename key_type>
CASABLANCA_DEPRECATED("This API is deprecated and will be removed in a future release.")
bool bind(const key_type& text, utility::string_t& ref) // const
{
ref = text;
return true;
}
namespace details
{
template<typename key_type, typename _t>
bool bind_impl(const key_type& text, _t& ref)
{
utility::istringstream_t iss(text);
iss.imbue(std::locale::classic());
iss >> ref;
if (iss.fail() || !iss.eof())
{
return false;
}
return true;
}
template<typename key_type>
bool bind_impl(const key_type& text, utf16string& ref)
{
ref = utility::conversions::to_utf16string(text);
return true;
}
template<typename key_type>
bool bind_impl(const key_type& text, std::string& ref)
{
ref = utility::conversions::to_utf8string(text);
return true;
}
} // namespace details
/// <summary>
/// Represents HTTP headers, acts like a map.
/// </summary>
class http_headers
{
public:
/// Function object to perform case insensitive comparison of wstrings.
struct _case_insensitive_cmp
{
bool operator()(const utility::string_t& str1, const utility::string_t& str2) const
{
return utility::details::str_iless(str1, str2);
}
};
private:
typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp> inner_container;
public:
/// <summary>
/// STL-style typedefs
/// </summary>
typedef inner_container::key_type key_type;
typedef inner_container::key_compare key_compare;
typedef inner_container::allocator_type allocator_type;
typedef inner_container::size_type size_type;
typedef inner_container::difference_type difference_type;
typedef inner_container::pointer pointer;
typedef inner_container::const_pointer const_pointer;
typedef inner_container::reference reference;
typedef inner_container::const_reference const_reference;
typedef inner_container::iterator iterator;
typedef inner_container::const_iterator const_iterator;
typedef inner_container::reverse_iterator reverse_iterator;
typedef inner_container::const_reverse_iterator const_reverse_iterator;
/// <summary>
/// Constructs an empty set of HTTP headers.
/// </summary>
http_headers() {}
/// <summary>
/// Copy constructor.
/// </summary>
/// <param name="other">An <c>http_headers</c> object to copy from.</param>
http_headers(const http_headers& other) : m_headers(other.m_headers) {}
/// <summary>
/// Assignment operator.
/// </summary>
/// <param name="other">An <c>http_headers</c> object to copy from.</param>
http_headers& operator=(const http_headers& other)
{
if (this != &other)
{
m_headers = other.m_headers;
}
return *this;
}
/// <summary>
/// Move constructor.
/// </summary>
/// <param name="other">An <c>http_headers</c> object to move.</param>
http_headers(http_headers&& other) : m_headers(std::move(other.m_headers)) {}
/// <summary>
/// Move assignment operator.
/// </summary>
/// <param name="other">An <c>http_headers</c> object to move.</param>
http_headers& operator=(http_headers&& other)
{
if (this != &other)
{
m_headers = std::move(other.m_headers);
}
return *this;
}
/// <summary>
/// Adds a header field using the '&lt;&lt;' operator.
/// </summary>
/// <param name="name">The name of the header field.</param>
/// <param name="value">The value of the header field.</param>
/// <remarks>If the header field exists, the value will be combined as comma separated string.</remarks>
template<typename _t1>
void add(const key_type& name, const _t1& value)
{
auto printedValue = utility::conversions::details::print_string(value);
auto& mapVal = m_headers[name];
if (mapVal.empty())
{
mapVal = std::move(printedValue);
}
else
{
mapVal.append(_XPLATSTR(", ")).append(std::move(printedValue));
}
}
/// <summary>
/// Removes a header field.
/// </summary>
/// <param name="name">The name of the header field.</param>
void remove(const key_type& name) { m_headers.erase(name); }
/// <summary>
/// Removes all elements from the headers.
/// </summary>
void clear() { m_headers.clear(); }
/// <summary>
/// Checks if there is a header with the given key.
/// </summary>
/// <param name="name">The name of the header field.</param>
/// <returns><c>true</c> if there is a header with the given name, <c>false</c> otherwise.</returns>
bool has(const key_type& name) const { return m_headers.find(name) != m_headers.end(); }
/// <summary>
/// Returns the number of header fields.
/// </summary>
/// <returns>Number of header fields.</returns>
size_type size() const { return m_headers.size(); }
/// <summary>
/// Tests to see if there are any header fields.
/// </summary>
/// <returns><c>true</c> if there are no headers, <c>false</c> otherwise.</returns>
bool empty() const { return m_headers.empty(); }
/// <summary>
/// Returns a reference to header field with given name, if there is no header field one is inserted.
/// </summary>
utility::string_t& operator[](const key_type& name) { return m_headers[name]; }
/// <summary>
/// Checks if a header field exists with given name and returns an iterator if found. Otherwise
/// and iterator to end is returned.
/// </summary>
/// <param name="name">The name of the header field.</param>
/// <returns>An iterator to where the HTTP header is found.</returns>
iterator find(const key_type& name) { return m_headers.find(name); }
const_iterator find(const key_type& name) const { return m_headers.find(name); }
/// <summary>
/// Attempts to match a header field with the given name using the '>>' operator.
/// </summary>
/// <param name="name">The name of the header field.</param>
/// <param name="value">The value of the header field.</param>
/// <returns><c>true</c> if header field was found and successfully stored in value parameter.</returns>
template<typename _t1>
bool match(const key_type& name, _t1& value) const
{
auto iter = m_headers.find(name);
if (iter == m_headers.end())
{
return false;
}
return web::http::details::bind_impl(iter->second, value) || iter->second.empty();
}
/// <summary>
/// Returns an iterator referring to the first header field.
/// </summary>
/// <returns>An iterator to the beginning of the HTTP headers</returns>
iterator begin() { return m_headers.begin(); }
const_iterator begin() const { return m_headers.begin(); }
/// <summary>
/// Returns an iterator referring to the past-the-end header field.
/// </summary>
/// <returns>An iterator to the element past the end of the HTTP headers.</returns>
iterator end() { return m_headers.end(); }
const_iterator end() const { return m_headers.end(); }
/// <summary>
/// Gets the content length of the message.
/// </summary>
/// <returns>The length of the content.</returns>
_ASYNCRTIMP utility::size64_t content_length() const;
/// <summary>
/// Sets the content length of the message.
/// </summary>
/// <param name="length">The length of the content.</param>
_ASYNCRTIMP void set_content_length(utility::size64_t length);
/// <summary>
/// Gets the content type of the message.
/// </summary>
/// <returns>The content type of the body.</returns>
_ASYNCRTIMP utility::string_t content_type() const;
/// <summary>
/// Sets the content type of the message.
/// </summary>
/// <param name="type">The content type of the body.</param>
_ASYNCRTIMP void set_content_type(utility::string_t type);
/// <summary>
/// Gets the cache control header of the message.
/// </summary>
/// <returns>The cache control header value.</returns>
_ASYNCRTIMP utility::string_t cache_control() const;
/// <summary>
/// Sets the cache control header of the message.
/// </summary>
/// <param name="control">The cache control header value.</param>
_ASYNCRTIMP void set_cache_control(utility::string_t control);
/// <summary>
/// Gets the date header of the message.
/// </summary>
/// <returns>The date header value.</returns>
_ASYNCRTIMP utility::string_t date() const;
/// <summary>
/// Sets the date header of the message.
/// </summary>
/// <param name="date">The date header value.</param>
_ASYNCRTIMP void set_date(const utility::datetime& date);
private:
// Headers are stored in a map with case insensitive key.
inner_container m_headers;
};
} // namespace http
} // namespace web

View File

@ -0,0 +1,342 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* HTTP Library: HTTP listener (server-side) APIs
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#ifndef CASA_HTTP_LISTENER_H
#define CASA_HTTP_LISTENER_H
#include "cpprest/http_msg.h"
#include <functional>
#include <limits>
#if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
#include <boost/asio/ssl.hpp>
#endif
#if !defined(_WIN32) || (_WIN32_WINNT >= _WIN32_WINNT_VISTA && !defined(__cplusplus_winrt)) || \
defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
namespace web
{
namespace http
{
/// HTTP listener is currently in beta.
namespace experimental
{
/// HTTP server side library.
namespace listener
{
/// <summary>
/// Configuration class used to set various options when constructing and http_listener instance.
/// </summary>
class http_listener_config
{
public:
/// <summary>
/// Create an http_listener configuration with default options.
/// </summary>
http_listener_config() : m_timeout(utility::seconds(120)), m_backlog(0) {}
/// <summary>
/// Copy constructor.
/// </summary>
/// <param name="other">http_listener_config to copy.</param>
http_listener_config(const http_listener_config& other)
: m_timeout(other.m_timeout)
, m_backlog(other.m_backlog)
#if !defined(_WIN32) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
, m_ssl_context_callback(other.m_ssl_context_callback)
#endif
{
}
/// <summary>
/// Move constructor.
/// <summary>
/// <param name="other">http_listener_config to move from.</param>
http_listener_config(http_listener_config&& other)
: m_timeout(std::move(other.m_timeout))
, m_backlog(std::move(other.m_backlog))
#if !defined(_WIN32) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
, m_ssl_context_callback(std::move(other.m_ssl_context_callback))
#endif
{
}
/// <summary>
/// Assignment operator.
/// </summary>
/// <returns>http_listener_config instance.</returns>
http_listener_config& operator=(const http_listener_config& rhs)
{
if (this != &rhs)
{
m_timeout = rhs.m_timeout;
m_backlog = rhs.m_backlog;
#if !defined(_WIN32) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
m_ssl_context_callback = rhs.m_ssl_context_callback;
#endif
}
return *this;
}
/// <summary>
/// Assignment operator.
/// </summary>
/// <returns>http_listener_config instance.</returns>
http_listener_config& operator=(http_listener_config&& rhs)
{
if (this != &rhs)
{
m_timeout = std::move(rhs.m_timeout);
m_backlog = std::move(rhs.m_backlog);
#if !defined(_WIN32) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
m_ssl_context_callback = std::move(rhs.m_ssl_context_callback);
#endif
}
return *this;
}
/// <summary>
/// Get the timeout
/// </summary>
/// <returns>The timeout (in seconds).</returns>
utility::seconds timeout() const { return m_timeout; }
/// <summary>
/// Set the timeout
/// </summary>
/// <param name="timeout">The timeout (in seconds) used for each send and receive operation on the client.</param>
void set_timeout(utility::seconds timeout) { m_timeout = std::move(timeout); }
/// <summary>
/// Get the listen backlog
/// </summary>
/// <returns>The maximum length of the queue of pending connections, or zero for the implementation
/// default.</returns> <remarks>The implementation may not honour this value.</remarks>
int backlog() const { return m_backlog; }
/// <summary>
/// Set the listen backlog
/// </summary>
/// <param name="backlog">The maximum length of the queue of pending connections, or zero for the implementation
/// default.</param> <remarks>The implementation may not honour this value.</remarks>
void set_backlog(int backlog) { m_backlog = backlog; }
#if !defined(_WIN32) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
/// <summary>
/// Get the callback of ssl context
/// </summary>
/// <returns>The function defined by the user of http_listener_config to configure a ssl context.</returns>
const std::function<void(boost::asio::ssl::context&)>& get_ssl_context_callback() const
{
return m_ssl_context_callback;
}
/// <summary>
/// Set the callback of ssl context
/// </summary>
/// <param name="ssl_context_callback">The function to configure a ssl context which will setup https
/// connections.</param>
void set_ssl_context_callback(const std::function<void(boost::asio::ssl::context&)>& ssl_context_callback)
{
m_ssl_context_callback = ssl_context_callback;
}
#endif
private:
utility::seconds m_timeout;
int m_backlog;
#if !defined(_WIN32) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
std::function<void(boost::asio::ssl::context&)> m_ssl_context_callback;
#endif
};
namespace details
{
/// <summary>
/// Internal class for pointer to implementation design pattern.
/// </summary>
class http_listener_impl
{
public:
http_listener_impl() : m_closed(true), m_close_task(pplx::task_from_result()) {}
_ASYNCRTIMP http_listener_impl(http::uri address);
_ASYNCRTIMP http_listener_impl(http::uri address, http_listener_config config);
_ASYNCRTIMP pplx::task<void> open();
_ASYNCRTIMP pplx::task<void> close();
/// <summary>
/// Handler for all requests. The HTTP host uses this to dispatch a message to the pipeline.
/// </summary>
/// <remarks>Only HTTP server implementations should call this API.</remarks>
_ASYNCRTIMP void handle_request(http::http_request msg);
const http::uri& uri() const { return m_uri; }
const http_listener_config& configuration() const { return m_config; }
// Handlers
std::function<void(http::http_request)> m_all_requests;
std::map<http::method, std::function<void(http::http_request)>> m_supported_methods;
private:
// Default implementation for TRACE and OPTIONS.
void handle_trace(http::http_request message);
void handle_options(http::http_request message);
// Gets a comma separated string containing the methods supported by this listener.
utility::string_t get_supported_methods() const;
http::uri m_uri;
http_listener_config m_config;
// Used to record that the listener is closed.
bool m_closed;
pplx::task<void> m_close_task;
};
} // namespace details
/// <summary>
/// A class for listening and processing HTTP requests at a specific URI.
/// </summary>
class http_listener
{
public:
/// <summary>
/// Create a listener from a URI.
/// </summary>
/// <remarks>The listener will not have been opened when returned.</remarks>
/// <param name="address">URI at which the listener should accept requests.</param>
http_listener(http::uri address)
: m_impl(utility::details::make_unique<details::http_listener_impl>(std::move(address)))
{
}
/// <summary>
/// Create a listener with specified URI and configuration.
/// </summary>
/// <param name="address">URI at which the listener should accept requests.</param>
/// <param name="config">Configuration to create listener with.</param>
http_listener(http::uri address, http_listener_config config)
: m_impl(utility::details::make_unique<details::http_listener_impl>(std::move(address), std::move(config)))
{
}
/// <summary>
/// Default constructor.
/// </summary>
/// <remarks>The resulting listener cannot be used for anything, but is useful to initialize a variable
/// that will later be overwritten with a real listener instance.</remarks>
http_listener() : m_impl(utility::details::make_unique<details::http_listener_impl>()) {}
/// <summary>
/// Destructor frees any held resources.
/// </summary>
/// <remarks>Call close() before allowing a listener to be destroyed.</remarks>
~http_listener()
{
if (m_impl)
{
// As a safe guard close the listener if not already done.
// Users are required to call close, but this is just a safeguard.
try
{
m_impl->close().wait();
}
catch (...)
{
}
}
}
/// <summary>
/// Asynchronously open the listener, i.e. start accepting requests.
/// </summary>
/// <returns>A task that will be completed once this listener is actually opened, accepting requests.</returns>
pplx::task<void> open() { return m_impl->open(); }
/// <summary>
/// Asynchronously stop accepting requests and close all connections.
/// </summary>
/// <returns>A task that will be completed once this listener is actually closed, no longer accepting
/// requests.</returns> <remarks> This function will stop accepting requests and wait for all outstanding handler
/// calls to finish before completing the task. Waiting on the task returned from close() within a handler and
/// blocking waiting for its result will result in a deadlock.
///
/// Call close() before allowing a listener to be destroyed.
/// </remarks>
pplx::task<void> close() { return m_impl->close(); }
/// <summary>
/// Add a general handler to support all requests.
/// </summary>
/// <param name="handler">Function object to be called for all requests.</param>
void support(const std::function<void(http_request)>& handler) { m_impl->m_all_requests = handler; }
/// <summary>
/// Add support for a specific HTTP method.
/// </summary>
/// <param name="method">An HTTP method.</param>
/// <param name="handler">Function object to be called for all requests for the given HTTP method.</param>
void support(const http::method& method, const std::function<void(http_request)>& handler)
{
m_impl->m_supported_methods[method] = handler;
}
/// <summary>
/// Get the URI of the listener.
/// </summary>
/// <returns>The URI this listener is for.</returns>
const http::uri& uri() const { return m_impl->uri(); }
/// <summary>
/// Get the configuration of this listener.
/// </summary>
/// <returns>Configuration this listener was constructed with.</returns>
const http_listener_config& configuration() const { return m_impl->configuration(); }
/// <summary>
/// Move constructor.
/// </summary>
/// <param name="other">http_listener instance to construct this one from.</param>
http_listener(http_listener&& other) : m_impl(std::move(other.m_impl)) {}
/// <summary>
/// Move assignment operator.
/// </summary>
/// <param name="other">http_listener to replace this one with.</param>
http_listener& operator=(http_listener&& other)
{
if (this != &other)
{
m_impl = std::move(other.m_impl);
}
return *this;
}
private:
// No copying of listeners.
http_listener(const http_listener& other);
http_listener& operator=(const http_listener& other);
std::unique_ptr<details::http_listener_impl> m_impl;
};
} // namespace listener
} // namespace experimental
} // namespace http
} // namespace web
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,554 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Adapter classes for async and STD stream buffers, used to connect std-based and async-based APIs.
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include "cpprest/astreambuf.h"
#include "cpprest/streams.h"
#include "pplx/pplxtasks.h"
#if defined(_WIN32)
#pragma warning(push)
#pragma warning(disable : 4250)
#endif
namespace Concurrency
{
namespace streams
{
template<typename CharType>
class stdio_ostream;
template<typename CharType>
class stdio_istream;
namespace details
{
/// <summary>
/// The basic_stdio_buffer class serves to support interoperability with STL stream buffers.
/// Sitting atop a std::streambuf, which does all the I/O, instances of this class may read
/// and write data to standard iostreams. The class itself should not be used in application
/// code, it is used by the stream definitions farther down in the header file.
/// </summary>
template<typename _CharType>
class basic_stdio_buffer : public streambuf_state_manager<_CharType>
{
typedef concurrency::streams::char_traits<_CharType> traits;
typedef typename traits::int_type int_type;
typedef typename traits::pos_type pos_type;
typedef typename traits::off_type off_type;
/// <summary>
/// Private constructor
/// </summary>
basic_stdio_buffer(_In_ std::basic_streambuf<_CharType>* streambuf, std::ios_base::openmode mode)
: streambuf_state_manager<_CharType>(mode), m_buffer(streambuf)
{
}
public:
/// <summary>
/// Destructor
/// </summary>
virtual ~basic_stdio_buffer()
{
this->_close_read();
this->_close_write();
}
private:
//
// The functions overridden below here are documented elsewhere.
// See astreambuf.h for further information.
//
virtual bool can_seek() const { return this->is_open(); }
virtual bool has_size() const { return false; }
virtual size_t in_avail() const { return (size_t)m_buffer->in_avail(); }
virtual size_t buffer_size(std::ios_base::openmode) const { return 0; }
virtual void set_buffer_size(size_t, std::ios_base::openmode) { return; }
virtual pplx::task<bool> _sync() { return pplx::task_from_result(m_buffer->pubsync() == 0); }
virtual pplx::task<int_type> _putc(_CharType ch) { return pplx::task_from_result(m_buffer->sputc(ch)); }
virtual pplx::task<size_t> _putn(const _CharType* ptr, size_t size)
{
return pplx::task_from_result((size_t)m_buffer->sputn(ptr, size));
}
size_t _sgetn(_Out_writes_(size) _CharType* ptr, _In_ size_t size) const { return m_buffer->sgetn(ptr, size); }
virtual size_t _scopy(_Out_writes_(size) _CharType*, _In_ size_t size)
{
(void)(size);
return (size_t)-1;
}
virtual pplx::task<size_t> _getn(_Out_writes_(size) _CharType* ptr, _In_ size_t size)
{
return pplx::task_from_result((size_t)m_buffer->sgetn(ptr, size));
}
virtual int_type _sbumpc() { return m_buffer->sbumpc(); }
virtual int_type _sgetc() { return m_buffer->sgetc(); }
virtual pplx::task<int_type> _bumpc() { return pplx::task_from_result<int_type>(m_buffer->sbumpc()); }
virtual pplx::task<int_type> _getc() { return pplx::task_from_result<int_type>(m_buffer->sgetc()); }
virtual pplx::task<int_type> _nextc() { return pplx::task_from_result<int_type>(m_buffer->snextc()); }
virtual pplx::task<int_type> _ungetc() { return pplx::task_from_result<int_type>(m_buffer->sungetc()); }
virtual pos_type getpos(std::ios_base::openmode mode) const
{
return m_buffer->pubseekoff(0, std::ios_base::cur, mode);
}
virtual pos_type seekpos(pos_type pos, std::ios_base::openmode mode) { return m_buffer->pubseekpos(pos, mode); }
virtual pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode mode)
{
return m_buffer->pubseekoff(off, dir, mode);
}
virtual _CharType* _alloc(size_t) { return nullptr; }
virtual void _commit(size_t) {}
virtual bool acquire(_CharType*&, size_t&) { return false; }
virtual void release(_CharType*, size_t) {}
template<typename CharType>
friend class concurrency::streams::stdio_ostream;
template<typename CharType>
friend class concurrency::streams::stdio_istream;
std::basic_streambuf<_CharType>* m_buffer;
};
} // namespace details
/// <summary>
/// stdio_ostream represents an async ostream derived from a standard synchronous stream, as
/// defined by the "std" namespace. It is constructed from a reference to a standard stream, which
/// must be valid for the lifetime of the asynchronous stream.
/// </summary>
/// <typeparam name="CharType">
/// The data type of the basic element of the <c>stdio_ostream</c>.
/// </typeparam>
/// <remarks>
/// Since std streams are not reference-counted, great care must be taken by an application to make
/// sure that the std stream does not get destroyed until all uses of the asynchronous stream are
/// done and have been serviced.
/// </remarks>
template<typename CharType>
class stdio_ostream : public basic_ostream<CharType>
{
public:
/// <summary>
/// Constructor
/// </summary>
/// <typeparam name="AlterCharType">
/// The data type of the basic element of the source output stream.
/// </typeparam>
/// <param name="stream">The synchronous stream that this is using for its I/O</param>
template<typename AlterCharType>
stdio_ostream(std::basic_ostream<AlterCharType>& stream)
: basic_ostream<CharType>(
streams::streambuf<AlterCharType>(std::shared_ptr<details::basic_stdio_buffer<AlterCharType>>(
new details::basic_stdio_buffer<AlterCharType>(stream.rdbuf(), std::ios_base::out))))
{
}
/// <summary>
/// Copy constructor
/// </summary>
/// <param name="other">The source object</param>
stdio_ostream(const stdio_ostream& other) : basic_ostream<CharType>(other) {}
/// <summary>
/// Assignment operator
/// </summary>
/// <param name="other">The source object</param>
/// <returns>A reference to the output stream object that contains the result of the assignment.</returns>
stdio_ostream& operator=(const stdio_ostream& other)
{
basic_ostream<CharType>::operator=(other);
return *this;
}
};
/// <summary>
/// stdio_istream represents an async istream derived from a standard synchronous stream, as
/// defined by the "std" namespace. It is constructed from a reference to a standard stream, which
/// must be valid for the lifetime of the asynchronous stream.
/// </summary>
/// <typeparam name="CharType">
/// The data type of the basic element of the <c>stdio_istream</c>.
/// </typeparam>
/// <remarks>
/// Since std streams are not reference-counted, great care must be taken by an application to make
/// sure that the std stream does not get destroyed until all uses of the asynchronous stream are
/// done and have been serviced.
/// </remarks>
template<typename CharType>
class stdio_istream : public basic_istream<CharType>
{
public:
/// <summary>
/// Constructor
/// </summary>
/// <typeparam name="AlterCharType">
/// The data type of the basic element of the source <c>istream</c>
/// </typeparam>
/// <param name="stream">The synchronous stream that this is using for its I/O</param>
template<typename AlterCharType>
stdio_istream(std::basic_istream<AlterCharType>& stream)
: basic_istream<CharType>(
streams::streambuf<AlterCharType>(std::shared_ptr<details::basic_stdio_buffer<AlterCharType>>(
new details::basic_stdio_buffer<AlterCharType>(stream.rdbuf(), std::ios_base::in))))
{
}
/// <summary>
/// Copy constructor
/// </summary>
/// <param name="other">The source object</param>
stdio_istream(const stdio_istream& other) : basic_istream<CharType>(other) {}
/// <summary>
/// Assignment operator
/// </summary>
/// <param name="other">The source object</param>
/// <returns>A reference to the input stream object that contains the result of the assignment.</returns>
stdio_istream& operator=(const stdio_istream& other)
{
basic_istream<CharType>::operator=(other);
return *this;
}
};
namespace details
{
/// <summary>
/// IO streams stream buffer implementation used to interface with an async streambuffer underneath.
/// Used for implementing the standard synchronous streams that provide interop between std:: and concurrency::streams::
/// </summary>
template<typename CharType>
class basic_async_streambuf : public std::basic_streambuf<CharType>
{
public:
typedef concurrency::streams::char_traits<CharType> traits;
typedef typename traits::int_type int_type;
typedef typename traits::pos_type pos_type;
typedef typename traits::off_type off_type;
basic_async_streambuf(const streams::streambuf<CharType>& async_buf) : m_buffer(async_buf) {}
protected:
//
// The following are the functions in std::basic_streambuf that we need to override.
//
/// <summary>
/// Writes one byte to the stream buffer.
/// </summary>
int_type overflow(int_type ch)
{
try
{
return m_buffer.putc(CharType(ch)).get();
}
catch (...)
{
return traits::eof();
}
}
/// <summary>
/// Gets one byte from the stream buffer without moving the read position.
/// </summary>
int_type underflow()
{
try
{
return m_buffer.getc().get();
}
catch (...)
{
return traits::eof();
}
}
/// <summary>
/// Gets one byte from the stream buffer and move the read position one character.
/// </summary>
int_type uflow()
{
try
{
return m_buffer.bumpc().get();
}
catch (...)
{
return traits::eof();
}
}
/// <summary>
/// Gets a number of characters from the buffer and place it into the provided memory block.
/// </summary>
std::streamsize xsgetn(_Out_writes_(count) CharType* ptr, _In_ std::streamsize count)
{
size_t cnt = size_t(count);
size_t read_so_far = 0;
try
{
while (read_so_far < cnt)
{
size_t rd = m_buffer.getn(ptr + read_so_far, cnt - read_so_far).get();
read_so_far += rd;
if (rd == 0) break;
}
return read_so_far;
}
catch (...)
{
return 0;
}
}
/// <summary>
/// Writes a given number of characters from the provided block into the stream buffer.
/// </summary>
std::streamsize xsputn(const CharType* ptr, std::streamsize count)
{
try
{
return m_buffer.putn_nocopy(ptr, static_cast<size_t>(count)).get();
}
catch (...)
{
return 0;
}
}
/// <summary>
/// Synchronizes with the underlying medium.
/// </summary>
int sync() // must be int as per std::basic_streambuf
{
try
{
m_buffer.sync().wait();
}
catch (...)
{
}
return 0;
}
/// <summary>
/// Seeks to the given offset relative to the beginning, end, or current position.
/// </summary>
pos_type seekoff(off_type offset,
std::ios_base::seekdir dir,
std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
{
try
{
if (dir == std::ios_base::cur && offset == 0) // Special case for getting the current position.
return m_buffer.getpos(mode);
return m_buffer.seekoff(offset, dir, mode);
}
catch (...)
{
return (pos_type(-1));
}
}
/// <summary>
/// Seeks to the given offset relative to the beginning of the stream.
/// </summary>
pos_type seekpos(pos_type pos, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
{
try
{
return m_buffer.seekpos(pos, mode);
}
catch (...)
{
return (pos_type(-1));
}
}
private:
concurrency::streams::streambuf<CharType> m_buffer;
};
} // namespace details
/// <summary>
/// A concrete STL ostream which relies on an asynchronous stream for its I/O.
/// </summary>
/// <typeparam name="CharType">
/// The data type of the basic element of the stream.
/// </typeparam>
template<typename CharType>
class async_ostream : public std::basic_ostream<CharType>
{
public:
/// <summary>
/// Constructor
/// </summary>
/// <typeparam name="AlterCharType">
/// The data type of the basic element of the source ostream.
/// </typeparam>
/// <param name="astream">The asynchronous stream whose stream buffer should be used for I/O</param>
template<typename AlterCharType>
async_ostream(const streams::basic_ostream<AlterCharType>& astream)
: std::basic_ostream<CharType>(&m_strbuf), m_strbuf(astream.streambuf())
{
}
/// <summary>
/// Constructor
/// </summary>
/// <typeparam name="AlterCharType">
/// The data type of the basic element of the source <c>streambuf</c>.
/// </typeparam>
/// <param name="strbuf">The asynchronous stream buffer to use for I/O</param>
template<typename AlterCharType>
async_ostream(const streams::streambuf<AlterCharType>& strbuf)
: std::basic_ostream<CharType>(&m_strbuf), m_strbuf(strbuf)
{
}
private:
details::basic_async_streambuf<CharType> m_strbuf;
};
/// <summary>
/// A concrete STL istream which relies on an asynchronous stream for its I/O.
/// </summary>
/// <typeparam name="CharType">
/// The data type of the basic element of the stream.
/// </typeparam>
template<typename CharType>
class async_istream : public std::basic_istream<CharType>
{
public:
/// <summary>
/// Constructor
/// </summary>
/// <typeparam name="AlterCharType">
/// The data type of the basic element of the source istream.
/// </typeparam>
/// <param name="astream">The asynchronous stream whose stream buffer should be used for I/O</param>
template<typename AlterCharType>
async_istream(const streams::basic_istream<AlterCharType>& astream)
: std::basic_istream<CharType>(&m_strbuf), m_strbuf(astream.streambuf())
{
}
/// <summary>
/// Constructor
/// </summary>
/// <typeparam name="AlterCharType">
/// The data type of the basic element of the source <c>streambuf</c>.
/// </typeparam>
/// <param name="strbuf">The asynchronous stream buffer to use for I/O</param>
template<typename AlterCharType>
async_istream(const streams::streambuf<AlterCharType>& strbuf)
: std::basic_istream<CharType>(&m_strbuf), m_strbuf(strbuf)
{
}
private:
details::basic_async_streambuf<CharType> m_strbuf;
};
/// <summary>
/// A concrete STL istream which relies on an asynchronous stream buffer for its I/O.
/// </summary>
/// <typeparam name="CharType">
/// The data type of the basic element of the stream.
/// </typeparam>
template<typename CharType>
class async_iostream : public std::basic_iostream<CharType>
{
public:
/// <summary>
/// Constructor
/// </summary>
/// <param name="strbuf">The asynchronous stream buffer to use for I/O</param>
async_iostream(const streams::streambuf<CharType>& strbuf)
: std::basic_iostream<CharType>(&m_strbuf), m_strbuf(strbuf)
{
}
private:
details::basic_async_streambuf<CharType> m_strbuf;
};
#if defined(__cplusplus_winrt)
/// <summary>
/// Static class containing factory functions for WinRT streams implemented on top of Casablanca async streams.
/// </summary>
/// <remarks>WinRT streams are defined in terms of single-byte characters only.</remarks>
class winrt_stream
{
public:
/// <summary>
/// Creates a WinRT <c>IInputStream</c> reference from an asynchronous stream buffer.
/// </summary>
/// <param name="buffer">A stream buffer based on a single-byte character.</param>
/// <returns>A reference to a WinRT <c>IInputStream</c>.</returns>
/// <remarks>
/// The stream buffer passed in must allow reading.
/// The stream buffer is shared with the caller, allowing data to be passed between the two contexts. For
/// example, using a <c>producer_consumer_buffer</c>, a Casablanca-based caller can pass data to a WinRT component.
/// </remarks>
_ASYNCRTIMP static Windows::Storage::Streams::IInputStream ^
__cdecl create_input_stream(const concurrency::streams::streambuf<uint8_t>& buffer);
/// <summary>
/// Creates a WinRT <c>IOutputStream</c> reference from an asynchronous stream buffer.
/// </summary>
/// <param name="buffer">A stream buffer based on a single-byte character.</param>
/// <returns>A reference to a WinRT <c>IOutputStream</c>.</returns>
/// <remarks>
/// The stream buffer passed in must allow writing.
/// The stream buffer is shared with the caller, allowing data to be passed between the two contexts. For
/// example, using a <c>producer_consumer_buffer</c>, a Casablanca-based caller can retrieve data from a WinRT
/// component.
/// </remarks>
_ASYNCRTIMP static Windows::Storage::Streams::IOutputStream ^
__cdecl create_output_stream(const concurrency::streams::streambuf<uint8_t>& buffer);
/// <summary>
/// Creates a WinRT <c>IRandomAccessStream reference from an asynchronous input stream.
/// </summary>
/// <param name="buffer">A stream based on a single-byte character.</param>
/// <returns>A reference to a WinRT <c>IRandomAccessStream</c>.</returns>
/// <remarks>
/// The stream buffer is shared with the caller, allowing data to be passed between the two contexts. For
/// example, using a <c>producer_consumer_buffer</c>, a Casablanca-based caller can pass data to and retrieve data
/// from a WinRT component.
/// </remarks>
_ASYNCRTIMP static Windows::Storage::Streams::IRandomAccessStream ^
__cdecl create_random_access_stream(const concurrency::streams::streambuf<uint8_t>& buffer);
};
#endif
} // namespace streams
} // namespace Concurrency
#if defined(_WIN32)
#pragma warning(pop)
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,576 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* HTTP Library: Oauth 1.0
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#ifndef CASA_OAUTH1_H
#define CASA_OAUTH1_H
#include "cpprest/details/web_utilities.h"
#include "cpprest/http_msg.h"
namespace web
{
namespace http
{
namespace client
{
// Forward declaration to avoid circular include dependency.
class http_client_config;
} // namespace client
/// oAuth 1.0 library.
namespace oauth1
{
namespace details
{
class oauth1_handler;
// State currently used by oauth1_config to authenticate request.
// The state varies for every request (due to timestamp and nonce).
// The state also contains extra transmitted protocol parameters during
// authorization flow (i.e. 'oauth_callback' or 'oauth_verifier').
class oauth1_state
{
public:
oauth1_state(utility::string_t timestamp,
utility::string_t nonce,
utility::string_t extra_key = utility::string_t(),
utility::string_t extra_value = utility::string_t())
: m_timestamp(std::move(timestamp))
, m_nonce(std::move(nonce))
, m_extra_key(std::move(extra_key))
, m_extra_value(std::move(extra_value))
{
}
const utility::string_t& timestamp() const { return m_timestamp; }
void set_timestamp(utility::string_t timestamp) { m_timestamp = std::move(timestamp); }
const utility::string_t& nonce() const { return m_nonce; }
void set_nonce(utility::string_t nonce) { m_nonce = std::move(nonce); }
const utility::string_t& extra_key() const { return m_extra_key; }
void set_extra_key(utility::string_t key) { m_extra_key = std::move(key); }
const utility::string_t& extra_value() const { return m_extra_value; }
void set_extra_value(utility::string_t value) { m_extra_value = std::move(value); }
private:
utility::string_t m_timestamp;
utility::string_t m_nonce;
utility::string_t m_extra_key;
utility::string_t m_extra_value;
};
// Constant strings for OAuth 1.0.
typedef utility::string_t oauth1_string;
class oauth1_strings
{
public:
#define _OAUTH1_STRINGS
#define DAT(a_, b_) _ASYNCRTIMP static const oauth1_string a_;
#include "cpprest/details/http_constants.dat"
#undef _OAUTH1_STRINGS
#undef DAT
};
} // namespace details
/// oAuth functionality is currently in beta.
namespace experimental
{
/// <summary>
/// Constant strings for OAuth 1.0 signature methods.
/// </summary>
typedef utility::string_t oauth1_method;
class oauth1_methods
{
public:
#define _OAUTH1_METHODS
#define DAT(a, b) _ASYNCRTIMP static const oauth1_method a;
#include "cpprest/details/http_constants.dat"
#undef _OAUTH1_METHODS
#undef DAT
};
/// <summary>
/// Exception type for OAuth 1.0 errors.
/// </summary>
class oauth1_exception : public std::exception
{
public:
oauth1_exception(utility::string_t msg) : m_msg(utility::conversions::to_utf8string(std::move(msg))) {}
~oauth1_exception() CPPREST_NOEXCEPT {}
const char* what() const CPPREST_NOEXCEPT { return m_msg.c_str(); }
private:
std::string m_msg;
};
/// <summary>
/// OAuth 1.0 token and associated information.
/// </summary>
class oauth1_token
{
public:
/// <summary>
/// Constructs an initially empty invalid access token.
/// </summary>
oauth1_token() {}
/// <summary>
/// Constructs a OAuth1 token from a given access token and secret.
/// </summary>
/// <param name="access_token">Access token string.</param>
/// <param name="secret">Token secret string.</param>
oauth1_token(utility::string_t access_token, utility::string_t secret)
: m_token(std::move(access_token)), m_secret(std::move(secret))
{
}
/// <summary>
/// Get access token validity state.
/// If true, token is a valid access token.
/// </summary>
/// <returns>Access token validity state of the token.</returns>
bool is_valid_access_token() const { return !(access_token().empty() || secret().empty()); }
/// <summary>
/// Get access token.
/// </summary>
/// <returns>The access token string.</returns>
const utility::string_t& access_token() const { return m_token; }
/// <summary>
/// Set access token.
/// </summary>
/// <param name="access_token">Access token string to set.</param>
void set_access_token(utility::string_t&& access_token) { m_token = std::move(access_token); }
/// <summary>
/// Set access token.
/// </summary>
/// <param name="access_token">Access token string to set.</param>
void set_access_token(const utility::string_t& access_token) { m_token = access_token; }
/// <summary>
/// Get token secret.
/// </summary>
/// <returns>Token secret string.</returns>
const utility::string_t& secret() const { return m_secret; }
/// <summary>
/// Set token secret.
/// </summary>
/// <param name="secret">Token secret string to set.</param>
void set_secret(utility::string_t&& secret) { m_secret = std::move(secret); }
/// <summary>
/// Set token secret.
/// </summary>
/// <param name="secret">Token secret string to set.</param>
void set_secret(const utility::string_t& secret) { m_secret = secret; }
/// <summary>
/// Retrieves any additional parameters.
/// </summary>
/// <returns>A map containing the additional parameters.</returns>
const std::map<utility::string_t, utility::string_t>& additional_parameters() const
{
return m_additional_parameters;
}
/// <summary>
/// Sets a specific parameter additional parameter.
/// </summary>
/// <param name="paramName">Parameter name.</param>
/// <param name="paramValue">Parameter value.</param>
void set_additional_parameter(utility::string_t&& paramName, utility::string_t&& paramValue)
{
m_additional_parameters[std::move(paramName)] = std::move(paramValue);
}
/// <summary>
/// Sets a specific parameter additional parameter.
/// </summary>
/// <param name="paramName">Parameter name.</param>
/// <param name="paramValue">Parameter value.</param>
void set_additional_parameter(const utility::string_t& paramName, const utility::string_t& paramValue)
{
m_additional_parameters[paramName] = paramValue;
}
/// <summary>
/// Clears all additional parameters.
/// </summary>
void clear_additional_parameters() { m_additional_parameters.clear(); }
private:
friend class oauth1_config;
utility::string_t m_token;
utility::string_t m_secret;
std::map<utility::string_t, utility::string_t> m_additional_parameters;
};
/// <summary>
/// OAuth 1.0 configuration class.
/// </summary>
class oauth1_config
{
public:
oauth1_config(utility::string_t consumer_key,
utility::string_t consumer_secret,
utility::string_t temp_endpoint,
utility::string_t auth_endpoint,
utility::string_t token_endpoint,
utility::string_t callback_uri,
oauth1_method method,
utility::string_t realm = utility::string_t())
: m_consumer_key(std::move(consumer_key))
, m_consumer_secret(std::move(consumer_secret))
, m_temp_endpoint(std::move(temp_endpoint))
, m_auth_endpoint(std::move(auth_endpoint))
, m_token_endpoint(std::move(token_endpoint))
, m_callback_uri(std::move(callback_uri))
, m_realm(std::move(realm))
, m_method(std::move(method))
, m_is_authorization_completed(false)
{
}
/// <summary>
/// Builds an authorization URI to be loaded in a web browser/view.
/// The URI is built with auth_endpoint() as basis.
/// The method creates a task for HTTP request to first obtain a
/// temporary token. The authorization URI build based on this token.
/// </summary>
/// <returns>Authorization URI to be loaded in a web browser/view.</returns>
_ASYNCRTIMP pplx::task<utility::string_t> build_authorization_uri();
/// <summary>
/// Fetch an access token based on redirected URI.
/// The URI is expected to contain 'oauth_verifier'
/// parameter, which is then used to fetch an access token using the
/// token_from_verifier() method.
/// See: http://tools.ietf.org/html/rfc5849#section-2.2
/// The received 'oauth_token' is parsed and verified to match the current token().
/// When access token is successfully obtained, set_token() is called, and config is
/// ready for use by oauth1_handler.
/// </summary>
/// <param name="redirected_uri">The URI where web browser/view was redirected after resource owner's
/// authorization.</param> <returns>Task that fetches the access token based on redirected URI.</returns>
_ASYNCRTIMP pplx::task<void> token_from_redirected_uri(const web::http::uri& redirected_uri);
/// <summary>
/// Creates a task with HTTP request to fetch an access token from the token endpoint.
/// The request exchanges a verifier code to an access token.
/// If successful, the resulting token is set as active via set_token().
/// See: http://tools.ietf.org/html/rfc5849#section-2.3
/// </summary>
/// <param name="verifier">Verifier received via redirect upon successful authorization.</param>
/// <returns>Task that fetches the access token based on the verifier.</returns>
pplx::task<void> token_from_verifier(utility::string_t verifier)
{
return _request_token(_generate_auth_state(details::oauth1_strings::verifier, std::move(verifier)), false);
}
/// <summary>
/// Creates a task with HTTP request to fetch an access token from the token endpoint.
/// If successful, the resulting token is set as active via set_token().
/// </summary>
/// <returns>Task that fetches the access token based on the verifier.</returns>
pplx::task<void> refresh_token(const utility::string_t& key)
{
return _request_token(_generate_auth_state(key, m_token.additional_parameters().at(key)), false);
}
/// <summary>
/// Get consumer key used in authorization and authentication.
/// </summary>
/// <returns>Consumer key string.</returns>
const utility::string_t& consumer_key() const { return m_consumer_key; }
/// <summary>
/// Set consumer key used in authorization and authentication.
/// </summary>
/// <param name="key">Consumer key string to set.</param>
void set_consumer_key(utility::string_t key) { m_consumer_key = std::move(key); }
/// <summary>
/// Get consumer secret used in authorization and authentication.
/// </summary>
/// <returns>Consumer secret string.</returns>
const utility::string_t& consumer_secret() const { return m_consumer_secret; }
/// <summary>
/// Set consumer secret used in authorization and authentication.
/// </summary>
/// <param name="secret">Consumer secret string to set.</param>
void set_consumer_secret(utility::string_t secret) { m_consumer_secret = std::move(secret); }
/// <summary>
/// Get temporary token endpoint URI string.
/// </summary>
/// <returns>Temporary token endpoint URI string.</returns>
const utility::string_t& temp_endpoint() const { return m_temp_endpoint; }
/// <summary>
/// Set temporary token endpoint URI string.
/// </summary>
/// <param name="temp_endpoint">Temporary token endpoint URI string to set.</param>
void set_temp_endpoint(utility::string_t temp_endpoint) { m_temp_endpoint = std::move(temp_endpoint); }
/// <summary>
/// Get authorization endpoint URI string.
/// </summary>
/// <returns>Authorization endpoint URI string.</returns>
const utility::string_t& auth_endpoint() const { return m_auth_endpoint; }
/// <summary>
/// Set authorization endpoint URI string.
/// </summary>
/// <param name="auth_endpoint">Authorization endpoint URI string to set.</param>
void set_auth_endpoint(utility::string_t auth_endpoint) { m_auth_endpoint = std::move(auth_endpoint); }
/// <summary>
/// Get token endpoint URI string.
/// </summary>
/// <returns>Token endpoint URI string.</returns>
const utility::string_t& token_endpoint() const { return m_token_endpoint; }
/// <summary>
/// Set token endpoint URI string.
/// </summary>
/// <param name="token_endpoint">Token endpoint URI string to set.</param>
void set_token_endpoint(utility::string_t token_endpoint) { m_token_endpoint = std::move(token_endpoint); }
/// <summary>
/// Get callback URI string.
/// </summary>
/// <returns>Callback URI string.</returns>
const utility::string_t& callback_uri() const { return m_callback_uri; }
/// <summary>
/// Set callback URI string.
/// </summary>
/// <param name="callback_uri">Callback URI string to set.</param>
void set_callback_uri(utility::string_t callback_uri) { m_callback_uri = std::move(callback_uri); }
/// <summary>
/// Get token.
/// </summary>
/// <returns>Token.</returns>
_ASYNCRTIMP const oauth1_token& token() const;
/// <summary>
/// Set token.
/// </summary>
/// <param name="token">Token to set.</param>
void set_token(oauth1_token token)
{
m_token = std::move(token);
m_is_authorization_completed = true;
}
/// <summary>
/// Get signature method.
/// </summary>
/// <returns>Signature method.</returns>
const oauth1_method& method() const { return m_method; }
/// <summary>
/// Set signature method.
/// </summary>
/// <param name="method">Signature method.</param>
void set_method(oauth1_method method) { m_method = std::move(method); }
/// <summary>
/// Get authentication realm.
/// </summary>
/// <returns>Authentication realm string.</returns>
const utility::string_t& realm() const { return m_realm; }
/// <summary>
/// Set authentication realm.
/// </summary>
/// <param name="realm">Authentication realm string to set.</param>
void set_realm(utility::string_t realm) { m_realm = std::move(realm); }
/// <summary>
/// Returns enabled state of the configuration.
/// The oauth1_handler will perform OAuth 1.0 authentication only if
/// this method returns true.
/// Return value is true if access token is valid (=fetched or manually set)
/// and both consumer_key() and consumer_secret() are set (=non-empty).
/// </summary>
/// <returns>The configuration enabled state.</returns>
bool is_enabled() const
{
return token().is_valid_access_token() && !(consumer_key().empty() || consumer_secret().empty());
}
// Builds signature base string according to:
// http://tools.ietf.org/html/rfc5849#section-3.4.1.1
_ASYNCRTIMP utility::string_t _build_signature_base_string(http_request request, details::oauth1_state state) const;
// Builds HMAC-SHA1 signature according to:
// http://tools.ietf.org/html/rfc5849#section-3.4.2
utility::string_t _build_hmac_sha1_signature(http_request request, details::oauth1_state state) const
{
auto text(_build_signature_base_string(std::move(request), std::move(state)));
auto digest(_hmac_sha1(_build_key(), std::move(text)));
auto signature(utility::conversions::to_base64(std::move(digest)));
return signature;
}
// Builds PLAINTEXT signature according to:
// http://tools.ietf.org/html/rfc5849#section-3.4.4
utility::string_t _build_plaintext_signature() const { return _build_key(); }
details::oauth1_state _generate_auth_state(utility::string_t extra_key, utility::string_t extra_value)
{
return details::oauth1_state(
_generate_timestamp(), _generate_nonce(), std::move(extra_key), std::move(extra_value));
}
details::oauth1_state _generate_auth_state()
{
return details::oauth1_state(_generate_timestamp(), _generate_nonce());
}
/// <summary>
/// Gets map of parameters to sign.
/// </summary>
/// <returns>Map of parameters.</returns>
const std::map<utility::string_t, utility::string_t>& parameters() const { return m_parameters_to_sign; }
/// <summary>
/// Adds a key value parameter.
/// </summary>
/// <param name="key">Key as a string value.</param>
/// <param name="value">Value as a string value.</param>
void add_parameter(const utility::string_t& key, const utility::string_t& value)
{
m_parameters_to_sign[key] = value;
}
/// <summary>
/// Adds a key value parameter.
/// </summary>
/// <param name="key">Key as a string value.</param>
/// <param name="value">Value as a string value.</param>
void add_parameter(utility::string_t&& key, utility::string_t&& value)
{
m_parameters_to_sign[std::move(key)] = std::move(value);
}
/// <summary>
/// Sets entire map or parameters replacing all previously values.
/// </summary>
/// <param name="parameters">Map of values.</param>
void set_parameters(const std::map<utility::string_t, utility::string_t>& parameters)
{
m_parameters_to_sign.clear();
m_parameters_to_sign = parameters;
}
/// <summary>
/// Clears all parameters.
/// </summary>
void clear_parameters() { m_parameters_to_sign.clear(); }
/// <summary>
/// Get the web proxy object
/// </summary>
/// <returns>A reference to the web proxy object.</returns>
const web_proxy& proxy() const { return m_proxy; }
/// <summary>
/// Set the web proxy object that will be used by token_from_code and token_from_refresh
/// </summary>
/// <param name="proxy">A reference to the web proxy object.</param>
void set_proxy(const web_proxy& proxy) { m_proxy = proxy; }
private:
friend class web::http::client::http_client_config;
friend class web::http::oauth1::details::oauth1_handler;
oauth1_config() : m_is_authorization_completed(false) {}
utility::string_t _generate_nonce() { return m_nonce_generator.generate(); }
static utility::string_t _generate_timestamp()
{
return utility::conversions::details::to_string_t(utility::datetime::utc_timestamp());
}
_ASYNCRTIMP static std::vector<unsigned char> __cdecl _hmac_sha1(const utility::string_t& key,
const utility::string_t& data);
static utility::string_t _build_base_string_uri(const uri& u);
utility::string_t _build_normalized_parameters(web::http::uri u, const details::oauth1_state& state) const;
utility::string_t _build_signature(http_request request, details::oauth1_state state) const;
utility::string_t _build_key() const
{
return uri::encode_data_string(consumer_secret()) + _XPLATSTR("&") + uri::encode_data_string(m_token.secret());
}
void _authenticate_request(http_request& req) { _authenticate_request(req, _generate_auth_state()); }
_ASYNCRTIMP void _authenticate_request(http_request& req, details::oauth1_state state);
_ASYNCRTIMP pplx::task<void> _request_token(details::oauth1_state state, bool is_temp_token_request);
utility::string_t m_consumer_key;
utility::string_t m_consumer_secret;
oauth1_token m_token;
utility::string_t m_temp_endpoint;
utility::string_t m_auth_endpoint;
utility::string_t m_token_endpoint;
utility::string_t m_callback_uri;
utility::string_t m_realm;
oauth1_method m_method;
std::map<utility::string_t, utility::string_t> m_parameters_to_sign;
web::web_proxy m_proxy;
utility::nonce_generator m_nonce_generator;
bool m_is_authorization_completed;
};
} // namespace experimental
namespace details
{
class oauth1_handler : public http_pipeline_stage
{
public:
oauth1_handler(std::shared_ptr<experimental::oauth1_config> cfg) : m_config(std::move(cfg)) {}
virtual pplx::task<http_response> propagate(http_request request) override
{
if (m_config)
{
m_config->_authenticate_request(request);
}
return next_stage()->propagate(request);
}
private:
std::shared_ptr<experimental::oauth1_config> m_config;
};
} // namespace details
} // namespace oauth1
} // namespace http
} // namespace web
#endif

View File

@ -0,0 +1,540 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* HTTP Library: Oauth 2.0
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#ifndef CASA_OAUTH2_H
#define CASA_OAUTH2_H
#include "cpprest/details/web_utilities.h"
#include "cpprest/http_msg.h"
namespace web
{
namespace http
{
namespace client
{
// Forward declaration to avoid circular include dependency.
class http_client_config;
} // namespace client
/// oAuth 2.0 library.
namespace oauth2
{
namespace details
{
class oauth2_handler;
// Constant strings for OAuth 2.0.
typedef utility::string_t oauth2_string;
class oauth2_strings
{
public:
#define _OAUTH2_STRINGS
#define DAT(a_, b_) _ASYNCRTIMP static const oauth2_string a_;
#include "cpprest/details/http_constants.dat"
#undef _OAUTH2_STRINGS
#undef DAT
};
} // namespace details
/// oAuth functionality is currently in beta.
namespace experimental
{
/// <summary>
/// Exception type for OAuth 2.0 errors.
/// </summary>
class oauth2_exception : public std::exception
{
public:
oauth2_exception(utility::string_t msg) : m_msg(utility::conversions::to_utf8string(std::move(msg))) {}
~oauth2_exception() CPPREST_NOEXCEPT {}
const char* what() const CPPREST_NOEXCEPT { return m_msg.c_str(); }
private:
std::string m_msg;
};
/// <summary>
/// OAuth 2.0 token and associated information.
/// </summary>
class oauth2_token
{
public:
/// <summary>
/// Value for undefined expiration time in expires_in().
/// </summary>
enum
{
undefined_expiration = -1
};
oauth2_token(utility::string_t access_token = utility::string_t())
: m_access_token(std::move(access_token)), m_expires_in(undefined_expiration)
{
}
/// <summary>
/// Get access token validity state.
/// If true, access token is a valid.
/// </summary>
/// <returns>Access token validity state.</returns>
bool is_valid_access_token() const { return !access_token().empty(); }
/// <summary>
/// Get access token.
/// </summary>
/// <returns>Access token string.</returns>
const utility::string_t& access_token() const { return m_access_token; }
/// <summary>
/// Set access token.
/// </summary>
/// <param name="access_token">Access token string to set.</param>
void set_access_token(utility::string_t access_token) { m_access_token = std::move(access_token); }
/// <summary>
/// Get refresh token.
/// </summary>
/// <returns>Refresh token string.</returns>
const utility::string_t& refresh_token() const { return m_refresh_token; }
/// <summary>
/// Set refresh token.
/// </summary>
/// <param name="refresh_token">Refresh token string to set.</param>
void set_refresh_token(utility::string_t refresh_token) { m_refresh_token = std::move(refresh_token); }
/// <summary>
/// Get token type.
/// </summary>
/// <returns>Token type string.</returns>
const utility::string_t& token_type() const { return m_token_type; }
/// <summary>
/// Set token type.
/// </summary>
/// <param name="token_type">Token type string to set.</param>
void set_token_type(utility::string_t token_type) { m_token_type = std::move(token_type); }
/// <summary>
/// Get token scope.
/// </summary>
/// <returns>Token scope string.</returns>
const utility::string_t& scope() const { return m_scope; }
/// <summary>
/// Set token scope.
/// </summary>
/// <param name="scope">Token scope string to set.</param>
void set_scope(utility::string_t scope) { m_scope = std::move(scope); }
/// <summary>
/// Get the lifetime of the access token in seconds.
/// For example, 3600 means the access token will expire in one hour from
/// the time when access token response was generated by the authorization server.
/// Value of undefined_expiration means expiration time is either
/// unset or that it was not returned by the server with the access token.
/// </summary>
/// <returns>Lifetime of the access token in seconds or undefined_expiration if not set.</returns>
int64_t expires_in() const { return m_expires_in; }
/// <summary>
/// Set lifetime of access token (in seconds).
/// </summary>
/// <param name="expires_in">Lifetime of access token in seconds.</param>
void set_expires_in(int64_t expires_in) { m_expires_in = expires_in; }
private:
utility::string_t m_access_token;
utility::string_t m_refresh_token;
utility::string_t m_token_type;
utility::string_t m_scope;
int64_t m_expires_in;
};
/// <summary>
/// OAuth 2.0 configuration.
///
/// Encapsulates functionality for:
/// - Authenticating requests with an access token.
/// - Performing the OAuth 2.0 authorization code grant authorization flow.
/// See: http://tools.ietf.org/html/rfc6749#section-4.1
/// - Performing the OAuth 2.0 implicit grant authorization flow.
/// See: http://tools.ietf.org/html/rfc6749#section-4.2
///
/// Performing OAuth 2.0 authorization:
/// 1. Set service and client/app parameters:
/// - Client/app key & secret (as provided by the service).
/// - The service authorization endpoint and token endpoint.
/// - Your client/app redirect URI.
/// - Use set_state() to assign a unique state string for the authorization
/// session (default: "").
/// - If needed, use set_bearer_auth() to control bearer token passing in either
/// query or header (default: header). See: http://tools.ietf.org/html/rfc6750#section-2
/// - If needed, use set_access_token_key() to set "non-standard" access token
/// key (default: "access_token").
/// - If needed, use set_implicit_grant() to enable implicit grant flow.
/// 2. Build authorization URI with build_authorization_uri() and open this in web browser/control.
/// 3. The resource owner should then clicks "Yes" to authorize your client/app, and
/// as a result the web browser/control is redirected to redirect_uri().
/// 5. Capture the redirected URI either in web control or by HTTP listener.
/// 6. Pass the redirected URI to token_from_redirected_uri() to obtain access token.
/// - The method ensures redirected URI contains same state() as set in step 1.
/// - In implicit_grant() is false, this will create HTTP request to fetch access token
/// from the service. Otherwise access token is already included in the redirected URI.
///
/// Usage for issuing authenticated requests:
/// 1. Perform authorization as above to obtain the access token or use an existing token.
/// - Some services provide option to generate access tokens for testing purposes.
/// 2. Pass the resulting oauth2_config with the access token to http_client_config::set_oauth2().
/// 3. Construct http_client with this http_client_config. As a result, all HTTP requests
/// by that client will be OAuth 2.0 authenticated.
///
/// </summary>
class oauth2_config
{
public:
oauth2_config(utility::string_t client_key,
utility::string_t client_secret,
utility::string_t auth_endpoint,
utility::string_t token_endpoint,
utility::string_t redirect_uri,
utility::string_t scope = utility::string_t(),
utility::string_t user_agent = utility::string_t())
: m_client_key(std::move(client_key))
, m_client_secret(std::move(client_secret))
, m_auth_endpoint(std::move(auth_endpoint))
, m_token_endpoint(std::move(token_endpoint))
, m_redirect_uri(std::move(redirect_uri))
, m_scope(std::move(scope))
, m_user_agent(std::move(user_agent))
, m_implicit_grant(false)
, m_bearer_auth(true)
, m_http_basic_auth(true)
, m_access_token_key(details::oauth2_strings::access_token)
{
}
/// <summary>
/// Builds an authorization URI to be loaded in the web browser/view.
/// The URI is built with auth_endpoint() as basis.
/// The implicit_grant() affects the built URI by selecting
/// either authorization code or implicit grant flow.
/// You can set generate_state to generate a new random state string.
/// </summary>
/// <param name="generate_state">If true, a new random state() string is generated
/// which replaces the current state(). If false, state() is unchanged and used as-is.</param>
/// <returns>Authorization URI string.</returns>
_ASYNCRTIMP utility::string_t build_authorization_uri(bool generate_state);
/// <summary>
/// Fetch an access token (and possibly a refresh token) based on redirected URI.
/// Behavior depends on the implicit_grant() setting.
/// If implicit_grant() is false, the URI is parsed for 'code'
/// parameter, and then token_from_code() is called with this code.
/// See: http://tools.ietf.org/html/rfc6749#section-4.1
/// Otherwise, redirect URI fragment part is parsed for 'access_token'
/// parameter, which directly contains the token(s).
/// See: http://tools.ietf.org/html/rfc6749#section-4.2
/// In both cases, the 'state' parameter is parsed and is verified to match state().
/// </summary>
/// <param name="redirected_uri">The URI where web browser/view was redirected after resource owner's
/// authorization.</param> <returns>Task that fetches the token(s) based on redirected URI.</returns>
_ASYNCRTIMP pplx::task<void> token_from_redirected_uri(const web::http::uri& redirected_uri);
/// <summary>
/// Fetches an access token (and possibly a refresh token) from the token endpoint.
/// The task creates an HTTP request to the token_endpoint() which exchanges
/// the authorization code for the token(s).
/// This also sets the refresh token if one was returned.
/// See: http://tools.ietf.org/html/rfc6749#section-4.1.3
/// </summary>
/// <param name="authorization_code">Code received via redirect upon successful authorization.</param>
/// <returns>Task that fetches token(s) based on the authorization code.</returns>
pplx::task<void> token_from_code(utility::string_t authorization_code)
{
uri_builder ub;
ub.append_query(details::oauth2_strings::grant_type, details::oauth2_strings::authorization_code, false);
ub.append_query(details::oauth2_strings::code, uri::encode_data_string(std::move(authorization_code)), false);
ub.append_query(details::oauth2_strings::redirect_uri, uri::encode_data_string(redirect_uri()), false);
return _request_token(ub);
}
/// <summary>
/// Fetches a new access token (and possibly a new refresh token) using the refresh token.
/// The task creates a HTTP request to the token_endpoint().
/// If successful, resulting access token is set as active via set_token().
/// See: http://tools.ietf.org/html/rfc6749#section-6
/// This also sets a new refresh token if one was returned.
/// </summary>
/// <returns>Task that fetches the token(s) using the refresh token.</returns>
pplx::task<void> token_from_refresh()
{
uri_builder ub;
ub.append_query(details::oauth2_strings::grant_type, details::oauth2_strings::refresh_token, false);
ub.append_query(
details::oauth2_strings::refresh_token, uri::encode_data_string(token().refresh_token()), false);
return _request_token(ub);
}
/// <summary>
/// Returns enabled state of the configuration.
/// The oauth2_handler will perform OAuth 2.0 authentication only if
/// this method returns true.
/// Return value is true if access token is valid (=fetched or manually set).
/// </summary>
/// <returns>The configuration enabled state.</returns>
bool is_enabled() const { return token().is_valid_access_token(); }
/// <summary>
/// Get client key.
/// </summary>
/// <returns>Client key string.</returns>
const utility::string_t& client_key() const { return m_client_key; }
/// <summary>
/// Set client key.
/// </summary>
/// <param name="client_key">Client key string to set.</param>
void set_client_key(utility::string_t client_key) { m_client_key = std::move(client_key); }
/// <summary>
/// Get client secret.
/// </summary>
/// <returns>Client secret string.</returns>
const utility::string_t& client_secret() const { return m_client_secret; }
/// <summary>
/// Set client secret.
/// </summary>
/// <param name="client_secret">Client secret string to set.</param>
void set_client_secret(utility::string_t client_secret) { m_client_secret = std::move(client_secret); }
/// <summary>
/// Get authorization endpoint URI string.
/// </summary>
/// <returns>Authorization endpoint URI string.</returns>
const utility::string_t& auth_endpoint() const { return m_auth_endpoint; }
/// <summary>
/// Set authorization endpoint URI string.
/// </summary>
/// <param name="auth_endpoint">Authorization endpoint URI string to set.</param>
void set_auth_endpoint(utility::string_t auth_endpoint) { m_auth_endpoint = std::move(auth_endpoint); }
/// <summary>
/// Get token endpoint URI string.
/// </summary>
/// <returns>Token endpoint URI string.</returns>
const utility::string_t& token_endpoint() const { return m_token_endpoint; }
/// <summary>
/// Set token endpoint URI string.
/// </summary>
/// <param name="token_endpoint">Token endpoint URI string to set.</param>
void set_token_endpoint(utility::string_t token_endpoint) { m_token_endpoint = std::move(token_endpoint); }
/// <summary>
/// Get redirect URI string.
/// </summary>
/// <returns>Redirect URI string.</returns>
const utility::string_t& redirect_uri() const { return m_redirect_uri; }
/// <summary>
/// Set redirect URI string.
/// </summary>
/// <param name="redirect_uri">Redirect URI string to set.</param>
void set_redirect_uri(utility::string_t redirect_uri) { m_redirect_uri = std::move(redirect_uri); }
/// <summary>
/// Get scope used in authorization for token.
/// </summary>
/// <returns>Scope string used in authorization.</returns>
const utility::string_t& scope() const { return m_scope; }
/// <summary>
/// Set scope for authorization for token.
/// </summary>
/// <param name="scope">Scope string for authorization for token.</param>
void set_scope(utility::string_t scope) { m_scope = std::move(scope); }
/// <summary>
/// Get client state string used in authorization.
/// </summary>
/// <returns>Client state string used in authorization.</returns>
const utility::string_t& state() { return m_state; }
/// <summary>
/// Set client state string for authorization for token.
/// The state string is used in authorization for security reasons
/// (to uniquely identify authorization sessions).
/// If desired, suitably secure state string can be automatically generated
/// by build_authorization_uri().
/// A good state string consist of 30 or more random alphanumeric characters.
/// </summary>
/// <param name="state">Client authorization state string to set.</param>
void set_state(utility::string_t state) { m_state = std::move(state); }
/// <summary>
/// Get token.
/// </summary>
/// <returns>Token.</returns>
const oauth2_token& token() const { return m_token; }
/// <summary>
/// Set token.
/// </summary>
/// <param name="token">Token to set.</param>
void set_token(oauth2_token token) { m_token = std::move(token); }
/// <summary>
/// Get implicit grant setting for authorization.
/// </summary>
/// <returns>Implicit grant setting for authorization.</returns>
bool implicit_grant() const { return m_implicit_grant; }
/// <summary>
/// Set implicit grant setting for authorization.
/// False means authorization code grant is used for authorization.
/// True means implicit grant is used.
/// Default: False.
/// </summary>
/// <param name="implicit_grant">The implicit grant setting to set.</param>
void set_implicit_grant(bool implicit_grant) { m_implicit_grant = implicit_grant; }
/// <summary>
/// Get bearer token authentication setting.
/// </summary>
/// <returns>Bearer token authentication setting.</returns>
bool bearer_auth() const { return m_bearer_auth; }
/// <summary>
/// Set bearer token authentication setting.
/// This must be selected based on what the service accepts.
/// True means access token is passed in the request header. (http://tools.ietf.org/html/rfc6750#section-2.1)
/// False means access token in passed in the query parameters. (http://tools.ietf.org/html/rfc6750#section-2.3)
/// Default: True.
/// </summary>
/// <param name="bearer_auth">The bearer token authentication setting to set.</param>
void set_bearer_auth(bool bearer_auth) { m_bearer_auth = bearer_auth; }
/// <summary>
/// Get HTTP Basic authentication setting for token endpoint.
/// </summary>
/// <returns>HTTP Basic authentication setting for token endpoint.</returns>
bool http_basic_auth() const { return m_http_basic_auth; }
/// <summary>
/// Set HTTP Basic authentication setting for token endpoint.
/// This setting must be selected based on what the service accepts.
/// True means HTTP Basic authentication is used for the token endpoint.
/// False means client key & secret are passed in the HTTP request body.
/// Default: True.
/// </summary>
/// <param name="http_basic_auth">The HTTP Basic authentication setting to set.</param>
void set_http_basic_auth(bool http_basic_auth) { m_http_basic_auth = http_basic_auth; }
/// <summary>
/// Get access token key.
/// </summary>
/// <returns>Access token key string.</returns>
const utility::string_t& access_token_key() const { return m_access_token_key; }
/// <summary>
/// Set access token key.
/// If the service requires a "non-standard" key you must set it here.
/// Default: "access_token".
/// </summary>
void set_access_token_key(utility::string_t access_token_key) { m_access_token_key = std::move(access_token_key); }
/// <summary>
/// Get the web proxy object
/// </summary>
/// <returns>A reference to the web proxy object.</returns>
const web_proxy& proxy() const { return m_proxy; }
/// <summary>
/// Set the web proxy object that will be used by token_from_code and token_from_refresh
/// </summary>
/// <param name="proxy">A reference to the web proxy object.</param>
void set_proxy(const web_proxy& proxy) { m_proxy = proxy; }
/// <summary>
/// Get user agent to be used in oauth2 flows.
/// </summary>
/// <returns>User agent string.</returns>
const utility::string_t& user_agent() const { return m_user_agent; }
/// <summary>
/// Set user agent to be used in oauth2 flows.
/// If none is provided a default user agent is provided.
/// </summary>
void set_user_agent(utility::string_t user_agent) { m_user_agent = std::move(user_agent); }
private:
friend class web::http::client::http_client_config;
friend class web::http::oauth2::details::oauth2_handler;
oauth2_config() : m_implicit_grant(false), m_bearer_auth(true), m_http_basic_auth(true) {}
_ASYNCRTIMP pplx::task<void> _request_token(uri_builder& request_body);
oauth2_token _parse_token_from_json(const json::value& token_json);
void _authenticate_request(http_request& req) const
{
if (bearer_auth())
{
req.headers().add(header_names::authorization, _XPLATSTR("Bearer ") + token().access_token());
}
else
{
uri_builder ub(req.request_uri());
ub.append_query(access_token_key(), token().access_token());
req.set_request_uri(ub.to_uri());
}
}
utility::string_t m_client_key;
utility::string_t m_client_secret;
utility::string_t m_auth_endpoint;
utility::string_t m_token_endpoint;
utility::string_t m_redirect_uri;
utility::string_t m_scope;
utility::string_t m_state;
utility::string_t m_user_agent;
web::web_proxy m_proxy;
bool m_implicit_grant;
bool m_bearer_auth;
bool m_http_basic_auth;
utility::string_t m_access_token_key;
oauth2_token m_token;
utility::nonce_generator m_state_generator;
};
} // namespace experimental
namespace details
{
class oauth2_handler : public http_pipeline_stage
{
public:
oauth2_handler(std::shared_ptr<experimental::oauth2_config> cfg) : m_config(std::move(cfg)) {}
virtual pplx::task<http_response> propagate(http_request request) override
{
if (m_config)
{
m_config->_authenticate_request(request);
}
return next_stage()->propagate(request);
}
private:
std::shared_ptr<experimental::oauth2_config> m_config;
};
} // namespace details
} // namespace oauth2
} // namespace http
} // namespace web
#endif

View File

@ -0,0 +1,655 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* This file defines a basic memory-based stream buffer, which allows consumer / producer pairs to communicate
* data via a buffer.
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#ifndef CASA_PRODUCER_CONSUMER_STREAMS_H
#define CASA_PRODUCER_CONSUMER_STREAMS_H
#include "cpprest/astreambuf.h"
#include "pplx/pplxtasks.h"
#include <algorithm>
#include <iterator>
#include <queue>
#include <vector>
namespace Concurrency
{
namespace streams
{
namespace details
{
/// <summary>
/// The basic_producer_consumer_buffer class serves as a memory-based steam buffer that supports both writing and
/// reading sequences of characters. It can be used as a consumer/producer buffer.
/// </summary>
template<typename _CharType>
class basic_producer_consumer_buffer : public streams::details::streambuf_state_manager<_CharType>
{
public:
typedef typename ::concurrency::streams::char_traits<_CharType> traits;
typedef typename basic_streambuf<_CharType>::int_type int_type;
typedef typename basic_streambuf<_CharType>::pos_type pos_type;
typedef typename basic_streambuf<_CharType>::off_type off_type;
/// <summary>
/// Constructor
/// </summary>
basic_producer_consumer_buffer(size_t alloc_size)
: streambuf_state_manager<_CharType>(std::ios_base::out | std::ios_base::in)
, m_alloc_size(alloc_size)
, m_allocBlock(nullptr)
, m_total(0)
, m_total_read(0)
, m_total_written(0)
, m_synced(0)
{
}
/// <summary>
/// Destructor
/// </summary>
virtual ~basic_producer_consumer_buffer()
{
// Note: there is no need to call 'wait()' on the result of close(),
// since we happen to know that close() will return without actually
// doing anything asynchronously. Should the implementation of _close_write()
// change in that regard, this logic may also have to change.
this->_close_read();
this->_close_write();
_ASSERTE(m_requests.empty());
m_blocks.clear();
}
/// <summary>
/// <c>can_seek<c/> is used to determine whether a stream buffer supports seeking.
/// </summary>
virtual bool can_seek() const { return false; }
/// <summary>
/// <c>has_size<c/> is used to determine whether a stream buffer supports size().
/// </summary>
virtual bool has_size() const { return false; }
/// <summary>
/// Get the stream buffer size, if one has been set.
/// </summary>
/// <param name="direction">The direction of buffering (in or out)</param>
/// <remarks>An implementation that does not support buffering will always return '0'.</remarks>
virtual size_t buffer_size(std::ios_base::openmode = std::ios_base::in) const { return 0; }
/// <summary>
/// Sets the stream buffer implementation to buffer or not buffer.
/// </summary>
/// <param name="size">The size to use for internal buffering, 0 if no buffering should be done.</param>
/// <param name="direction">The direction of buffering (in or out)</param>
/// <remarks>An implementation that does not support buffering will silently ignore calls to this function and it
/// will not have any effect on what is returned by subsequent calls to <see cref="::buffer_size method"
/// />.</remarks>
virtual void set_buffer_size(size_t, std::ios_base::openmode = std::ios_base::in) { return; }
/// <summary>
/// For any input stream, <c>in_avail</c> returns the number of characters that are immediately available
/// to be consumed without blocking. May be used in conjunction with <cref="::sbumpc method"/> to read data without
/// incurring the overhead of using tasks.
/// </summary>
virtual size_t in_avail() const { return m_total; }
/// <summary>
/// Gets the current read or write position in the stream.
/// </summary>
/// <param name="direction">The I/O direction to seek (see remarks)</param>
/// <returns>The current position. EOF if the operation fails.</returns>
/// <remarks>Some streams may have separate write and read cursors.
/// For such streams, the direction parameter defines whether to move the read or the write
/// cursor.</remarks>
virtual pos_type getpos(std::ios_base::openmode mode) const
{
if (((mode & std::ios_base::in) && !this->can_read()) || ((mode & std::ios_base::out) && !this->can_write()))
return static_cast<pos_type>(traits::eof());
if (mode == std::ios_base::in)
return (pos_type)m_total_read;
else if (mode == std::ios_base::out)
return (pos_type)m_total_written;
else
return (pos_type)traits::eof();
}
// Seeking is not supported
virtual pos_type seekpos(pos_type, std::ios_base::openmode) { return (pos_type)traits::eof(); }
virtual pos_type seekoff(off_type, std::ios_base::seekdir, std::ios_base::openmode)
{
return (pos_type)traits::eof();
}
/// <summary>
/// Allocates a contiguous memory block and returns it.
/// </summary>
/// <param name="count">The number of characters to allocate.</param>
/// <returns>A pointer to a block to write to, null if the stream buffer implementation does not support
/// alloc/commit.</returns>
virtual _CharType* _alloc(size_t count)
{
if (!this->can_write())
{
return nullptr;
}
// We always allocate a new block even if the count could be satisfied by
// the current write block. While this does lead to wasted space it allows for
// easier book keeping
_ASSERTE(!m_allocBlock);
m_allocBlock = std::make_shared<_block>(count);
return m_allocBlock->wbegin();
}
/// <summary>
/// Submits a block already allocated by the stream buffer.
/// </summary>
/// <param name="count">The number of characters to be committed.</param>
virtual void _commit(size_t count)
{
pplx::extensibility::scoped_critical_section_t l(m_lock);
// The count does not reflect the actual size of the block.
// Since we do not allow any more writes to this block it would suffice.
// If we ever change the algorithm to reuse blocks then this needs to be revisited.
_ASSERTE((bool)m_allocBlock);
m_allocBlock->update_write_head(count);
m_blocks.push_back(m_allocBlock);
m_allocBlock = nullptr;
update_write_head(count);
}
/// <summary>
/// Gets a pointer to the next already allocated contiguous block of data.
/// </summary>
/// <param name="ptr">A reference to a pointer variable that will hold the address of the block on success.</param>
/// <param name="count">The number of contiguous characters available at the address in 'ptr'.</param>
/// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
/// <remarks>
/// A return of false does not necessarily indicate that a subsequent read operation would fail, only that
/// there is no block to return immediately or that the stream buffer does not support the operation.
/// The stream buffer may not de-allocate the block until <see cref="::release method" /> is called.
/// If the end of the stream is reached, the function will return <c>true</c>, a null pointer, and a count of zero;
/// a subsequent read will not succeed.
/// </remarks>
virtual bool acquire(_Out_ _CharType*& ptr, _Out_ size_t& count)
{
count = 0;
ptr = nullptr;
if (!this->can_read()) return false;
pplx::extensibility::scoped_critical_section_t l(m_lock);
if (m_blocks.empty())
{
// If the write head has been closed then have reached the end of the
// stream (return true), otherwise more data could be written later (return false).
return !this->can_write();
}
else
{
auto block = m_blocks.front();
count = block->rd_chars_left();
ptr = block->rbegin();
_ASSERTE(ptr != nullptr);
return true;
}
}
/// <summary>
/// Releases a block of data acquired using <see cref="::acquire method"/>. This frees the stream buffer to
/// de-allocate the memory, if it so desires. Move the read position ahead by the count.
/// </summary>
/// <param name="ptr">A pointer to the block of data to be released.</param>
/// <param name="count">The number of characters that were read.</param>
virtual void release(_Out_writes_opt_(count) _CharType* ptr, _In_ size_t count)
{
if (ptr == nullptr) return;
pplx::extensibility::scoped_critical_section_t l(m_lock);
auto block = m_blocks.front();
_ASSERTE(block->rd_chars_left() >= count);
block->m_read += count;
update_read_head(count);
}
protected:
virtual pplx::task<bool> _sync()
{
pplx::extensibility::scoped_critical_section_t l(m_lock);
m_synced = in_avail();
fulfill_outstanding();
return pplx::task_from_result(true);
}
virtual pplx::task<int_type> _putc(_CharType ch)
{
return pplx::task_from_result((this->write(&ch, 1) == 1) ? static_cast<int_type>(ch) : traits::eof());
}
virtual pplx::task<size_t> _putn(const _CharType* ptr, size_t count)
{
return pplx::task_from_result<size_t>(this->write(ptr, count));
}
virtual pplx::task<size_t> _getn(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
{
pplx::task_completion_event<size_t> tce;
enqueue_request(_request(count, [this, ptr, count, tce]() {
// VS 2010 resolves read to a global function. Explicit
// invocation through the "this" pointer fixes the issue.
tce.set(this->read(ptr, count));
}));
return pplx::create_task(tce);
}
virtual size_t _sgetn(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
{
pplx::extensibility::scoped_critical_section_t l(m_lock);
return can_satisfy(count) ? this->read(ptr, count) : (size_t)traits::requires_async();
}
virtual size_t _scopy(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
{
pplx::extensibility::scoped_critical_section_t l(m_lock);
return can_satisfy(count) ? this->read(ptr, count, false) : (size_t)traits::requires_async();
}
virtual pplx::task<int_type> _bumpc()
{
pplx::task_completion_event<int_type> tce;
enqueue_request(_request(1, [this, tce]() { tce.set(this->read_byte(true)); }));
return pplx::create_task(tce);
}
virtual int_type _sbumpc()
{
pplx::extensibility::scoped_critical_section_t l(m_lock);
return can_satisfy(1) ? this->read_byte(true) : traits::requires_async();
}
virtual pplx::task<int_type> _getc()
{
pplx::task_completion_event<int_type> tce;
enqueue_request(_request(1, [this, tce]() { tce.set(this->read_byte(false)); }));
return pplx::create_task(tce);
}
int_type _sgetc()
{
pplx::extensibility::scoped_critical_section_t l(m_lock);
return can_satisfy(1) ? this->read_byte(false) : traits::requires_async();
}
virtual pplx::task<int_type> _nextc()
{
pplx::task_completion_event<int_type> tce;
enqueue_request(_request(1, [this, tce]() {
this->read_byte(true);
tce.set(this->read_byte(false));
}));
return pplx::create_task(tce);
}
virtual pplx::task<int_type> _ungetc() { return pplx::task_from_result<int_type>(traits::eof()); }
private:
/// <summary>
/// Close the stream buffer for writing
/// </summary>
pplx::task<void> _close_write()
{
// First indicate that there could be no more writes.
// Fulfill outstanding relies on that to flush all the
// read requests.
this->m_stream_can_write = false;
{
pplx::extensibility::scoped_critical_section_t l(this->m_lock);
// This runs on the thread that called close.
this->fulfill_outstanding();
}
return pplx::task_from_result();
}
/// <summary>
/// Updates the write head by an offset specified by count
/// </summary>
/// <remarks>This should be called with the lock held</remarks>
void update_write_head(size_t count)
{
m_total += count;
m_total_written += count;
fulfill_outstanding();
}
/// <summary>
/// Writes count characters from ptr into the stream buffer
/// </summary>
size_t write(const _CharType* ptr, size_t count)
{
if (!this->can_write() || (count == 0)) return 0;
// If no one is going to read, why bother?
// Just pretend to be writing!
if (!this->can_read()) return count;
pplx::extensibility::scoped_critical_section_t l(m_lock);
// Allocate a new block if necessary
if (m_blocks.empty() || m_blocks.back()->wr_chars_left() < count)
{
msl::safeint3::SafeInt<size_t> alloc = m_alloc_size.Max(count);
m_blocks.push_back(std::make_shared<_block>(alloc));
}
// The block at the back is always the write head
auto last = m_blocks.back();
auto countWritten = last->write(ptr, count);
_ASSERTE(countWritten == count);
update_write_head(countWritten);
return countWritten;
}
/// <summary>
/// Fulfill pending requests
/// </summary>
/// <remarks>This should be called with the lock held</remarks>
void fulfill_outstanding()
{
while (!m_requests.empty())
{
auto req = m_requests.front();
// If we cannot satisfy the request then we need
// to wait for the producer to write data
if (!can_satisfy(req.size())) return;
// We have enough data to satisfy this request
req.complete();
// Remove it from the request queue
m_requests.pop();
}
}
/// <summary>
/// Represents a memory block
/// </summary>
class _block
{
public:
_block(size_t size) : m_read(0), m_pos(0), m_size(size), m_data(new _CharType[size]) {}
~_block() { delete[] m_data; }
// Read head
size_t m_read;
// Write head
size_t m_pos;
// Allocation size (of m_data)
size_t m_size;
// The data store
_CharType* m_data;
// Pointer to the read head
_CharType* rbegin() { return m_data + m_read; }
// Pointer to the write head
_CharType* wbegin() { return m_data + m_pos; }
// Read up to count characters from the block
size_t read(_Out_writes_(count) _CharType* dest, _In_ size_t count, bool advance = true)
{
msl::safeint3::SafeInt<size_t> avail(rd_chars_left());
auto countRead = static_cast<size_t>(avail.Min(count));
_CharType* beg = rbegin();
_CharType* end = rbegin() + countRead;
#if defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL != 0
// Avoid warning C4996: Use checked iterators under SECURE_SCL
std::copy(beg, end, stdext::checked_array_iterator<_CharType*>(dest, count));
#else
std::copy(beg, end, dest);
#endif // _WIN32
if (advance)
{
m_read += countRead;
}
return countRead;
}
// Write count characters into the block
size_t write(const _CharType* src, size_t count)
{
msl::safeint3::SafeInt<size_t> avail(wr_chars_left());
auto countWritten = static_cast<size_t>(avail.Min(count));
const _CharType* srcEnd = src + countWritten;
#if defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL != 0
// Avoid warning C4996: Use checked iterators under SECURE_SCL
std::copy(src, srcEnd, stdext::checked_array_iterator<_CharType*>(wbegin(), static_cast<size_t>(avail)));
#else
std::copy(src, srcEnd, wbegin());
#endif // _WIN32
update_write_head(countWritten);
return countWritten;
}
void update_write_head(size_t count) { m_pos += count; }
size_t rd_chars_left() const { return m_pos - m_read; }
size_t wr_chars_left() const { return m_size - m_pos; }
private:
// Copy is not supported
_block(const _block&);
_block& operator=(const _block&);
};
/// <summary>
/// Represents a request on the stream buffer - typically reads
/// </summary>
class _request
{
public:
typedef std::function<void()> func_type;
_request(size_t count, const func_type& func) : m_func(func), m_count(count) {}
void complete() { m_func(); }
size_t size() const { return m_count; }
private:
func_type m_func;
size_t m_count;
};
void enqueue_request(_request req)
{
pplx::extensibility::scoped_critical_section_t l(m_lock);
if (can_satisfy(req.size()))
{
// We can immediately fulfill the request.
req.complete();
}
else
{
// We must wait for data to arrive.
m_requests.push(req);
}
}
/// <summary>
/// Determine if the request can be satisfied.
/// </summary>
bool can_satisfy(size_t count) { return (m_synced > 0) || (this->in_avail() >= count) || !this->can_write(); }
/// <summary>
/// Reads a byte from the stream and returns it as int_type.
/// Note: This routine shall only be called if can_satisfy() returned true.
/// </summary>
/// <remarks>This should be called with the lock held</remarks>
int_type read_byte(bool advance = true)
{
_CharType value;
auto read_size = this->read(&value, 1, advance);
return read_size == 1 ? static_cast<int_type>(value) : traits::eof();
}
/// <summary>
/// Reads up to count characters into ptr and returns the count of characters copied.
/// The return value (actual characters copied) could be <= count.
/// Note: This routine shall only be called if can_satisfy() returned true.
/// </summary>
/// <remarks>This should be called with the lock held</remarks>
size_t read(_Out_writes_(count) _CharType* ptr, _In_ size_t count, bool advance = true)
{
_ASSERTE(can_satisfy(count));
size_t read = 0;
for (auto iter = begin(m_blocks); iter != std::end(m_blocks); ++iter)
{
auto block = *iter;
auto read_from_block = block->read(ptr + read, count - read, advance);
read += read_from_block;
_ASSERTE(count >= read);
if (read == count) break;
}
if (advance)
{
update_read_head(read);
}
return read;
}
/// <summary>
/// Updates the read head by the specified offset
/// </summary>
/// <remarks>This should be called with the lock held</remarks>
void update_read_head(size_t count)
{
m_total -= count;
m_total_read += count;
if (m_synced > 0) m_synced = (m_synced > count) ? (m_synced - count) : 0;
// The block at the front is always the read head.
// Purge empty blocks so that the block at the front reflects the read head
while (!m_blocks.empty())
{
// If front block is not empty - we are done
if (m_blocks.front()->rd_chars_left() > 0) break;
// The block has no more data to be read. Relase the block
m_blocks.pop_front();
}
}
// The in/out mode for the buffer
std::ios_base::openmode m_mode;
// Default block size
msl::safeint3::SafeInt<size_t> m_alloc_size;
// Block used for alloc/commit
std::shared_ptr<_block> m_allocBlock;
// Total available data
size_t m_total;
size_t m_total_read;
size_t m_total_written;
// Keeps track of the number of chars that have been flushed but still
// remain to be consumed by a read operation.
size_t m_synced;
// The producer-consumer buffer is intended to be used concurrently by a reader
// and a writer, who are not coordinating their accesses to the buffer (coordination
// being what the buffer is for in the first place). Thus, we have to protect
// against some of the internal data elements against concurrent accesses
// and the possibility of inconsistent states. A simple non-recursive lock
// should be sufficient for those purposes.
pplx::extensibility::critical_section_t m_lock;
// Memory blocks
std::deque<std::shared_ptr<_block>> m_blocks;
// Queue of requests
std::queue<_request> m_requests;
};
} // namespace details
/// <summary>
/// The producer_consumer_buffer class serves as a memory-based steam buffer that supports both writing and reading
/// sequences of bytes. It can be used as a consumer/producer buffer.
/// </summary>
/// <typeparam name="_CharType">
/// The data type of the basic element of the <c>producer_consumer_buffer</c>.
/// </typeparam>
/// <remarks>
/// This is a reference-counted version of basic_producer_consumer_buffer.</remarks>
template<typename _CharType>
class producer_consumer_buffer : public streambuf<_CharType>
{
public:
typedef _CharType char_type;
/// <summary>
/// Create a producer_consumer_buffer.
/// </summary>
/// <param name="alloc_size">The internal default block size.</param>
producer_consumer_buffer(size_t alloc_size = 512)
: streambuf<_CharType>(std::make_shared<details::basic_producer_consumer_buffer<_CharType>>(alloc_size))
{
}
};
} // namespace streams
} // namespace Concurrency
#endif

View File

@ -0,0 +1,598 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* This file defines a stream buffer that is based on a raw pointer and block size. Unlike a vector-based
* stream buffer, the buffer cannot be expanded or contracted, it has a fixed capacity.
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#ifndef CASA_RAWPTR_STREAMS_H
#define CASA_RAWPTR_STREAMS_H
#include "cpprest/astreambuf.h"
#include "cpprest/streams.h"
#include "pplx/pplxtasks.h"
#include <algorithm>
#include <iterator>
#include <queue>
#include <vector>
namespace Concurrency
{
namespace streams
{
// Forward declarations
template<typename _CharType>
class rawptr_buffer;
namespace details
{
/// <summary>
/// The basic_rawptr_buffer class serves as a memory-based steam buffer that supports both writing and reading
/// sequences of characters to and from a fixed-size block.
/// </summary>
template<typename _CharType>
class basic_rawptr_buffer : public streams::details::streambuf_state_manager<_CharType>
{
public:
typedef _CharType char_type;
typedef typename basic_streambuf<_CharType>::traits traits;
typedef typename basic_streambuf<_CharType>::int_type int_type;
typedef typename basic_streambuf<_CharType>::pos_type pos_type;
typedef typename basic_streambuf<_CharType>::off_type off_type;
/// <summary>
/// Constructor
/// </summary>
basic_rawptr_buffer()
: streambuf_state_manager<_CharType>(std::ios_base::in | std::ios_base::out)
, m_data(nullptr)
, m_current_position(0)
, m_size(0)
{
}
/// <summary>
/// Destructor
/// </summary>
virtual ~basic_rawptr_buffer()
{
this->_close_read();
this->_close_write();
}
protected:
/// <summary>
/// can_seek is used to determine whether a stream buffer supports seeking.
/// </summary>
virtual bool can_seek() const { return this->is_open(); }
/// <summary>
/// <c>has_size<c/> is used to determine whether a stream buffer supports size().
/// </summary>
virtual bool has_size() const { return this->is_open(); }
/// <summary>
/// Gets the size of the stream, if known. Calls to <c>has_size</c> will determine whether
/// the result of <c>size</c> can be relied on.
/// </summary>
virtual utility::size64_t size() const { return utility::size64_t(m_size); }
/// <summary>
/// Get the stream buffer size, if one has been set.
/// </summary>
/// <param name="direction">The direction of buffering (in or out)</param>
/// <remarks>An implementation that does not support buffering will always return '0'.</remarks>
virtual size_t buffer_size(std::ios_base::openmode = std::ios_base::in) const { return 0; }
/// <summary>
/// Set the stream buffer implementation to buffer or not buffer.
/// </summary>
/// <param name="size">The size to use for internal buffering, 0 if no buffering should be done.</param>
/// <param name="direction">The direction of buffering (in or out)</param>
/// <remarks>An implementation that does not support buffering will silently ignore calls to this function and it
/// will not have
/// any effect on what is returned by subsequent calls to buffer_size().</remarks>
virtual void set_buffer_size(size_t, std::ios_base::openmode = std::ios_base::in) { return; }
/// <summary>
/// For any input stream, in_avail returns the number of characters that are immediately available
/// to be consumed without blocking. May be used in conjunction with <cref="::sbumpc method"/> and sgetn() to
/// read data without incurring the overhead of using tasks.
/// </summary>
virtual size_t in_avail() const
{
// See the comment in seek around the restiction that we do not allow read head to
// seek beyond the current size.
_ASSERTE(m_current_position <= m_size);
msl::safeint3::SafeInt<size_t> readhead(m_current_position);
msl::safeint3::SafeInt<size_t> writeend(m_size);
return (size_t)(writeend - readhead);
}
/// <summary>
/// Closes the stream buffer, preventing further read or write operations.
/// </summary>
/// <param name="mode">The I/O mode (in or out) to close for.</param>
virtual pplx::task<void> close(std::ios_base::openmode mode)
{
if (mode & std::ios_base::in)
{
this->_close_read().get(); // Safe to call get() here.
}
if (mode & std::ios_base::out)
{
this->_close_write().get(); // Safe to call get() here.
}
if (!this->can_read() && !this->can_write())
{
m_data = nullptr;
}
// Exceptions will be propagated out of _close_read or _close_write
return pplx::task_from_result();
}
virtual pplx::task<bool> _sync() { return pplx::task_from_result(true); }
virtual pplx::task<int_type> _putc(_CharType ch)
{
if (m_current_position >= m_size) return pplx::task_from_result<int_type>(traits::eof());
int_type retVal = (this->write(&ch, 1) == 1) ? static_cast<int_type>(ch) : traits::eof();
return pplx::task_from_result<int_type>(retVal);
}
virtual pplx::task<size_t> _putn(const _CharType* ptr, size_t count)
{
msl::safeint3::SafeInt<size_t> newSize = msl::safeint3::SafeInt<size_t>(count) + m_current_position;
if (newSize > m_size)
return pplx::task_from_exception<size_t>(
std::make_exception_ptr(std::runtime_error("Writing past the end of the buffer")));
return pplx::task_from_result<size_t>(this->write(ptr, count));
}
/// <summary>
/// Allocates a contiguous memory block and returns it.
/// </summary>
/// <param name="count">The number of characters to allocate.</param>
/// <returns>A pointer to a block to write to, null if the stream buffer implementation does not support
/// alloc/commit.</returns>
_CharType* _alloc(size_t count)
{
if (!this->can_write()) return nullptr;
msl::safeint3::SafeInt<size_t> readhead(m_current_position);
msl::safeint3::SafeInt<size_t> writeend(m_size);
size_t space_left = (size_t)(writeend - readhead);
if (space_left < count) return nullptr;
// Let the caller copy the data
return (_CharType*)(m_data + m_current_position);
}
/// <summary>
/// Submits a block already allocated by the stream buffer.
/// </summary>
/// <param name="count">The number of characters to be committed.</param>
void _commit(size_t actual)
{
// Update the write position and satisfy any pending reads
update_current_position(m_current_position + actual);
}
/// <summary>
/// Gets a pointer to the next already allocated contiguous block of data.
/// </summary>
/// <param name="ptr">A reference to a pointer variable that will hold the address of the block on success.</param>
/// <param name="count">The number of contiguous characters available at the address in 'ptr'.</param>
/// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
/// <remarks>
/// A return of false does not necessarily indicate that a subsequent read operation would fail, only that
/// there is no block to return immediately or that the stream buffer does not support the operation.
/// The stream buffer may not de-allocate the block until <see cref="::release method" /> is called.
/// If the end of the stream is reached, the function will return <c>true</c>, a null pointer, and a count of zero;
/// a subsequent read will not succeed.
/// </remarks>
virtual bool acquire(_Out_ _CharType*& ptr, _Out_ size_t& count)
{
count = 0;
ptr = nullptr;
if (!this->can_read()) return false;
count = in_avail();
if (count > 0)
{
ptr = (_CharType*)(m_data + m_current_position);
return true;
}
else
{
ptr = nullptr;
// Can only be open for read OR write, not both. If there is no data then
// we have reached the end of the stream so indicate such with true.
return true;
}
}
/// <summary>
/// Releases a block of data acquired using <see cref="::acquire method"/>. This frees the stream buffer to
/// de-allocate the memory, if it so desires. Move the read position ahead by the count.
/// </summary>
/// <param name="ptr">A pointer to the block of data to be released.</param>
/// <param name="count">The number of characters that were read.</param>
virtual void release(_Out_writes_opt_(count) _CharType* ptr, _In_ size_t count)
{
if (ptr != nullptr) update_current_position(m_current_position + count);
}
virtual pplx::task<size_t> _getn(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
{
return pplx::task_from_result(this->read(ptr, count));
}
size_t _sgetn(_Out_writes_(count) _CharType* ptr, _In_ size_t count) { return this->read(ptr, count); }
virtual size_t _scopy(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
{
return this->read(ptr, count, false);
}
virtual pplx::task<int_type> _bumpc() { return pplx::task_from_result(this->read_byte(true)); }
virtual int_type _sbumpc() { return this->read_byte(true); }
virtual pplx::task<int_type> _getc() { return pplx::task_from_result(this->read_byte(false)); }
int_type _sgetc() { return this->read_byte(false); }
virtual pplx::task<int_type> _nextc()
{
if (m_current_position >= m_size - 1) return pplx::task_from_result(basic_streambuf<_CharType>::traits::eof());
this->read_byte(true);
return pplx::task_from_result(this->read_byte(false));
}
virtual pplx::task<int_type> _ungetc()
{
auto pos = seekoff(-1, std::ios_base::cur, std::ios_base::in);
if (pos == (pos_type)traits::eof()) return pplx::task_from_result(traits::eof());
return this->getc();
}
/// <summary>
/// Gets the current read or write position in the stream.
/// </summary>
/// <param name="direction">The I/O direction to seek (see remarks)</param>
/// <returns>The current position. EOF if the operation fails.</returns>
/// <remarks>Some streams may have separate write and read cursors.
/// For such streams, the direction parameter defines whether to move the read or the write
/// cursor.</remarks>
virtual pos_type getpos(std::ios_base::openmode mode) const
{
if (((mode & std::ios_base::in) && !this->can_read()) || ((mode & std::ios_base::out) && !this->can_write()))
return static_cast<pos_type>(traits::eof());
if (mode == std::ios_base::in)
return (pos_type)m_current_position;
else if (mode == std::ios_base::out)
return (pos_type)m_current_position;
else
return (pos_type)traits::eof();
}
/// <summary>
/// Seeks to the given position.
/// </summary>
/// <param name="pos">The offset from the beginning of the stream.</param>
/// <param name="direction">The I/O direction to seek (see remarks).</param>
/// <returns>The position. EOF if the operation fails.</returns>
/// <remarks>Some streams may have separate write and read cursors. For such streams, the direction parameter
/// defines whether to move the read or the write cursor.</remarks>
virtual pos_type seekpos(pos_type position, std::ios_base::openmode mode)
{
pos_type beg(0);
pos_type end(m_size);
if (position >= beg)
{
auto pos = static_cast<size_t>(position);
// Read head
if ((mode & std::ios_base::in) && this->can_read())
{
if (position <= end)
{
// We do not allow reads to seek beyond the end or before the start position.
update_current_position(pos);
return static_cast<pos_type>(m_current_position);
}
}
// Write head
if ((mode & std::ios_base::out) && this->can_write())
{
// Update write head and satisfy read requests if any
update_current_position(pos);
return static_cast<pos_type>(m_current_position);
}
}
return static_cast<pos_type>(traits::eof());
}
/// <summary>
/// Seeks to a position given by a relative offset.
/// </summary>
/// <param name="offset">The relative position to seek to</param>
/// <param name="way">The starting point (beginning, end, current) for the seek.</param>
/// <param name="mode">The I/O direction to seek (see remarks)</param>
/// <returns>The position. EOF if the operation fails.</returns>
/// <remarks>Some streams may have separate write and read cursors.
/// For such streams, the mode parameter defines whether to move the read or the write cursor.</remarks>
virtual pos_type seekoff(off_type offset, std::ios_base::seekdir way, std::ios_base::openmode mode)
{
pos_type beg = 0;
pos_type cur = static_cast<pos_type>(m_current_position);
pos_type end = static_cast<pos_type>(m_size);
switch (way)
{
case std::ios_base::beg: return seekpos(beg + offset, mode);
case std::ios_base::cur: return seekpos(cur + offset, mode);
case std::ios_base::end: return seekpos(end + offset, mode);
default: return static_cast<pos_type>(traits::eof());
}
}
private:
template<typename _CharType1>
friend class ::concurrency::streams::rawptr_buffer;
/// <summary>
/// Constructor
/// </summary>
/// <param name="data">The address (pointer to) the memory block.</param>
/// <param name="size">The memory block size, measured in number of characters.</param>
basic_rawptr_buffer(const _CharType* data, size_t size)
: streambuf_state_manager<_CharType>(std::ios_base::in)
, m_data(const_cast<_CharType*>(data))
, m_size(size)
, m_current_position(0)
{
validate_mode(std::ios_base::in);
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="data">The address (pointer to) the memory block.</param>
/// <param name="size">The memory block size, measured in number of characters.</param>
/// <param name="mode">The stream mode (in, out, etc.).</param>
basic_rawptr_buffer(_CharType* data, size_t size, std::ios_base::openmode mode)
: streambuf_state_manager<_CharType>(mode), m_data(data), m_size(size), m_current_position(0)
{
validate_mode(mode);
}
static void validate_mode(std::ios_base::openmode mode)
{
// Disallow simultaneous use of the stream buffer for writing and reading.
if ((mode & std::ios_base::in) && (mode & std::ios_base::out))
throw std::invalid_argument("this combination of modes on raw pointer stream not supported");
}
/// <summary>
/// Determines if the request can be satisfied.
/// </summary>
bool can_satisfy(size_t) const
{
// We can always satisfy a read, at least partially, unless the
// read position is at the very end of the buffer.
return (in_avail() > 0);
}
/// <summary>
/// Reads a byte from the stream and returns it as int_type.
/// Note: This routine must only be called if can_satisfy() returns true.
/// </summary>
int_type read_byte(bool advance = true)
{
_CharType value;
auto read_size = this->read(&value, 1, advance);
return read_size == 1 ? static_cast<int_type>(value) : traits::eof();
}
/// <summary>
/// Reads up to count characters into ptr and returns the count of characters copied.
/// The return value (actual characters copied) could be <= count.
/// Note: This routine must only be called if can_satisfy() returns true.
/// </summary>
size_t read(_Out_writes_(count) _CharType* ptr, _In_ size_t count, bool advance = true)
{
if (!can_satisfy(count)) return 0;
msl::safeint3::SafeInt<size_t> request_size(count);
msl::safeint3::SafeInt<size_t> read_size = request_size.Min(in_avail());
size_t newPos = m_current_position + read_size;
auto readBegin = m_data + m_current_position;
auto readEnd = m_data + newPos;
#if defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL != 0
// Avoid warning C4996: Use checked iterators under SECURE_SCL
std::copy(readBegin, readEnd, stdext::checked_array_iterator<_CharType*>(ptr, count));
#else
std::copy(readBegin, readEnd, ptr);
#endif // _WIN32
if (advance)
{
update_current_position(newPos);
}
return (size_t)read_size;
}
/// <summary>
/// Write count characters from the ptr into the stream buffer
/// </summary>
size_t write(const _CharType* ptr, size_t count)
{
if (!this->can_write() || (count == 0)) return 0;
msl::safeint3::SafeInt<size_t> newSize = msl::safeint3::SafeInt<size_t>(count) + m_current_position;
if (newSize > m_size) throw std::runtime_error("Writing past the end of the buffer");
// Copy the data
#if defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL != 0
// Avoid warning C4996: Use checked iterators under SECURE_SCL
std::copy(ptr, ptr + count, stdext::checked_array_iterator<_CharType*>(m_data, m_size, m_current_position));
#else
std::copy(ptr, ptr + count, m_data + m_current_position);
#endif // _WIN32
// Update write head and satisfy pending reads if any
update_current_position(newSize);
return count;
}
/// <summary>
/// Updates the current read or write position
/// </summary>
void update_current_position(size_t newPos)
{
// The new write head
m_current_position = newPos;
_ASSERTE(m_current_position <= m_size);
}
// The actual memory block
_CharType* m_data;
// The size of the memory block
size_t m_size;
// Read/write head
size_t m_current_position;
};
} // namespace details
/// <summary>
/// The <c>rawptr_buffer</c> class serves as a memory-based stream buffer that supports reading
/// sequences of characters to or from a fixed-size block. Note that it cannot be used simultaneously for reading as
/// well as writing.
/// </summary>
/// <typeparam name="_CharType">
/// The data type of the basic element of the <c>rawptr_buffer</c>.
/// </typeparam>
template<typename _CharType>
class rawptr_buffer : public streambuf<_CharType>
{
public:
typedef _CharType char_type;
/// <summary>
/// Create a rawptr_buffer given a pointer to a memory block and the size of the block.
/// </summary>
/// <param name="data">The address (pointer to) the memory block.</param>
/// <param name="size">The memory block size, measured in number of characters.</param>
rawptr_buffer(const char_type* data, size_t size)
: streambuf<char_type>(std::shared_ptr<details::basic_rawptr_buffer<char_type>>(
new details::basic_rawptr_buffer<char_type>(data, size)))
{
}
/// <summary>
/// Create a rawptr_buffer given a pointer to a memory block and the size of the block.
/// </summary>
/// <param name="data">The address (pointer to) the memory block.</param>
/// <param name="size">The memory block size, measured in number of characters.</param>
rawptr_buffer(char_type* data, size_t size, std::ios_base::openmode mode = std::ios::out)
: streambuf<char_type>(std::shared_ptr<details::basic_rawptr_buffer<char_type>>(
new details::basic_rawptr_buffer<char_type>(data, size, mode)))
{
}
/// <summary>
/// Default constructor.
/// </summary>
rawptr_buffer() {}
};
/// <summary>
/// The rawptr_stream class is used to create memory-backed streams that support writing or reading
/// sequences of characters to / from a fixed-size block.
/// </summary>
/// <typeparam name="_CharType">
/// The data type of the basic element of the <c>rawptr_stream</c>.
/// </typeparam>
template<typename _CharType>
class rawptr_stream
{
public:
typedef _CharType char_type;
typedef rawptr_buffer<_CharType> buffer_type;
/// <summary>
/// Create a rawptr-stream given a pointer to a read-only memory block and the size of the block.
/// </summary>
/// <param name="data">The address (pointer to) the memory block.</param>
/// <param name="size">The memory block size, measured in number of characters.</param>
/// <returns>An opened input stream.</returns>
static concurrency::streams::basic_istream<char_type> open_istream(const char_type* data, size_t size)
{
return concurrency::streams::basic_istream<char_type>(buffer_type(data, size));
}
/// <summary>
/// Create a rawptr-stream given a pointer to a writable memory block and the size of the block.
/// </summary>
/// <param name="data">The address (pointer to) the memory block.</param>
/// <param name="size">The memory block size, measured in number of characters.</param>
/// <returns>An opened input stream.</returns>
static concurrency::streams::basic_istream<char_type> open_istream(char_type* data, size_t size)
{
return concurrency::streams::basic_istream<char_type>(buffer_type(data, size, std::ios::in));
}
/// <summary>
/// Create a rawptr-stream given a pointer to a writable memory block and the size of the block.
/// </summary>
/// <param name="data">The address (pointer to) the memory block.</param>
/// <param name="size">The memory block size, measured in number of characters.</param>
/// <returns>An opened output stream.</returns>
static concurrency::streams::basic_ostream<char_type> open_ostream(char_type* data, size_t size)
{
return concurrency::streams::basic_ostream<char_type>(buffer_type(data, size, std::ios::out));
}
};
} // namespace streams
} // namespace Concurrency
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,21 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Protocol independent support for URIs.
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#ifndef CASA_URI_H
#define CASA_URI_H
#include "cpprest/base_uri.h"
#include "cpprest/uri_builder.h"
#endif

View File

@ -0,0 +1,295 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Builder style class for creating URIs.
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include "cpprest/base_uri.h"
#include <string>
namespace web
{
/// <summary>
/// Builder for constructing URIs incrementally.
/// </summary>
class uri_builder
{
public:
/// <summary>
/// Creates a builder with an initially empty URI.
/// </summary>
uri_builder() = default;
/// <summary>
/// Creates a builder with a existing URI object.
/// </summary>
/// <param name="uri_str">Encoded string containing the URI.</param>
uri_builder(const uri& uri_str) : m_uri(uri_str.m_components) {}
/// <summary>
/// Get the scheme component of the URI as an encoded string.
/// </summary>
/// <returns>The URI scheme as a string.</returns>
const utility::string_t& scheme() const { return m_uri.m_scheme; }
/// <summary>
/// Get the user information component of the URI as an encoded string.
/// </summary>
/// <returns>The URI user information as a string.</returns>
const utility::string_t& user_info() const { return m_uri.m_user_info; }
/// <summary>
/// Get the host component of the URI as an encoded string.
/// </summary>
/// <returns>The URI host as a string.</returns>
const utility::string_t& host() const { return m_uri.m_host; }
/// <summary>
/// Get the port component of the URI. Returns -1 if no port is specified.
/// </summary>
/// <returns>The URI port as an integer.</returns>
int port() const { return m_uri.m_port; }
/// <summary>
/// Get the path component of the URI as an encoded string.
/// </summary>
/// <returns>The URI path as a string.</returns>
const utility::string_t& path() const { return m_uri.m_path; }
/// <summary>
/// Get the query component of the URI as an encoded string.
/// </summary>
/// <returns>The URI query as a string.</returns>
const utility::string_t& query() const { return m_uri.m_query; }
/// <summary>
/// Get the fragment component of the URI as an encoded string.
/// </summary>
/// <returns>The URI fragment as a string.</returns>
const utility::string_t& fragment() const { return m_uri.m_fragment; }
/// <summary>
/// Set the scheme of the URI.
/// </summary>
/// <param name="scheme">Uri scheme.</param>
/// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
uri_builder& set_scheme(const utility::string_t& scheme)
{
m_uri.m_scheme = scheme;
return *this;
}
/// <summary>
/// Set the user info component of the URI.
/// </summary>
/// <param name="user_info">User info as a decoded string.</param>
/// <param name="do_encoding">Specify whether to apply URI encoding to the given string.</param>
/// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
uri_builder& set_user_info(const utility::string_t& user_info, bool do_encoding = false)
{
if (do_encoding)
{
m_uri.m_user_info = uri::encode_uri(user_info, uri::components::user_info);
}
else
{
m_uri.m_user_info = user_info;
}
return *this;
}
/// <summary>
/// Set the host component of the URI.
/// </summary>
/// <param name="host">Host as a decoded string.</param>
/// <param name="do_encoding">Specify whether to apply URI encoding to the given string.</param>
/// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
uri_builder& set_host(const utility::string_t& host, bool do_encoding = false)
{
if (do_encoding)
{
m_uri.m_host = uri::encode_uri(host, uri::components::host);
}
else
{
m_uri.m_host = host;
}
return *this;
}
/// <summary>
/// Set the port component of the URI.
/// </summary>
/// <param name="port">Port as an integer.</param>
/// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
uri_builder& set_port(int port)
{
m_uri.m_port = port;
return *this;
}
/// <summary>
/// Set the port component of the URI.
/// </summary>
/// <param name="port">Port as a string.</param>
/// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
/// <remarks>When string can't be converted to an integer the port is left unchanged.</remarks>
_ASYNCRTIMP uri_builder& set_port(const utility::string_t& port);
/// <summary>
/// Set the path component of the URI.
/// </summary>
/// <param name="path">Path as a decoded string.</param>
/// <param name="do_encoding">Specify whether to apply URI encoding to the given string.</param>
/// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
uri_builder& set_path(const utility::string_t& path, bool do_encoding = false)
{
if (do_encoding)
{
m_uri.m_path = uri::encode_uri(path, uri::components::path);
}
else
{
m_uri.m_path = path;
}
return *this;
}
/// <summary>
/// Set the query component of the URI.
/// </summary>
/// <param name="query">Query as a decoded string.</param>
/// <param name="do_encoding">Specify whether apply URI encoding to the given string.</param>
/// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
uri_builder& set_query(const utility::string_t& query, bool do_encoding = false)
{
if (do_encoding)
{
m_uri.m_query = uri::encode_uri(query, uri::components::query);
}
else
{
m_uri.m_query = query;
}
return *this;
}
/// <summary>
/// Set the fragment component of the URI.
/// </summary>
/// <param name="fragment">Fragment as a decoded string.</param>
/// <param name="do_encoding">Specify whether to apply URI encoding to the given string.</param>
/// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
uri_builder& set_fragment(const utility::string_t& fragment, bool do_encoding = false)
{
if (do_encoding)
{
m_uri.m_fragment = uri::encode_uri(fragment, uri::components::fragment);
}
else
{
m_uri.m_fragment = fragment;
}
return *this;
}
/// <summary>
/// Clears all components of the underlying URI in this uri_builder.
/// </summary>
void clear() { m_uri = details::uri_components(); }
/// <summary>
/// Appends another path to the path of this uri_builder.
/// </summary>
/// <param name="path">Path to append as a already encoded string.</param>
/// <param name="do_encoding">Specify whether to apply URI encoding to the given string.</param>
/// <returns>A reference to this uri_builder to support chaining.</returns>
_ASYNCRTIMP uri_builder& append_path(const utility::string_t& path, bool do_encoding = false);
/// <summary>
/// Appends the raw contents of the path argument to the path of this uri_builder with no separator de-duplication.
/// </summary>
/// <remarks>
/// The path argument is appended after adding a '/' separator without regards to the contents of path. If an empty
/// string is provided, this function will immediately return without changes to the stored path value. For example:
/// if the current contents are "/abc" and path="/xyz", the result will be "/abc//xyz".
/// </remarks>
/// <param name="path">Path to append as a already encoded string.</param>
/// <param name="do_encoding">Specify whether to apply URI encoding to the given string.</param>
/// <returns>A reference to this uri_builder to support chaining.</returns>
_ASYNCRTIMP uri_builder& append_path_raw(const utility::string_t& path, bool do_encoding = false);
/// <summary>
/// Appends another query to the query of this uri_builder.
/// </summary>
/// <param name="query">Query to append as a decoded string.</param>
/// <param name="do_encoding">Specify whether to apply URI encoding to the given string.</param>
/// <returns>A reference to this uri_builder to support chaining.</returns>
_ASYNCRTIMP uri_builder& append_query(const utility::string_t& query, bool do_encoding = false);
/// <summary>
/// Appends an relative uri (Path, Query and fragment) at the end of the current uri.
/// </summary>
/// <param name="relative_uri">The relative uri to append.</param>
/// <returns>A reference to this uri_builder to support chaining.</returns>
_ASYNCRTIMP uri_builder& append(const uri& relative_uri);
/// <summary>
/// Appends another query to the query of this uri_builder, encoding it first. This overload is useful when building
/// a query segment of the form "element=10", where the right hand side of the query is stored as a type other than
/// a string, for instance, an integral type.
/// </summary>
/// <param name="name">The name portion of the query string</param>
/// <param name="value">The value portion of the query string</param>
/// <returns>A reference to this uri_builder to support chaining.</returns>
template<typename T>
uri_builder& append_query(const utility::string_t& name, const T& value, bool do_encoding = true)
{
if (do_encoding)
append_query_encode_impl(name, utility::conversions::details::print_utf8string(value));
else
append_query_no_encode_impl(name, utility::conversions::details::print_string(value));
return *this;
}
/// <summary>
/// Combine and validate the URI components into a encoded string. An exception will be thrown if the URI is
/// invalid.
/// </summary>
/// <returns>The created URI as a string.</returns>
_ASYNCRTIMP utility::string_t to_string() const;
/// <summary>
/// Combine and validate the URI components into a URI class instance. An exception will be thrown if the URI is
/// invalid.
/// </summary>
/// <returns>The create URI as a URI class instance.</returns>
_ASYNCRTIMP uri to_uri() const;
/// <summary>
/// Validate the generated URI from all existing components of this uri_builder.
/// </summary>
/// <returns>Whether the URI is valid.</returns>
_ASYNCRTIMP bool is_valid();
private:
_ASYNCRTIMP void append_query_encode_impl(const utility::string_t& name, const utf8string& value);
_ASYNCRTIMP void append_query_no_encode_impl(const utility::string_t& name, const utility::string_t& value);
details::uri_components m_uri;
};
} // namespace web

View File

@ -0,0 +1,10 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
*/
#define CPPREST_VERSION_MINOR 10
#define CPPREST_VERSION_MAJOR 2
#define CPPREST_VERSION_REVISION 12
#define CPPREST_VERSION (CPPREST_VERSION_MAJOR * 100000 + CPPREST_VERSION_MINOR * 100 + CPPREST_VERSION_REVISION)

View File

@ -0,0 +1,611 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Websocket client side implementation
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#ifndef CASA_WS_CLIENT_H
#define CASA_WS_CLIENT_H
#include "cpprest/details/basic_types.h"
#if !defined(CPPREST_EXCLUDE_WEBSOCKETS)
#include "cpprest/asyncrt_utils.h"
#include "cpprest/details/web_utilities.h"
#include "cpprest/http_headers.h"
#include "cpprest/uri.h"
#include "cpprest/ws_msg.h"
#include "pplx/pplxtasks.h"
#include <condition_variable>
#include <limits>
#include <memory>
#include <mutex>
#if !defined(_WIN32) || !defined(__cplusplus_winrt)
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
#endif
#include "boost/asio/ssl.hpp"
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
#endif
namespace web
{
// For backwards compatibility for when in the experimental namespace.
// At next major release this should be deleted.
namespace experimental = web;
// In the past namespace was accidentally called 'web_sockets'. To avoid breaking code
// alias it. At our next major release this should be deleted.
namespace web_sockets = websockets;
namespace websockets
{
/// WebSocket client side library.
namespace client
{
/// Websocket close status values.
enum class websocket_close_status
{
normal = 1000,
going_away = 1001,
protocol_error = 1002,
unsupported = 1003, // or data_mismatch
abnormal_close = 1006,
inconsistent_datatype = 1007,
policy_violation = 1008,
too_large = 1009,
negotiate_error = 1010,
server_terminate = 1011,
};
/// <summary>
/// Websocket client configuration class, used to set the possible configuration options
/// used to create an websocket_client instance.
/// </summary>
class websocket_client_config
{
public:
/// <summary>
/// Creates a websocket client configuration with default settings.
/// </summary>
websocket_client_config() : m_sni_enabled(true), m_validate_certificates(true) {}
/// <summary>
/// Get the web proxy object
/// </summary>
/// <returns>A reference to the web proxy object.</returns>
const web_proxy& proxy() const { return m_proxy; }
/// <summary>
/// Set the web proxy object
/// </summary>
/// <param name="proxy">The web proxy object.</param>
void set_proxy(const web_proxy& proxy) { m_proxy = proxy; }
/// <summary>
/// Get the client credentials
/// </summary>
/// <returns>A reference to the client credentials.</returns>
const web::credentials& credentials() const { return m_credentials; }
/// <summary>
/// Set the client credentials
/// </summary>
/// <param name="cred">The client credentials.</param>
void set_credentials(const web::credentials& cred) { m_credentials = cred; }
/// <summary>
/// Disables Server Name Indication (SNI). Default is on.
/// </summary>
void disable_sni() { m_sni_enabled = false; }
/// <summary>
/// Determines if Server Name Indication (SNI) is enabled.
/// </summary>
/// <returns>True if enabled, false otherwise.</returns>
bool is_sni_enabled() const { return m_sni_enabled; }
/// <summary>
/// Sets the server host name to use for TLS Server Name Indication (SNI).
/// </summary>
/// <remarks>By default the host name is set to the websocket URI host.</remarks>
/// <param name="name">The host name to use, as a string.</param>
void set_server_name(const utf8string& name) { m_sni_hostname = name; }
/// <summary>
/// Gets the server host name to use for TLS Server Name Indication (SNI).
/// </summary>
/// <returns>Host name as a string.</returns>
const utf8string& server_name() const { return m_sni_hostname; }
/// <summary>
/// Sets the User Agent to be used for the connection
/// </summary>
/// <param name="name">The User Agent to use, as a string.</param>
_ASYNCRTIMP void set_user_agent(const utf8string& user_agent);
/// <summary>
/// Gets the headers of the HTTP request message used in the WebSocket protocol handshake.
/// </summary>
/// <returns>HTTP headers for the WebSocket protocol handshake.</returns>
/// <remarks>
/// Use the <seealso cref="http_headers::add Method"/> to fill in desired headers.
/// </remarks>
web::http::http_headers& headers() { return m_headers; }
/// <summary>
/// Gets a const reference to the headers of the WebSocket protocol handshake HTTP message.
/// </summary>
/// <returns>HTTP headers.</returns>
const web::http::http_headers& headers() const { return m_headers; }
/// <summary>
/// Adds a subprotocol to the request headers.
/// </summary>
/// <param name="name">The name of the subprotocol.</param>
/// <remarks>If additional subprotocols have already been specified, the new one will just be added.</remarks>
_ASYNCRTIMP void add_subprotocol(const ::utility::string_t& name);
/// <summary>
/// Gets list of the specified subprotocols.
/// </summary>
/// <returns>Vector of all the subprotocols </returns>
/// <remarks>If you want all the subprotocols in a comma separated string
/// they can be directly looked up in the headers using 'Sec-WebSocket-Protocol'.</remarks>
_ASYNCRTIMP std::vector<::utility::string_t> subprotocols() const;
/// <summary>
/// Gets the server certificate validation property.
/// </summary>
/// <returns>True if certificates are to be verified, false otherwise.</returns>
bool validate_certificates() const { return m_validate_certificates; }
/// <summary>
/// Sets the server certificate validation property.
/// </summary>
/// <param name="validate_certs">False to turn ignore all server certificate validation errors, true
/// otherwise.</param> <remarks>Note ignoring certificate errors can be dangerous and should be done with
/// caution.</remarks>
void set_validate_certificates(bool validate_certs) { m_validate_certificates = validate_certs; }
#if !defined(_WIN32) || !defined(__cplusplus_winrt)
/// <summary>
/// Sets a callback to enable custom setting of the ssl context, at construction time.
/// </summary>
/// <param name="callback">A user callback allowing for customization of the ssl context at construction
/// time.</param>
void set_ssl_context_callback(const std::function<void(boost::asio::ssl::context&)>& callback)
{
m_ssl_context_callback = callback;
}
/// <summary>
/// Gets the user's callback to allow for customization of the ssl context.
/// </summary>
const std::function<void(boost::asio::ssl::context&)>& get_ssl_context_callback() const
{
return m_ssl_context_callback;
}
#endif
private:
web::web_proxy m_proxy;
web::credentials m_credentials;
web::http::http_headers m_headers;
bool m_sni_enabled;
utf8string m_sni_hostname;
bool m_validate_certificates;
#if !defined(_WIN32) || !defined(__cplusplus_winrt)
std::function<void(boost::asio::ssl::context&)> m_ssl_context_callback;
#endif
};
/// <summary>
/// Represents a websocket error. This class holds an error message and an optional error code.
/// </summary>
class websocket_exception : public std::exception
{
public:
/// <summary>
/// Creates an <c>websocket_exception</c> with just a string message and no error code.
/// </summary>
/// <param name="whatArg">Error message string.</param>
websocket_exception(const utility::string_t& whatArg) : m_msg(utility::conversions::to_utf8string(whatArg)) {}
#ifdef _WIN32
/// <summary>
/// Creates an <c>websocket_exception</c> with just a string message and no error code.
/// </summary>
/// <param name="whatArg">Error message string.</param>
websocket_exception(std::string whatArg) : m_msg(std::move(whatArg)) {}
#endif
/// <summary>
/// Creates a <c>websocket_exception</c> from a error code using the current platform error category.
/// The message of the error code will be used as the what() string message.
/// </summary>
/// <param name="errorCode">Error code value.</param>
websocket_exception(int errorCode) : m_errorCode(utility::details::create_error_code(errorCode))
{
m_msg = m_errorCode.message();
}
/// <summary>
/// Creates a <c>websocket_exception</c> from a error code using the current platform error category.
/// </summary>
/// <param name="errorCode">Error code value.</param>
/// <param name="whatArg">Message to use in what() string.</param>
websocket_exception(int errorCode, const utility::string_t& whatArg)
: m_errorCode(utility::details::create_error_code(errorCode))
, m_msg(utility::conversions::to_utf8string(whatArg))
{
}
#ifdef _WIN32
/// <summary>
/// Creates a <c>websocket_exception</c> from a error code and string message.
/// </summary>
/// <param name="errorCode">Error code value.</param>
/// <param name="whatArg">Message to use in what() string.</param>
websocket_exception(int errorCode, std::string whatArg)
: m_errorCode(utility::details::create_error_code(errorCode)), m_msg(std::move(whatArg))
{
}
/// <summary>
/// Creates a <c>websocket_exception</c> from a error code and string message to use as the what() argument.
/// <param name="code">Error code.</param>
/// <param name="whatArg">Message to use in what() string.</param>
/// </summary>
websocket_exception(std::error_code code, std::string whatArg)
: m_errorCode(std::move(code)), m_msg(std::move(whatArg))
{
}
#endif
/// <summary>
/// Creates a <c>websocket_exception</c> from a error code and category. The message of the error code will be used
/// as the <c>what</c> string message.
/// </summary>
/// <param name="errorCode">Error code value.</param>
/// <param name="cat">Error category for the code.</param>
websocket_exception(int errorCode, const std::error_category& cat) : m_errorCode(std::error_code(errorCode, cat))
{
m_msg = m_errorCode.message();
}
/// <summary>
/// Creates a <c>websocket_exception</c> from a error code and string message to use as the what() argument.
/// <param name="code">Error code.</param>
/// <param name="whatArg">Message to use in what() string.</param>
/// </summary>
websocket_exception(std::error_code code, const utility::string_t& whatArg)
: m_errorCode(std::move(code)), m_msg(utility::conversions::to_utf8string(whatArg))
{
}
/// <summary>
/// Gets a string identifying the cause of the exception.
/// </summary>
/// <returns>A null terminated character string.</returns>
const char* what() const CPPREST_NOEXCEPT { return m_msg.c_str(); }
/// <summary>
/// Gets the underlying error code for the cause of the exception.
/// </summary>
/// <returns>The <c>error_code</c> object associated with the exception.</returns>
const std::error_code& error_code() const CPPREST_NOEXCEPT { return m_errorCode; }
private:
std::error_code m_errorCode;
std::string m_msg;
};
namespace details
{
// Interface to be implemented by the websocket client callback implementations.
class websocket_client_callback_impl
{
public:
websocket_client_callback_impl(websocket_client_config config) : m_config(std::move(config)) {}
virtual ~websocket_client_callback_impl() CPPREST_NOEXCEPT {}
virtual pplx::task<void> connect() = 0;
virtual pplx::task<void> send(websocket_outgoing_message& msg) = 0;
virtual void set_message_handler(const std::function<void(const websocket_incoming_message&)>& handler) = 0;
virtual pplx::task<void> close() = 0;
virtual pplx::task<void> close(websocket_close_status close_status,
const utility::string_t& close_reason = _XPLATSTR("")) = 0;
virtual void set_close_handler(
const std::function<void(websocket_close_status, const utility::string_t&, const std::error_code&)>&
handler) = 0;
const web::uri& uri() const { return m_uri; }
void set_uri(const web::uri& uri) { m_uri = uri; }
const websocket_client_config& config() const { return m_config; }
static void verify_uri(const web::uri& uri)
{
// Most of the URI schema validation is taken care by URI class.
// We only need to check certain things specific to websockets.
if (uri.scheme() != _XPLATSTR("ws") && uri.scheme() != _XPLATSTR("wss"))
{
throw std::invalid_argument("URI scheme must be 'ws' or 'wss'");
}
if (uri.host().empty())
{
throw std::invalid_argument("URI must contain a hostname.");
}
// Fragment identifiers are meaningless in the context of WebSocket URIs
// and MUST NOT be used on these URIs.
if (!uri.fragment().empty())
{
throw std::invalid_argument("WebSocket URI must not contain fragment identifiers");
}
}
protected:
web::uri m_uri;
websocket_client_config m_config;
};
// Interface to be implemented by the websocket client task implementations.
class websocket_client_task_impl
{
public:
_ASYNCRTIMP websocket_client_task_impl(websocket_client_config config);
_ASYNCRTIMP virtual ~websocket_client_task_impl() CPPREST_NOEXCEPT;
_ASYNCRTIMP pplx::task<websocket_incoming_message> receive();
_ASYNCRTIMP void close_pending_tasks_with_error(const websocket_exception& exc);
const std::shared_ptr<websocket_client_callback_impl>& callback_client() const { return m_callback_client; };
private:
void set_handler();
// When a message arrives, if there are tasks waiting for a message, signal the topmost one.
// Else enqueue the message in a queue.
// m_receive_queue_lock : to guard access to the queue & m_client_closed
std::mutex m_receive_queue_lock;
// Queue to store incoming messages when there are no tasks waiting for a message
std::queue<websocket_incoming_message> m_receive_msg_queue;
// Queue to maintain the receive tasks when there are no messages(yet).
std::queue<pplx::task_completion_event<websocket_incoming_message>> m_receive_task_queue;
// Initially set to false, becomes true if a close frame is received from the server or
// if the underlying connection is aborted or terminated.
bool m_client_closed;
std::shared_ptr<websocket_client_callback_impl> m_callback_client;
};
} // namespace details
/// <summary>
/// Websocket client class, used to maintain a connection to a remote host for an extended session.
/// </summary>
class websocket_client
{
public:
/// <summary>
/// Creates a new websocket_client.
/// </summary>
websocket_client() : m_client(std::make_shared<details::websocket_client_task_impl>(websocket_client_config())) {}
/// <summary>
/// Creates a new websocket_client.
/// </summary>
/// <param name="config">The client configuration object containing the possible configuration options to initialize
/// the <c>websocket_client</c>. </param>
websocket_client(websocket_client_config config)
: m_client(std::make_shared<details::websocket_client_task_impl>(std::move(config)))
{
}
/// <summary>
/// Connects to the remote network destination. The connect method initiates the websocket handshake with the
/// remote network destination, takes care of the protocol upgrade request.
/// </summary>
/// <param name="uri">The uri address to connect. </param>
/// <returns>An asynchronous operation that is completed once the client has successfully connected to the websocket
/// server.</returns>
pplx::task<void> connect(const web::uri& uri)
{
m_client->callback_client()->verify_uri(uri);
m_client->callback_client()->set_uri(uri);
auto client = m_client;
return m_client->callback_client()->connect().then([client](pplx::task<void> result) {
try
{
result.get();
}
catch (const websocket_exception& ex)
{
client->close_pending_tasks_with_error(ex);
throw;
}
});
}
/// <summary>
/// Sends a websocket message to the server .
/// </summary>
/// <returns>An asynchronous operation that is completed once the message is sent.</returns>
pplx::task<void> send(websocket_outgoing_message msg) { return m_client->callback_client()->send(msg); }
/// <summary>
/// Receive a websocket message.
/// </summary>
/// <returns>An asynchronous operation that is completed when a message has been received by the client
/// endpoint.</returns>
pplx::task<websocket_incoming_message> receive() { return m_client->receive(); }
/// <summary>
/// Closes a websocket client connection, sends a close frame to the server and waits for a close message from the
/// server.
/// </summary>
/// <returns>An asynchronous operation that is completed the connection has been successfully closed.</returns>
pplx::task<void> close() { return m_client->callback_client()->close(); }
/// <summary>
/// Closes a websocket client connection, sends a close frame to the server and waits for a close message from the
/// server.
/// </summary>
/// <param name="close_status">Endpoint MAY use the following pre-defined status codes when sending a Close
/// frame.</param> <param name="close_reason">While closing an established connection, an endpoint may indicate the
/// reason for closure.</param> <returns>An asynchronous operation that is completed the connection has been
/// successfully closed.</returns>
pplx::task<void> close(websocket_close_status close_status, const utility::string_t& close_reason = _XPLATSTR(""))
{
return m_client->callback_client()->close(close_status, close_reason);
}
/// <summary>
/// Gets the websocket client URI.
/// </summary>
/// <returns>URI connected to.</returns>
const web::uri& uri() const { return m_client->callback_client()->uri(); }
/// <summary>
/// Gets the websocket client config object.
/// </summary>
/// <returns>A reference to the client configuration object.</returns>
const websocket_client_config& config() const { return m_client->callback_client()->config(); }
private:
std::shared_ptr<details::websocket_client_task_impl> m_client;
};
/// <summary>
/// Websocket client class, used to maintain a connection to a remote host for an extended session, uses callback APIs
/// for handling receive and close event instead of async task. For some scenarios would be a alternative for the
/// websocket_client like if you want to special handling on close event.
/// </summary>
class websocket_callback_client
{
public:
/// <summary>
/// Creates a new websocket_callback_client.
/// </summary>
_ASYNCRTIMP websocket_callback_client();
/// <summary>
/// Creates a new websocket_callback_client.
/// </summary>
/// <param name="client_config">The client configuration object containing the possible configuration options to
/// initialize the <c>websocket_client</c>. </param>
_ASYNCRTIMP websocket_callback_client(websocket_client_config client_config);
/// <summary>
/// Connects to the remote network destination. The connect method initiates the websocket handshake with the
/// remote network destination, takes care of the protocol upgrade request.
/// </summary>
/// <param name="uri">The uri address to connect. </param>
/// <returns>An asynchronous operation that is completed once the client has successfully connected to the websocket
/// server.</returns>
pplx::task<void> connect(const web::uri& uri)
{
m_client->verify_uri(uri);
m_client->set_uri(uri);
return m_client->connect();
}
/// <summary>
/// Sends a websocket message to the server .
/// </summary>
/// <returns>An asynchronous operation that is completed once the message is sent.</returns>
pplx::task<void> send(websocket_outgoing_message msg) { return m_client->send(msg); }
/// <summary>
/// Set the received handler for notification of client websocket messages.
/// </summary>
/// <param name="handler">A function representing the incoming websocket messages handler. It's parameters are:
/// msg: a <c>websocket_incoming_message</c> value indicating the message received
/// </param>
/// <remarks>If this handler is not set before connecting incoming messages will be missed.</remarks>
void set_message_handler(const std::function<void(const websocket_incoming_message& msg)>& handler)
{
m_client->set_message_handler(handler);
}
/// <summary>
/// Closes a websocket client connection, sends a close frame to the server and waits for a close message from the
/// server.
/// </summary>
/// <returns>An asynchronous operation that is completed the connection has been successfully closed.</returns>
pplx::task<void> close() { return m_client->close(); }
/// <summary>
/// Closes a websocket client connection, sends a close frame to the server and waits for a close message from the
/// server.
/// </summary>
/// <param name="close_status">Endpoint MAY use the following pre-defined status codes when sending a Close
/// frame.</param> <param name="close_reason">While closing an established connection, an endpoint may indicate the
/// reason for closure.</param> <returns>An asynchronous operation that is completed the connection has been
/// successfully closed.</returns>
pplx::task<void> close(websocket_close_status close_status, const utility::string_t& close_reason = _XPLATSTR(""))
{
return m_client->close(close_status, close_reason);
}
/// <summary>
/// Set the closed handler for notification of client websocket closing event.
/// </summary>
/// <param name="handler">The handler for websocket closing event, It's parameters are:
/// close_status: The pre-defined status codes used by the endpoint when sending a Close frame.
/// reason: The reason string used by the endpoint when sending a Close frame.
/// error: The error code if the websocket is closed with abnormal error.
/// </param>
void set_close_handler(const std::function<void(websocket_close_status close_status,
const utility::string_t& reason,
const std::error_code& error)>& handler)
{
m_client->set_close_handler(handler);
}
/// <summary>
/// Gets the websocket client URI.
/// </summary>
/// <returns>URI connected to.</returns>
const web::uri& uri() const { return m_client->uri(); }
/// <summary>
/// Gets the websocket client config object.
/// </summary>
/// <returns>A reference to the client configuration object.</returns>
const websocket_client_config& config() const { return m_client->config(); }
private:
std::shared_ptr<details::websocket_client_callback_impl> m_client;
};
} // namespace client
} // namespace websockets
} // namespace web
#endif
#endif

View File

@ -0,0 +1,231 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Websocket incoming and outgoing message definitions.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include "cpprest/details/basic_types.h"
#if !defined(CPPREST_EXCLUDE_WEBSOCKETS)
#include "cpprest/asyncrt_utils.h"
#include "cpprest/containerstream.h"
#include "cpprest/streams.h"
#include "cpprest/uri.h"
#include "pplx/pplxtasks.h"
#include <limits>
#include <memory>
namespace web
{
namespace websockets
{
namespace client
{
namespace details
{
class winrt_callback_client;
class wspp_callback_client;
#if defined(__cplusplus_winrt)
ref class ReceiveContext;
#endif
} // namespace details
/// <summary>
/// The different types of websocket message.
/// Text type contains UTF-8 encoded data.
/// Interpretation of Binary type is left to the application.
/// Note: The fragment types and control frames like close, ping, pong are not supported on WinRT.
/// </summary>
enum class websocket_message_type
{
text_message,
binary_message,
close,
ping,
pong
};
/// <summary>
/// Represents an outgoing websocket message
/// </summary>
class websocket_outgoing_message
{
public:
#if !defined(__cplusplus_winrt)
/// <summary>
/// Sets a the outgoing message to be an unsolicited pong message.
/// This is useful when the client side wants to check whether the server is alive.
/// </summary>
void set_pong_message() { this->set_message_pong(); }
#endif
/// <summary>
/// Sets a UTF-8 message as the message body.
/// </summary>
/// <param name="data">UTF-8 String containing body of the message.</param>
void set_utf8_message(std::string&& data)
{
this->set_message(concurrency::streams::container_buffer<std::string>(std::move(data)));
}
/// <summary>
/// Sets a UTF-8 message as the message body.
/// </summary>
/// <param name="data">UTF-8 String containing body of the message.</param>
void set_utf8_message(const std::string& data)
{
this->set_message(concurrency::streams::container_buffer<std::string>(data));
}
/// <summary>
/// Sets a UTF-8 message as the message body.
/// </summary>
/// <param name="istream">casablanca input stream representing the body of the message.</param>
/// <remarks>Upon sending, the entire stream may be buffered to determine the length.</remarks>
void set_utf8_message(const concurrency::streams::istream& istream)
{
this->set_message(istream, SIZE_MAX, websocket_message_type::text_message);
}
/// <summary>
/// Sets a UTF-8 message as the message body.
/// </summary>
/// <param name="istream">casablanca input stream representing the body of the message.</param>
/// <param name="len">number of bytes to send.</param>
void set_utf8_message(const concurrency::streams::istream& istream, size_t len)
{
this->set_message(istream, len, websocket_message_type::text_message);
}
/// <summary>
/// Sets binary data as the message body.
/// </summary>
/// <param name="istream">casablanca input stream representing the body of the message.</param>
/// <param name="len">number of bytes to send.</param>
void set_binary_message(const concurrency::streams::istream& istream, size_t len)
{
this->set_message(istream, len, websocket_message_type::binary_message);
}
/// <summary>
/// Sets binary data as the message body.
/// </summary>
/// <param name="istream">Input stream representing the body of the message.</param>
/// <remarks>Upon sending, the entire stream may be buffered to determine the length.</remarks>
void set_binary_message(const concurrency::streams::istream& istream)
{
this->set_message(istream, SIZE_MAX, websocket_message_type::binary_message);
}
private:
friend class details::winrt_callback_client;
friend class details::wspp_callback_client;
pplx::task_completion_event<void> m_body_sent;
concurrency::streams::streambuf<uint8_t> m_body;
websocket_message_type m_msg_type;
size_t m_length;
void signal_body_sent() const { m_body_sent.set(); }
void signal_body_sent(const std::exception_ptr& e) const { m_body_sent.set_exception(e); }
const pplx::task_completion_event<void>& body_sent() const { return m_body_sent; }
#if !defined(__cplusplus_winrt)
void set_message_pong()
{
concurrency::streams::container_buffer<std::string> buffer("");
m_msg_type = websocket_message_type::pong;
m_length = static_cast<size_t>(buffer.size());
m_body = buffer;
}
#endif
void set_message(const concurrency::streams::container_buffer<std::string>& buffer)
{
m_msg_type = websocket_message_type::text_message;
m_length = static_cast<size_t>(buffer.size());
m_body = buffer;
}
void set_message(const concurrency::streams::istream& istream, size_t len, websocket_message_type msg_type)
{
m_msg_type = msg_type;
m_length = len;
m_body = istream.streambuf();
}
};
/// <summary>
/// Represents an incoming websocket message
/// </summary>
class websocket_incoming_message
{
public:
/// <summary>
/// Extracts the body of the incoming message as a string value, only if the message type is UTF-8.
/// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
/// </summary>
/// <returns>String containing body of the message.</returns>
_ASYNCRTIMP pplx::task<std::string> extract_string() const;
/// <summary>
/// Produces a stream which the caller may use to retrieve body from an incoming message.
/// Can be used for both UTF-8 (text) and binary message types.
/// </summary>
/// <returns>A readable, open asynchronous stream.</returns>
/// <remarks>
/// This cannot be used in conjunction with any other means of getting the body of the message.
/// </remarks>
concurrency::streams::istream body() const
{
auto to_uint8_t_stream =
[](const concurrency::streams::streambuf<uint8_t>& buf) -> concurrency::streams::istream {
return buf.create_istream();
};
return to_uint8_t_stream(m_body);
}
/// <summary>
/// Returns the length of the received message.
/// </summary>
size_t length() const { return static_cast<size_t>(m_body.size()); }
/// <summary>
/// Returns the type of the received message.
/// </summary>
CASABLANCA_DEPRECATED("Incorrectly spelled API, use message_type() instead.")
websocket_message_type messge_type() const { return m_msg_type; }
/// <summary>
/// Returns the type of the received message, either string or binary.
/// </summary>
/// <returns>websocket_message_type</returns>
websocket_message_type message_type() const { return m_msg_type; }
private:
friend class details::winrt_callback_client;
friend class details::wspp_callback_client;
#if defined(__cplusplus_winrt)
friend ref class details::ReceiveContext;
#endif
// Store message body in a container buffer backed by a string.
// Allows for optimization in the string message cases.
concurrency::streams::container_buffer<std::string> m_body;
websocket_message_type m_msg_type;
};
} // namespace client
} // namespace websockets
} // namespace web
#endif

View File

@ -0,0 +1,211 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Parallel Patterns Library
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#ifndef _PPLX_H
#define _PPLX_H
#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) && !CPPREST_FORCE_PPLX
#error This file must not be included for Visual Studio 12 or later
#endif
#ifndef _WIN32
#if defined(_WIN32) || defined(__cplusplus_winrt)
#define _WIN32
#endif
#endif // _WIN32
#ifdef _NO_PPLXIMP
#define _PPLXIMP
#else
#ifdef _PPLX_EXPORT
#define _PPLXIMP __declspec(dllexport)
#else
#define _PPLXIMP __declspec(dllimport)
#endif
#endif
#include "cpprest/details/cpprest_compat.h"
// Use PPLx
#ifdef _WIN32
#include "pplx/pplxwin.h"
#elif defined(__APPLE__)
#undef _PPLXIMP
#define _PPLXIMP
#include "pplx/pplxlinux.h"
#else
#include "pplx/pplxlinux.h"
#endif // _WIN32
// Common implementation across all the non-concrt versions
#include "pplx/pplxcancellation_token.h"
#include <functional>
// conditional expression is constant
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4127)
#endif
#pragma pack(push, _CRT_PACKING)
/// <summary>
/// The <c>pplx</c> namespace provides classes and functions that give you access to the Concurrency Runtime,
/// a concurrent programming framework for C++. For more information, see <see cref="Concurrency Runtime"/>.
/// </summary>
/**/
namespace pplx
{
/// <summary>
/// Sets the ambient scheduler to be used by the PPL constructs.
/// </summary>
_PPLXIMP void _pplx_cdecl set_ambient_scheduler(std::shared_ptr<pplx::scheduler_interface> _Scheduler);
/// <summary>
/// Gets the ambient scheduler to be used by the PPL constructs
/// </summary>
_PPLXIMP std::shared_ptr<pplx::scheduler_interface> _pplx_cdecl get_ambient_scheduler();
namespace details
{
//
// An internal exception that is used for cancellation. Users do not "see" this exception except through the
// resulting stack unwind. This exception should never be intercepted by user code. It is intended
// for use by the runtime only.
//
class _Interruption_exception : public std::exception
{
public:
_Interruption_exception() {}
};
template<typename _T>
struct _AutoDeleter
{
_AutoDeleter(_T* _PPtr) : _Ptr(_PPtr) {}
~_AutoDeleter() { delete _Ptr; }
_T* _Ptr;
};
struct _TaskProcHandle
{
_TaskProcHandle() {}
virtual ~_TaskProcHandle() {}
virtual void invoke() const = 0;
static void _pplx_cdecl _RunChoreBridge(void* _Parameter)
{
auto _PTaskHandle = static_cast<_TaskProcHandle*>(_Parameter);
_AutoDeleter<_TaskProcHandle> _AutoDeleter(_PTaskHandle);
_PTaskHandle->invoke();
}
};
enum _TaskInliningMode
{
// Disable inline scheduling
_NoInline = 0,
// Let runtime decide whether to do inline scheduling or not
_DefaultAutoInline = 16,
// Always do inline scheduling
_ForceInline = -1,
};
// This is an abstraction that is built on top of the scheduler to provide these additional functionalities
// - Ability to wait on a work item
// - Ability to cancel a work item
// - Ability to inline work on invocation of RunAndWait
class _TaskCollectionImpl
{
public:
typedef _TaskProcHandle _TaskProcHandle_t;
_TaskCollectionImpl(scheduler_ptr _PScheduler) : _M_pScheduler(_PScheduler) {}
void _ScheduleTask(_TaskProcHandle_t* _PTaskHandle, _TaskInliningMode _InliningMode)
{
if (_InliningMode == _ForceInline)
{
_TaskProcHandle_t::_RunChoreBridge(_PTaskHandle);
}
else
{
_M_pScheduler->schedule(_TaskProcHandle_t::_RunChoreBridge, _PTaskHandle);
}
}
void _Cancel()
{
// No cancellation support
}
void _RunAndWait()
{
// No inlining support yet
_Wait();
}
void _Wait() { _M_Completed.wait(); }
void _Complete() { _M_Completed.set(); }
scheduler_ptr _GetScheduler() const { return _M_pScheduler; }
// Fire and forget
static void _RunTask(TaskProc_t _Proc, void* _Parameter, _TaskInliningMode _InliningMode)
{
if (_InliningMode == _ForceInline)
{
_Proc(_Parameter);
}
else
{
// Schedule the work on the ambient scheduler
get_ambient_scheduler()->schedule(_Proc, _Parameter);
}
}
static bool _pplx_cdecl _Is_cancellation_requested()
{
// We do not yet have the ability to determine the current task. So return false always
return false;
}
private:
extensibility::event_t _M_Completed;
scheduler_ptr _M_pScheduler;
};
// For create_async lambdas that return a (non-task) result, we oversubscriber the current task for the duration of the
// lambda.
struct _Task_generator_oversubscriber
{
};
typedef _TaskCollectionImpl _TaskCollection_t;
typedef _TaskInliningMode _TaskInliningMode_t;
typedef _Task_generator_oversubscriber _Task_generator_oversubscriber_t;
} // namespace details
} // namespace pplx
#pragma pack(pop)
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
#endif // _PPLX_H

Some files were not shown because too many files have changed in this diff Show More